Salta al contenuto principale

06 - Git Hooks e Automazione: Quality Gates Automatici

Implementa git hooks per automazione: pre-commit, commit-msg, pre-push con Husky, lint-staged, commitlint e integrazione CI/CD.

Cosa ne pensi di questo articolo?

In Breve

Implementa git hooks per automazione: pre-commit, commit-msg, pre-push con Husky, lint-staged, commitlint e integrazione CI/CD.

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:

.git/hooks/pre-commit (Bash)
#!/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
Rendere lo script eseguibile
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.

Installare Husky
# Installa Husky
npm install --save-dev husky

# Inizializza Husky
npx husky init

# Crea .husky/ directory con hooks condivisibili
Creare Pre-Commit Hook con Husky
# 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
package.json con Scripts
{{ '{' }}
  "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!

Installare lint-staged
npm install --save-dev lint-staged
package.json con lint-staged
{{ '{' }}
  "lint-staged": {{ '{' }}
    "*.{{ '{' }}ts,tsx{{ '}' }}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{{ '{' }}js,jsx{{ '}' }}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.css": [
      "prettier --write"
    ]
  {{ '}' }},
  "scripts": {{ '{' }}
    "prepare": "husky install"
  {{ '}' }}
{{ '}' }}
.husky/pre-commit con lint-staged
#!/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:

Installare commitlint
npm install --save-dev @commitlint/cli @commitlint/config-conventional
commitlint.config.js
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
    ]]
  {{ '}' }}
{{ '}' }};
.husky/commit-msg
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no-install commitlint --edit "$1"
Test Commit-Msg Hook
# ✅ 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:

.husky/pre-push
#!/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:

package.json Completo
{{ '{' }}
  "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"
  {{ '}' }}
{{ '}' }}
Struttura File
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 Hooks
# 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:

.github/workflows/ci.yml
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

prepare-commit-msg: Template Automatico
#!/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
post-commit: Notifica Slack
#!/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

Problemi Comuni
# 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

Domande frequenti

Cosa sono i Git hooks?

I Git hooks sono script salvati in che Git esegue automaticamente in momenti specifici del workflow.

Perché gli hook non vengono committati?

Gli hook in non vengono committati perché `.git` è gitignored, quindi ogni sviluppatore deve configurarli manualmente.

Quali sono i vantaggi di utilizzare Husky per la gestione degli hook?

Husky permette di definire hooks in e condividerli con il team via Git.

Perché è importante eseguire test completi prima di push tramite un pre-push hook?

Eseguendo test completi prima di push, si può prevenire la creazione di build rotte sul CI.

Fonti e approfondimenti

Prova Questi Strumenti

Strumenti gratuiti correlati a questo argomento.

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti