# Quality Checks & Pre-commit Hooks **When to use:** Setting up your dev environment, understanding CI/local parity, or debugging pre-commit failures. ## Prerequisites - Rust toolchain installed (`rustup`) - `jscpd` for duplication checks: `npm install -g jscpd` - Go toolchain (for SDK work) - `goimports`: `go install golang.org/x/tools/cmd/goimports@latest` - `golangci-lint` (optional): `go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest` ## Quick Start ```bash # Run all quality checks (same as CI) make quality # Auto-fix formatting (Rust) make fmt # Auto-fix formatting (Go) make go-fmt # See clippy errors make lint # Lint Go SDK make go-lint ``` ## Pre-commit Hook (v2) The pre-commit hook at `.git/hooks/pre-commit` uses a **two-phase approach**: ### Phase 1: Auto-fix - Runs formatters (`cargo fmt`, `gofmt`, `goimports`) - Re-stages formatted files automatically - Transparent to the developer ### Phase 2: Verify - Checks formatting (should pass after phase 1) - Runs linters (`cargo clippy`, `go vet`, `golangci-lint`) - Checks file length (max 500 lines) - Blocks commit if any check fails ### Languages Supported | Language | Format | Lint | File Length | |----------|--------|------|-------------| | Rust | `cargo fmt` | `cargo clippy` | ✅ | | Go | `gofmt` + `goimports` | `go vet` + `golangci-lint` | ✅ | ### Smart Detection - Only checks staged files (fast feedback) - Auto-detects Go modules (walks up to find `go.mod`) - Skips checks if no Rust/Go files are staged - Re-stages files after formatting ### Installing the Hook The hook should already exist. If not: ```bash chmod +x .git/hooks/pre-commit ``` ### Bypassing (Emergency Only) ```bash # Skip pre-commit hook (logged, use sparingly) git commit --no-verify -m "emergency fix" ``` ## What Gets Checked ### Rust | Check | Command | What it catches | |-------|---------|-----------------| | Format | `cargo fmt --check` | Inconsistent formatting | | Lint | `cargo clippy -- -D warnings` | Code smells, potential bugs | | Duplication | `jscpd` (CI only) | Copy-pasted code blocks | | Tests | `cargo test` (CI only) | Broken functionality | ### Go (SDK) | Check | Command | What it catches | |-------|---------|-----------------| | Format | `gofmt -l` | Inconsistent formatting | | Imports | `goimports` | Unorganized imports | | Vet | `go vet ./...` | Suspicious constructs | | Lint | `golangci-lint run` | Code quality issues | ### Universal | Check | Threshold | What it catches | |-------|-----------|-----------------| | File length | 500 lines max | Files too large to reason about | ## Enforced Lints These are set to `deny` in `Cargo.toml` (CI will fail): | Lint | Why | |------|-----| | `clippy::unwrap_used` | Panics are forbidden in production code | | `clippy::expect_used` | Panics are forbidden in production code | | `clippy::panic` | Explicit panics are forbidden in production code | **Tests are exempt** via `clippy.toml`: - `allow-unwrap-in-tests = true` - `allow-expect-in-tests = true` - `allow-panic-in-tests = true` To add a new enforced lint, update `[workspace.lints.clippy]` in root `Cargo.toml`. ## Troubleshooting ### "Format check failed" ```bash make fmt # Auto-fix Rust make go-fmt # Auto-fix Go git add -u # Re-stage (hook does this automatically) ``` ### "Clippy warnings treated as errors" Fix the warnings. Common ones: ```rust // Bad: unused variable let x = 5; // Good: prefix with underscore let _x = 5; ``` ### "go vet failed" Check for suspicious constructs: ```bash cd sdk/go/steme && go vet ./... ``` ### "File too long" Split into smaller modules. Keep files under 500 lines for maintainability. ### "Duplication detected" Refactor the duplicated code into a shared function or module. ## CI/Local Parity The pre-commit hook runs a subset of what CI runs (format, lint, file length). Full CI also runs: - Tests (`cargo test`, `go test`) - Duplication detection (`jscpd`) - Security audit (`cargo audit`) ## Performance Target: **<10 seconds** on staged files | Scenario | Time | |----------|------| | No Rust/Go files staged | <0.2s | | Go files only (warm) | <2s | | Rust files only (warm) | <3s | | Both (warm) | <5s | | Cold compile | ~11s | ## Related - [Testing Guide](./testing.md) - Running tests - [Rust Guidelines](../backend/rust-guidelines.md) - Code standards