Tóm tắt: cài pre-commit → viết .pre-commit-config.yaml (pre-commit-hooks, ruff-pre-commit, gitleaks) → pre-commit install để gắn vào .git/hooks/pre-commit → pre-commit run --all-files lần đầu → chạy lại trong CI để chặn người quên cài hook.
Luồng hoạt động
flowchart LR A[git commit] --> B[".git/hooks/pre-commit"] B --> C["pre-commit đọc .pre-commit-config.yaml"] C --> D["hook chạy trên file đã stage"] D -->|pass| E[commit được tạo] D -->|fail hoặc sửa file| F[commit bị chặn] F --> G["git add, commit lại"] E --> H[git push] H --> I["CI: pre-commit run --all-files"]
Cấu hình
Cài đặt
uv tool install pre-commit # hoặc: pipx install pre-commit
pre-commit --version
cd repo/
pre-commit install # gắn hook vào repo này (mỗi clone mới phải chạy lại)File cấu hình
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
args: [--allow-multiple-documents]
- id: check-added-large-files
args: [--maxkb=1024]
- id: check-merge-conflict
- id: detect-private-key
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.17.4
hooks:
- id: ruff-check # linter (id cũ "ruff" vẫn là alias)
args: [--fix]
- id: ruff-format # formatter, đặt sau ruff-check --fix
- repo: https://github.com/gitleaks/gitleaks
rev: v8.30.1
hooks:
- id: gitleaks # build từ source Go; pre-commit tự tải Go nếu máy chưa córev phải là tag/commit thật của repo hook. Đừng tự gõ, chạy pre-commit autoupdate để nâng lên tag mới nhất.
Cấu hình ruff trong pyproject.toml
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
select = ["E", "F", "I", "B", "UP"] # pycodestyle, pyflakes, isort, bugbear, pyupgradeBỏ qua false positive của gitleaks
[extend]
useDefault = true
[[allowlists]] # gitleaks >= 8.25; bản cũ hơn dùng [allowlist]
description = "file mẫu và test"
paths = ['''tests/fixtures/.*''', '''\.env\.example$''']Hoặc thêm comment # gitleaks:allow ở cuối dòng bị báo nhầm.
Lệnh
| Mục đích | Lệnh |
|---|---|
| Chạy mọi hook trên toàn repo | pre-commit run --all-files |
| Chạy một hook | pre-commit run ruff-check --all-files |
| Chạy trên một số file | pre-commit run --files app/main.py app/config.py |
Nâng rev lên bản mới nhất |
pre-commit autoupdate |
| Bỏ qua tạm một hook | SKIP=gitleaks git commit -m "..." |
| Bỏ qua mọi hook (hạn chế dùng) | git commit --no-verify |
| Gỡ hook khỏi repo | pre-commit uninstall |
| Xoá cache môi trường hook | pre-commit clean |
| Tự cài hook cho mọi repo clone mới có config | git config --global init.templateDir ~/.git-template && pre-commit init-templatedir ~/.git-template |
Chạy trong GitHub Actions
name: pre-commit
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.12"
- uses: actions/cache@v6
with:
path: ~/.cache/pre-commit
key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }}
- run: pipx run pre-commit run --all-files --show-diff-on-failureThêm cú pháp workflow xem GitHub Actions cheatsheet.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách xử lý |
|---|---|---|
Commit bị chặn, hook báo files were modified by this hook |
Hook (ruff-format, end-of-file-fixer) đã tự sửa file | git add -u rồi commit lại |
| Hook không chạy khi commit | Chưa pre-commit install trong clone này |
pre-commit install |
InvalidManifestError hoặc rev không tồn tại |
Gõ sai tag | pre-commit autoupdate |
| Hook gitleaks cài lâu hoặc lỗi khi build lần đầu | Hook gitleaks build bằng Go (pre-commit tự tải Go nếu thiếu, cần Internet) |
Cài sẵn Go, hoặc đổi id sang gitleaks-system (dùng binary có sẵn) / gitleaks-docker |
| ruff-check và ruff-format sửa qua lại | Rule format conflict (vd E501, ISC001) |
Đặt ruff-format sau ruff-check, bỏ rule conflict |
| CI pass nhưng máy dev fail (hoặc ngược lại) | Version ruff khác nhau giữa rev và uv.lock |
Dùng một nguồn version, cập nhật cả hai cùng lúc |
| gitleaks báo secret đã commit từ trước | Secret nằm trong lịch sử | Rotate secret, xem git-secret-leak |