# Lefthook - Full Context > Generated by docmd --- ## [ai](https://lefthook.dev/configuration/ai/) --- title: "ai" --- # `ai` πŸ§ͺ (beta) > This is a beta feature and still in development. Declare LLM agent hooks directly in `lefthook.yml`. During `lefthook install`, lefthook generates the provider-specific settings file so that the agent calls `lefthook run ` when the event fires. Each sub-key is a provider name. Its value is a map from the provider's **event name** to a **lefthook hook name** defined elsewhere in the same config. ## Supported providers | Provider | Generated file | Docs | |---|---|---| | `claude` | `.claude/settings.json` | [Claude Code hooks](https://code.claude.com/docs/en/hooks.md) | | `codex` | `.codex/hooks.json` | [Codex CLI hooks](https://developers.openai.com/codex/hooks) | | `cursor` | `.cursor/hooks.json` | [Cursor hooks](https://cursor.com/docs/agent/hooks) | | `copilot` | `.github/hooks/lefthook.json` | [GitHub Copilot hooks](https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/use-hooks) | Keys under each provider must be that provider's hook event names. See the provider's hooks documentation for the supported events and their behaviour. ## Install and uninstall behaviour Claude, Codex, and Cursor preserve user-authored entries in their settings files. On `lefthook install`, old lefthook-managed entries are replaced with fresh ones derived from the current config. On `lefthook uninstall`, lefthook-managed entries are stripped while user-authored entries stay intact. Copilot is handled differently: `lefthook install` rewrites `.github/hooks/lefthook.json` from scratch, and `lefthook uninstall` removes that file entirely. Generated hook commands use the `lefthook` config value when set, otherwise the absolute path of the lefthook binary that ran `install` (via `os.Executable()`), so AI tools do not depend on `lefthook` being on `PATH`. ## Example ```yml # lefthook.yml ai: claude: Stop: validate PreToolUse: security-check codex: Stop: validate cursor: stop: validate preToolUse: security-check copilot: postToolUse: validate validate: jobs: - run: go test ./... security-check: jobs: - run: ./scripts/security.sh ``` Running `lefthook install` creates (or updates) `.claude/settings.json`: ```json { "hooks": { "Stop": [ { "hooks": [ { "type": "command", "command": "lefthook run validate" } ] } ], "PreToolUse": [ { "hooks": [ { "type": "command", "command": "lefthook run security-check" } ] } ] } } ``` And `.codex/hooks.json`: ```json { "hooks": { "Stop": [ { "hooks": [ { "type": "command", "command": "lefthook run validate" } ] } ] } } ``` And `.cursor/hooks.json`: ```json { "version": 1, "hooks": { "stop": [ { "command": "lefthook run validate" } ], "preToolUse": [ { "command": "lefthook run security-check" } ] } } ``` And `.github/hooks/lefthook.json`: ```json { "version": 1, "hooks": { "postToolUse": [ { "command": "lefthook run validate" } ] } } ``` --- ## [args](https://lefthook.dev/configuration/args/) --- title: "args" --- # `args` ::: callout tip New feature Added in lefthook `2.0.5` ::: Sometimes you want to pass arguments to the scripts or be able to overwrite arguments to the commands in `lefthook-local.yml`. For this you can use `args` option which will simply be appended to the command. You can use the same templates as in [`run`](./run.md). Arguments passed by Git will be omitted if you specify `args` in the config. Providing no `args` or providing `args: "{0}"` works the same way. See [`run`](./run.md) for supported templates. #### Example ```yml # lefthook.yml pre-commit: jobs: - script: check-python-files.sh runner: bash args: "{staged_files}" glob: "*.py" - run: yarn lint args: "{staged_files}" glob: - "*.ts" - "*.js" ``` --- ## [assert_lefthook_installed](https://lefthook.dev/configuration/assert_lefthook_installed/) --- title: "assert_lefthook_installed" --- # `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. #### Example ```yml # lefthook.yml assert_lefthook_installed: true ``` --- ## [colors](https://lefthook.dev/configuration/colors/) --- title: "colors" --- # `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' ``` Control via ENV variable. - Set `NO_COLOR=true` to disable colored output in lefthook and all subcommands that lefthook calls. - Set `CLICOLOR_FORCE=true` to force colored output in lefthook and all subcommands. --- ## [commands](https://lefthook.dev/configuration/Commands/) --- title: "commands" --- # `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) --- ## [configs](https://lefthook.dev/configuration/configs/) --- title: "configs" --- # `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 ``` --- ## [env](https://lefthook.dev/configuration/env/) --- title: "env" --- # `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 a GUI program and you use PATH tweaks in your `~/.rc`, you might see an *executable not found* error. You can extend `$PATH` via `lefthook-local.yml`: ```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 ``` ::: callout tip Useful when running lefthook across different OSes or shells where environment variables are set differently. ::: --- ## [exclude_tags](https://lefthook.dev/configuration/exclude_tags/) --- title: "exclude_tags" --- # `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 ``` ::: callout tip Useful in `lefthook-local.yml` to skip specific commands locally without modifying the shared config. ::: ```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 ``` --- ## [exclude](https://lefthook.dev/configuration/exclude/) --- title: "exclude" --- # `exclude` This option allows to setup a list of globs for files to be excluded in files template. ::: callout info Note The glob patterns used in `exclude` are affected by the [`glob_matcher`](./glob_matcher.md) setting. See the glob_matcher documentation for details on how `**` patterns behave. ::: #### 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: jobs: - name: 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} ``` 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: exclude: - "*/application.rb" jobs: - name: lint run: bundle exec rubocop # will skip if only application.rb was staged ``` --- ## [extends](https://lefthook.dev/configuration/extends/) --- title: "extends" --- # `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 ``` ::: callout info Note Settings are applied in this order: - `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. ::: --- ## [fail_on_changes_diff](https://lefthook.dev/configuration/fail_on_changes_diff/) --- title: "fail_on_changes_diff" --- # `fail_on_changes_diff` **Default:** outputs diff only in CI When [`fail_on_changes`](./fail_on_changes.md) triggers, lefthook can optionally print a diff of the detected changes. Set this boolean to explicitly enable or disable the diff output regardless of environment. #### Example ```yml # lefthook.yml pre-commit: parallel: true fail_on_changes: "always" fail_on_changes_diff: true commands: lint: run: yarn lint test: run: yarn test ``` --- ## [fail_on_changes](https://lefthook.dev/configuration/fail_on_changes/) --- title: "fail_on_changes" --- # `fail_on_changes` The behaviour of lefthook when files (tracked by git) are modified can set by modifying the `fail_on_changes` configuration parameter. The possible values are: - `never`: never exit with a non-zero status if files were modified (default). - `always`: always exit with a non-zero status if files were modified. - `ci`: exit with a non-zero status only when the `CI` environment variable is set. This can be useful when combined with `stage_fixed` to ensure a frictionless devX locally, and a robust CI. - `non-ci`: exit with a non-zero status only when the `CI` environment variable is _not_ set. This can be useful in setups where the CI pipeline commits changes automatically, such as [autofix.ci](https://autofix.ci). See also [`fail_on_changes_diff`](./fail_on_changes_diff.md). #### Example ```yml # lefthook.yml pre-commit: parallel: true fail_on_changes: "always" commands: lint: run: yarn lint test: run: yarn test ``` --- ## [fail_text](https://lefthook.dev/configuration/fail_text/) --- title: "fail_text" --- # `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 ``` --- ## [file_types](https://lefthook.dev/configuration/file_types/) --- title: "file_types" --- # `file_types` Filter files in a [`run`](./run.md) templates by their type. Special file types and MIME types are supported[^1]: |File type| Explanation| |---------|-----------| |`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. | |`text/html` | An HTML file. | |`text/xml` | An XML file. | |`text/javascript` | A Javascript file. | |`text/x-php` | A PHP file. | |`text/x-lua` | A Lua file. | |`text/x-perl` | A Perl file. | |`text/x-python` | A Python file. | |`text/x-shellscript` | Shell script file. | |`text/x-sh` | Also shell script file. | |`application/json` | JSON file. | ::: callout info Note The following types are applied using AND logic: `text`, `binary`, `executable`, `not executable`, `symlink`, `not symlink`. MIME types are applied using OR logic β€” you can combine `text/x-lua` and `text/x-sh`, but not `symlink` and `not symlink`. ::: #### Example 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 ``` Check typos in scripts. ```yml # lefthook.yml pre-commit: jobs: - run: typos -w -- {staged_files} file_types: - text/x-perl - text/x-python - text/x-php - text/x-lua - text/x-sh ``` [^1]: All supported MIME types can be found here: [supported_mimes.md](https://github.com/gabriel-vasile/mimetype/blob/v1.4.11/supported_mimes.md) --- ## [files (hook-level)](https://lefthook.dev/configuration/files-global/) --- title: "files (hook-level)" --- # `files` A custom command executed by the `sh` shell that returns the files or directories to be referenced in `{files}` template. See [`run`](./run.md) and [`files`](./files.md). 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: ... ``` --- ## [files (job-level)](https://lefthook.dev/configuration/files/) --- title: "files (job-level)" --- # `files` A custom command executed by the `sh` shell that returns the 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} ``` --- ## [follow](https://lefthook.dev/configuration/follow/) --- title: "follow" --- # `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 ``` ::: callout info Note If used with [`parallel`](#parallel) the output can be a mess, so please avoid setting both options to `true` ::: --- ## [git_url](https://lefthook.dev/configuration/git_url/) --- title: "git_url" --- # `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 ``` --- ## [glob_matcher](https://lefthook.dev/configuration/glob_matcher/) --- title: "glob_matcher" --- # `glob_matcher` Configure which glob matching engine lefthook uses to filter files. **Values:** - `gobwas` (default): see https://github.com/gobwas/glob - `doublestar`: Usual glob behavior (like in Bash) #### Example ```yml # lefthook.yml glob_matcher: doublestar pre-commit: jobs: - name: lint run: yarn eslint {staged_files} glob: "**/*.{js,ts}" ``` #### Behaviour comparison ```yml # gobwas (default): **/*.js matches src/app.js but NOT app.js # doublestar: **/*.js matches app.js, src/app.js, a/b/c/app.js ``` Use `doublestar` when migrating from other tools or when you need `**` to match files at any depth including the root. The setting applies globally to all `glob` and `exclude` patterns and is backwards compatible. --- ## [glob](https://lefthook.dev/configuration/glob/) --- title: "glob" --- # `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: jobs: - name: lint run: yarn eslint {staged_files} glob: "*.{js,ts,jsx,tsx}" ``` ::: callout info Note From lefthook version `1.10.10` you can also provide a list of globs: ```yml # lefthook.yml pre-commit: jobs: - run: yarn lint {staged_files} glob: - "*.ts" - "*.js" ``` ::: 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. #### When using `root` Globs are still calculated from the actual root of the git repo β€” `root` is ignored. #### Behaviour of `**` The `**` pattern matches **1 or more** directories deep (not zero or more, unlike most other tools). To match files at both the top level and nested, use separate patterns or opt-in to standard behavior with [`glob_matcher: doublestar`](./glob_matcher.md). ```yaml glob: "src/**/*.js" # does NOT match src/file.js glob: "src/*.js" # matches src/file.js only ``` #### Using `glob` without a files template in `run` 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: jobs: - name: lint run: npm run lint # skipped if no .js files staged glob: "*.js" ``` --- ## [group](https://lefthook.dev/configuration/group/) --- title: "group" --- # `group` You can define a group of jobs and configure how they should execute using the following options: - [`parallel`](./parallel.md): Executes all jobs in the group simultaneously. - [`piped`](./piped.md): Executes jobs sequentially, passing output between them. - [`jobs`](./jobs.md): Specifies the jobs within the group. #### Example ```yml # lefthook.yml pre-commit: jobs: - group: parallel: true jobs: - run: echo 1 - run: echo 2 - run: echo 3 ``` If you specify `env`, `root`, `glob`, or `exclude` on a group, they will be inherited to the underlying jobs. ```yml # lefthook.yml pre-commit: jobs: - env: E1: hello glob: - "*.md" exclude: - "README.md" root: "subdir/" group: parallel: true jobs: - run: echo $E1 - run: echo $E1 env: E1: bonjour ``` ::: callout info Note To make a group mergeable with settings defined in local config or extends you have to specify the name of the job group belongs to: ```yml pre-commit: jobs: - name: a name of a group group: jobs: - name: lint run: yarn lint - name: test run: yarn test ``` ::: --- ## [Hook](https://lefthook.dev/configuration/Hook/) --- title: "Hook" --- # 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` #### Example ```yml # lefthook.yml # Git hook pre-commit: jobs: - run: yarn lint {staged_files} --fix stage_fixed: true # Custom hook check-docs: jobs: - run: yarn check-docs - run: typos ``` --- ## [Untitled](https://lefthook.dev/configuration/) ## Config file name Lefthook supports the following file names for the main config: | Format | File name | |-------|-----------| | YAML | `lefthook.yml` | | YAML | `.lefthook.yml` | | YAML | `.config/lefthook.yml` | | | | | YAML | `lefthook.yaml` | | YAML | `.lefthook.yaml` | | YAML | `.config/lefthook.yaml` | | | | | TOML | `lefthook.toml` | | TOML | `.lefthook.toml` | | TOML | `.config/lefthook.toml` | | | | | JSON | `lefthook.json` | | JSON | `.lefthook.json` | | JSON | `.config/lefthook.json` | | | | | JSONC | `lefthook.jsonc` | | JSONC | `.lefthook.jsonc` | | JSONC | `.config/lefthook.jsonc` | 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. Filenames without the leading dot will also be looked up from the [`.config` subdirectory](https://github.com/pi0/config-dir). 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`. The `-local` config can be used without a main config file. This is useful when you want to use lefthook locally without imposing it on your teammates – just create a `lefthook-local.yml` file and add it to your global `.gitignore`. ## Options - [`assert_lefthook_installed`](./assert_lefthook_installed.md) - [`colors`](./colors.md) - [`extends`](./extends.md) - [`lefthook`](./lefthook.md) - [`min_version`](./min_version.md) - [`no_tty`](./no_tty.md) - [`output`](./output.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) - [`source_dir`](./source_dir.md) - [`source_dir_local`](./source_dir_local.md) - [`skip_lfs`](./skip_lfs.md) - [`templates`](./templates.md) - [{Git hook name}](./Hook.md) (e.g. `pre-commit`) - [`files` (global)](./files-global.md) - [`parallel`](./parallel.md) - [`piped`](./piped.md) - [`follow`](./follow.md) - [`fail_on_changes`](./fail_on_changes.md) - [`fail_on_changes_diff`](./fail_on_changes_diff.md) - [`exclude_tags`](./exclude_tags.md) - [`exclude`](./exclude.md) - [`skip`](./skip.md) - [`only`](./only.md) - [`jobs`](./jobs.md) - [`name`](./name.md) - [`run`](./run.md) - [`script`](./script.md) - [`runner`](./runner.md) - [`args`](./args.md) - [`group`](./group.md) - [`parallel`](./parallel.md) - [`piped`](./piped.md) - [`jobs`](./jobs.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) - [`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) - [`args`](./args.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) --- ## [Configuration](https://lefthook.dev/configuration/) --- title: "Configuration" --- # Config file name Lefthook supports the following file names for the main config: | Format | Acceptable config names | |-------|-----------| | YAML | `lefthook.yml` `lefthook.yaml` `.lefthook.yml` `.lefthook.yaml` `.config/lefthook.yml` `.config/lefthook.yaml` | | TOML | `lefthook.toml` `.lefthook.toml` `.config/lefthook.toml` | | JSON | `lefthook.json` `.lefthook.json` `.config/lefthook.json` | | JSONC | `lefthook.jsonc` `.lefthook.jsonc` `.config/lefthook.jsonc` | 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. Filenames without the leading dot will also be looked up from the [`.config` subdirectory](https://github.com/pi0/config-dir). 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`. The `-local` config can be used without a main config file. This is useful when you want to use lefthook locally without imposing it on your teammates – just create a `lefthook-local.yml` file and add it to your global `.gitignore`. --- ## [install_non_git_hooks](https://lefthook.dev/configuration/install_non_git_hooks/) --- title: "install_non_git_hooks" --- # `install_non_git_hooks` ::: callout tip New feature Added in lefthook `2.0.17` ::: Install non-Git hooks into `.git/hooks`. May be useful for using with tools like https://git-flow.sh/. --- ## [interactive](https://lefthook.dev/configuration/interactive/) --- title: "interactive" --- # `interactive` **Default: `false`** ::: callout info 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. --- ## [jobs](https://lefthook.dev/configuration/jobs/) --- title: "jobs" --- # `jobs` ::: callout tip New feature Added in lefthook `1.10.0` ::: Jobs provide a flexible way to define tasks, supporting both commands and scripts. Jobs can be grouped for advanced flow control. Named jobs are merged across [`extends`](./extends.md) and local config; unnamed jobs are appended in definition order. Groups can include other jobs with their own parallel or piped flow β€” `glob`, `root`, and `exclude` on a group apply to all nested jobs. #### Example ::: callout info Note Currently, only `root`, `glob`, and `exclude` options are applied to group jobs. Other options must be set for each job individually. Submit a [feature request](https://github.com/evilmartians/lefthook/issues/new?assignees=&labels=feature+request&projects=&template=feature_request.md) if this limits your workflow. ::: A configuration demonstrating a piped group running in parallel with other jobs: ```yml # lefthook.yml pre-commit: parallel: true jobs: - name: migrate root: backend/ glob: "db/migrations/*" group: piped: true jobs: - run: bundle install - run: rails db:migrate - run: yarn lint --fix {staged_files} root: frontend/ stage_fixed: true - run: bundle exec rubocop root: backend/ - run: golangci-lint root: proxy/ - script: verify.sh runner: bash ``` This configuration runs migrate jobs in a piped flow while other jobs execute in parallel. --- ## [lefthook](https://lefthook.dev/configuration/lefthook/) --- title: "lefthook" --- # `lefthook` **Default:** `null` ::: callout tip New feature Added in lefthook `1.10.5` ::: Provide a full path to lefthook executable or a command to run lefthook. Bourne shell (`sh`) syntax is supported. ::: callout warn This option does not merge from `remotes` or `extends` for security reasons. It does get merged from `lefthook-local.yml` if specified. ::: There are three reasons you may want to specify `lefthook`: 1. You want to force using specific lefthook version from your dependencies (e.g. npm package) 1. You use PnP loader for your JS/TS project, and your `package.json` with lefthook dependency locates in a subfolder 1. You want to make sure you use concrete lefthook executable path and want to defined it in `lefthook-local.yml` #### Specify lefthook executable ```yml # lefthook.yml lefthook: /usr/bin/lefthook pre-commit: jobs: - run: yarn lint ``` #### Specify a command to run lefthook ```yml # lefthook.yml lefthook: | cd project-with-lefthook pnpm lefthook pre-commit: jobs: - run: yarn lint root: project-with-lefthook ``` #### Force using a version from Rubygems ```yml # lefthook.yml lefthook: bundle exec lefthook pre-commit: jobs: - run: bundle exec rubocop -- {staged_files} ``` #### Enable debug logs ```yml # lefthook-local.yml lefthook: LEFTHOOK_VERBOSE=1 lefthook ``` --- ## [min_version](https://lefthook.dev/configuration/min_version/) --- title: "min_version" --- # `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 ``` --- ## [name](https://lefthook.dev/configuration/name/) --- title: "name" --- # `name` Name of a job. Will be printed in summary. If specified, the jobs can be merged with a jobs of the same name in a [local config](../examples/lefthook-local.md) or [extends](./extends.md). #### Example ```yml # lefthook.yml pre-commit: jobs: - name: lint and fix run: yarn run eslint --fix {staged_files} ``` --- ## [no_auto_install](https://lefthook.dev/configuration/no_auto_install/) --- title: "no_auto_install" --- # `no_auto_install` **Default: `false`** Disable automatic installation and synchronization of git hooks when running lefthook. By default, lefthook automatically installs and updates hooks when you run `lefthook run` if the configuration has changed. Setting this to `true` disables that behavior. This can also be controlled with the `--no-auto-install` option for the `lefthook run` command. #### Example ```yml # lefthook.yml no_auto_install: true pre-commit: commands: lint: run: npm run lint ``` --- ## [no_tty](https://lefthook.dev/configuration/no_tty/) --- title: "no_tty" --- # `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 ``` --- ## [only](https://lefthook.dev/configuration/only/) --- title: "only" --- # `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. ::: callout info 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 ``` --- ## [output](https://lefthook.dev/configuration/output/) --- title: "output" --- # `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. #### 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 - execution_out # Print execution output - execution_info # Print `EXECUTE > ...` logging - skips # Print "skip" (i.e. no files matched) ``` You can also override this list with the environment variable `LEFTHOOK_OUTPUT`: ```bash LEFTHOOK_OUTPUT="meta,success,summary" lefthook run pre-commit ``` --- ## [parallel](https://lefthook.dev/configuration/parallel/) --- title: "parallel" --- # `parallel` **Default: `false`** ::: callout info Note Lefthook runs commands and scripts **sequentially** by default ::: Run commands and scripts concurrently. #### Example ```yml # lefthook.yml pre-commit: parallel: true commands: lint: run: yarn lint test: run: yarn test ``` --- ## [piped](https://lefthook.dev/configuration/piped/) --- title: "piped" --- # `piped` **Default: `false`** ::: callout info 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 ``` --- ## [priority](https://lefthook.dev/configuration/priority/) --- title: "priority" --- # `priority` **Default: `0`** ::: callout info 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 ``` --- ## [rc](https://lefthook.dev/configuration/rc/) --- title: "rc" --- # `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: --- ## [ref](https://lefthook.dev/configuration/ref/) --- title: "ref" --- # `ref` An optional *branch* or *tag* name. ::: callout info 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. ::: See also [`refetch_frequency`](./refetch_frequency.md). #### Example ```yml # lefthook.yml remotes: - git_url: git@github.com:evilmartians/lefthook ref: v1.0.0 ``` --- ## [refetch_frequency](https://lefthook.dev/configuration/refetch_frequency/) --- title: "refetch_frequency" --- # `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. It is recommended to configure remotes that point to mutable references (including ones without a `ref`) to be refetched with some frequency appropriate for the project. Failure to fetch does not cause an error, but just a warning message. If a successfully fetched previous configuration exists, it will be used. Otherwise, the remote will be ignored. #### Example ```yml # lefthook.yml remotes: - git_url: https://github.com/evilmartians/lefthook refetch_frequency: 24h # Refetches once every 24 hours ``` ::: callout warn If [`refetch`](./refetch.md) is set to `true`, it overrides any setting in `refetch_frequency`. ::: --- ## [refetch](https://lefthook.dev/configuration/refetch/) --- title: "refetch" --- # `refetch` **Default:** `false` Force remote config refetching on every run. Lefthook will be refetching the specified remote every time it is called. See [`refetch_frequency`](./refetch_frequency.md) for more flexible refetching options and additional considerations. #### Example ```yml # lefthook.yml remotes: - git_url: https://github.com/evilmartians/lefthook refetch: true ``` --- ## [remotes](https://lefthook.dev/configuration/remotes/) --- title: "remotes" --- # `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 [script `source_dir`](./source_dir.md) must also be in the **root of the remote repository**. ::: callout info Note Configs are merged in this order: `lefthook.yml` β†’ `remotes` β†’ `lefthook-local.yml`. For simplicity, keep jobs in remote configs independent from other steps. ::: #### Example ```yml # lefthook.yml remotes: - git_url: git@github.com:evilmartians/lefthook ref: v1.0.0 configs: - examples/ruby-linter.yml ``` --- ## [root](https://lefthook.dev/configuration/root/) --- title: "root" --- # `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} ``` ::: callout info Note Globs are always calculated from the actual root of the git repo β€” `root` does not affect glob matching. ::: --- ## [run](https://lefthook.dev/configuration/run/) --- title: "run" --- # `run` This is a mandatory option for a command, which specifies the actual command to be run using the `sh` shell. 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. - `{1}` - shorthand for the 1-st git hook argument (and so on for `{2}`, `{3}`, etc.) - `{lefthook_job_name}` - current job/command/script name ::: callout info Note Command line length has a limit on every system. If your list of files is quite long, lefthook splits your files list to fit in the limit and runs few commands sequentially. ::: #### 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}` 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}` 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}` Simply run `bundle exec rubocop` on all files with `.rb` extension excluding `application.rb` and `routes.rb` files. ::: callout info 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}` ```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 Prevent commits from containing multiple sign-offs. ```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'` ``` #### Scripts ```yml # lefthook.yml pre-commit: jobs: - name: a whole script in a run run: | for file in $(ls .); do yarn lint $file done ``` --- ## [runner](https://lefthook.dev/configuration/runner/) --- title: "runner" --- # `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 ``` --- ## [script](https://lefthook.dev/configuration/script/) --- title: "script" --- # `script` Name of a script to execute. The rules are the same as for [`scripts`](./Scripts.md) Use [`args`](./args.md) to append arguments to the script. Configuring `args` replaces arguments passed by Git unless the `{0}` template is included. #### Example ```yml # lefthook.yml pre-commit: jobs: - script: linter.sh runner: bash args: "{staged_files}" ``` ```bash # .lefthook/pre-commit/linter.sh echo "Everything is OK" ``` --- ## [Scripts](https://lefthook.dev/configuration/Scripts/) --- title: "Scripts" --- # 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. Use [`args`](./args.md) to append arguments to a script. Arguments passed by Git are omitted when `args` is configured, so use the `{0}` template if they should be preserved. ```yml commit-msg: scripts: "template_checker": runner: bash args: "{0}" ``` --- ## [setup](https://lefthook.dev/configuration/setup/) --- title: 'setup' --- # `setup` ::: callout tip New feature Added in lefthook `2.1.2` ::: A list of instructions to run before any job. Supports templates and Git args like in [`run`](./run.md). ::: callout info Note When merging configs (with `lefthook-local.yml` or files from [`extends`](./extends.md)) `setup` instructions get **prepended**. When there are multiple `extends`, they get **appended** in the same order as extend files are specified. ::: #### Example ```yml # lefthook.yml pre-commit: setup: - run: | if ! command -v golangci-lint >/dev/null 2>&1; then go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.10.1 fi jobs: - run: golangci-lint -- {staged_files} glob: "*.go" ``` --- ## [skip_lfs](https://lefthook.dev/configuration/skip_lfs/) --- title: "skip_lfs" --- # `skip_lfs` **Default:** `false` Skip running LFS hooks even if it exists on your system. #### Example ```yml # lefthook.yml skip_lfs: true pre-push: commands: test: run: yarn test ``` --- ## [skip](https://lefthook.dev/configuration/skip/) --- title: "skip" --- # `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 ``` Skipping a command conditionally based on the existence of a CLI tool: ```yml prepare-commit-msg: skip: - merge - rebase commands: aiautocommit: interactive: true run: aiautocommit commit --output-file "{1}" env: LOG_LEVEL: info skip: # only run this if the tool exists - run: "! which aiautocommit" ``` ::: callout 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 ``` ::: --- ## [source_dir_local](https://lefthook.dev/configuration/source_dir_local/) --- title: "source_dir_local" --- # `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. #### Example ```yml # lefthook-local.yml source_dir_local: .lefthook-local/ ``` --- ## [source_dir](https://lefthook.dev/configuration/source_dir/) --- title: "source_dir" --- # `source_dir` **Default: `.lefthook/`** Change a directory for script files. The directory contains subfolders named after git hooks, each containing script files. #### Example ``` .lefthook/ β”œβ”€β”€ pre-commit/ β”‚ β”œβ”€β”€ lint.sh β”‚ └── test.py └── pre-push/ └── check-files.rb ``` --- ## [stage_fixed](https://lefthook.dev/configuration/stage_fixed/) --- title: "stage_fixed" --- # `stage_fixed` **Default: `false`** ::: callout info Note Works **only** for the `pre-commit` hook. ::: 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. If the `git add` call fails, the hook fails too. Otherwise the commit would silently go through with the unfixed content. #### Example ```yml # lefthook.yml pre-commit: commands: lint: run: npm run lint --fix {staged_files} stage_fixed: true ``` --- ## [tags](https://lefthook.dev/configuration/tags/) --- title: "tags" --- # `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 ``` --- ## [templates](https://lefthook.dev/configuration/templates/) --- title: "templates" --- # `templates` ::: callout tip New feature Added in lefthook `1.10.8` ::: Provide custom replacement for templates in `run` values. With `templates` you can specify what can be overridden via `lefthook-local.yml` without a need to overwrite every jobs in your configuration. #### Override with lefthook-local.yml ```yml # lefthook.yml templates: dip: # empty pre-commit: jobs: # Will run: `bundle exec rubocop -- file1 file2 file3 ...` - run: "{dip} bundle exec rubocop -- {staged_files}" ``` ```yml # lefthook-local.yml templates: dip: dip # Will run: `dip bundle exec rubocop -- file1 file2 file3 ...` ``` #### Reduce redundancy ```yml # lefthook.yml templates: wrapper: docker-compose run --rm -v $(pwd):/app service pre-commit: jobs: - run: "{wrapper} yarn format" - run: "{wrapper} yarn lint" - run: "{wrapper} yarn test" ``` --- ## [use_stdin](https://lefthook.dev/configuration/use_stdin/) --- title: "use_stdin" --- # `use_stdin` ::: callout info 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 ``` --- ## [Commitlint and commitzen](https://lefthook.dev/examples/commitlint/) # Commitlint and commitzen Use lefthook to generate commit messages using commitzen and validate them with commitlint. ## Install dependencies ```bash yarn add -D @commitlint/cli @commitlint/config-conventional # For commitzen yarn add -D commitizen cz-conventional-changelog ``` ## Configure Setup `commitlint.config.js`. Conventional configuration: ```js // commitlint.config.js module.exports = {extends: ['@commitlint/config-conventional']}; ``` If you are using commitzen, make sure to add this in `package.json`: ```json "config": { "commitizen": { "path": "./node_modules/cz-conventional-changelog" } } ``` Configure lefthook: ```yml # lefthook.yml # Build commit messages prepare-commit-msg: commands: commitzen: interactive: true run: yarn run cz --hook # Or npx cz --hook env: LEFTHOOK: "0" # Validate commit messages commit-msg: commands: "lint commit message": run: yarn run commitlint --edit {1} ``` ## Test it ```bash # You can type it without message, if you are using commitzen git commit # Or provide a commit message is using only commitlint git commit -am 'fix: typo' ``` --- ## [Filters](https://lefthook.dev/examples/filters/) # Filters Files passed to your hooks can be filtered with the following options - [`glob`](../configuration/glob.md) - [`exclude`](../configuration/exclude.md) - [`file_types`](../configuration/file_types.md) - [`root`](../configuration/root.md) In this example all **staged files** will pass through these filters. ```yml # lefthook.yml pre-commit: commands: lint: run: yarn lint {staged_files} --fix glob: "*.{js,ts}" root: frontend exclude: - *.config.js - *.config.ts file_types: - not executable ``` Imagine you've staged the following files ```bash backend/asset.js frontend/src/index.ts frontend/bin/cli.js # <- executable frontend/eslint.config.js frontend/README.md ``` After all filters applied the `lint` command will execute the following: ```bash yarn lint frontend/src/index.ts --fix ``` --- ## [lefthook-local.yml](https://lefthook.dev/examples/lefthook-local/) # lefthook-local.yml ::: callout tip Tip You can put `lefthook-local.yml` into your `~/.gitignore`, so in every project you can have your local-only overrides. ::: `lefthook-local.yml` overrides and extends the configuration of your main `lefthook.yml`. ```yml # lefthook.yml pre-commit: commands: lint: run: bundle exec rubocop -- {staged_files} glob: "*.rb" check-links: run: lychee -- {staged_files} ``` ```yml # lefthook-local.yml pre-commit: parallel: true # run all commands concurrently commands: lint: run: docker-compose run backend {cmd} # wrap the original command with docker-compose check-links: skip: true # skip checking links # Add another hook post-merge: files: "git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD" commands: dependencies: glob: "Gemfile*" run: docker-compose run backend bundle install ``` --- ### The merged config lefthook will use ```yml pre-commit: parallel: true commands: lint: run: docker-compose run backend bundle exec rubocop -- {staged_files} glob: "*.rb" check-links: run: lychee -- {staged_files} skip: true post-merge: files: "git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD" commands: dependencies: glob: "Gemfile*" run: docker-compose run backend bundle install ``` --- ## [Remotes](https://lefthook.dev/examples/remotes/) # Remotes Use configurations from other Git repositories via `remotes` feature. Lefthook will automatically download the remote config files and merge them into existing configuration. ```yml remotes: - git_url: https://github.com/evilmartians/lefthook configs: - examples/remote/ping.yml ``` --- ## [Skip or run on condition](https://lefthook.dev/examples/skip/) # Skip or run on condition Here are two hooks. `pre-commit` hook will only be executed when you're committing something on a branch starting with `dev/` prefix. In `pre-push` hook: - `test` command will be skipped if `NO_TEST` env variable is set to `1` - `lint` command will only be executed if you're pushing the `main` branch ```yml # lefthook.yml pre-commit: only: - ref: dev/* commands: lint: run: yarn lint {staged_files} --fix glob: "*.{ts,js}" test: run: yarn test pre-push: commands: test: run: yarn test skip: - run: test "$NO_TEST" -eq 1 lint: run: yarn lint only: - ref: main ``` --- ## [Stage fixed files](https://lefthook.dev/examples/stage_fixed/) # Stage fixed files > Works only for `pre-commit` Git hook Sometimes your linter fixes the changes and you usually want to commit them automatically. To enable auto-staging of the fixed files use [`stage_fixed`](/configuration/stage_fixed.md) option. ```yml # lefthook.yml pre-commit: commands: lint: run: yarn lint {staged_files} --fix stage_fixed: true ``` --- ## [Wrap commands in local config](https://lefthook.dev/examples/wrap-commands/) # Wrap commands in local config Wrapping some commands defined in a main config with `dip`[^1]. ```yml # lefthook.yml pre-commit: jobs: - name: rubocop run: bundle exec rubocop -A -- {staged_files} ``` ```yml # lefthook-local.yml pre-commit: jobs: - name: rubocop run: dip {cmd} ``` [^1]: [dip](https://github.com/bibendi/dip) – dockerized dev experience with, similar to `docker-compose run` --- ## [What is Lefthook?](https://lefthook.dev/) --- title: "What is Lefthook?" description: "Welcome to Lefthook documentation" --- **Lefthook** is a Git hooks manager. It is - Fast - Powerful - Simple ## How does lefthook work? You - Create [`lefthook.yml`](./configuration.md) configuration file - Run `lefthook install` Lefthook installs the configured hooks into `.git/hooks/`. Hook is a simple script that calls `lefthook run {hook-name}` when executed. ## How to install lefthook? The most common way is to use the package manager of your project, e.g. [gem](./installation/ruby.md) or [npm package](./installation/node.md). You can also install lefthook via [Homebrew](./installation/homebrew.md), [`winget`](./installation/winget.md), [`yum`](./installation/rpm.md), [`apt`](./installation/deb.md), [`apk`](./installation/alpine.md), [`scoop`](./installation/scoop.md) ## Example configuration Run linters on `pre-commit` hook. ```yml # lefthook.yml pre-commit: parallel: true jobs: - run: yarn run stylelint --fix '{staged_files}' glob: "*.css" stage_fixed: true - run: yarn run eslint --fix '{staged_files}' glob: - "*.ts" - "*.js" - "*.tsx" - "*.jsx" stage_fixed: true ``` --- **Lefthook** is built by **[Evil Martians](https://evilmartians.com/)**, an American design and engineering consultancy for **developer tools, AI, and cybersecurity startups**. --- ## [Install Lefthook](https://lefthook.dev/install/) --- title: "Install Lefthook" --- Lefthook distributes as a standalone, no-deps binary. There are multiple ways to install lefthook but the most common is via package manager for your programming language (see the options in the dropdown on the left). You can also download just the [binary](https://github.com/evilmartians/lefthook/releases/latest) for your OS and architecture and put it somewhere in your `$PATH` and update it with ``` lefthook self-update ``` --- ## [Alpine](https://lefthook.dev/installation/alpine/) --- title: "Alpine" --- # 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") --- ## [Arch Linux](https://lefthook.dev/installation/arch/) --- title: "Arch Linux" --- # AUR for Arch - Official [AUR package](https://aur.archlinux.org/packages/lefthook) (compiles from sources) - Community [AUR package](https://aur.archlinux.org/packages/lefthook-bin) (delivers pre-compiled binaries) ```sh # To compile from sources yay -S lefthook # To install only executable yay -S lefthook-bin ``` --- ## [Debian-based](https://lefthook.dev/installation/deb/) --- title: "Debian-based" --- # 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") --- ## [Devbox](https://lefthook.dev/installation/devbox/) # Devbox Add lefthook in the devbox environment. lefthook already exists in the [Nix package](https://search.nixos.org/packages?channel=25.05&show=lefthook&from=0&size=50&sort=relevance&type=packages&query=lefthook) ```bash devbox add lefthook@latest ``` ::: callout info Note The devbox plugin for lefthook is maintained by the community. While we appreciate their contribution, the lefthook team cannot provide direct support for devbox-specific installation issues. ::: --- ## [Go](https://lefthook.dev/installation/go/) # Go The minimum Go version required is 1.26 and you can install - as global package ```bash go install github.com/evilmartians/lefthook/v2@v2.1.12 ``` - or as a go tool in your project ```bash go get -tool github.com/evilmartians/lefthook/v2 ``` --- ## [Homebrew](https://lefthook.dev/installation/homebrew/) --- title: "Homebrew" --- # Homebrew for MacOS and Linux ```bash brew install lefthook ``` --- ## [Manual](https://lefthook.dev/installation/manual/) --- title: "Manual" --- # Manual installation with prebuilt executable Download binaries from [latest release](https://github.com/evilmartians/lefthook/releases/latest) and install manually. --- ## [Mise](https://lefthook.dev/installation/mise/) # Mise > See [https://github.com/jdx/mise](https://github.com/jdx/mise) ```bash mise use lefthook@latest ``` ::: callout info Note The mise plugin for lefthook is maintained by the community. While we appreciate their contribution, the lefthook team cannot provide direct support for mise-specific installation issues. ::: --- ## [NPM](https://lefthook.dev/installation/node/) --- title: "NPM" --- # NPM package ```bash npm install --save-dev lefthook ``` ```bash yarn add --dev lefthook ``` ```bash pnpm add -D lefthook ``` ::: callout info Note If you use `pnpm` package manager make sure to update `pnpm-workspace.yaml`s `onlyBuiltDependencies` with `lefthook` and add `lefthook` to `pnpm.onlyBuiltDependencies` in your root `package.json`, otherwise the `postinstall` script of the `lefthook` package won't be executed and hooks won't be installed. ::: ## Choose right package Lefthook supports three NPM packages with different ways to deliver the executables 1. [lefthook](https://www.npmjs.com/package/lefthook) installs one executable for your system ```bash npm install --save-dev lefthook ``` 1. **legacy**[^1] [@evilmartians/lefthook](https://www.npmjs.com/package/@evilmartians/lefthook) installs executables for all OS ```bash npm install --save-dev @evilmartians/lefthook ``` 1. **legacy**[^1] [@evilmartians/lefthook-installer](https://www.npmjs.com/package/@evilmartians/lefthook-installer) fetches the right executable on installation ```bash npm install --save-dev @evilmartians/lefthook-installer ``` [^1]: Legacy distributions are still maintained but they will be shut down in the future. --- ## [Python](https://lefthook.dev/installation/python/) # Python ```sh python -m pip install --user lefthook ``` ```sh uv add --dev lefthook ``` ```sh pipx install lefthook ``` --- ## [RPM-based](https://lefthook.dev/installation/rpm/) --- title: "RPM-based" --- # 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") --- ## [Ruby](https://lefthook.dev/installation/ruby/) # Ruby ```ruby # Gemfile group :development do gem "lefthook", require: false end ``` Or globally ```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. --- ## [Scoop](https://lefthook.dev/installation/scoop/) --- title: "Scoop" --- # Scoop for Windows ```sh scoop install lefthook ``` --- ## [Snap](https://lefthook.dev/installation/snap/) --- title: "Snap" --- # Snap for Linux ```sh snap install --classic lefthook ``` --- ## [Swift](https://lefthook.dev/installation/swift/) # 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: "2.1.12"), ``` Or, with [mint](https://github.com/yonaskolb/Mint): ```bash mint run csjones/lefthook-plugin ``` --- ## [Winget](https://lefthook.dev/installation/winget/) --- title: "Winget" --- # Winget for Windows ```sh winget install evilmartians.lefthook ``` --- ## [Contributors](https://lefthook.dev/misc/contributors/) # Contributors - [Arkweid](https://github.com/Arkweid) - [Envek](https://github.com/Envek) - [mrexox](https://github.com/mrexox) - [skryukov](https://github.com/skryukov) - [scop](https://github.com/scop) - [hyperupcall](https://github.com/hyperupcall) - [MartijnCuppens](https://github.com/MartijnCuppens) - [palkan](https://github.com/palkan) - [markovichecha](https://github.com/markovichecha) - [technicalpickles](https://github.com/technicalpickles) - [aminya](https://github.com/aminya) - [prog-supdex](https://github.com/prog-supdex) - [HellSquirrel](https://github.com/HellSquirrel) - [Evilweed](https://github.com/Evilweed) - [PikachuEXE](https://github.com/PikachuEXE) - [jsmestad](https://github.com/jsmestad) - [DmitryTsepelev](https://github.com/DmitryTsepelev) - [pmirecki](https://github.com/pmirecki) - [0legovich](https://github.com/0legovich) - [zachahn](https://github.com/zachahn) - [sitiom](https://github.com/sitiom) - [spearmootz](https://github.com/spearmootz) - [pwinckles](https://github.com/pwinckles) - [pablobirukov](https://github.com/pablobirukov) - [nihalgonsalves](https://github.com/nihalgonsalves) - [nesk](https://github.com/nesk) - [jaydorsey](https://github.com/jaydorsey) - [fantua](https://github.com/fantua) - [orsinium](https://github.com/orsinium) - [fabn](https://github.com/fabn) If you feel you’re missing from this list, feel free to add yourself in a PR. [//]: # 'curl https://api.github.com/repos/evilmartians/lefthook/contributors | jq -r ".[] | \"- [\" + .login + \"](\" + .html_url + \")\""' --- ## [lefthook add](https://lefthook.dev/usage/commands/add/) --- title: "lefthook add" --- ## `lefthook add` Installs the given hook to Git hook. With argument `--dirs` creates a directory `.git/hooks//` if it doesn't exist. Use it before adding a script to configuration. #### Example ```bash $ lefthook add pre-push --dirs ``` Describe pre-push commands in `lefthook.yml`: ```yml pre-push: jobs: - script: "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 check-install](https://lefthook.dev/usage/commands/check-install/) --- title: "lefthook check-install" --- ## `lefthook check-install` Checks if Git hooks are installed and synchronized. Returns: - `0` if hooks installed and synchronized - `1` if hooks not installed or need a sync --- ## [lefthook dump](https://lefthook.dev/usage/commands/dump/) --- title: "lefthook dump" --- ## `lefthook dump` Prints the whole configuration after merging all secondary configs. This is the actual config lefthook uses, it can be build from the main config (`lefthook.yml`), remotes, extends, and `lefthook-local.yml` overrides. --- ## [lefthook install](https://lefthook.dev/usage/commands/install/) --- title: "lefthook install" --- ## `lefthook install` Creates an empty `lefthook.yml` if a configuration file does not exist. Installs configured hooks to Git hooks. ::: callout info Note Reinstall is not required when you modify `lefthook.yml`, the configuration file is read every time a git hook is run. ::: ::: callout info Note NPM package `lefthook` installs the hooks in a postinstall script automatically. For projects not using NPM package run `lefthook install` after cloning the repo. The postinstall script runs a plain `lefthook install`, so when `core.hooksPath` is set it stops and prints the same message as running the command by hand. Run `lefthook install --force` or `lefthook install --reset-hooks-path` once, deliberately, to resolve it. ::: ### Installing specific hooks You can install only specific hooks by running `lefthook install ...`. --- ## [lefthook run](https://lefthook.dev/usage/commands/run/) --- title: "lefthook run" --- ## `lefthook run` Executes the commands and scripts configured for a given hook. Installed Git hooks call `lefthook run` implicitly. #### Example ```yml # lefthook.yml pre-commit: jobs: - name: lint run: yarn lint --fix {staged_files} test: jobs: - name: test run: yarn test ``` Install the hook. ```bash $ lefthook install ``` ```bash $ lefthook run test # will run 'yarn test' $ git commit # will run pre-commit hook ('yarn lint --fix') $ lefthook run pre-commit # will run pre-commit hook (`yarn lint --fix`) ``` ### Run specific jobs You can specify which jobs to run (also `--tag` supported). ```bash $ lefthook run pre-commit --job lints --job pretty --tag checks ``` ### Specify files You can force replacing files templates (like `{staged_files}`) with either all files (will acts as `{all_files}` template) 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 self-update](https://lefthook.dev/usage/commands/self-update/) --- title: "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. --- ## [lefthook uninstall](https://lefthook.dev/usage/commands/uninstall/) --- title: "lefthook uninstall" --- ## `lefthook uninstall` Clears Git hooks installed by lefthook. --- ## [lefthook validate](https://lefthook.dev/usage/commands/validate/) --- title: "lefthook validate" --- ## `lefthook validate` Validates your lefthook configuration. Use `lefthook dump` to see it. It uses JSON schema from the lefthook Github repo. --- ## [lefthook version](https://lefthook.dev/usage/commands/version/) --- title: "lefthook version" --- ## `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 ``` --- ## [CI](https://lefthook.dev/usage/envs/CI/) --- title: "CI" --- ## `CI` When using NPM package `lefthook`, set `CI=true` in your CI (if it does not set it automatically) to prevent lefthook from installing hooks in the postinstall script: ```bash CI=true npm install CI=true yarn install CI=true pnpm install ``` ::: callout info Note Set `LEFTHOOK=1` or `LEFTHOOK=true` to override this behavior and install hooks in the postinstall script (despite `CI=true`). ::: --- ## [CLICOLOR_FORCE](https://lefthook.dev/usage/envs/CLICOLOR_FORCE/) --- title: "CLICOLOR_FORCE" --- ## `CLICOLOR_FORCE` Set `CLICOLOR_FORCE=true` to force colored output in lefthook and all subcommands. --- ## [LEFTHOOK_BIN](https://lefthook.dev/usage/envs/LEFTHOOK_BIN/) --- title: "LEFTHOOK_BIN" --- ## `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 --- ## [LEFTHOOK_CONFIG](https://lefthook.dev/usage/envs/LEFTHOOK_CONFIG/) --- title: "LEFTHOOK_CONFIG" --- ## `LEFTHOOK_CONFIG` Override the main lefthook config with `LEFTHOOK_CONFIG=~/global_lefthook.yml`. Note: local config, specified extends, and remotes will still be loaded. --- ## [LEFTHOOK_EXCLUDE](https://lefthook.dev/usage/envs/LEFTHOOK_EXCLUDE/) --- title: "LEFTHOOK_EXCLUDE" --- ## `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](https://lefthook.dev/usage/envs/LEFTHOOK_OUTPUT/) --- title: "LEFTHOOK_OUTPUT" --- ## `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_VERBOSE](https://lefthook.dev/usage/envs/LEFTHOOK_VERBOSE/) --- title: "LEFTHOOK_VERBOSE" --- ## `LEFTHOOK_VERBOSE` Set `LEFTHOOK_VERBOSE=1` or `LEFTHOOK_VERBOSE=true` to enable verbose printing. #### Example ```bash LEFTHOOK_VERBOSE=1 lefthook run pre-commit ``` --- ## [LEFTHOOK](https://lefthook.dev/usage/envs/LEFTHOOK/) --- title: "LEFTHOOK" --- ## `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" ``` When using NPM package `lefthook` in CI, and your CI sets `CI=true` automatically, use `LEFTHOOK=1` or `LEFTHOOK=true` to install hooks in the postinstall script: #### Example ```bash LEFTHOOK=1 npm install LEFTHOOK=1 yarn install LEFTHOOK=1 pnpm install ``` --- ## [NO_COLOR](https://lefthook.dev/usage/envs/NO_COLOR/) --- title: "NO_COLOR" --- ## `NO_COLOR` Set `NO_COLOR=true` to disable colored output in lefthook and all subcommands that lefthook calls. --- ## [Capture ARGS from git in the script](https://lefthook.dev/usage/features/git-args/) --- title: "Capture ARGS from git in the script" --- # Capture ARGS from git in the script Lefthook passes Git arguments to your commands and scripts. ``` β”œβ”€β”€ .lefthook β”‚Β Β  └── prepare-commit-msg β”‚Β Β  └── message.sh └── lefthook.yml ``` ```yml # lefthook.yml prepare-commit-msg: jobs: - script: "message.sh" runner: bash - run: echo "Git args: {1} {2} {3}" ``` ```bash # .lefthook/prepare-commit-msg/message.sh # Arguments get passed from Git COMMIT_MSG_FILE=$1 COMMIT_SOURCE=$2 SHA1=$3 # ... ``` --- ## [Git LFS support](https://lefthook.dev/usage/features/git-lfs/) --- title: "Git LFS support" --- # Git LFS support ::: callout info Note 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. Git LFS hooks may be slow. Disable them with the global `skip_lfs: true` setting. ::: 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`](../envs/LEFTHOOK_VERBOSE.md) ENV to make lefthook show git LFS output. To avoid calling LFS hooks set [`skip_lfs: true`](../../configuration/skip_lfs.md) in lefthook.yml or lefthook-local.yml --- ## [Interactive commands](https://lefthook.dev/usage/features/interactive/) --- title: "Interactive commands" --- # 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. --- ## [Local config](https://lefthook.dev/usage/features/local/) --- title: "Local config" --- # Local config You can extend and override options of your main configuration with `lefthook-local.yml`. Don't forget to add the file to `.gitignore`. You can also use `lefthook-local.yml` without a main config file. This is useful when you want to use lefthook locally without imposing it on your teammates. ```yml # lefthook.yml (committed into your repo) pre-commit: jobs: - name: linter run: yarn lint - name: tests run: yarn test ``` ```yml # lefthook-local.yml (ignored by git) pre-commit: jobs: - name: tests skip: true # don't want to run tests on every commit - name: linter run: yarn lint {staged_files} # lint only staged files ``` --- ## [Pass stdin to a command or script](https://lefthook.dev/usage/features/pass-stdin/) --- title: "Pass stdin to a command or script" --- # 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). --- ## [Usage](https://lefthook.dev/usage/) # Usage Here are the most common usage cases. You can find more info in the docs. ## Basic CLI commands ```bash # Create/update Git hooks based on lefthook.yml, or create an empty lefthook.yml lefthook install # Run pre-commit hook commands and scripts (requires lefthook.yml) lefthook run pre-commit # Validate the configuration lefthook validate # Dump the configuration (useful when you have remotes, extends that overwrite the configuration) lefthook dump ``` ## Skip running lefthook when committing changes ```bash LEFTHOOK=0 git commit ``` ---