Pre-commit Hooks Setup
Overview
Pre-commit hooks automatically validate code quality, formatting, and commit messages before allowing commits. We use Husky + lint-staged for consistent code quality across all projects.
Quick Start
Installation
# Install Husky and lint-staged
npm install --save-dev husky lint-staged
# Initialize Husky
npx husky install
npm pkg set scripts.prepare="husky install"
# Create pre-commit hook
npx husky add .husky/pre-commit "npx lint-staged"
chmod +x .husky/pre-commit
Basic Configuration
Add to package.json:
{
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.py": [
"black",
"isort",
"flake8"
],
"*.{css,scss}": [
"stylelint --fix",
"prettier --write"
],
"*.{json,yaml,yml,md}": [
"prettier --write"
]
}
}
Hook Execution Flow
graph TD
A[git commit] --> B[Husky intercepts]
B --> C[lint-staged runs on changed files]
C --> D{Linting & formatting}
D -->|Pass| E[commitlint validates message]
D -->|Fail| F[Commit rejected]
E -->|Pass| G[Commit succeeds]
E -->|Fail| F
F --> H[Fix issues and retry]
style G fill:#00b894
style F fill:#ff6b6b
Core Configurations
Pre-commit Hook
# .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
npm run type-check
Advanced lint-staged Config
For more control, create lint-staged.config.js:
module.exports = {
'*.{js,jsx,ts,tsx}': [
'eslint --fix',
'prettier --write',
() => 'tsc --noEmit',
],
'*.py': [
'black --line-length 88',
'isort --profile black',
'flake8 --max-line-length 88',
],
'*.{css,scss}': ['stylelint --fix', 'prettier --write'],
'*.{md,mdx}': ['markdownlint --fix', 'prettier --write'],
'*.{json,yaml,yml}': ['prettier --write'],
'package.json': ['prettier --write', 'npm audit --audit-level moderate'],
}
Commit Message Validation
Setup Commitlint
# Install
npm install --save-dev @commitlint/cli @commitlint/config-conventional
# Configure
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
# Create commit-msg hook
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
chmod +x .husky/commit-msg
Custom Commitlint Rules
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', 'ci', 'perf'],
],
'scope-enum': [
2,
'always',
['auth', 'api', 'ui', 'database', 'config', 'deps', 'security'],
],
'subject-max-length': [2, 'always', 72],
},
}
Tool Configurations
ESLint
// .eslintrc.js - Basic setup
module.exports = {
extends: [
'@typescript-eslint/recommended',
'prettier', // Must be last
],
plugins: ['@typescript-eslint', 'import'],
rules: {
'@typescript-eslint/no-unused-vars': 'error',
'import/order': ['error', { 'newlines-between': 'always' }],
},
}
For detailed ESLint configuration, see ESLint documentation.
Prettier
Python Tools
# pyproject.toml
[tool.black]
line-length = 88
target-version = ['py39', 'py310', 'py311']
[tool.isort]
profile = "black"
line_length = 88
For detailed Python tool configurations, see Black, isort, and Flake8 documentation.
Pre-push Hook
# .husky/pre-push
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run test:ci
npm audit --audit-level moderate
Team-Specific Configurations
Frontend Projects
{
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write",
"jest --bail --findRelatedTests --passWithNoTests"
],
"*.{css,scss}": ["stylelint --fix", "prettier --write"]
}
}
Backend Projects
{
"lint-staged": {
"*.py": [
"black --check",
"isort --check-only",
"flake8",
"pytest tests/ -x"
]
}
}
Performance Optimization
Skip CI Environment
# .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# Skip in CI
if [ "$CI" = "true" ]; then
exit 0
fi
npx lint-staged
Profile Execution
Troubleshooting
Hooks Not Running
# Reinstall Husky
npx husky install
# Fix permissions
chmod +x .husky/pre-commit
chmod +x .husky/commit-msg
# Verify Git config
git config core.hooksPath
Tool Not Found Errors
ESLint/Prettier Conflicts
Ensure Prettier is last in ESLint extends:
Skip Hooks (Emergency Only)
# Skip pre-commit (use sparingly)
git commit --no-verify -m "hotfix: critical production fix"
# Skip pre-push
git push --no-verify
IDE Integration
VS Code
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.organizeImports": true
},
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
}
}
Install recommended extensions: - ESLint - Prettier - Python (with Black, Flake8)
ClickUp Integration
Pre-commit hooks work seamlessly with our ClickUp workflow:
- Hooks validate code quality before commit
- Conventional commits enable automatic ClickUp status updates
- Commit messages link to ClickUp tasks (e.g.,
feat(CU-123): add feature)
See GitHub-ClickUp Integration for details.
Common Commands
# Run lint-staged manually
npx lint-staged
# Test commitlint locally
echo "feat: test message" | npx commitlint
# Check specific files
npx eslint src/**/*.ts --fix
npx prettier --write "src/**/*.{js,json}"
# Python checks
black --check .
isort --check-only .
flake8 .
Related Documentation
- Git Commit Standards - Detailed commit message guidelines
- Git Workflow - Complete Git workflow with hooks
- Code Quality Standards - Overall quality standards
- GitHub-ClickUp Workflow - Integration workflow
External Resources
Last Updated: 2025-01-20 Tools Required: Husky, lint-staged, ESLint, Prettier, commitlint