lefthook 配置文件完全指南:命名规则、加载顺序与顶层配置项解析
【免费下载链接】lefthookFast and powerful Git hooks manager for any type of projects.项目地址: https://gitcode.com/GitHub_Trending/le/lefthook
lefthook 是一款快速且强大的 Git hooks 管理器,其一切行为都由项目根目录下的配置文件驱动。本文以 docs/configuration.md 为主线,系统讲解 lefthook 主配置文件的合法命名与格式、lefthook-local本地配置合并机制、配置的加载与覆盖顺序,以及全部顶层配置项(min_version、extends、remotes、source_dir、output、rc、lefthook等)和 Git hook 内部结构(commands/scripts/jobs)的用法。读完本文,你将能写出规范、可维护、可跨项目复用的 lefthook 配置,并理解底层加载器的工作方式。
配置文件名称与支持格式
lefthook 的主配置文件支持 YAML、TOML、JSON、JSONC 四种格式,每种格式都有多个可接受的命名。官方支持的完整清单如下:
| 格式 | 可接受的配置文件名 |
|---|---|
| YAML | lefthook.ymllefthook.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 |
几点重要约定:
- 一个项目只使用一种格式。如果项目中同时存在多个配置文件,lefthook 只会使用其中的某一个,而且你无法确定最终加载的是哪一个(实际取决于加载顺序)。因此在团队中务必统一格式,避免歧义。
- 无前导点的文件名也会在
.config子目录中查找。例如lefthook.yml会被解析为lefthook.yml和.config/lefthook.yml两种可能位置。 - 从源码看,加载器在 internal/config/loader.go 中按扩展名顺序
[]string{".yml", ".yaml", ".json", ".jsonc", ".toml"}遍历,并为每个扩展名依次尝试lefthook、.lefthook、.config/lefthook三种基础名称(MainConfigNames),找到第一个存在的文件即加载并停止。
lefthook-local:本地私有配置的合并机制
除了主配置文件,lefthook 还会额外合并一个名为lefthook-local的配置文件。它适用于同样的四种格式:
lefthook-local.yml/lefthook-local.yaml/.lefthook-local.yml/.lefthook-local.yaml/.config/lefthook-local.yml/.config/lefthook-local.yamllefthook-local.toml/.lefthook-local.toml/.config/lefthook-local.tomllefthook-local.json/.lefthook-local.json/.config/lefthook-local.jsonlefthook-local.jsonc/.lefthook-local.jsonc/.config/lefthook-local.jsonc
命名规则与主配置一一对应:如果主配置使用前导点命名(如.lefthook.json),那么本地配置也必须使用前导点命名(.lefthook-local.json)。源码中LocalConfigNames为[]string{"lefthook-local", ".lefthook-local", ".config/lefthook-local"}(见 internal/config/loader.go)。
lefthook-local可以独立存在,即使没有主配置文件也能使用。这是它的核心使用场景:当你想在本地单独启用 lefthook、而不把配置强加给队友时,只需创建一个lefthook-local.yml并把它加入全局.gitignore即可——它不会进入版本库,也不会影响其他开发者的环境。
合并优先级
lefthook 配置的覆盖顺序(从低到高)为:
lefthook.yml—— 主配置文件extends—— 通过extends选项引入的配置remotes—— 通过remotes选项下载的远程配置lefthook-local.yml—— 本地配置文件,可以覆盖以上所有设置
从源码看,loader.go 的LoadKoanf先加载主配置(loadMain),再通过LoadSecondary加载extends、remotes与本地配置,最后在unmarshalConfigs中执行main.Merge(secondary)(见 loader.go)完成合并。钩子(hook)级别的合并是逐个进行的:addHook将本地配置中同名 hook 的内容合并到主配置的 hook 上(见 loader.go)。这也解释了为什么本地配置最适合存放"个人专属"的 lint 参数、rc路径或调试开关。
顶层配置项总览
lefthook 的配置结构由 internal/config/config.go 中的Config结构体定义。顶层可配置项如下(完整说明见 docs/configuration/README.md):
| 配置项 | 说明 |
|---|---|
assert_lefthook_installed | 断言 lefthook 已正确安装 |
colors | 开启/关闭或自定义输出颜色 |
extends | 引入其他配置文件进行合并 |
lefthook | 指定 lefthook 可执行文件路径或运行命令 |
min_version | 指定 lefthook 二进制的最低版本 |
no_tty | 隐藏 spinner 等交互元素 |
output | 控制输出内容的详细程度 |
rc | 提供一个 rc 文件(简单的 sh 脚本) |
remotes | 从远程仓库拉取并合并共享配置 |
source_dir | 更改脚本文件目录(默认.lefthook/) |
source_dir_local | 更改本地脚本文件目录(默认.lefthook-local/) |
skip_lfs | 跳过运行 Git LFS hooks(默认开启) |
templates | 为 run 命令中的替换提供自定义模板 |
{Git hook name} | 任意 Git hook 的配置,如pre-commit |
min_version
如果你想强制使用某个最低版本的 lefthook(例如需要旧版本不具备的特性),可以设置min_version:
# lefthook.yml min_version: 1.1.3当本机 lefthook 版本低于该值时,命令会直接报错退出,从而避免因版本不一致导致的配置解析失败。
lefthook
默认值:null(自 lefthook1.10.5起提供)
提供一个 lefthook 可执行文件的完整路径,或一条运行 lefthook 的命令,支持 Bourne shell(sh)语法。设置它的典型场景有三种:
- 强制使用依赖中特定版本的 lefthook(如 npm 包自带的二进制);
- 项目使用 PnP loader,且包含 lefthook 依赖的
package.json位于子目录; - 想在
lefthook-local.yml中固定本机 lefthook 的可执行路径。
# lefthook.yml —— 指定可执行文件路径 lefthook: /usr/bin/lefthook pre-commit: jobs: - run: yarn lint# lefthook.yml —— 指定运行命令(支持多行 sh 语法) lefthook: | cd project-with-lefthook pnpm lefthook pre-commit: jobs: - run: yarn lint root: project-with-lefthook# lefthook.yml —— 强制使用 Rubygems 提供的版本 lefthook: bundle exec lefthook pre-commit: jobs: - run: bundle exec rubocop -- {staged_files}# lefthook-local.yml —— 开启调试日志 lefthook: LEFTHOOK_VERBOSE=1 lefthook注意:出于安全原因,lefthook选项不会从remotes或extends中合并,但会从lefthook-local.yml中合并。
extends
你可以通过extends用一个或多个 YAML 文件扩展当前配置,其内容会被合并进来。lefthook.yml、lefthook-local.yml和remotes配置的 extends 是分开处理的,因此这些文件可以有不同的 extends。路径支持通配符*:
# 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源码中extend使用afero.Glob展开通配符,并支持递归合并(被 extend 的文件里还可以继续 extend),同时通过visited集合检测循环引用,若同一路径被重复指定会报错 "possible recursion in extends"(见 internal/config/loader.go)。
remotes
如果你希望在多项目间共享 lefthook 配置,可以使用remotes。lefthook 会自动下载远程仓库中的配置文件并合并到本地lefthook.yml。远程配置中若使用extends,路径必须相对于远程仓库根目录;若远程配置中包含scripts,对应的source_dir也必须位于远程仓库的根目录。
# lefthook.yml remotes: - git_url: git@github.com:evilmartians/lefthook ref: v1.0.0 configs: - examples/ruby-linter.yml合并顺序同样遵循lefthook.yml → remotes → lefthook-local.yml。remotes的加载实现在 loader.go:每个 remote 通过git_url+ref定位远程缓存目录,configs未指定时默认使用lefthook.yml(DefaultConfigName)。
source_dir与source_dir_local
source_dir默认.lefthook/:存放脚本文件的目录,其下按 Git hook 名分子目录,每个子目录放对应 hook 的脚本文件:
.lefthook/ ├── pre-commit/ │ ├── lint.sh │ └── test.py └── pre-push/ └── check-files.rbsource_dir_local默认.lefthook-local/:存放本地脚本文件(不纳入 VCS)。当你有lefthook-local.yml且需要引用不同的本地脚本时非常有用:
# lefthook-local.yml source_dir_local: .lefthook-local/两个默认值在源码常量中定义(DefaultSourceDir = ".lefthook"、DefaultSourceDirLocal = ".lefthook-local",见 internal/config/loader.go)。
output
output用于精细控制输出内容的详细程度,可选的打印项包括meta, summary, success, failure, execution, execution_out, execution_info, skips,默认全部开启。也可以设置output: false一键关闭所有输出(此时只打印错误):
# lefthook.yml output: - meta # 打印 lefthook 版本 - summary # 打印汇总块(成功与失败的步骤) - empty_summary # 无步骤可运行时打印汇总标题 - success # 打印成功步骤 - failure # 打印失败步骤 - execution # 打印执行日志 - execution_out # 打印执行输出 - execution_info # 打印 `EXECUTE > ...` 日志 - skips # 打印 "skip"(无文件匹配时)该列表还可以通过环境变量LEFTHOOK_OUTPUT覆盖:
LEFTHOOK_OUTPUT="meta,success,summary" lefthook run pre-commitrc
rc提供一个rc 文件,本质是一个简单的sh脚本。它的主要用途是设置那些非 shell 程序无法访问的环境变量。典型场景包括:
- 使用 GUI 程序(如 VSCode)触发 Git hooks;
- 引用的可执行文件只存在于经过修改的
$PATH中(如 rbenv、nvm、fnm 管理下的命令); - GUI 程序无法定位
lefthook可执行文件; - 想在
lefthook.yml中使用控制可执行文件行为的环境变量。
例如,你的npm由 nvm 管理,路径在/home/user/.nvm/versions/node/v15.14.0/bin/npm,而 GUI 程序找不到它:
# lefthook-local.yml # 文件名可自选,可在多个项目间共享;确保路径是绝对路径。 rc: ~/.lefthookrc若路径包含空格,需要加引号:
# lefthook-local.yml rc: '"${XDG_CONFIG_HOME:-$HOME/.config}/lefthookrc"'在 rc 文件中导出或修改环境变量:
# ~/.lefthookrc # nvm 方式 export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # fnm 方式 export FNM_DIR="$HOME/.fnm" [ -s "$FNM_DIR/fnm.sh" ] && \. "$FNM_DIR/fnm.sh" # 或者直接追加 PATH PATH=$PATH:$HOME/.nvm/versions/node/v15.14.0/bin修改后必须重新安装 Git hooks 使其生效:
$ lefthook install -f此后,任何运行 hooks 的程序都会获得调整后的$PATH,从而能够找到npm等工具。
Git hook 配置:commands、scripts与jobs
顶层配置中,每个 Git hook(如pre-commit、commit-msg,也可以是你自定义的 hook,如test、check-docs)下可以定义命令、脚本与作业。详见 docs/configuration/Hook.md、docs/configuration/Commands.md、docs/configuration/Scripts.md 与 docs/configuration/jobs.md。
Git hook 基本结构
# lefthook.yml # Git hook pre-commit: jobs: - run: yarn lint {staged_files} --fix stage_fixed: true # 自定义 hook check-docs: jobs: - run: yarn check-docs - run: typos源码中,hook 名称通过正则^(?P<hookName>[^.]+)\.(?:scripts|commands|jobs)识别,支持在本地配置中追加额外的自定义 hook(见 internal/config/loader.go 与 loader.go)。
commands
commands定义 hook 要执行的命令,每条命令有名字和对应的 run 选项:
# lefthook.yml pre-commit: commands: lint: ... # command options每条命令可用的选项包括:run、skip、only、tags、glob、files、file_types、env、root、exclude、fail_text、stage_fixed、interactive、use_stdin、priority。这些选项的逐个说明见 docs/configuration/Commands.md 及 docs/configuration/run.md、docs/configuration/glob.md、docs/configuration/files.md 等子文档。
scripts
脚本存放在<source_dir>/<hook-name>/目录下,是项目根目录中运行的自有可执行文件。添加一个pre-commithook 脚本的流程:
- 运行
lefthook add -d pre-commit; - 编辑
.lefthook/pre-commit/my-script.sh; - 在
lefthook.yml中登记:
# lefthook.yml pre-commit: scripts: "my-script.sh": runner: bash典型实战:写一个检查 commit 模板的脚本.lefthook/commit-msg/template_checker:
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然后在lefthook.yml中让commit-msghook 运行它:
# lefthook.yml commit-msg: scripts: "template_checker": runner: bash当执行git commit -m "bad commit text"时,template_checker会被执行;由于提交信息不匹配TICKET-123: ...模式,提交过程会被中断。
使用args为脚本追加参数。注意:配置了args后,Git 传入的参数会被省略,如需保留请使用{0}模板:
commit-msg: scripts: "template_checker": runner: bash args: "{0}"jobs
jobs(自 lefthook1.10.0起)提供更灵活的任务定义方式,同时支持命令和脚本,并支持分组以实现高级流程控制。命名 job 会跨extends和本地配置按名称合并,未命名 job 按定义顺序追加;分组(group)内的 job 可拥有自己的并行(parallel)或管道(piped)流程,分组上的glob、root、exclude会应用到组内所有嵌套 job(目前仅这三个选项作用于组级别,其余选项需在单个 job 上设置)。
# 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该配置中,migrate组内的两个 job 以管道方式依次执行(bundle install→rails db:migrate),而其余 job 并行运行。
配置加载流程小结
结合源码(internal/config/loader.go 与 internal/config/config.go),lefthook 的配置加载可以归纳为四个步骤:
- 定位主配置:按扩展名与基础名称顺序查找第一个存在的文件(也支持
LEFTHOOK_CONFIG环境变量直接指定配置文件路径,见 loader.go); - 加载次级配置:依次合并主配置的
extends、remotes远程配置,再合并可选的lefthook-local本地配置及其extends(loader.go); - 合并 hook:逐个 hook 执行合并(含
{cmd}模板替换、命名 job 按名覆盖、setup前置追加等特殊逻辑,loader.go); - 解析为结构体:
main.Merge(secondary)后统一反序列化到Config结构体,并处理colors等运行期选项(loader.go)。
理解这套流程后,你就能准确预判"哪个配置会赢",从而在团队共享配置与个人本地配置之间做出清晰的分层设计:把公共的 hook、命令放在lefthook.yml中,把个人的rc、调试开关与专属覆盖放在被 gitignore 的lefthook-local.yml中。更多顶层配置项(如colors、no_tty、skip_lfs、templates、assert_lefthook_installed)的逐项说明,可继续阅读 docs/configuration/README.md 中列出的对应子文档。
【免费下载链接】lefthookFast and powerful Git hooks manager for any type of projects.项目地址: https://gitcode.com/GitHub_Trending/le/lefthook
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考