登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  软件教程

Lefthook 配置在 monorepo 中怎么让子项目复用根目录钩子

来源:17golang原创

时间:2026-11-05 12:43:39 238浏览 收藏

在 monorepo 里,Git 仓库只有一个根目录,但 frontend、backend、tools 往往各自有 package.json、Gemfile 或独立的检查命令。Lefthook 的稳定做法是:把 Git 钩子安装在仓库根目录,根配置用 extends 汇总子项目配置,再给每个子项目任务设置自己的 root 和文件匹配规则。这样提交入口只有一套,命令却不会跑错目录。

官方地址:https://github.com/evilmartians/lefthook

不要在每个子目录重复安装 Lefthook。根目录保留唯一主配置,子项目只提供可合并的任务定义;执行目录交给 root: "frontend/" 这类任务级配置,文件过滤仍然按照 Git 仓库根目录计算。

先把目录和职责分开

假设仓库结构如下,两个应用共用一次 pre-commit,但检查器不能互相读取对方的依赖目录:

monorepo/
├── lefthook.yml
├── frontend/
│   ├── package.json
│   └── src/
├── backend/
│   ├── pyproject.toml
│   └── app/
└── tools/
    └── scripts/

这里有三个容易混淆的概念:

  • lefthook.yml 是根目录的入口,负责把配置组装起来;
  • root 改变具体任务执行时的当前工作目录;
  • glob 仍从实际 Git 根目录匹配路径,不能把它理解成“先进入 root 再从零开始匹配”。

如果你把 frontend 和 backend 初始化成两个完全独立的 Git 仓库,这套方案就不再是本文讨论的范围;本文只针对一个 Git 根目录下的多个子项目。

在根目录登记唯一的 Lefthook 入口

操作路径是“项目根目录 → 新建 lefthook.yml → 写入 extends”。根配置不承担所有命令细节,只负责把子项目配置纳入同一套钩子。

# 这行注释说明配置入口只放在 Git 仓库根目录
extends:
  # 让前端任务从自己的配置文件维护
  - frontend/lefthook.yml
  # 让后端任务保持与前端相同的提交入口
  - backend/lefthook.yml

pre-commit:
  # 根层只负责统一调度,具体任务由 extends 文件提供
  parallel: true

可见状态应当是:根目录存在一个主配置,子项目文件只作为被合并的配置来源。Lefthook 官方说明中,extends 的内容会合并到主配置;如果多个任务希望在合并时被识别为同一个任务,给它们稳定的 name 会比依赖数组位置更容易维护。

根目录 Lefthook 配置通过 extends 连接 frontend 和 backend 子项目配置的界面示意
图1:根入口与子项目配置的原创操作示意图,不是截图。

在子项目文件里设置 root 和任务

操作路径是“frontend → lefthook.yml → pre-commit.jobs → 设置 root、glob、run”。下面的配置把前端与后端拆成两个命名任务,并让命令在各自目录里执行:

# frontend/lefthook.yml:命令在 frontend/ 下执行
pre-commit:
  jobs:
    - name: frontend-lint
      # 注意保留末尾斜杠,避免路径拼接出现歧义
      root: "frontend/"
      # glob 仍按 Git 根目录匹配,所以这里写 frontend 下的相对路径
      glob: "frontend/**/*.{js,ts,tsx}"
      # staged_files 会把匹配到的暂存文件交给前端脚本
      run: npm run lint -- {staged_files}

# backend/lefthook.yml:命令在 backend/ 下执行
pre-commit:
  jobs:
    - name: backend-check
      # Python 工具读取 backend/ 下的 pyproject.toml
      root: "backend/"
      # 只筛选后端源码,避免前端提交触发后端检查
      glob: "backend/**/*.py"
      # 这里使用仓库根目录传入的文件路径,命令仍在 backend/ 中启动
      run: python -m ruff check {staged_files}

这里最容易出现的误区是把 glob 写成 "src/**/*.{js,ts}"。当 Lefthook 从仓库根目录计算暂存文件时,frontend 的任务应写成 "frontend/src/**/*.{js,ts}";而命令的当前目录由 root: "frontend/" 决定。两个字段解决的是不同问题。

如果你使用的是嵌套任务组,可以把共同的 root、glob、env 或 files 放到 group 上,让组内任务继承;需要被 extends 或本地配置稳定合并的组,也应设置名称。

把钩子安装到根目录并查看合并结果

操作路径是“项目根目录 → Lefthook 命令 → install → dump”。这一步先安装一次根钩子,再查看 Lefthook 实际使用的合并配置:

# 在 monorepo 根目录执行,确保 Git 根和配置入口一致
lefthook install

# 输出 main config、extends 和 local config 合并后的实际配置
lefthook dump

# 手动运行一次提交钩子,观察哪些任务被匹配
lefthook run pre-commit

界面或命令输出里应能看到 frontend-lint 与 backend-check 两个命名任务。提交只改了 frontend/src/App.tsx 时,前端任务应保留,后端任务应因 glob 没有匹配文件而跳过;反过来修改 backend/app/main.py 也是同理。

Lefthook 合并配置后的 pre-commit 任务、root 工作目录和 glob 匹配状态界面示意
图2:合并后的钩子任务与路径匹配状态示意图,不是运行截图。

路径匹配为空时怎么判断

遇到任务显示跳过,不要先把 root 改成仓库根目录。按下面的顺序看最省时间:

  1. 先确认文件已经暂存,因为 {staged_files} 只代表当前提交候选文件。
  2. 再比较 glob 与 Git 根目录的相对路径,例如 frontend/src/index.ts 是否真的带有 frontend/ 前缀。
  3. 最后确认 root 以目录形式书写,并且命令需要的 package 或配置文件确实存在于该目录。
# 把目标文件加入暂存区,模拟一次前端提交候选
git add frontend/src/index.ts

# 只运行提交钩子,不创建提交,便于查看匹配和工作目录
lefthook run pre-commit

如果 dump 里任务存在但运行时为空,通常是文件没有暂存,或者 glob 少写了子项目目录。若任务能匹配到文件但命令报找不到 package,通常是 root 写错;此时修正执行目录,而不是扩大 glob。

常见配置误区

每个子项目都执行一次 lefthook install

在一个 Git 仓库中重复安装会让维护者误以为每个子项目都是独立入口。把安装动作固定在根目录,子项目只维护被 extends 的配置即可。

把 root 当成 glob 的起点

root 是命令工作目录和路径过滤边界,官方文档明确说明 glob 仍按 Git 仓库真实根目录计算。只要理解这一点,绝大多数“任务为什么跳过”都能直接定位。

直接复制整段 pre-commit 而不命名任务

配置拆分后,多个文件可能同时定义同一个钩子。给具体 job 或 group 写上稳定的 name,再用 dump 查看最终合并结果,后续覆盖和扩展会更可控。

最后确认这套结构是否适合你的仓库

如果你的目标是“一个 Git 仓库、一套根钩子、多个子项目各自运行检查”,推荐保留下面的最小结构:

monorepo/
├── lefthook.yml                 # 唯一入口:extends 子项目配置
├── frontend/lefthook.yml       # root: frontend/,glob: frontend/**
└── backend/lefthook.yml        # root: backend/,glob: backend/**

安装时只在根目录运行 lefthook install,排查时先运行 lefthook dump,执行目录看 root,文件范围看 glob。这四个动作能把“钩子没有触发”“命令读错配置”和“子项目互相干扰”拆成三个独立问题处理。

官方文档入口:https://lefthook.dev/configuration/

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>