diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml new file mode 100644 index 00000000..0adb0ce5 --- /dev/null +++ b/.github/workflows/gh-pages.yml @@ -0,0 +1,29 @@ +name: Publish Docs + +on: + push: + branches: + - master + pull_request: + +jobs: + gh-pages: + runs-on: ubuntu-20.04 + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + steps: + - uses: actions/checkout@v2 + + - name: Setup mdBook + uses: peaceiris/actions-mdbook@v2 + with: + mdbook-version: 'latest' + + - run: mdbook build + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + if: ${{ github.ref == 'refs/heads/master' }} + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./book diff --git a/.gitignore b/.gitignore index b5562bfd..4c317ad7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ tmp/ dist/ +book/ # Packages packaging/pypi/lefthook/__pycache__/ diff --git a/book.toml b/book.toml new file mode 100644 index 00000000..470eba59 --- /dev/null +++ b/book.toml @@ -0,0 +1,12 @@ +[book] +authors = ["Evil Martians"] +language = "en" +multilingual = false +src = "docs/mdbook" +title = "Lefthook" + +[output.html] +no-section-label = true + +[output.html.fold] +enable = true diff --git a/docs/mdbook/SUMMARY.md b/docs/mdbook/SUMMARY.md new file mode 100644 index 00000000..53902c8c --- /dev/null +++ b/docs/mdbook/SUMMARY.md @@ -0,0 +1,75 @@ +# Lefthook + +# User guide + +- [Installation](./install.md) + - [Ruby](./installation/ruby.md) + - [Node.js](./installation/node.md) + - [Go](./installation/go.md) + - [Python](./installation/python.md) + - [Swift](./installation/swift.md) + - [Scoop](./installation/scoop.md) + - [Homebrew](./installation/homebrew.md) + - [Winget](./installation/winget.md) + - [Snap](./installation/snap.md) + - [Debian-based distro](./installation/deb.md) + - [RPM-based distro](./installation/rpm.md) + - [Alpine](./installation/alpine.md) + - [Arch Linux](./installation/arch.md) + - [Manual](./installation/manual.md) +- [Configuration](./configuration/README.md) + - [`assert_lefthook_installed`](./configuration/assert_lefthook_installed.md) + - [`colors`](./configuration/colors.md) + - [`no_tty`](./configuration/no_tty.md) + - [`extends`](./configuration/extends.md) + - [`min_version`](./configuration/min_version.md) + - [`output`](./configuration/output.md) + - [`skip_output`](./configuration/skip_output.md) + - [`source_dir`](./configuration/source_dir.md) + - [`source_dir_local`](./configuration/source_dir_local.md) + - [`rc`](./configuration/rc.md) + - [`remotes`](./configuration/remotes.md) + - [`git_url`](./configuration/git_url.md) + - [`ref`](./configuration/ref.md) + - [`refetch`](./configuration/refetch.md) + - [`refetch_frequency`](./configuration/refetch_frequency.md) + - [`configs`](./configuration/configs.md) + - [Git hook](./configuration/Hook.md) + - [`files`](./configuration/files-global.md) + - [`parallel`](./configuration/parallel.md) + - [`piped`](./configuration/piped.md) + - [`follow`](./configuration/follow.md) + - [`exclude_tags`](./configuration/exclude_tags.md) + - [`skip`](./configuration/skip.md) + - [`only`](./configuration/only.md) + - [`commands`](./configuration/Commands.md) + - [`run`](./configuration/run.md) + - [`skip`](./configuration/skip.md) + - [`only`](./configuration/only.md) + - [`tags`](./configuration/tags.md) + - [`glob`](./configuration/glob.md) + - [`files`](./configuration/files.md) + - [`file_types`](./configuration/file_types.md) + - [`env`](./configuration/env.md) + - [`root`](./configuration/root.md) + - [`exclude`](./configuration/exclude.md) + - [`fail_text`](./configuration/fail_text.md) + - [`stage_fixed`](./configuration/stage_fixed.md) + - [`interactive`](./configuration/interactive.md) + - [`use_stdin`](./configuration/use_stdin.md) + - [`priority`](./configuration/priority.md) + - [`scripts`](./configuration/Scripts.md) + - [`runner`](./configuration/runner.md) + - [`skip`](./configuration/skip.md) + - [`only`](./configuration/only.md) + - [`tags`](./configuration/tags.md) + - [`env`](./configuration/env.md) + - [`fail_text`](./configuration/fail_text.md) + - [`stage_fixed`](./configuration/stage_fixed.md) + - [`interactive`](./configuration/interactive.md) + - [`use_stdin`](./configuration/use_stdin.md) + - [`priority`](./configuration/priority.md) +- [Usage](./usage.md) + - [Commands](./usage/commands.md) + - [ENV variables](./usage/env.md) + - [Tips](./usage/tips.md) diff --git a/docs/mdbook/configuration/Commands.md b/docs/mdbook/configuration/Commands.md new file mode 100644 index 00000000..ef4c216e --- /dev/null +++ b/docs/mdbook/configuration/Commands.md @@ -0,0 +1,32 @@ +### `commands` + +Commands to be executed for the hook. Each command has a name and associated run [options](#command). + +**Example** + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + ... # command options +``` + +### Command options + +- [`run`](./run.md) +- [`skip`](./skip.md) +- [`only`](./only.md) +- [`tags`](./tags.md) +- [`glob`](./glob.md) +- [`files`](./files.md) +- [`file_types`](./file_types.md) +- [`env`](./env.md) +- [`root`](./root.md) +- [`exclude`](./exclude.md) +- [`fail_text`](./fail_text.md) +- [`stage_fixed`](./stage_fixed.md) +- [`interactive`](./interactive.md) +- [`use_stdin`](./use_stdin.md) +- [`priority`](./priority.md) diff --git a/docs/mdbook/configuration/Hook.md b/docs/mdbook/configuration/Hook.md new file mode 100644 index 00000000..edfe7f67 --- /dev/null +++ b/docs/mdbook/configuration/Hook.md @@ -0,0 +1,40 @@ +## Git hook + +Contains settings for the git hook (commands, scripts, skip rules, etc.). You can specify any Git hook or your own custom, e.g. `test` + +### Hook options + +- [`files`](./files-global.md) +- [`parallel`](./parallel.md) +- [`piped`](./piped.md) +- [`follow`](./follow.md) +- [`exclude_tags`](./exclude_tags.md) +- [`skip`](./skip.md) +- [`only`](./only.md) +- [`commands`](./Commands.md) + - [`run`](./run.md) + - [`skip`](./skip.md) + - [`only`](./only.md) + - [`tags`](./tags.md) + - [`glob`](./glob.md) + - [`files`](./files.md) + - [`file_types`](./file_types.md) + - [`env`](./env.md) + - [`root`](./root.md) + - [`exclude`](./exclude.md) + - [`fail_text`](./fail_text.md) + - [`stage_fixed`](./stage_fixed.md) + - [`interactive`](./interactive.md) + - [`use_stdin`](./use_stdin.md) + - [`priority`](./priority.md) +- [`scripts`](./Scripts.md) + - [`runner`](./runner.md) + - [`skip`](./skip.md) + - [`only`](./only.md) + - [`tags`](./tags.md) + - [`env`](./env.md) + - [`fail_text`](./fail_text.md) + - [`stage_fixed`](./stage_fixed.md) + - [`interactive`](./interactive.md) + - [`use_stdin`](./use_stdin.md) + - [`priority`](./priority.md) diff --git a/docs/mdbook/configuration/README.md b/docs/mdbook/configuration/README.md new file mode 100644 index 00000000..c7095ea4 --- /dev/null +++ b/docs/mdbook/configuration/README.md @@ -0,0 +1,53 @@ +# Config options + +- [`assert_lefthook_installed`](./assert_lefthook_installed.md) +- [`colors`](./colors.md) +- [`no_tty`](./no_tty.md) +- [`extends`](./extends.md) +- [`min_version`](./min_version.md) +- [`output`](./output.md) +- [`skip_output`](./skip_output.md) +- [`source_dir`](./source_dir.md) +- [`source_dir_local`](./source_dir_local.md) +- [`rc`](./rc.md) +- [`remotes`](./remotes.md) + - [`git_url`](./git_url.md) + - [`ref`](./ref.md) + - [`refetch`](./refetch.md) + - [`refetch_frequency`](./refetch_frequency.md) + - [`configs`](./configs.md) +- [Git hook](./Hook.md) + - [`files` (global)](./files-global.md) + - [`parallel`](./parallel.md) + - [`piped`](./piped.md) + - [`follow`](./follow.md) + - [`exclude_tags`](./exclude_tags.md) + - [`skip`](./skip.md) + - [`only`](./only.md) + - [`commands`](./Commands.md) + - [`run`](./run.md) + - [`skip`](./skip.md) + - [`only`](./only.md) + - [`tags`](./tags.md) + - [`glob`](./glob.md) + - [`files`](./files.md) + - [`file_types`](./file_types.md) + - [`env`](./env.md) + - [`root`](./root.md) + - [`exclude`](./exclude.md) + - [`fail_text`](./fail_text.md) + - [`stage_fixed`](./stage_fixed.md) + - [`interactive`](./interactive.md) + - [`use_stdin`](./use_stdin.md) + - [`priority`](./priority.md) + - [`scripts`](./Scripts.md) + - [`runner`](./runner.md) + - [`skip`](./skip.md) + - [`only`](./only.md) + - [`tags`](./tags.md) + - [`env`](./env.md) + - [`fail_text`](./fail_text.md) + - [`stage_fixed`](./stage_fixed.md) + - [`interactive`](./interactive.md) + - [`use_stdin`](./use_stdin.md) + - [`priority`](./priority.md) diff --git a/docs/mdbook/configuration/Scripts.md b/docs/mdbook/configuration/Scripts.md new file mode 100644 index 00000000..1e45a6ef --- /dev/null +++ b/docs/mdbook/configuration/Scripts.md @@ -0,0 +1,58 @@ +## Scripts + +Scripts are stored under `//` folder. These scripts are your own executables which are being run in the project root. + +To add a script for a `pre-commit` hook: + +1. Run `lefthook add -d pre-commit` +1. Edit `.lefthook/pre-commit/my-script.sh` +1. Add an entry to `lefthook.yml` + ```yml + # lefthook.yml + + pre-commit: + scripts: + "my-script.sh": + runner: bash + ``` + +**Example** + +Let's create a bash script to check commit templates `.lefthook/commit-msg/template_checker`: + +```bash +INPUT_FILE=$1 +START_LINE=`head -n1 $INPUT_FILE` +PATTERN="^(TICKET)-[[:digit:]]+: " +if ! [[ "$START_LINE" =~ $PATTERN ]]; then + echo "Bad commit message, see example: TICKET-123: some text" + exit 1 +fi +``` + +Now we can ask lefthook to run our bash script by adding this code to +`lefthook.yml` file: + +```yml +# lefthook.yml + +commit-msg: + scripts: + "template_checker": + runner: bash +``` + +When you try to commit `git commit -m "bad commit text"` script `template_checker` will be executed. Since commit text doesn't match the described pattern the commit process will be interrupted. + +### Script options + +- [`runner`](./runner.md) +- [`skip`](./skip.md) +- [`only`](./only.md) +- [`tags`](./tags.md) +- [`env`](./env.md) +- [`fail_text`](./fail_text.md) +- [`stage_fixed`](./stage_fixed.md) +- [`interactive`](./interactive.md) +- [`use_stdin`](./use_stdin.md) +- [`priority`](./priority.md) diff --git a/docs/mdbook/configuration/assert_lefthook_installed.md b/docs/mdbook/configuration/assert_lefthook_installed.md new file mode 100644 index 00000000..ace5a136 --- /dev/null +++ b/docs/mdbook/configuration/assert_lefthook_installed.md @@ -0,0 +1,5 @@ +# `assert_lefthook_installed` + +**Default: `false`** + +When set to `true`, fail (with exit status 1) if `lefthook` executable can't be found in $PATH, under node_modules/, as a Ruby gem, or other supported method. This makes sure git hook won't omit `lefthook` rules if `lefthook` ever was installed. diff --git a/docs/mdbook/configuration/colors.md b/docs/mdbook/configuration/colors.md new file mode 100644 index 00000000..204828b8 --- /dev/null +++ b/docs/mdbook/configuration/colors.md @@ -0,0 +1,29 @@ +### `colors` + +**Default: `auto`** + +Whether enable or disable colorful output of Lefthook. This option can be overwritten with `--colors` option. You can also provide your own color codes. + +**Example** + +Disable colors. + +```yml +# lefthook.yml + +colors: false +``` + +Custom color codes. Can be hex or ANSI codes. + +```yml +# lefthook.yml + +colors: + cyan: 14 + gray: 244 + green: '#32CD32' + red: '#FF1493' + yellow: '#F0E68C' +``` + diff --git a/docs/mdbook/configuration/configs.md b/docs/mdbook/configuration/configs.md new file mode 100644 index 00000000..2b93172c --- /dev/null +++ b/docs/mdbook/configuration/configs.md @@ -0,0 +1,40 @@ +### `configs` + +**Default:** `[lefthook.yml]` + +An optional array of config paths from remote's root. + +**Example** + +```yml +# lefthook.yml + +remotes: + - git_url: git@github.com:evilmartians/lefthook + ref: v1.0.0 + configs: + - examples/ruby-linter.yml + - examples/test.yml +``` + +Example with multiple remotes merging multiple configurations. + +```yml +# lefthook.yml + +remotes: + - git_url: git@github.com:org/lefthook-configs + ref: v1.0.0 + configs: + - examples/ruby-linter.yml + - examples/test.yml + - git_url: https://github.com/org2/lefthook-configs + configs: + - lefthooks/pre_commit.yml + - lefthooks/post_merge.yml + - git_url: https://github.com/org3/lefthook-configs + ref: feature/new + configs: + - configs/pre-push.yml + +``` diff --git a/docs/mdbook/configuration/env.md b/docs/mdbook/configuration/env.md new file mode 100644 index 00000000..3b1aa88b --- /dev/null +++ b/docs/mdbook/configuration/env.md @@ -0,0 +1,43 @@ +### `env` + +You can specify some ENV variables for the command or script. + +**Example** + +```yml +# lefthook.yml + +pre-commit: + commands: + test: + env: + RAILS_ENV: test + run: bundle exec rspec +``` + +#### Extending PATH + +If your hook is run by GUI program, and you use some PATH tweaks in your ~/.rc, you might see an error saying *executable not found*. In that case You can extend the **$PATH** variable with `lefthook-local.yml` configuration the following way. + +```yml +# lefthook.yml + +pre-commit: + commands: + test: + run: yarn test +``` + +```yml +# lefthook-local.yml + +pre-commit: + commands: + test: + env: + PATH: $PATH:/home/me/path/to/yarn +``` + +**Notes** + +This option is useful when using lefthook on different OSes or shells where ENV variables are set in different ways. diff --git a/docs/mdbook/configuration/exclude.md b/docs/mdbook/configuration/exclude.md new file mode 100644 index 00000000..ed714563 --- /dev/null +++ b/docs/mdbook/configuration/exclude.md @@ -0,0 +1,61 @@ +### `exclude` + +For the `exclude` option two variants are supported: + +- A list of globs to be excluded +- A single regular expression (deprecated) + + +> NOTE +> +> The regular expression is matched against full paths to files in the repo, +> relative to the repo root, using `/` as the directory separator on all platforms. +> File paths do not begin with the separator or any other prefix. + +**Example** + +Run Rubocop on staged files with `.rb` extension except for `application.rb`, `routes.rb`, `rails_helper.rb`, and all Ruby files in `config/initializers/`. + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + glob: "*.rb" + exclude: + - config/routes.rb + - config/application.rb + - config/initializers/*.rb + - spec/rails_helper.rb + run: bundle exec rubocop --force-exclusion {staged_files} +``` + +The same example using a regular expression. + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + glob: "*.rb" + exclude: '(^|/)(application|routes|rails_helper|initializers/\w+)\.rb$' + run: bundle exec rubocop --force-exclusion {staged_files} +``` + +**Notes** + +Be careful with the config file format's string quoting and escaping rules when writing regexps in it. For YAML, single quotes are often the simplest choice. + +If you've specified `exclude` but don't have a files template in [`run`](./run.md) option, lefthook will check `{staged_files}` for `pre-commit` hook and `{push_files}` for `pre-push` hook and apply filtering. If no files left, the command will be skipped. + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + exclude: '(^|/)application\.rb$' + run: bundle exec rubocop # skipped if only application.rb was staged +``` diff --git a/docs/mdbook/configuration/exclude_tags.md b/docs/mdbook/configuration/exclude_tags.md new file mode 100644 index 00000000..671c639d --- /dev/null +++ b/docs/mdbook/configuration/exclude_tags.md @@ -0,0 +1,56 @@ +### `exclude_tags` + +[Tags](./tags.md) or command names that you want to exclude. This option can be overwritten with `LEFTHOOK_EXCLUDE` env variable. + +**Example** + +```yml +# lefthook.yml + +pre-commit: + exclude_tags: frontend + commands: + lint: + tags: frontend + ... + test: + tags: frontend + ... + check-syntax: + tags: documentation +``` + +```bash +lefthook run pre-commit # will only run check-syntax command +``` + +**Notes** + +This option is good to specify in `lefthook-local.yml` when you want to skip some execution locally. + +```yml +# lefthook.yml + +pre-push: + commands: + packages-audit: + tags: + - frontend + - security + run: yarn audit + gems-audit: + tags: + - backend + - security + run: bundle audit +``` + +You can skip commands by tags: + +```yml +# lefthook-local.yml + +pre-push: + exclude_tags: + - frontend +``` diff --git a/docs/mdbook/configuration/extends.md b/docs/mdbook/configuration/extends.md new file mode 100644 index 00000000..9b66f4f3 --- /dev/null +++ b/docs/mdbook/configuration/extends.md @@ -0,0 +1,29 @@ +### `extends` + +You can extend your config with another one YAML file. Its content will be merged. Extends for `lefthook.yml`, `lefthook-local.yml`, and [`remotes`](./remotes.md) configs are handled separately, so you can have different extends in these files. + +You can use asterisk to make a glob. + +**Example** + +```yml +# lefthook.yml + +extends: + - /home/user/work/lefthook-extend.yml + - /home/user/work/lefthook-extend-2.yml + - lefthook-extends/file.yml + - ../extend.yml + - projects/*/specific-lefthook-config.yml +``` + +> The extends will be merged to the main configuration in your file. Here is the order of settings applied: +> +> - `lefthook.yml` – main config file +> - `extends` – configs specified in [extends](./extends.md) option +> - `remotes` – configs specified in [remotes](./remotes.md) option +> - `lefthook-local.yml` – local config file +> +> So, `extends` override settings from `lefthook.yml`, `remotes` override `extends`, and `lefthook-local.yml` can override everything. + + diff --git a/docs/mdbook/configuration/fail_text.md b/docs/mdbook/configuration/fail_text.md new file mode 100644 index 00000000..3173762f --- /dev/null +++ b/docs/mdbook/configuration/fail_text.md @@ -0,0 +1,27 @@ +### `fail_text` + +You can specify a text to show when the command or script fails. + +**Example** + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + run: yarn lint + fail_text: Add node executable to $PATH +``` + +```bash +$ git commit -m 'fix: Some bug' + +Lefthook v1.1.3 +RUNNING HOOK: pre-commit + + EXECUTE > lint + +SUMMARY: (done in 0.01 seconds) +🥊 lint: Add node executable to $PATH env +``` diff --git a/docs/mdbook/configuration/file_types.md b/docs/mdbook/configuration/file_types.md new file mode 100644 index 00000000..7f5f1c76 --- /dev/null +++ b/docs/mdbook/configuration/file_types.md @@ -0,0 +1,60 @@ +### `file_types` + +Filter files in a [`run`](./run.md) templates by their type. Supported types: + +|File type| Exlanation| +|---------|-----------| +|`text` | Any file that contains text. Symlinks are not followed. | +|`binary` | Any file that contains non-text bytes. Symlinks are not followed. | +|`executable` | Any file that has executable bits set. Symlinks are not followed. | +|`not executable` | Any file without executable bits in file mode. Symlinks included. | +|`symlink` | A symlink file. | +|`not symlink` | Any non-symlink file. | + +> IMPORTANT +> +> When passed multiple file types all constraints will be applied to the resulting list of files. + +**Examples** + +Apply some different linters on text and binary files. + +```yml +# lefthook.yml + +pre-commit: + commands: + lint-code: + run: yarn lint {staged_files} + file_types: text + check-hex-codes: + run: yarn check-hex {staged_files} + file_types: binary +``` + +Skip symlinks. + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + run: yarn lint --fix {staged_files} + file_types: + - not symlink +``` + +Lint executable scripts. + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + run: yarn lint --fix {staged_files} + file_types: + - executable + - text +``` diff --git a/docs/mdbook/configuration/files-global.md b/docs/mdbook/configuration/files-global.md new file mode 100644 index 00000000..edc60e19 --- /dev/null +++ b/docs/mdbook/configuration/files-global.md @@ -0,0 +1,16 @@ +### `files` (global) + +A custom git command for files to be referenced in `{files}` template. See [`run`](#run) and [`files`](#files). + +If the result of this command is empty, the execution of commands will be skipped. + +**Example** + +```yml +# lefthook.yml + +pre-commit: + files: git diff --name-only master # custom list of files + commands: + ... +``` diff --git a/docs/mdbook/configuration/files.md b/docs/mdbook/configuration/files.md new file mode 100644 index 00000000..490b3763 --- /dev/null +++ b/docs/mdbook/configuration/files.md @@ -0,0 +1,39 @@ +### `files` + +A custom git command for files or directories to be referenced in `{files}` template for [`run`](./run.md) setting. + +If the result of this command is empty, the execution of commands will be skipped. + +This option overwrites the [hook-level `files`](./files-global.md) option. + +**Example** + +Provide a git command to list files. + +```yml +# lefthook.yml + +pre-push: + commands: + stylelint: + tags: + - frontend + - style + files: git diff --name-only master + glob: "*.js" + run: yarn stylelint {files} +``` + +Call a custom script for listing files. + +```yml +# lefthook.yml + +pre-push: + commands: + rubocop: + tags: backend + glob: "**/*.rb" + files: node ./lefthook-scripts/ls-files.js # you can call your own scripts + run: bundle exec rubocop --force-exclusion --parallel {files} +``` diff --git a/docs/mdbook/configuration/follow.md b/docs/mdbook/configuration/follow.md new file mode 100644 index 00000000..85338784 --- /dev/null +++ b/docs/mdbook/configuration/follow.md @@ -0,0 +1,23 @@ +### `follow` + +**Default: `false`** + +Follow the STDOUT of the running commands and scripts. + +**Example** + +```yml +# lefthook.yml + +pre-push: + follow: true + commands: + backend-tests: + run: bundle exec rspec + frontend-tests: + run: yarn test +``` + +> NOTE +> +> If used with [`parallel`](#parallel) the output can be a mess, so please avoid setting both options to `true`. diff --git a/docs/mdbook/configuration/git_url.md b/docs/mdbook/configuration/git_url.md new file mode 100644 index 00000000..9a45b7b7 --- /dev/null +++ b/docs/mdbook/configuration/git_url.md @@ -0,0 +1,21 @@ +### `git_url` + +A URL to Git repository. It will be accessed with privileges of the machine lefthook runs on. + +**Example** + +```yml +# lefthook.yml + +remotes: + - git_url: git@github.com:evilmartians/lefthook +``` + +Or + +```yml +# lefthook.yml + +remotes: + - git_url: https://github.com/evilmartians/lefthook +``` diff --git a/docs/mdbook/configuration/glob.md b/docs/mdbook/configuration/glob.md new file mode 100644 index 00000000..2e7a7004 --- /dev/null +++ b/docs/mdbook/configuration/glob.md @@ -0,0 +1,31 @@ +### `glob` + +You can set a glob to filter files for your command. This is only used if you use a file template in [`run`](./run.md) option or provide your custom [`files`](./files.md) command. + +**Example** + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + glob: "*.{js,ts,jsx,tsx}" + run: yarn eslint {staged_files} +``` + +**Notes** + +For patterns that you can use see [this](https://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm) reference. We use [glob](https://github.com/gobwas/glob) library. + +If you've specified `glob` but don't have a files template in [`run`](./run.md) option, lefthook will check `{staged_files}` for `pre-commit` hook and `{push_files}` for `pre-push` hook and apply filtering. If no files left, the command will be skipped. + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + glob: "*.js" + run: npm run lint # skipped if no .js files staged +``` diff --git a/docs/mdbook/configuration/interactive.md b/docs/mdbook/configuration/interactive.md new file mode 100644 index 00000000..de1e316c --- /dev/null +++ b/docs/mdbook/configuration/interactive.md @@ -0,0 +1,13 @@ +### `interactive` + +**Default: `false`** + +> NOTE +> +> If you want to pass stdin to your command or script but don't need to get the input from CLI, use [`use_stdin`](./use_stdin.md) option instead. + + +Whether to use interactive mode. This applies the certain behavior: +- All `interactive` commands/scripts are executed after non-interactive. Exception: [`piped`](./piped.md) option is set to `true`. +- When executing, lefthook tries to open /dev/tty (Linux/Unix only) and use it as stdin. +- When [`no_tty`](./no_tty.md) option is set, `interactive` is ignored. diff --git a/docs/mdbook/configuration/min_version.md b/docs/mdbook/configuration/min_version.md new file mode 100644 index 00000000..8de390ce --- /dev/null +++ b/docs/mdbook/configuration/min_version.md @@ -0,0 +1,11 @@ +### `min_version` + +If you want to specify a minimum version for lefthook binary (e.g. if you need some features older versions don't have) you can set this option. + +**Example** + +```yml +# lefthook.yml + +min_version: 1.1.3 +``` diff --git a/docs/mdbook/configuration/no_tty.md b/docs/mdbook/configuration/no_tty.md new file mode 100644 index 00000000..470eb034 --- /dev/null +++ b/docs/mdbook/configuration/no_tty.md @@ -0,0 +1,13 @@ +### `no_tty` + +**Default: `false`** + +Whether hide spinner and other interactive things. This can be also controlled with `--no-tty` option for `lefthook run` command. + +**Example** + +```yml +# lefthook.yml + +no_tty: true +``` diff --git a/docs/mdbook/configuration/only.md b/docs/mdbook/configuration/only.md new file mode 100644 index 00000000..b833c7bd --- /dev/null +++ b/docs/mdbook/configuration/only.md @@ -0,0 +1,42 @@ +### `only` + +You can force a command, script, or the whole hook to execute only in certain conditions. This option acts like the opposite of [`skip`](./skip.md). It accepts the same values but skips execution only if the condition is not satisfied. + +> NOTE +> +> `skip` option takes precedence over `only` option, so if you have conflicting conditions the execution will be skipped. + +**Example** + +Execute a hook only for `dev/*` branches. + +```yml +# lefthook.yml + +pre-commit: + only: + - ref: dev/* + commands: + lint: + run: yarn lint + test: + run: yarn test +``` + +When rebasing execute quick linter but skip usual linter and tests. + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + skip: rebase + run: yarn lint + test: + skip: rebase + run: yarn test + lint-on-rebase: + only: rebase + run: yarn lint-quickly +``` diff --git a/docs/mdbook/configuration/output.md b/docs/mdbook/configuration/output.md new file mode 100644 index 00000000..66967219 --- /dev/null +++ b/docs/mdbook/configuration/output.md @@ -0,0 +1,35 @@ +### `output` + +You can manage verbosity using the `output` config. You can specify what to print in your output by setting these values, which you need to have + +Possible values are `meta,summary,success,failure,execution,execution_out,execution_info,skips`. +By default, all output values are enabled + +You can also disable all output with setting `output: false`. In this case only errors will be printed. + +This config quiets all outputs except for errors. + +`output` is enabled if there is no `skip_output` and `LEFTHOOK_QUIET`. + +**Example** + +```yml +# lefthook.yml + +output: + - meta # Print lefthook version + - summary # Print summary block (successful and failed steps) + - empty_summary # Print summary heading when there are no steps to run + - success # Print successful steps + - failure # Print failed steps printing + - execution # Print any execution logs (but prints if the execution failed) + - execution_out # Print execution output (but still prints failed commands output) + - execution_info # Print `EXECUTE > ...` logging + - skips # Print "skip" (i.e. no files matched) +``` + +You can also *extend* this list with an environment variable `LEFTHOOK_OUTPUT`: + +```bash +LEFTHOOK_OUTPUT="meta,success,summary" lefthook run pre-commit +``` diff --git a/docs/mdbook/configuration/parallel.md b/docs/mdbook/configuration/parallel.md new file mode 100644 index 00000000..c549cb26 --- /dev/null +++ b/docs/mdbook/configuration/parallel.md @@ -0,0 +1,9 @@ +### `parallel` + +**Default: `false`** + +> NOTE +> +> Lefthook runs commands and scripts **sequentially** by default. + +Run commands and scripts concurrently. diff --git a/docs/mdbook/configuration/piped.md b/docs/mdbook/configuration/piped.md new file mode 100644 index 00000000..e660d7f3 --- /dev/null +++ b/docs/mdbook/configuration/piped.md @@ -0,0 +1,25 @@ +### `piped` + +**Default: `false`** + +> NOTE +> +> Lefthook will return an error if both `piped: true` and `parallel: true` are set. + +Stop running commands and scripts if one of them fail. + +**Example** + +```yml +# lefthook.yml + +database: + piped: true # Stop if one of the steps fail + commands: + 1_create: + run: rake db:create + 2_migrate: + run: rake db:migrate + 3_seed: + run: rake db:seed +``` diff --git a/docs/mdbook/configuration/priority.md b/docs/mdbook/configuration/priority.md new file mode 100644 index 00000000..763d5ba5 --- /dev/null +++ b/docs/mdbook/configuration/priority.md @@ -0,0 +1,38 @@ +### `priority` + +**Default: `0`** + +> NOTE +> +> This option makes sense only when `parallel: false` or `piped: true` is set. +> +> Value `0` is considered an `+Infinity`, so commands or scripts with `priority: 0` or without this setting will be run at the very end. + +Set priority from 1 to +Infinity. This option can be used to configure the order of the sequential steps. + +**Example** + +```yml +# lefthook.yml + +post-checkout: + piped: true + commands: + db-create: + priority: 1 + run: rails db:create + db-migrate: + priority: 2 + run: rails db:migrate + db-seed: + priority: 3 + run: rails db:seed + + scripts: + "check-spelling.sh": + runner: bash + priority: 1 + "check-grammar.rb": + runner: ruby + priority: 2 +``` diff --git a/docs/mdbook/configuration/rc.md b/docs/mdbook/configuration/rc.md new file mode 100644 index 00000000..3d9c5c8f --- /dev/null +++ b/docs/mdbook/configuration/rc.md @@ -0,0 +1,71 @@ +### `rc` + +Provide an [**rc**](https://www.baeldung.com/linux/rc-files) file, which is actually a simple `sh` script. Currently it can be used to set ENV variables that are not accessible from non-shell programs. + +**Example** + +Use cases: + +- You have a GUI program that runs git hooks (e.g., VSCode) +- You reference executables that are accessible only from a tweaked $PATH environment variable (e.g., when using rbenv or nvm, fnm) +- Or even if your GUI program cannot locate the `lefthook` executable :scream: +- Or if you want to use ENV variables that control the executables behavior in `lefthook.yml` + +```bash +# An npm executable which is managed by nvm +$ which npm +/home/user/.nvm/versions/node/v15.14.0/bin/npm +``` + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + run: npm run eslint {staged_files} +``` + +Provide a tweak to access `npm` executable the same way you do it in your ~/rc. + +```yml +# lefthook-local.yml + +# You can choose whatever name you want. +# You can share it between projects where you use lefthook. +# Make sure the path is absolute. +rc: ~/.lefthookrc +``` + +Or + +```yml +# lefthook-local.yml + +# If the path contains spaces, you need to quote it. +rc: '"${XDG_CONFIG_HOME:-$HOME/.config}/lefthookrc"' +``` + +In the rc file, export any new environment variables or modify existing ones. + +```bash +# ~/.lefthookrc + +# An nvm way +export NVM_DIR="$HOME/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + +# An fnm way +export FNM_DIR="$HOME/.fnm" +[ -s "$FNM_DIR/fnm.sh" ] && \. "$FNM_DIR/fnm.sh" + +# Or maybe just +PATH=$PATH:$HOME/.nvm/versions/node/v15.14.0/bin +``` + +```bash +# Make sure you updated git hooks. This is important. +$ lefthook install -f +``` + +Now any program that runs your hooks will have a tweaked PATH environment variable and will be able to get `nvm` :wink: diff --git a/docs/mdbook/configuration/ref.md b/docs/mdbook/configuration/ref.md new file mode 100644 index 00000000..b797a66b --- /dev/null +++ b/docs/mdbook/configuration/ref.md @@ -0,0 +1,17 @@ +### `ref` + +An optional *branch* or *tag* name. + +> NOTE +> +> If you initially had `ref` option, ran `lefthook install`, and then removed it, lefthook won't decide which branch/tag to use as a ref. So, if you added it once, please, use it always to avoid issues in local setups. + +**Example** + +```yml +# lefthook.yml + +remotes: + - git_url: git@github.com:evilmartians/lefthook + ref: v1.0.0 +``` diff --git a/docs/mdbook/configuration/refetch.md b/docs/mdbook/configuration/refetch.md new file mode 100644 index 00000000..cdf45d38 --- /dev/null +++ b/docs/mdbook/configuration/refetch.md @@ -0,0 +1,15 @@ +### `refetch` + +**Default:** `false` + +Force remote config refetching on every run. Lefthook will be refetching the specified remote every time it is called. + +**Example** + +```yml +# lefthook.yml + +remotes: + - git_url: https://github.com/evilmartians/lefthook + refetch: true +``` diff --git a/docs/mdbook/configuration/refetch_frequency.md b/docs/mdbook/configuration/refetch_frequency.md new file mode 100644 index 00000000..b6e10009 --- /dev/null +++ b/docs/mdbook/configuration/refetch_frequency.md @@ -0,0 +1,23 @@ +### `refetch_frequency` + +**Default:** Not set + +Specifies how frequently Lefthook should refetch the remote configuration. This can be set to `always`, `never` or a time duration like `24h`, `30m`, etc. + +- When set to `always`, Lefthook will always refetch the remote configuration on each run. +- When set to a duration (e.g., `24h`), Lefthook will check the last fetch time and refetch the configuration only if the specified amount of time has passed. +- When set to `never` or not set, Lefthook will not fetch from remote. + +**Example** + +```yml +# lefthook.yml + +remotes: + - git_url: https://github.com/evilmartians/lefthook + refetch_frequency: 24h # Refetches once every 24 hours +``` + +> WARNING +> If `refetch` is set to `true`, it overrides any setting in `refetch_frequency`. + diff --git a/docs/mdbook/configuration/remotes.md b/docs/mdbook/configuration/remotes.md new file mode 100644 index 00000000..d66d46b5 --- /dev/null +++ b/docs/mdbook/configuration/remotes.md @@ -0,0 +1,17 @@ +## `remotes` + +You can provide multiple remote configs if you want to share yours lefthook configurations across many projects. Lefthook will automatically download and merge configurations into your local `lefthook.yml`. + +You can use [`extends`](./extends.md) but the paths must be relative to the remote repository root. + +If you provide [`scripts`](./scripts.md) in a remote config file, the [scripts](./source_dir.md) folder must also be in the **root of the repository**. + +**Note** + +The configuration from `remotes` will be merged to the local config using the following priority: + +1. Local main config (`lefthook.yml`) +1. Remote configs (`remotes`) +1. Local overrides (`lefthook-local.yml`) + +This priority may be changed in the future. For convenience, if you use `remotes`, please don't configure any hooks. diff --git a/docs/mdbook/configuration/root.md b/docs/mdbook/configuration/root.md new file mode 100644 index 00000000..8d038e5f --- /dev/null +++ b/docs/mdbook/configuration/root.md @@ -0,0 +1,35 @@ +### `root` + +You can change the CWD for the command you execute using `root` option. + +This is useful when you execute some `npm` or `yarn` command but the `package.json` is in another directory. + +For `pre-push` and `pre-commit` hooks and for the custom `files` command `root` option is used to filter file paths. If all files are filtered the command will be skipped. + +**Example** + +Format and stage files from a `client/` folder. + +```bash +# Folders structure + +$ tree . +. +├── client/ +│ ├── package.json +│ ├── node_modules/ +| ├── ... +├── server/ +| ... +``` + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + root: "client/" + glob: "*.{js,ts}" + run: yarn eslint --fix {staged_files} && git add {staged_files} +``` diff --git a/docs/mdbook/configuration/run.md b/docs/mdbook/configuration/run.md new file mode 100644 index 00000000..05c9c486 --- /dev/null +++ b/docs/mdbook/configuration/run.md @@ -0,0 +1,163 @@ +### `run` + +This is a mandatory option for a command. This is actually a command that is executed for the hook. + +You can use files templates that will be substituted with the appropriate files on execution: + +- `{files}` - custom [`files`](./files.md) command result. +- `{staged_files}` - staged files which you try to commit. +- `{push_files}` - files that are committed but not pushed. +- `{all_files}` - all files tracked by git. +- `{cmd}` - shorthand for the command from `lefthook.yml`. +- `{0}` - shorthand for the single space-joint string of git hook arguments. +- `{N}` - shorthand for the N-th git hook argument. + +**Example** + +Run `yarn lint` on `pre-commit` hook. + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + run: yarn lint +``` + +#### `{files}` template + +Run `go vet` only on files listed with `git ls-files -m` command with `.go` extension. + +```yml +# lefthook.yml + +pre-commit: + commands: + govet: + files: git ls-files -m + glob: "*.go" + run: go vet {files} +``` + +#### `{staged_files}` template + +Run `yarn eslint` only on staged files with `.js`, `.ts`, `.jsx`, and `.tsx` extensions. + +```yml +# lefthook.yml + +pre-commit: + commands: + eslint: + glob: "*.{js,ts,jsx,tsx}" + run: yarn eslint {staged_files} +``` + +#### `{push_files}` template + +If you want to lint files only before pushing them. + +```yml +# lefthook.yml + +pre-push: + commands: + eslint: + glob: "*.{js,ts,jsx,tsx}" + run: yarn eslint {push_files} +``` + +#### `{all_files}` template + +Simply run `bundle exec rubocop` on all files with `.rb` extension excluding `application.rb` and `routes.rb` files. + +> NOTE +> +> `--force-exclusion` will apply `Exclude` configuration setting of Rubocop. + +```yml +# lefthook.yml + +pre-commit: + commands: + rubocop: + tags: + - backend + - style + glob: "*.rb" + exclude: + - config/application.rb + - config/routes.rb + run: bundle exec rubocop --force-exclusion {all_files} +``` + +#### `{cmd}` template + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + run: yarn lint + scripts: + "good_job.js": + runner: node +``` + +You can wrap it in docker runner locally: + +```yml +# lefthook-local.yml + +pre-commit: + commands: + lint: + run: docker run -it --rm {cmd} + scripts: + "good_job.js": + runner: docker run -it --rm {cmd} +``` + +#### Git arguments + +Make sure commits are signed. + +```yml +# lefthook.yml + +# Note: commit-msg hook takes a single parameter, +# the name of the file that holds the proposed commit log message. +# Source: https://git-scm.com/docs/githooks#_commit_msg +commit-msg: + commands: + multiple-sign-off: + run: 'test $(grep -c "^Signed-off-by: " {1}) -lt 2' +``` + +#### Rubocop + +If using `{all_files}` with RuboCop, it will ignore RuboCop's `Exclude` configuration setting. To avoid this, pass `--force-exclusion`. + +#### Quotes + +If you want to have all your files quoted with double quotes `"` or single quotes `'`, quote the appropriate shorthand: + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + glob: "*.js" + # Quoting with double quotes `"` might be helpful for Windows users + run: yarn eslint "{staged_files}" # will run `yarn eslint "file1.js" "file2.js" "[strange name].js"` + test: + glob: "*.{spec.js}" + run: yarn test '{staged_files}' # will run `yarn eslint 'file1.spec.js' 'file2.spec.js' '[strange name].spec.js'` + format: + glob: "*.js" + # Will quote where needed with single quotes + run: yarn test {staged_files} # will run `yarn eslint file1.js file2.js '[strange name].spec.js'` +``` diff --git a/docs/mdbook/configuration/runner.md b/docs/mdbook/configuration/runner.md new file mode 100644 index 00000000..23b3245a --- /dev/null +++ b/docs/mdbook/configuration/runner.md @@ -0,0 +1,16 @@ +### `runner` + +You should specify a runner for the script. This is a command that should execute a script file. It will be called the following way: ` ` (e.g. `ruby .lefthook/pre-commit/lint.rb`). + +**Example** + +```yml +# lefthook.yml + +pre-commit: + scripts: + "lint.js": + runner: node + "check.go": + runner: go run +``` diff --git a/docs/mdbook/configuration/skip.md b/docs/mdbook/configuration/skip.md new file mode 100644 index 00000000..d10d6673 --- /dev/null +++ b/docs/mdbook/configuration/skip.md @@ -0,0 +1,129 @@ +### `skip` + +You can skip all or specific commands and scripts using `skip` option. You can also skip when merging, rebasing, or being on a specific branch. Globs are available for branches. + +Possible skip values: +- `rebase` - when in rebase git state +- `merge` - when in merge git state +- `merge-commit` - when current HEAD commit is the merge commit +- `ref: main` - when on a `main` branch +- `run: test ${SKIP_ME} -eq 1` - when `test ${SKIP_ME} -eq 1` is successful (return code is 0) + +**Example** + +Always skipping a command: + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + skip: true + run: yarn lint +``` + +Skipping on merging and rebasing: + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + skip: + - merge + - rebase + run: yarn lint +``` + +Or + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + skip: merge + run: yarn lint +``` + +Skipping when your are on a merge commit: + +```yml +# lefthook.yml + +pre-push: + commands: + lint: + skip: merge-commit + run: yarn lint +``` + +Skipping the whole hook on `main` branch: + +```yml +# lefthook.yml + +pre-commit: + skip: + - ref: main + commands: + lint: + run: yarn lint + test: + run: yarn test +``` + +Skipping hook for all `dev/*` branches: + +```yml +# lefthook.yml + +pre-commit: + skip: + - ref: dev/* + commands: + lint: + run: yarn lint + test: + run: yarn test +``` + +Skipping hook by running a command: + +```yml +# lefthook.yml + +pre-commit: + skip: + - run: test "${NO_HOOK}" -eq 1 + commands: + lint: + run: yarn lint + test: + run: yarn test +``` + +> TIP +> +> Always skipping is useful when you have a `lefthook-local.yml` config and you don't want to run some commands locally. So you just overwrite the `skip` option for them to be `true`. +> +> ```yml +> # lefthook.yml +> +> pre-commit: +> commands: +> lint: +> run: yarn lint +> ``` +> +> ```yml +> # lefthook-local.yml +> +> pre-commit: +> commands: +> lint: +> skip: true +> ``` diff --git a/docs/mdbook/configuration/skip_output.md b/docs/mdbook/configuration/skip_output.md new file mode 100644 index 00000000..2c775c89 --- /dev/null +++ b/docs/mdbook/configuration/skip_output.md @@ -0,0 +1,35 @@ +### `skip_output` + +> **DEPRECATED** This feature is deprecated and might be removed in future versions. Please, use `[output]` instead for managing verbosity. + +You can manage the verbosity using the `skip_output` config. You can set whether lefthook should print some parts of its output. + +Possible values are `meta,summary,success,failure,execution,execution_out,execution_info,skips`. + +You can also disable all output with setting `skip_output: true`. In this case only errors will be printed. + +This config quiets all outputs except for errors. + +**Example** + +```yml +# lefthook.yml + +skip_output: + - meta # Skips lefthook version printing + - summary # Skips summary block (successful and failed steps) printing + - empty_summary # Skips summary heading when there are no steps to run + - success # Skips successful steps printing + - failure # Skips failed steps printing + - execution # Skips printing any execution logs (but prints if the execution failed) + - execution_out # Skips printing execution output (but still prints failed commands output) + - execution_info # Skips printing `EXECUTE > ...` logging + - skips # Skips "skip" printing (i.e. no files matched) +``` + +You can also *extend* this list with an environment variable `LEFTHOOK_QUIET`: + +```bash +LEFTHOOK_QUIET="meta,success,summary" lefthook run pre-commit +``` + diff --git a/docs/mdbook/configuration/source_dir.md b/docs/mdbook/configuration/source_dir.md new file mode 100644 index 00000000..4c20fd60 --- /dev/null +++ b/docs/mdbook/configuration/source_dir.md @@ -0,0 +1,17 @@ +### `source_dir` + +**Default: `.lefthook/`** + +Change a directory for script files. Directory for script files contains folders with git hook names which contain script files. + +Example of directory tree: + +``` +.lefthook/ +├── pre-commit/ +│ ├── lint.sh +│ └── test.py +└── pre-push/ + └── check-files.rb +``` + diff --git a/docs/mdbook/configuration/source_dir_local.md b/docs/mdbook/configuration/source_dir_local.md new file mode 100644 index 00000000..415b1356 --- /dev/null +++ b/docs/mdbook/configuration/source_dir_local.md @@ -0,0 +1,8 @@ +### `source_dir_local` + +**Default: `.lefthook-local/`** + +Change a directory for *local* script files (not stored in VCS). + +This option is useful if you have a `lefthook-local.yml` config file and want to reference different scripts there. + diff --git a/docs/mdbook/configuration/stage_fixed.md b/docs/mdbook/configuration/stage_fixed.md new file mode 100644 index 00000000..17ef7c38 --- /dev/null +++ b/docs/mdbook/configuration/stage_fixed.md @@ -0,0 +1,20 @@ +### `stage_fixed` + +**Default: `false`** + +> Used **only for `pre-commit`** hook. Is ignored for other hooks. + +When set to `true` lefthook will automatically call `git add` on files after running the command or script. For a command if [`files`](./files.md) option was specified, the specified command will be used to retrieve files for `git add`. For scripts and commands without [`files`](./files.md) option `{staged_files}` template will be used. All filters ([`glob`](./glob.md), [`exclude`](./exclude.md)) will be applied if specified. + +**Example** + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + run: npm run lint --fix {staged_files} + stage_fixed: true +``` + diff --git a/docs/mdbook/configuration/tags.md b/docs/mdbook/configuration/tags.md new file mode 100644 index 00000000..5d8c4212 --- /dev/null +++ b/docs/mdbook/configuration/tags.md @@ -0,0 +1,22 @@ +### `tags` + +You can specify tags for commands and scripts. This is useful for [excluding](./exclude_tags.md). You can specify more than one tag using comma. + +**Example** + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + tags: + - frontend + - js + run: yarn lint + test: + tags: + - backend + - ruby + run: bundle exec rspec +``` diff --git a/docs/mdbook/configuration/use_stdin.md b/docs/mdbook/configuration/use_stdin.md new file mode 100644 index 00000000..17753d32 --- /dev/null +++ b/docs/mdbook/configuration/use_stdin.md @@ -0,0 +1,31 @@ +### `use_stdin` + +> NOTE +> +> With many commands or scripts having `use_stdin: true`, only one will receive the data. The others will have nothing. If you need to pass the data from stdin to every command or script, please, submit a [feature request](https://github.com/evilmartians/lefthook/issues/new?assignees=&labels=feature+request&projects=&template=feature_request.md). + +Pass the stdin from the OS to the command/script. + +**Example** + +Use this option for the `pre-push` hook when you have a script that does `while read ...`. Without this option lefthook will hang: lefthook uses [pseudo TTY](https://github.com/creack/pty) by default, and it doesn't close stdin when all data is read. + +```bash +# .lefthook/pre-push/do-the-magic.sh + +remote="$1" +url="$2" + +while read local_ref local_oid remote_ref remote_oid; do + # ... +done +``` + +```yml +# lefthook.yml +pre-push: + scripts: + "do-the-magic.sh": + runner: bash + use_stdin: true +``` diff --git a/docs/mdbook/file_name.md b/docs/mdbook/file_name.md new file mode 100644 index 00000000..c4b88492 --- /dev/null +++ b/docs/mdbook/file_name.md @@ -0,0 +1,17 @@ +## Config file name + +Lefthook supports the following file names for the main config: + +- `lefthook.yml` +- `.lefthook.yml` +- `lefthook.yaml` +- `.lefthook.yaml` +- `lefthook.toml` +- `.lefthook.toml` +- `lefthook.json` +- `.lefthook.json` + +If there are more than 1 file in the project, only one will be used, and you'll never know which one. So, please, use one format in a project. + +Lefthook also merges an extra config with the name `lefthook-local`. All supported formats can be applied to this `-local` config. If you name your main config with the leading dot, like `.lefthook.json`, the `-local` config also must be named with the leading dot: `.lefthook-local.json`. + diff --git a/docs/mdbook/install.md b/docs/mdbook/install.md new file mode 100644 index 00000000..ff935837 --- /dev/null +++ b/docs/mdbook/install.md @@ -0,0 +1,20 @@ +# Install lefthook + +Choose your fighter: + +- [Ruby](./installation/ruby.md) +- [Node.js](./installation/node.md) +- [Go](./installation/go.md) +- [Python](./installation/python.md) +- [Swift](./installation/swift.md) +- [Scoop](./installation/scoop.md) +- [Homebrew](./installation/homebrew.md) +- [Winget](./installation/winget.md) +- [Snap](./installation/snap.md) +- [Debian-based distro](./installation/deb.md) +- [RPM-based distro](./installation/rpm.md) +- [Alpine](./installation/alpine.md) +- [Arch Linux](./installation/arch.md) +- [Manual](./installation/manual.md) + +Have a question? – Start a [discussion](https://github.com/evilmartians/lefthook/discussions) diff --git a/docs/mdbook/installation/alpine.md b/docs/mdbook/installation/alpine.md new file mode 100644 index 00000000..d8ac9ca0 --- /dev/null +++ b/docs/mdbook/installation/alpine.md @@ -0,0 +1,11 @@ +## APK packages for Alpine + +```sh +sudo apk add --no-cache bash curl +curl -1sLf 'https://dl.cloudsmith.io/public/evilmartians/lefthook/setup.alpine.sh' | sudo -E bash +sudo apk add lefthook +``` + +See all instructions: https://cloudsmith.io/~evilmartians/repos/lefthook/setup/#formats-alpine + +[![Hosted By: Cloudsmith](https://img.shields.io/badge/OSS%20hosting%20by-cloudsmith-blue?logo=cloudsmith&style=flat-square)](https://cloudsmith.com "RPM package repository hosting is graciously provided by Cloudsmith") diff --git a/docs/mdbook/installation/arch.md b/docs/mdbook/installation/arch.md new file mode 100644 index 00000000..27aa4599 --- /dev/null +++ b/docs/mdbook/installation/arch.md @@ -0,0 +1,7 @@ +## AUR for Arch + +You can install lefthook [package](https://aur.archlinux.org/packages/lefthook) from AUR. + +```sh +yay -S lefthook +``` diff --git a/docs/mdbook/installation/deb.md b/docs/mdbook/installation/deb.md new file mode 100644 index 00000000..bc781ab2 --- /dev/null +++ b/docs/mdbook/installation/deb.md @@ -0,0 +1,10 @@ +## APT packages for Debian/Ubuntu Linux + +```sh +curl -1sLf 'https://dl.cloudsmith.io/public/evilmartians/lefthook/setup.deb.sh' | sudo -E bash +sudo apt install lefthook +``` +See all instructions: https://cloudsmith.io/~evilmartians/repos/lefthook/setup/#formats-deb + +[![Hosted By: Cloudsmith](https://img.shields.io/badge/OSS%20hosting%20by-cloudsmith-blue?logo=cloudsmith&style=flat-square)](https://cloudsmith.com "Debian package repository hosting is graciously provided by Cloudsmith") + diff --git a/docs/mdbook/installation/go.md b/docs/mdbook/installation/go.md new file mode 100644 index 00000000..47a37804 --- /dev/null +++ b/docs/mdbook/installation/go.md @@ -0,0 +1,5 @@ +## Go + +```bash +go install github.com/evilmartians/lefthook@latest +``` diff --git a/docs/mdbook/installation/homebrew.md b/docs/mdbook/installation/homebrew.md new file mode 100644 index 00000000..dff5bc8d --- /dev/null +++ b/docs/mdbook/installation/homebrew.md @@ -0,0 +1,5 @@ +## Homebrew for MacOS and Linux + +```bash +brew install lefthook +``` diff --git a/docs/mdbook/installation/manual.md b/docs/mdbook/installation/manual.md new file mode 100644 index 00000000..656a97c1 --- /dev/null +++ b/docs/mdbook/installation/manual.md @@ -0,0 +1,6 @@ +## Manuall installation with prebuilt executable + +Or take it from [binaries](https://github.com/evilmartians/lefthook/releases) and install manually. + +1. Download the executable for your OS and Arch +1. Put the executable under the $PATH (for unix systems) diff --git a/docs/mdbook/installation/node.md b/docs/mdbook/installation/node.md new file mode 100644 index 00000000..a17c482c --- /dev/null +++ b/docs/mdbook/installation/node.md @@ -0,0 +1,30 @@ +## Node.js + +Lefthook is available on NPM in the following flavors: + + 1. [lefthook](https://www.npmjs.com/package/lefthook) that will install the proper binary: + + ```bash + npm install lefthook --save-dev + # or yarn: + yarn add -D lefthook + ``` + + 1. [@evilmartians/lefthook](https://www.npmjs.com/package/@evilmartians/lefthook) with pre-bundled binaries for all architectures: + + ```bash + npm install @evilmartians/lefthook --save-dev + # or yarn: + yarn add -D @evilmartians/lefthook + ``` + + 1. [@evilmartians/lefthook-installer](https://www.npmjs.com/package/@evilmartians/lefthook-installer) that will fetch binary file on installation: + + ```bash + npm install @evilmartians/lefthook-installer --save-dev + # or yarn: + yarn add -D @evilmartians/lefthook-installer + ``` + +> NOTE +> If you use `pnpm` package manager make sure you set `side-effects-cache = false` in your .npmrc, otherwise the postinstall script of the lefthook package won't be executed and hooks won't be installed. diff --git a/docs/mdbook/installation/python.md b/docs/mdbook/installation/python.md new file mode 100644 index 00000000..befca5e5 --- /dev/null +++ b/docs/mdbook/installation/python.md @@ -0,0 +1,5 @@ +## Python + +```sh +python -m pip install --user lefthook +``` diff --git a/docs/mdbook/installation/rpm.md b/docs/mdbook/installation/rpm.md new file mode 100644 index 00000000..289aac28 --- /dev/null +++ b/docs/mdbook/installation/rpm.md @@ -0,0 +1,10 @@ +## RPM packages for CentOS/Fedora Linux + +```sh +curl -1sLf 'https://dl.cloudsmith.io/public/evilmartians/lefthook/setup.rpm.sh' | sudo -E bash +sudo yum install lefthook +``` + +See all instructions: https://cloudsmith.io/~evilmartians/repos/lefthook/setup/#repository-setup-yum + +[![Hosted By: Cloudsmith](https://img.shields.io/badge/OSS%20hosting%20by-cloudsmith-blue?logo=cloudsmith&style=flat-square)](https://cloudsmith.com "RPM package repository hosting is graciously provided by Cloudsmith") diff --git a/docs/mdbook/installation/rpn.md b/docs/mdbook/installation/rpn.md new file mode 100644 index 00000000..86cc6209 --- /dev/null +++ b/docs/mdbook/installation/rpn.md @@ -0,0 +1 @@ +# RPM-based distro diff --git a/docs/mdbook/installation/ruby.md b/docs/mdbook/installation/ruby.md new file mode 100644 index 00000000..b5414608 --- /dev/null +++ b/docs/mdbook/installation/ruby.md @@ -0,0 +1,9 @@ +## Ruby + +```bash +gem install lefthook +``` + +**Troubleshooting** + +If you see the error `lefthook: command not found` you need to check your $PATH. Also try to restart your terminal. diff --git a/docs/mdbook/installation/scoop.md b/docs/mdbook/installation/scoop.md new file mode 100644 index 00000000..baca59a6 --- /dev/null +++ b/docs/mdbook/installation/scoop.md @@ -0,0 +1,5 @@ +## Scoop for Windows + +```sh +scoop install lefthook +``` diff --git a/docs/mdbook/installation/snap.md b/docs/mdbook/installation/snap.md new file mode 100644 index 00000000..f05cda70 --- /dev/null +++ b/docs/mdbook/installation/snap.md @@ -0,0 +1,5 @@ +## Snap for Linux + +```sh +snap install --classic lefthook +``` diff --git a/docs/mdbook/installation/swift.md b/docs/mdbook/installation/swift.md new file mode 100644 index 00000000..47ce4327 --- /dev/null +++ b/docs/mdbook/installation/swift.md @@ -0,0 +1,15 @@ +## Swift + +You can find the Swift wrapper plugin [here](https://github.com/csjones/lefthook-plugin). + +Utilize lefthook in your Swift project using Swift Package Manager: + +```swift +.package(url: "https://github.com/csjones/lefthook-plugin.git", exact: "1.9.2"), +``` + +Or, with [mint](https://github.com/yonaskolb/Mint): + +```bash +mint run csjones/lefthook-plugin +``` diff --git a/docs/mdbook/installation/winget.md b/docs/mdbook/installation/winget.md new file mode 100644 index 00000000..22b0a0ca --- /dev/null +++ b/docs/mdbook/installation/winget.md @@ -0,0 +1,5 @@ +## Winget for Windows + +```sh +winget install evilmartians.lefthook +``` diff --git a/docs/mdbook/usage.md b/docs/mdbook/usage.md new file mode 100644 index 00000000..253db533 --- /dev/null +++ b/docs/mdbook/usage.md @@ -0,0 +1,28 @@ +# Usage + +- [Commands](./usage/commands.md) + - [`lefthook install`](./usage/commands.md#lefthook-install) + - [`lefthook uninstall`](./usage/commands.md#lefthook-uninstall) + - [`lefthook add`](./usage/commands.md#lefthook-add) + - [`lefthook run`](./usage/commands.md#lefthook-run) + - [`lefthook version`](./usage/commands.md#lefthook-version) + - [`lefthook self-update`](./usage/commands.md#lefthook-self-update) +- [ENV variables](./usage/env.md) + - [`LEFTHOOK`](./usage/env.md#lefthook) + - [`LEFTHOOK_EXCLUDE`](./usage/env.md#lefthook_exclude) + - [`LEFTHOOK_OUTPUT`](./usage/env.md#lefthook_output) + - [`LEFTHOOK_QUIET`](./usage/env.md#lefthook_quiet) + - [`LEFTHOOK_VERBOSE`](./usage/env.md#lefthook_verbose) + - [`LEFTHOOK_BIN`](./usage/env.md#lefthook_bin) + - [`NO_COLOR`](./usage/env.md#no_color) + - [`CLICOLOR_FORCE`](./usage/env.md#clicolor_force) +- [Tips](./usage/tips.md) + - [Local config](./usage/tips.md#local-config) + - [Disable lefthook in CI](./usage/tips.md#disable-lefthook-in-ci) + - [Commitlint example](./usage/tips.md#commitlint-example) + - [Parallel execution](./usage/tips.md#parallel-execution) + - [Concurrent files overrides](./usage/tips.md#concurrent-files-overrides) + - [Capture ARGS from git in the script](./usage/tips.md#capture-args-from-git-in-the-script) + - [Git LFS support](./usage/tips.md#git-lfs-support) + - [Pass stdin to a command or script](./usage/tips.md#pass-stdin-to-a-command-or-script) + - [Using an interactive command or script](./usage/tips.md#using-an-interactive-command-or-script) diff --git a/docs/mdbook/usage/commands.md b/docs/mdbook/usage/commands.md new file mode 100644 index 00000000..ed2971d7 --- /dev/null +++ b/docs/mdbook/usage/commands.md @@ -0,0 +1,121 @@ +## Commands + +Use `lefthook help` or `lefthook -h/--help` to discover available commands and their options. + +### `lefthook install` + +`lefthook install` creates an empty `lefthook.yml` if a configuration file does not exist and updates git hooks to use lefthook. Run `lefthook install` after cloning the git repo. + +> NOTE +> +> NPM package `lefthook` installs the hooks in a postinstall script automatically. + +### `lefthook uninstall` + +`lefthook uninstall` clears git hooks affected by lefthook. If you have lefthook installed as an NPM package you should remove it manually. + +### `lefthook add` + +`lefthook add pre-commit` will create a file `.git/hooks/pre-commit`. This is the same lefthook does for [`install`](#lefthook-install) command but you don't need to create a configuration first. + +To use custom scripts as hooks create the required directories with `lefthook add pre-commit --dirs`. + +**Example** + +```bash +$ lefthook add pre-push --dirs +``` + +Describe pre-push commands in `lefthook.yml`: + +```yml +pre-push: + scripts: + "audit.sh": + runner: bash +``` + +Edit the script: + +```bash +$ vim .lefthook/pre-push/audit.sh +... +``` + +Run `git push` and lefthook will run `bash audit.sh` as a pre-push hook. + +### `lefthook run` + +`lefthook run` executes the commands and scripts configured for a given hook. Generated hooks call `lefthook run` implicitly. + +**Example** + +```yml +# lefthook.yml + +pre-commit: + commands: + lint: + run: yarn lint --fix + +test: + commands: + js-test: + run: yarn test +``` + +Install the hook. + +```bash +$ lefthook install +``` + +Run `test`. + +```bash +$ lefthook run test # will run 'yarn test' +``` + +Commit changes. + +```bash +$ git commit # will run pre-commit hook ('yarn lint --fix') +``` + +Or run manually also + +```bash +$ lefthook run pre-commit +``` + +You can also specify a flag to run only some commands: + +```bash +$ lefthook run pre-commit --commands lint +``` + +and optionally run either on all files (any `{staged_files}` placeholder acts as `{all_files}`) or a list of files: + +```bash +$ lefthook run pre-commit --all-files +$ lefthook run pre-commit --file file1.js --file file2.js +``` + +(if both are specified, `--all-files` is ignored) + +### `lefthook version` + +`lefthook version` prints the current binary version. Print the commit hash with `lefthook version --full` + +**Example** + +```bash +$ lefthook version --full + +1.1.3 bb099d13c24114d2859815d9d23671a32932ffe2 +``` + +### `lefthook self-update` + +`lefthook self-update` updates the binary with the latest lefthook release on Github. This command is available only if you install lefthook from sources or download the binary from the Github Releases. For other ways use package-specific commands to update lefthook. + diff --git a/docs/mdbook/usage/env.md b/docs/mdbook/usage/env.md new file mode 100644 index 00000000..b8c469ec --- /dev/null +++ b/docs/mdbook/usage/env.md @@ -0,0 +1,70 @@ +## ENV variables + +### `LEFTHOOK` + +Use `LEFTHOOK=0 git ...` or `LEFTHOOK=false git ...` to disable lefthook when running git commands. + +**Example** + +```bash +LEFTHOOK=0 git commit -am "Lefthook skipped" +``` + +### `LEFTHOOK_EXCLUDE` + +Use `LEFTHOOK_EXCLUDE={list of tags or command names to be excluded}` to skip some commands or scripts by tag or name (for commands only). See the [`exclude_tags`](../configuration/exclude_tags.md) configuration option for more details. + +**Example** + +```bash +LEFTHOOK_EXCLUDE=ruby,security,lint git commit -am "Skip some tag checks" +``` + +### `LEFTHOOK_OUTPUT` + +Use `LEFTHOOK_OUTPUT={list of output values}` to specify what to print in your output. You can also set `LEFTHOOK_OUTPUT=false` to disable all output except for errors. Refer to the [`output`](../configuration/output.md) configuration option for more details. + +**Example** + +```bash +$ LEFTHOOK_OUTPUT=summary lefthook run pre-commit +summary: (done in 0.52 seconds) +✔️ lint +``` + +### `LEFTHOOK_QUIET` + +You can skip some outputs printed by lefthook by setting the `LEFTHOOK_QUIET` environment variable. Provide a list of output types to be skipped. See the [`skip_output`](../configuration/skip_output.md) configuration option for more details. + +**Example** + +```bash +$ LEFTHOOK_QUIET=meta,execution lefthook run pre-commit + + EXECUTE > lint + +SUMMARY: (done in 0.01 seconds) +🥊 lint +``` + +### `LEFTHOOK_VERBOSE` + +Set `LEFTHOOK_VERBOSE=1` or `LEFTHOOK_VERBOSE=true` to enable verbose printing. + +### `LEFTHOOK_BIN` + +Set `LEFTHOOK_BIN` to a location where lefthook is installed to use that instead of trying to detect from the it the PATH or from a package manager. + +Useful for cases when: + +- lefthook is installed multiple ways, and you want to be explicit about which one is used (example: installed through homebrew, but also is in Gemfile but you are using a ruby version manager like rbenv that prepends it to the path) +- debugging and/or developing lefthook + +### `NO_COLOR` + +Set `NO_COLOR=true` to disable colored output in lefthook and all subcommands that lefthook calls. + +### `CLICOLOR_FORCE` + +Set `CLICOLOR_FORCE=true` to force colored output in lefthook and all subcommands. + diff --git a/docs/mdbook/usage/tips.md b/docs/mdbook/usage/tips.md new file mode 100644 index 00000000..02f64f53 --- /dev/null +++ b/docs/mdbook/usage/tips.md @@ -0,0 +1,128 @@ +## Tips + +### Local config + +Use `lefthook-local.yml` to overwrite or extend options from the main config. (Don't forget to add this file to `.gitignore`) + +### Disable lefthook in CI + +When using NPM package `lefthook` set `CI=true` in your CI (if it does not set automatically). When `CI=true` is set lefthook NPM package won't install the hooks in the postinstall script. + +### Commitlint example + +Let's create a bash script to check conventional commit status `.lefthook/commit-msg/commitlint.sh`: + +```bash +echo $(head -n1 $1) | npx commitlint --color +``` + +Now we can ask lefthook to run our bash script by adding this code to +`lefthook.yml` file: + +```yml +# lefthook.yml + +commit-msg: + scripts: + "commitlint.sh": + runner: bash +``` + +When you try to commit `git commit -m "haha bad commit text"` script `commitlint.sh` will be executed. Since commit text doesn't match the default config or custom config that you setup for `commitlint`, the process will be interrupted. + +### Parallel execution + +You can enable parallel execution if you want to speed up your checks. +Lets imagine we have the following rules to lint the whole project: + +``` +bundle exec rubocop --parallel && \ +bundle exec danger && \ +yarn eslint --ext .es6 app/assets/javascripts && \ +yarn eslint --ext .es6 test/javascripts && \ +yarn eslint --ext .es6 plugins/**/assets/javascripts && \ +yarn eslint --ext .es6 plugins/**/test/javascripts && \ +yarn eslint app/assets/javascripts test/javascripts +``` + +Rewrite it in lefthook custom group. We call it `lint`: + +```yml +# lefthook.yml + +lint: + parallel: true + commands: + rubocop: + run: bundle exec rubocop --parallel + danger: + run: bundle exec danger + eslint-assets: + run: yarn eslint --ext .es6 app/assets/javascripts + eslint-test: + run: yarn eslint --ext .es6 test/javascripts + eslint-plugins-assets: + run: yarn eslint --ext .es6 plugins/**/assets/javascripts + eslint-plugins-test: + run: yarn eslint --ext .es6 plugins/**/test/javascripts + eslint-assets-tests: + run: yarn eslint app/assets/javascripts test/javascripts +``` + +Then call this group directly: + +``` +lefthook run lint +``` + +### Concurrent files overrides + +To prevent concurrent problems with read/write files try `flock` +utility. + +```yml +# lefthook.yml + +graphql-schema: + glob: "{Gemfile.lock,app/graphql/**/*}" + run: flock webpack/application/typings/graphql-schema.json yarn typings:update && git diff --exit-code --stat HEAD webpack/application/typings +frontend-tests: + glob: "**/*.js" + run: flock -s webpack/application/typings/graphql-schema.json yarn test --findRelatedTests {files} +frontend-typings: + glob: "**/*.js" + run: flock -s webpack/application/typings/graphql-schema.json yarn run flow focus-check {files} +``` + +### Capture ARGS from git in the script + +Example script for `prepare-commit-msg` hook: + +```bash +COMMIT_MSG_FILE=$1 +COMMIT_SOURCE=$2 +SHA1=$3 + +# ... +``` + +### Git LFS support + +> If git-lfs binary is not installed and not required in your project, LFS hooks won't be executed, and you won't be warned about it. + +Lefthook runs LFS hooks internally for the following hooks: + +- post-checkout +- post-commit +- post-merge +- pre-push + +Errors are suppressed if git LFS is not required for the project. You can use [`LEFTHOOK_VERBOSE`](./env.md#lefthook_verbose) ENV to make lefthook show git LFS output. + +### Pass stdin to a command or script + +When you need to read the data from stdin – specify [`use_stdin: true`](../configuration/use_stdin.md). This option is good when you write a command or script that receives data from git using stdin (for the `pre-push` hook, for example). + +### Using an interactive command or script + +When you need to interact with user – specify [`interactive: true`](../configuration/interactive.md). Lefthook will connect to the current TTY and forward it to your command's or script's stdin.