Pre-commit hooks catch code quality issues before they enter your repository, preventing broken builds and maintaining consistent standards. This guide covers setting up the pre-commit framework for multi-language projects with linting, formatting, security scanning, and custom checks.
Installing pre-commit
# Install pre-commit framework
pip install pre-commit
# Or with Homebrew
brew install pre-commit
# Verify
pre-commit --version
Basic Configuration
# .pre-commit-config.yaml
repos:
# General hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-json
- id: check-merge-conflict
- id: check-added-large-files
args: [--maxkb=500]
- id: detect-private-key
- id: check-case-conflict
- id: mixed-line-ending
# Python
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
# JavaScript/TypeScript
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v9.5.0
hooks:
- id: eslint
types: [javascript, typescript]
additional_dependencies:
- eslint@9.5.0
- typescript
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
hooks:
- id: prettier
types_or: [javascript, typescript, json, yaml, markdown, css]
# Security
- repo: https://github.com/Yelp/detect-secrets
rev: v1.5.0
hooks:
- id: detect-secrets
# Docker
- repo: https://github.com/hadolint/hadolint
rev: v2.13.0
hooks:
- id: hadolint-docker
# Shell scripts
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.10.0.1
hooks:
- id: shellcheck
Installation in Repository
# Install hooks in the current repo
pre-commit install
# Also install commit-msg hooks (for conventional commits)
pre-commit install --hook-type commit-msg
# Run against all files (useful for first-time setup)
pre-commit run --all-files
# Run specific hook
pre-commit run ruff --all-files
# Update hooks to latest versions
pre-commit autoupdate
Language-Specific Configurations
Go
- repo: https://github.com/dnephin/pre-commit-golang
rev: v0.5.1
hooks:
- id: go-fmt
- id: go-vet
- id: go-imports
- id: golangci-lint
Rust
- repo: https://github.com/doublify/pre-commit-rust
rev: v1.0
hooks:
- id: fmt
- id: cargo-check
- id: clippy
PHP
- repo: https://github.com/digitalpulp/pre-commit-php
rev: 1.4.0
hooks:
- id: php-lint
- id: php-cs-fixer
args: [--rules=@PSR12]
Conventional Commits
- repo: https://github.com/compilerla/conventional-pre-commit
rev: v3.3.0
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
args: [feat, fix, docs, style, refactor, test, chore, ci]
# Enforces commit messages like:
# feat: add user registration
# fix: resolve login timeout issue
# docs: update API documentation
Custom Hooks
- repo: local
hooks:
- id: no-console-log
name: Check for console.log
entry: bash -c "! grep -rn console.log --include=*.ts --include=*.js src/"
language: system
pass_filenames: false
- id: check-env-example
name: Verify .env.example is up to date
entry: bash -c "diff <(grep -oP '^[A-Z_]+=' .env | sort) <(grep -oP '^[A-Z_]+=' .env.example | sort)"
language: system
pass_filenames: false
- id: run-tests
name: Run unit tests
entry: npm test
language: system
pass_filenames: false
stages: [pre-push]
CI Integration
# Run pre-commit in CI to catch anything missed locally
# .github/workflows/lint.yml
name: Lint
on: [pull_request]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: pre-commit/action@v3.0.1
Team Onboarding
# Add to project README or CONTRIBUTING.md:
# 1. Install pre-commit: pip install pre-commit
# 2. Install hooks: pre-commit install
# 3. Hooks run automatically on every commit
# To bypass hooks in emergencies (discouraged):
git commit --no-verify -m "emergency fix"
# To skip specific hooks:
SKIP=eslint git commit -m "work in progress"
Summary
Pre-commit hooks are the first line of defense for code quality, catching issues seconds after writing code rather than minutes later in CI. The pre-commit framework supports every major language and tool, from linters and formatters to security scanners and custom checks. Start with the basic hooks (trailing whitespace, large files, secret detection) and gradually add language-specific linting. The investment in setup pays back immediately through cleaner pull requests, fewer CI failures, and consistent code style across the team.