Python 项目为什么要用 pre-commit?它和 CI 有什么区别?
简化版
pre-commit 用 Git hooks 在提交前自动运行格式化、lint、类型检查、密钥扫描等任务,提前拦截低级问题。CI 是远端流水线,负责最终验证;pre-commit 更靠前、更快,CI 更权威、更完整,两者应配合使用。
详细版
常见 .pre-commit-config.yaml 会配置 ruff、black、mypy、detect-secrets 等工具。开发者提交前自动执行,失败就阻止提交。
pre-commit install
pre-commit run --all-files
它不能替代 CI,因为本地 hook 可能没安装、被跳过或环境不同。CI 仍要跑测试和质量检查。面试重点是:把质量检查前移,提高反馈速度,但最终门禁仍在 CI。
完整版教学
一、为什么需要提交前检查
如果格式错误、未排序 import、明显 lint 问题都等 CI 才发现,反馈会慢。pre-commit 把这些问题在提交前解决,减少无效 CI 和代码审查噪音。
编辑 -> git commit -> pre-commit 检查 -> CI -> review
越早发现问题,修复成本越低。
二、pre-commit 能跑什么
它可以跑格式化、lint、类型检查、YAML/JSON 校验、换行检查、大文件检查、密钥扫描等。
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
hooks:
- id: ruff
- id: ruff-format
每个 hook 有固定版本,团队结果更一致。
三、和 CI 的区别
pre-commit 在本地运行,快但不绝对可信;CI 在远端标准环境运行,慢一点但权威。
| 维度 | pre-commit | CI |
|---|---|---|
| 位置 | 本地提交前 | 远端合并前 |
| 速度 | 快 | 较慢 |
| 可信度 | 可被跳过 | 更权威 |
| 范围 | 快速检查 | 完整测试/构建 |
pre-commit 是第一道筛子,CI 是最终门禁。
四、为什么要固定版本
如果每个人本地工具版本不同,格式化结果可能来回变。pre-commit 配置里固定 hook rev,可以让团队统一。
dev A ruff 0.x
dev B ruff 1.x
格式规则不同 -> diff 抖动
固定版本后,升级工具变成一次明确变更。
五、什么不适合放 pre-commit
太慢的全量集成测试、依赖外部服务的测试、需要大量数据的检查不适合每次 commit 都跑。否则开发者会频繁跳过 hook。
适合: 1-10 秒快速检查
不适合: 10 分钟端到端测试
慢任务放 CI,或者手动触发。
六、工程落地要注意什么
README 要写安装命令,CI 也要跑同样检查。否则有人没装 hook,问题仍会进仓库。可以在 CI 中执行 pre-commit run --all-files 保持一致。
pre-commit run --all-files
不要把 hook 配成“只有你本机能跑”,要保证跨平台和依赖可安装。
七、和 Python 工具链怎么配合
Python 项目里常见组合是 ruff 负责 lint 和格式,mypy/pyright 负责类型,pytest 负责测试,detect-secrets/gitleaks 负责密钥扫描。pre-commit 负责把这些命令串到提交流程里。
ruff -> formatting/lint
mypy -> type check
pytest -> tests
detect-secrets -> secret scan
不要为了“工具多”而堆工具,重点是每个 hook 的职责清楚、速度可接受。
八、升级和维护
pre-commit 配置不是写完就不管。工具版本要定期升级,可以用 pre-commit autoupdate,升级后在 CI 跑全量检查。大版本升级可能带来大量格式变化,最好单独提交,避免和业务代码混在一起。
pre-commit autoupdate
pre-commit run --all-files
这样 review 时能区分“工具升级导致的机械修改”和真实业务改动。
九、常见误区与追问
- 误区:用了 pre-commit 就不用 CI。 本地 hook 可跳过,CI 仍是最终门禁。
- 误区:所有测试都放 pre-commit。 太慢会影响开发体验,应该分层。
- 误区:hook 不用固定版本。 不固定会导致团队结果不一致。
- 追问:如何临时跳过? Git 支持
--no-verify,但不应常用。 - 追问:pre-commit 能扫密钥吗? 可以配 detect-secrets、gitleaks 等。
- 追问:格式化失败怎么办? 很多 hook 会自动修改文件,重新 add/commit。
- 追问:CI 怎么复用? 跑
pre-commit run --all-files。
十、加强记忆
pre-commit 记成“提交前的快速质量闸”。它把格式、lint、密钥扫描前移,减少低级问题进入 review;CI 负责标准环境下完整验证。快检查放本地,慢检查放 CI,两者配合才舒服。