modulo 07 / lezione 35
Casi reali, debugging della storia, recupero
Hook Git: pre-commit, pre-push, commit-msg
Automatizzare controlli locali (lint, test, formato commit) senza affidarsi alla disciplina pura.
Hook Git: pre-commit, pre-push, commit-msg
Gli hook Git sono script eseguiti automaticamente in momenti specifici: prima di un commit, durante la creazione del messaggio, prima di un push, dopo un merge. Servono a spostare controlli ripetitivi dal ricordo umano al flusso di lavoro.
Un hook buono è veloce, prevedibile e aiuta lo sviluppatore. Un hook cattivo è lento, fragile e porta tutti a usare --no-verify.
Perché questa lezione conta
Lint dimenticato, test non lanciati, messaggi commit incoerenti e file generati fuori posto sono problemi banali ma frequenti. Gli hook riducono il rumore prima che arrivi in PR.
Non sostituiscono la CI. La CI resta la fonte autorevole perché gira in ambiente controllato. Gli hook sono il primo filtro locale.
Obiettivo operativo
Alla fine della lezione devi saper distinguere hook nativi e versionati, configurare pre-commit, commit-msg e pre-push, decidere cosa eseguire localmente e integrare gli hook con la policy del team.
Hook nativi
Git cerca script in:
.git/hooks/
Esempio:
.git/hooks/pre-commit
Il problema è che .git/hooks non viene versionata nel repository. Ogni clone ha i suoi hook locali. Per un team non basta dire “metti questo file nella tua cartella hooks”.
Puoi usare:
git config core.hooksPath .githooks
Così Git legge hook da una cartella versionata. In alternativa usa strumenti come Husky, lefthook o pre-commit.
Pre-commit
Esegue controlli prima che Git crei il commit:
#!/bin/sh
npm run lint --silent || exit 1
npm run format:check --silent || exit 1
Deve essere rapido. Se impiega minuti, verrà aggirato.
Buoni candidati:
- lint veloce;
- format check;
- typecheck parziale se rapido;
- blocco di file vietati;
- controllo segreti accidentali.
Da evitare:
- test end-to-end lenti;
- build completa pesante;
- chiamate a servizi esterni;
- controlli non deterministici.
Commit-msg
commit-msg valida il messaggio:
#!/bin/sh
MSG_FILE="$1"
grep -Eq '^(feat|fix|docs|test|refactor|build|ci|chore)(\\(.+\\))?: .+' "$MSG_FILE" || {
echo "Messaggio non valido. Usa type(scope): descrizione"
exit 1
}
Per team che usano Conventional Commits, strumenti come commitlint sono più robusti:
npm install -D @commitlint/cli @commitlint/config-conventional
Pre-push
pre-push è l’ultimo filtro locale:
#!/bin/sh
npm test -- --runInBand || exit 1
Qui puoi permetterti controlli un po’ più costosi, ma resta pragmatico. Se il push richiede sempre dieci minuti, gli sviluppatori cercheranno scorciatoie.
Una buona regola:
pre-commit: controlli sotto 10-20 secondi
pre-push: test mirati sotto pochi minuti
CI: suite completa e build ufficiale
Strumenti condivisi
Husky è comune nei progetti Node:
npx husky init
lefthook è veloce e multi-linguaggio:
brew install lefthook
lefthook install
pre-commit è molto usato in ecosistemi Python e polyglot:
pipx install pre-commit
pre-commit install
Scegli in base allo stack già presente. Non introdurre tooling pesante solo per un controllo che la CI copre bene.
--no-verify
Git permette:
git commit --no-verify
git push --no-verify
Non è un crimine usarlo in emergenza, ma deve essere raro e motivato. Se il team lo usa spesso, il problema è l’hook: troppo lento, fragile o scollegato dalla realtà.
Laboratorio guidato
Su un repo demo:
- crea cartella
.githooks; - configura
core.hooksPath; - aggiungi un
pre-commitche esegue lint o uno script fittizio; - aggiungi un
commit-msgper Conventional Commits; - prova un commit valido e uno non valido;
- documenta come installare gli hook dopo clone;
- verifica che la CI esegua comunque i controlli importanti.
Output atteso
Una policy hook:
Tool scelto:
Hook attivi:
Tempo massimo atteso:
Controlli locali:
Controlli solo CI:
Uso accettabile di --no-verify:
Installazione dopo clone:
Errori comuni
Affidarsi solo agli hook. Un contributor può non averli installati.
Hook troppo lenti. Verranno saltati.
Duplicare male la CI. Locale e CI devono completarsi, non contraddirsi.
Non versionare la configurazione. Ogni macchina si comporta diversamente.
Checklist di verifica
- so dove vivono gli hook nativi;
- so rendere gli hook condivisi nel team;
- so configurare pre-commit, commit-msg e pre-push;
- so decidere cosa resta locale e cosa va in CI;
- so riconoscere hook troppo costosi.
Esercizi
Base · facile
Crea un hook pre-commit che esegua un comando prima di ogni commit.
Soluzione
# .git/hooks/pre-commit (reso eseguibile: chmod +x)
#!/bin/sh
npm run lint || exit 1 # se il lint fallisce, il commit viene bloccato
Un hook è uno script che Git esegue automaticamente in un certo momento. pre-commit gira prima che il commit venga creato: se esce con codice diverso da zero, il commit è annullato.
Intermedio · medio
Scrivi un hook commit-msg che imponga un formato minimo al messaggio. Spiega la differenza tra pre-commit, commit-msg e pre-push.
Soluzione
# .git/hooks/commit-msg
#!/bin/sh
grep -qE '^(feat|fix|docs|chore): ' "$1" || {
echo "Messaggio non valido: usa 'tipo: descrizione'"; exit 1; }
pre-commit: prima di creare il commit → controlli su codice/stage (lint, test veloci).commit-msg: riceve il file col messaggio → valida/normalizza il testo del commit.pre-push: prima di inviare al remoto → controlli più pesanti (test completi).
Ognuno scatta in un momento diverso del ciclo, dal più locale (commit) al più condiviso (push).
Sfida · difficile
Spiega perché gli hook in .git/hooks non si condividono col team e come strumenti dedicati risolvono il problema.
Soluzione
# .git/hooks NON viene versionato → ogni clone parte senza hook.
# Soluzione: hook versionati nel repo + configurazione del path, es.
git config core.hooksPath .githooks # cartella versionata
# oppure strumenti come Husky / pre-commit (framework) che li installano
La cartella .git/hooks è locale e fuori dal controllo di versione: gli hook che crei lì non arrivano ai colleghi. Per renderli condivisi si versiona una cartella di hook nel repo e si punta core.hooksPath lì, oppure si usa un framework (Husky per JS, pre-commit per Python) che, dopo un comando di setup, installa gli stessi hook per tutti. Così le regole (lint, formato dei messaggi, test) valgono per l’intero team, non solo per chi le ha configurate a mano.
Collegamenti
- Modulo: Casi reali, debugging della storia, recupero - lezione 35 del corso.
- Si collega a Conventional Commits, CI e qualità prima della PR.