6.1 KiB
Pre-commit Hook Setup
Automated linting and validation before every commit.
Installation
1. Install pre-commit
macOS/Linux (via pip):
pip install pre-commit
# Or with pipx (recommended)
pipx install pre-commit
macOS (via Homebrew):
brew install pre-commit
Verify installation:
pre-commit --version
2. Install the hooks
From the project root:
pre-commit install
This creates .git/hooks/pre-commit that runs automatically on git commit.
What Gets Checked
The pre-commit hook runs these checks automatically before every commit:
General Checks
- Trailing whitespace - Removes trailing spaces (except in
.mdfiles) - End-of-file fixer - Ensures files end with newline
- YAML/TOML/JSON validation - Syntax checking
- Large files - Prevents files >1MB
- Merge conflicts - Detects conflict markers
- Line endings - Normalizes to LF
Language-Specific Linting
Rust
cargo fmt- Code formatting checkcargo clippy- Linting with warnings as errors
Shell Scripts
shellcheck- Bash/shell script analysis
Markdown (docs/ only)
markdownlint-cli2- Syntax validation (MD rules)vale- Prose quality (weasel words, etc.)
Nickel
nickel typecheck- Type checking for.nclfiles
Nushell
nu --check- Script validation for.nufiles
Security
cargo audit- Dependency vulnerability scan (manual stage only)
Usage
Automatic (Recommended)
Pre-commit runs automatically on git commit:
git add docs/README.md
git commit -m "Update README"
# Pre-commit hooks run automatically
Manual Run
Run all hooks on all files:
pre-commit run --all-files
Run specific hook:
pre-commit run cargo-fmt --all-files
pre-commit run markdownlint-cli2 --all-files
pre-commit run vale --all-files
Run hooks on staged files only:
pre-commit run
Skip Hooks (Emergency Only)
To bypass hooks (use sparingly):
git commit --no-verify -m "Emergency fix"
Note: CI will still run all checks, so bypassing locally doesn't skip validation.
Hook Behavior
Blocking vs Non-Blocking
Blocking (commit fails if check fails):
- cargo fmt
- cargo clippy
- markdownlint-cli2
- shellcheck
- nickel typecheck
- nushell check
Non-Blocking (warnings logged, commit succeeds):
- vale (prose quality warnings don't block commits)
Vale Configuration
Vale is configured to be informative but not blocking:
- 0 errors → Commit succeeds
- Warnings → Logged but commit proceeds
- Encourages better prose without being disruptive
To make Vale blocking, edit .pre-commit-config.yaml:
# Change this:
entry: bash -c 'vale docs/ || exit 0'
# To this:
entry: bash -c 'vale docs/'
Updating Hooks
Update to latest hook versions:
pre-commit autoupdate
This updates the rev: fields in .pre-commit-config.yaml.
Troubleshooting
Hook fails: "command not found"
Problem: Tool not installed (e.g., cargo, vale, nickel, nu)
Solution: Install the missing tool:
# Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Vale
brew install vale # macOS
# See https://vale.sh for other platforms
# Nickel
cargo install nickel-lang-cli
# Nushell
cargo install nu
Hooks are slow
Problem: cargo clippy takes 30+ seconds
Solution: Use --no-verify for quick commits, let CI handle full validation:
git commit --no-verify -m "WIP: quick fix"
# Later, when ready:
pre-commit run --all-files
Markdownlint fails on valid Markdown
Problem: False positive from markdownlint
Solution: Update .markdownlint-cli2.jsonc to disable the rule:
{
"config": {
"default": true,
"MD013": false, // Example: disable line length
"MD033": false // Example: allow HTML
}
}
Vale warnings are too noisy
Problem: Too many prose quality warnings
Solution: Adjust .vale.ini to disable specific rules:
[*.md]
write-good.Weasel = NO # Disable weasel word warnings
Or set higher alert level:
MinAlertLevel = error # Only show errors, not warnings
Best Practices
1. Run Before First Commit
After installing hooks, test them:
pre-commit run --all-files
Fix any issues before committing.
2. Keep Commits Small
Smaller commits = faster hook execution.
3. Stage Incrementally
Stage related changes together:
git add docs/README.md docs/build.md
git commit -m "Update build docs"
# Only checks staged files
4. Use --no-verify Sparingly
Reserve --no-verify for emergencies:
- Hotfixes
- Reverts
- Time-critical patches
CI will catch issues anyway.
5. Update Hooks Regularly
Monthly:
pre-commit autoupdate
pre-commit run --all-files
Integration with CI
Pre-commit hooks mirror CI checks:
Local (pre-commit):
- Fast feedback before commit
- Catches obvious issues early
- Optional (can bypass with
--no-verify)
CI (GitHub Actions):
- Comprehensive validation
- Required for PRs
- Cannot be bypassed
Both run the same checks, so passing locally = passing CI.
Disabling Hooks Temporarily
Disable all hooks:
pre-commit uninstall
Re-enable:
pre-commit install
Disable specific hook (edit .pre-commit-config.yaml):
- id: cargo-clippy
# Add this to disable:
stages: [manual]
Then run manually with:
pre-commit run cargo-clippy --all-files --hook-stage manual
Summary
Pre-commit hooks provide:
- ✅ Automated quality checks before every commit
- ✅ Fast feedback (catch issues in seconds, not minutes)
- ✅ Consistent standards across all contributors
- ✅ Optional but recommended (can bypass if needed)
Install once, benefit forever:
pip install pre-commit
pre-commit install
See also:
- .pre-commit-config.yaml - Hook configuration
- development.md - Development workflow
- CI configuration - GitHub Actions