Hooks pre-commit¶
Automatiser la validation du code avant chaque commit — linting, formatage et vérification.
Pourquoi des hooks¶
Un hook pre-commit est un script qui s'exécuté automatiquement avant chaque git commit. Si le script échoué (exit code != 0), le commit est bloque.
L'objectif : empêcher le code non conforme d'entrer dans l'historique Git, sans dépendre de la discipline individuelle.
graph LR
A["git commit"] -->|"declenche"| B["pre-commit hook"]
B -->|"succes"| C["commit cree"]
B -->|"echec"| D["commit bloque"]
D -->|"corriger"| A Le problème sans hooks¶
- Un développeur oublie de lancer le formatter → le diff de la MR est pollue de changements cosmétiques
- Un développeur commit un
console.logou unimport pdb→ détecté en review ou pire, en production - Le linter passe en CI mais le développeur ne le voit que 10 minutes plus tard → boucle de feedback lente
Le principe : fail fast, local, reproductible¶
| Principe | Explication |
|---|---|
| Fail fast | Détecter le problème au commit, pas au push ni en CI |
| Local | Le hook tourne sur la machine du dev, pas sur un serveur |
| Reproductible | Même version d'outil pour toute l'équipe, définie dans le repo |
Le mécanisme Git natif¶
Git fournit des hooks dans .git/hooks/. Par défaut, ce sont des scripts shell avec le suffixe .sample.
# Voir les hooks disponibles
ls .git/hooks/
# Creer un hook pre-commit basique
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
echo "Running pre-commit checks..."
# Verifier qu'il n'y a pas de console.log
if git diff --cached --name-only | xargs grep -l 'console.log' 2>/dev/null; then
echo "ERROR: console.log detecte dans les fichiers stages"
exit 1
fi
EOF
chmod +x .git/hooks/pre-commit
Limite du mécanisme natif
Les hooks .git/hooks/ ne sont pas versionnes — ils vivent dans .git/ qui est local. Chaque développeur doit les installer manuellement. C'est la que les frameworks de hooks interviennent.
Framework pre-commit (Python)¶
Le framework pre-commit est le plus utilisé. Il se configuré via un fichier .pre-commit-config.yaml à la racine du projet.
Installation¶
Configuration type¶
# .pre-commit-config.yaml
repos:
- 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-added-large-files
args: ['--maxkb=500']
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
hooks:
- id: prettier
types_or: [javascript, typescript, css, json, yaml, markdown]
Fonctionnement¶
graph TD
A["git commit"] --> B["pre-commit intercept"]
B --> C["Telecharge les hooks<br/>(premiere fois)"]
C --> D["Execute chaque hook<br/>sur les fichiers stages"]
D -->|"tous OK"| E["commit cree"]
D -->|"echec ou modif"| F["commit bloque"]
F --> G["Formatter a modifie des fichiers ?"]
G -->|"oui"| H["git add + git commit a nouveau"]
G -->|"non"| I["corriger manuellement"] Bypass en urgence
En cas d'urgence absolue, vous pouvez contourner les hooks avec git commit --no-verify. A n'utiliser que dans des situations exceptionnelles — la CI rattrapera les problèmes au push.
Husky (Node.js)¶
Husky est l'équivalent pour l'écosystème Node.js. Il utilise le mécanisme prepare de npm pour installer les hooks automatiquement.
Installation¶
Configuration¶
// package.json
{
"lint-staged": {
"*.{js,ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{css,json,md}": ["prettier --write"]
}
}
lint-staged¶
lint-staged ne lance les outils que sur les fichiers stages (git add), pas sur tout le projet. C'est essentiel pour la performance — un pre-commit qui prend 30 secondes ne sera pas respecte.
Lefthook (Go)¶
Lefthook est une alternative multi-langage, rapide et sans dépendance runtime.
Installation¶
Configuration¶
# lefthook.yml
pre-commit:
parallel: true
commands:
lint:
glob: "*.{js,ts}"
run: npx eslint --fix {staged_files}
format:
glob: "*.py"
run: ruff format {staged_files}
check-yaml:
glob: "*.{yml,yaml}"
run: yamllint {staged_files}
Avantage : parallélisme natif¶
Lefthook exécuté les hooks en parallèle par défaut, là où pre-commit et husky sont séquentiels. Sur un projet avec formatter + linter + validation YAML, le gain est significatif.
Comparatif des frameworks¶
| Critère | pre-commit (Python) | Husky (Node.js) | Lefthook (Go) |
|---|---|---|---|
| Langage | Python | Node.js | Go (binaire) |
| Config | YAML | Shell + JSON | YAML |
| Parallélisme | Non | Non | Oui |
| Écosystème hooks | 800+ hooks publics | Via lint-staged | Manuel |
| Poids | Léger | Léger | Très léger |
| Multi-langage | Oui | Principalement JS | Oui |
Quel framework choisir
- Projet Python → pre-commit (écosystème riche, standard de facto)
- Projet Node.js → Husky + lint-staged (intégration npm native)
- Projet multi-langage ou polyglotte → Lefthook (rapide, pas de runtime)
Quoi vérifier dans un pre-commit¶
| Vérification | Temps | Impact |
|---|---|---|
| Formatage (Prettier, Black) | < 1s | Élimine les diffs cosmétiques |
| Linting (ESLint, Ruff) | 1-3s | Détecté bugs et mauvaises pratiques |
| Trailing whitespace | < 1s | Proprete du code |
| Fichiers volumineux | < 1s | Evite de committer des binaires |
| Secrets (detect-secrets) | 1-2s | Empêche la fuite de credentials |
| YAML/JSON valide | < 1s | Evite les erreurs de syntaxe config |
Ne pas en faire trop
Un pre-commit qui prend plus de 10 secondes sera contourne par les développeurs. Gardez les verifications lourdes (tests, SAST, build) pour le pipeline CI.
Outils¶
| Outil | Description | Lien |
|---|---|---|
| pre-commit | Framework de hooks Python avec 800+ hooks publics | pre-commit.com |
| Husky | Hooks Git pour l'écosystème Node.js | typicode.github.io/husky |
| Lefthook | Framework de hooks rapide et multi-langage | github.com/evilmartians/lefthook |
| lint-staged | Exécuté les linters uniquement sur les fichiers stages | github.com/lint-staged/lint-staged |
| detect-secrets | Détection de secrets dans le code avant commit | github.com/Yelp/detect-secrets |