Add Pre-commit Hooks for Lint and Tests
Install the pre-commit framework, run lint and fast tests before every commit, and keep hooks reproducible across the team instead of relying on manual discipline.
Task
Configure a repository-managed pre-commit hook that blocks commits when linting or fast tests fail.
When to use this
Broken formatting or failing tests repeatedly reach CI because local checks are manual. The team wants the same checks on every developer machine.
Prerequisites
- Python 3.9+ or pipx for the pre-commit framework
- A lint script and a fast test command in package.json
- Git repository write access
Steps
1. Install the pre-commit framework once per machine, outside the project virtual environment.
pipx install pre-commitExpected output: The pre-commit command is available on PATH.
2. Add a configuration that runs local project scripts. Use local hooks so the same package manager and versions as CI are used.
repos:
- repo: local
hooks:
- id: lint
name: lint
entry: npm run lint
language: system
pass_filenames: false
- id: unit-tests
name: unit tests
entry: npx vitest run --changed
language: system
pass_filenames: false
Expected output: A .pre-commit-config.yaml file exists at the repository root.
3. Install the Git hook and run all checks once to establish a baseline.
pre-commit install && pre-commit run --all-filesExpected output: Hooks are installed and the baseline run reports pass or fixable issues.
4. Verify the hook blocks a bad commit by introducing a lint error and attempting to commit.
git commit -m 'test hook'Expected output: The commit is rejected and the lint failure is shown.
Troubleshooting
Problem
Hooks are slow
Solution
Run only changed files where supported, keep unit tests fast, and leave slower integration tests to CI.
Problem
Hooks differ across machines
Solution
Pin hook versions and use local hooks that call the project's locked package manager.
Limitations
- Hooks can be bypassed with --no-verify; CI must remain the authoritative gate.
- Pre-commit is client-side and cannot enforce checks on users who never install it.
Why repository-managed hooks
A hook living in one developer’s .git/hooks is invisible to everyone else. The pre-commit framework stores the configuration in the repository, pins versions, and installs the hook per clone.
repos:
- repo: local
hooks:
- id: lint
name: lint
entry: npm run lint
language: system
pass_filenames: false
pipx install pre-commit
pre-commit install
pre-commit run --all-files
Verify
- A commit with a lint error is rejected.
- A clean commit succeeds without
--no-verify. - A second developer clones the repo, runs
pre-commit install, and gets the same checks. - CI still runs the same scripts; hooks are a fast local filter, not a replacement.
Scope
Keep hooks fast: formatting, lint, type-check, and unit tests. Put end-to-end, performance, and integration suites in CI. If a hook auto-fixes files, re-stage them and commit again.