# Pre-commit Hook Setup Automated linting and validation before every commit. ## Installation ### 1. Install pre-commit **macOS/Linux (via pip):** ```bash pip install pre-commit # Or with pipx (recommended) pipx install pre-commit ``` **macOS (via Homebrew):** ```bash brew install pre-commit ``` **Verify installation:** ```bash pre-commit --version ``` ### 2. Install the hooks From the project root: ```bash 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 `.md` files) - **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 check - **`cargo 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 `.ncl` files #### Nushell - **`nu --check`** - Script validation for `.nu` files ### Security - **`cargo audit`** - Dependency vulnerability scan (manual stage only) ## Usage ### Automatic (Recommended) Pre-commit runs automatically on `git commit`: ```bash git add docs/README.md git commit -m "Update README" # Pre-commit hooks run automatically ``` ### Manual Run Run all hooks on all files: ```bash pre-commit run --all-files ``` Run specific hook: ```bash 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: ```bash pre-commit run ``` ### Skip Hooks (Emergency Only) To bypass hooks (use sparingly): ```bash 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`: ```yaml # Change this: entry: bash -c 'vale docs/ || exit 0' # To this: entry: bash -c 'vale docs/' ``` ## Updating Hooks Update to latest hook versions: ```bash 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: ```bash # 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: ```bash 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: ```json { "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: ```ini [*.md] write-good.Weasel = NO # Disable weasel word warnings ``` Or set higher alert level: ```ini MinAlertLevel = error # Only show errors, not warnings ``` ## Best Practices ### 1. Run Before First Commit After installing hooks, test them: ```bash 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: ```bash 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: ```bash 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: ```bash pre-commit uninstall ``` Re-enable: ```bash pre-commit install ``` Disable specific hook (edit `.pre-commit-config.yaml`): ```yaml - id: cargo-clippy # Add this to disable: stages: [manual] ``` Then run manually with: ```bash 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: ```bash pip install pre-commit pre-commit install ``` --- **See also**: - [.pre-commit-config.yaml](../.pre-commit-config.yaml) - Hook configuration - [development.md](development.md) - Development workflow - [CI configuration](../.github/workflows/ci.yml) - GitHub Actions