350 lines
6.1 KiB
Markdown
350 lines
6.1 KiB
Markdown
|
|
# 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
|