pre-commit-hooks
verifiede9adc444-ab38-44b7-9315-bb613c8d730d
Wire `pre-commit` to run linters, formatters, type-checks, and secret scans automatically on every commit. Use to catch issues at commit time instead of in CI.
Metadata
Skill file
# Pre-commit Hooks
Use when wiring automated checks to run on every `git commit` — so linters, formatters, type-checks, and secret scans fire before code ever reaches CI.
## The `.pre-commit-config.yaml` Anatomy
```yaml
# .pre-commit-config.yaml
repos:
# 1. Generic whitespace/file hygiene
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-json
- id: check-merge-conflict
- id: check-added-large-files
args: ["--maxkb=1024"]
# 2. Python linter + formatter
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
# 3. Type checking (slower — consider running only on changed files)
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.0
hooks:
- id: mypy
additional_dependencies: [types-requests]
# 4. Secret scanning
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
```
## Install and First Run
```bash
pip install pre-commit
# Install the git hook
pre-commit install
# Run against ALL files once (clears the backlog so future commits are clean)
pre-commit run --all-files
# Test on a single file
pre-commit run ruff --files path/to/file.py
```
## Stages (when hooks run)
```yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.0
hooks:
- id: ruff
stages: [commit] # run on `git commit`
- repo: local
hooks:
- id: run-tests
name: run-tests
entry: pytest -x
language: system
stages: [push] # run only on `git push`, not every commit
```
## CI Parity
Run the exact same hooks in CI so local and remote are identical:
```yaml
# .github/workflows/pre-commit.yml
name: pre-commit
on: [push, pull_request]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install pre-commit
- run: pre-commit run --all-files
```
## Skipping with Cause (the discipline)
```bash
# Skipping is allowed, but ONLY with an explicit reason
SKIP=mypy git commit -m "docs: fix typo in README"
# NEVER: git commit --no-verify (bypasses EVERYTHING silently)
```
Rule: `SKIP=mypy` names the one hook you're skipping. `--no-verify` skips everything with no trail.
## Guardrails
- **Never** make `--no-verify` a habit — it silently disables all safety checks, including secret scanning.
- **Never** run slow hooks (mypy, full test suite) on every commit — push them to `stages: [push]` or leave them to CI.
- **Always** pin hook versions (`rev: v4.6.0`) — floating `main` branches break reproducibility.
- **Always** commit `.pre-commit-config.yaml` so every dev gets the same hooks.
## Pitfalls
- **Hooks too slow on every commit**: mypy or a full test suite on each commit makes devs hate pre-commit and bypass it. Keep commit-stage hooks fast (<5s).
- **Bypassing with `--no-verify` as a habit**: The moment `--no-verify` becomes the default muscle memory, all your guardrails are gone.
- **Never running `--all-files` first**: Newly-installed hooks only check changed files, so pre-existing issues slip through and CI (running `--all-files`) fails on unrelated code.
- **Version drift**: The hook runs `rev: main` locally and CI runs a different version — different results. Pin exact versions.
- **Autofix hooks that don't stage their changes**: `ruff --fix` modifies files but doesn't re-add them. The commit goes through with stale content. Ensure hooks that modify files are set up to re-stage (pre-commit handles this automatically for most).
## Verify / Checklist
- [ ] `pre-commit install` succeeded (check `.git/hooks/pre-commit` exists)
- [ ] `.pre-commit-config.yaml` committed and all versions pinned
- [ ] `pre-commit run --all-files` passes clean
- [ ] Secret scanner (gitleaks) included in the hook set
- [ ] Commit-stage hooks complete in under ~5 seconds
- [ ] CI runs `pre-commit run --all-files` for parity
- [ ] Team understands `SKIP=` (with reason) vs `--no-verify` (never)
Attached files
No attached files.