Git Hooks e Automazione: Quality Gates Automatici
I Git hooks sono script che Git esegue automaticamente in risposta a eventi (commit, push, merge). Permettono di automatizzare verifiche, linting, testing e validazioni, creando quality gates che prevengono problemi prima che raggiungano il repository remoto. Con strumenti come Husky e lint-staged, configurare hooks diventa semplice ed efficace.
🎯 Cosa Imparerai
- Cosa sono i Git hooks e quando vengono eseguiti
- Pre-commit hook per linting e formatting
- Commit-msg hook per conventional commits
- Husky e lint-staged per gestire hooks facilmente
Cosa Sono i Git Hooks
I Git hooks sono script salvati in .git/hooks/ che Git esegue automaticamente
in momenti specifici del workflow. Possono essere scritti in qualsiasi linguaggio eseguibile
(bash, Python, Node.js, ecc.).
📋 Hooks Principali
- pre-commit: Prima di creare un commit (linting, tests)
- prepare-commit-msg: Prepara messaggio commit (template)
- commit-msg: Valida messaggio commit (conventional commits)
- post-commit: Dopo commit creato (notifiche)
- pre-push: Prima di push (test suite completa)
- pre-rebase: Prima di rebase
Pre-Commit Hook: Linting e Formatting
Il pre-commit hook è il più usato. Verifica codice prima di committare:
#!/bin/bash
# Esegui ESLint su file staged
npm run lint
if [ $? -ne 0 ]; then
echo "❌ Linting failed. Fix errors before committing."
exit 1
fi
# Esegui test
npm test
if [ $? -ne 0 ]; then
echo "❌ Tests failed. Fix tests before committing."
exit 1
fi
echo "✅ All checks passed!"
exit 0
chmod +x .git/hooks/pre-commit
Problema: Hook Non Condivisi
Gli hook in .git/hooks/ non vengono committati (`.git` è gitignored).
Ogni sviluppatore deve configurarli manualmente. Soluzione: Husky.
Husky: Gestione Hook Semplificata
Husky permette di definire hooks in package.json e condividerli
con il team via Git.
# Installa Husky
npm install --save-dev husky
# Inizializza Husky
npx husky init
# Crea .husky/ directory con hooks condivisibili
# Crea pre-commit hook
npx husky add .husky/pre-commit "npm run lint && npm test"
# File creato: .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run lint && npm test
{{ '{' }}
"scripts": {{ '{' }}
"lint": "eslint . --ext .ts,.tsx",
"format": "prettier --write .",
"test": "jest",
"prepare": "husky install"
{{ '}' }},
"devDependencies": {{ '{' }}
"husky": "^8.0.0"
{{ '}' }}
{{ '}' }}
Lint-Staged: Lintigare Solo File Modificati
lint-staged esegue linting/formatting solo sui file staged, non sull'intero progetto. Molto più veloce!
npm install --save-dev lint-staged
{{ '{' }}
"lint-staged": {{ '{' }}
"*.{{ '{' }}ts,tsx{{ '}' }}": [
"eslint --fix",
"prettier --write"
],
"*.{{ '{' }}js,jsx{{ '}' }}": [
"eslint --fix",
"prettier --write"
],
"*.css": [
"prettier --write"
]
{{ '}' }},
"scripts": {{ '{' }}
"prepare": "husky install"
{{ '}' }}
{{ '}' }}
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
Commit-Msg Hook: Conventional Commits
Forza conventional commits (feat:, fix:, docs:, etc.) per changelog automatici:
npm install --save-dev @commitlint/cli @commitlint/config-conventional
module.exports = {{ '{' }}
extends: ['@commitlint/config-conventional'],
rules: {{ '{' }}
'type-enum': [2, 'always', [
'feat', // Nuova feature
'fix', // Bug fix
'docs', // Documentazione
'style', // Formatting
'refactor', // Refactoring
'test', // Test
'chore' // Manutenzione
]]
{{ '}' }}
{{ '}' }};
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no-install commitlint --edit "$1"
# ✅ Valido
git commit -m "feat: add user authentication"
git commit -m "fix: resolve payment bug"
# ❌ Invalido (manca tipo)
git commit -m "add feature"
# ⧗ input: add feature
# ✖ subject may not be empty [subject-empty]
# ✖ type may not be empty [type-empty]
Pre-Push Hook: Test Suite Completa
Esegui test completi prima di push per prevenire build rotte sul CI:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# Esegui test suite completa
npm test
# Verifica build di produzione
npm run build
# Se uno fallisce, push viene bloccato
exit $?
Esempio Completo: Setup Professionale
Configurazione completa con Husky, lint-staged, commitlint e pre-push tests:
{{ '{' }}
"name": "my-project",
"scripts": {{ '{' }}
"lint": "eslint . --ext .ts,.tsx",
"format": "prettier --write .",
"test": "jest",
"test:ci": "jest --coverage",
"build": "tsc && vite build",
"prepare": "husky install"
{{ '}' }},
"lint-staged": {{ '{' }}
"*.{{ '{' }}ts,tsx{{ '}' }}": [
"eslint --fix",
"prettier --write",
"jest --bail --findRelatedTests"
],
"*.{{ '{' }}css,scss{{ '}' }}": [
"prettier --write"
]
{{ '}' }},
"devDependencies": {{ '{' }}
"husky": "^8.0.0",
"lint-staged": "^13.0.0",
"@commitlint/cli": "^17.0.0",
"@commitlint/config-conventional": "^17.0.0",
"eslint": "^8.0.0",
"prettier": "^2.8.0",
"jest": "^29.0.0"
{{ '}' }}
{{ '}' }}
my-project/
├── .husky/
│ ├── pre-commit # Lint-staged
│ ├── commit-msg # Commitlint
│ └── pre-push # Full tests + build
├── commitlint.config.js
├── .eslintrc.js
├── .prettierrc
├── package.json
└── src/
Bypass Hooks (Con Cautela)
A volte serve saltare hooks (es. WIP commit):
# Bypass tutti gli hooks
git commit --no-verify -m "WIP: incomplete feature"
git push --no-verify
# Abbreviazione
git commit -n -m "WIP"
# ⚠️ Usa solo quando assolutamente necessario!
CI/CD Integration
Hooks locali non bastano (si possono bypassare). Riproduci le stesse verifiche sul CI:
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
# Stesse verifiche dei hooks locali
- run: npm ci
- run: npm run lint
- run: npm run format -- --check
- run: npm run test:ci
- run: npm run build
# Verifica conventional commits
- uses: wagoid/commitlint-github-action@v5
Best Practices
✅ Do's
- Usa Husky per condividere hooks con il team
- Usa lint-staged per velocità (solo file staged)
- Verifica commits con commitlint
- Mantieni hooks veloci (< 10 secondi)
- Riproduci verifiche su CI/CD
- Documenta hook setup nel README
❌ Don'ts
- Non rendere hooks troppo lenti (frustrano sviluppatori)
- Non affidarti solo a hooks (possono essere bypassati)
- Non eseguire build complete in pre-commit (usa pre-push)
- Non dimenticare
"prepare": "husky install"in package.json
Hooks Avanzati
#!/usr/bin/env sh
# .husky/prepare-commit-msg
COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2
# Se è un nuovo commit (non merge/amend)
if [ -z "$COMMIT_SOURCE" ]; then
# Aggiungi template con branch name
BRANCH_NAME=$(git symbolic-ref --short HEAD)
echo "[$BRANCH_NAME] " > "$COMMIT_MSG_FILE"
fi
#!/usr/bin/env sh
# .husky/post-commit
COMMIT_MSG=$(git log -1 --pretty=%B)
AUTHOR=$(git log -1 --pretty=%an)
# Invia notifica a Slack
curl -X POST https://hooks.slack.com/services/YOUR/WEBHOOK/URL \
-H 'Content-Type: application/json' \
-d "{{ '{' }}\"text\":\"New commit by $AUTHOR: $COMMIT_MSG\"{{ '}' }}"
Troubleshooting
# Hook non eseguibile
chmod +x .husky/pre-commit
# Husky non installato
npm run prepare
# Hook eseguito due volte
# Rimuovi hook duplicati in .git/hooks/
# Lint-staged non trova file
# Verifica pattern in package.json
# Commitlint non funziona
# Controlla commitlint.config.js esista
Conclusione
I Git hooks automatizzano quality gates, prevenendo codice rotto, test falliti e commit mal formattati. Husky rende hooks facilmente condivisibili, lint-staged li rende veloci, e commitlint garantisce messaggi consistenti. Combinati con CI/CD, creano un workflow robusto che mantiene alta la qualità del codice.
🎯 Punti Chiave
- Git hooks automatizzano verifiche pre-commit e pre-push
- Husky condivide hooks via Git (no configurazione manuale)
- lint-staged esegue linting solo su file modificati
- commitlint forza conventional commits per changelog
- Riproduci verifiche su CI/CD per sicurezza







