Husky : alternative, hooks Git natifs
Husky n’est qu’un confort pour l’installation des hooks. Git sait déjà utiliser un dossier de hooks versionné grâce à core.hooksPath, sans aucune dépendance. C’est l’approche adaptée aux projets qui n’ont pas Node.js, ou qui veulent peu de dépendances.
Principe
Section intitulée « Principe »git config core.hooksPath .githooksGit cherche alors les hooks dans .githooks/ (dossier versionné) au lieu de .git/hooks/. Le réglage est local à chaque clone : il faut l’exécuter chez chaque contributeur.
monprojet/├── .githooks/│ ├── pre-commit│ └── commit-msg└── scripts/ └── installer-hooks.shActiver automatiquement
Section intitulée « Activer automatiquement »| Méthode | Mise en œuvre |
|---|---|
Script prepare de package.json |
"prepare": "git config core.hooksPath .githooks || true" : exécuté à npm install |
| Script d’installation | scripts/installer-hooks.sh à lancer une fois (documenté dans le README) |
Makefile |
make hooks |
Le \|\| true évite l’échec de l’installation hors d’un dépôt Git (archive sans .git, image Docker).
#!/usr/bin/env bashset -euo pipefailgit config core.hooksPath .githookschmod +x .githooks/*echo "Hooks Git activés (.githooks)"Les hooks doivent être exécutables : chmod +x .githooks/* puis git add --chmod=+x .githooks/pre-commit pour que Git conserve le bit d’exécution.
Un hook pre-commit contre les secrets
Section intitulée « Un hook pre-commit contre les secrets ».githooks/pre-commit :
#!/usr/bin/env bashset -euo pipefail
# 1. Refuser les fichiers d'environnementfichiers_env=$(git diff --cached --name-only --diff-filter=ACM | grep -E '(^|/)\.env($|\.)' | grep -v '\.env\.example$' || true)if [ -n "$fichiers_env" ]; then echo "Commit refusé : fichier d'environnement indexé :" echo "$fichiers_env" exit 1fi
# 2. Refuser les motifs ressemblant à un secret dans le diff indexéif git diff --cached -U0 | grep -E '^\+' | grep -Ei '(password|passwd|secret|token|api[_-]?key)\s*[:=]\s*["'\'']?[A-Za-z0-9/+_-]{12,}' >/dev/null; then echo "Commit refusé : secret potentiel détecté dans le diff indexé." echo "(contournement explicite uniquement : git commit --no-verify)" exit 1fi
# 3. Refuser les clés privéesif git diff --cached -U0 | grep -E '^\+' | grep -E 'BEGIN (RSA |OPENSSH |EC |DSA )?PRIVATE KEY' >/dev/null; then echo "Commit refusé : clé privée détectée." exit 1fiCe hook n’utilise que git, grep et bash : aucun outil à installer. Pour une détection plus complète, un outil dédié comme gitleaks ou trufflehog s’appelle de la même façon :
gitleaks protect --staged --redactUn hook commit-msg sans commitlint
Section intitulée « Un hook commit-msg sans commitlint ».githooks/commit-msg :
#!/usr/bin/env bashset -euo pipefail
message=$(head -n 1 "$1")motif='^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-z0-9-]+\))?!?: [a-z].{0,90}[^.]$'
case "$message" in Merge*|Revert*|fixup!*|squash!*) exit 0 ;;esac
if ! echo "$message" | grep -Eq "$motif"; then echo "Message refusé : « $message »" echo "Format attendu : type(portée): description à l'impératif, sans point final" echo "Types : feat fix docs style refactor perf test build ci chore revert" exit 1fiCette version vérifie le format de la première ligne sans Node.js ; commitlint (Messages de commit avec commitlint) est plus complet et configurable.
Tester un hook sans committer
Section intitulée « Tester un hook sans committer ».githooks/pre-commitecho $? # 0 : accepté ; autre : refuséecho "feat: essai" > /tmp/msg && .githooks/commit-msg /tmp/msg; echo $?Les hooks ordinaires se testent en les exécutant directement.
Comparaison
Section intitulée « Comparaison »core.hooksPath natif |
Husky | |
|---|---|---|
| Dépendance | Aucune | Paquet npm |
| Activation chez les contributeurs | Script (prepare, make) |
Automatique à npm install |
| Projets sans Node.js | Oui | Non adapté |
| Gestion de lint-staged, commitlint | À installer à part | Intégration documentée |
| Simplicité de l’écriture | Scripts shell | Scripts shell |
| Maintenance | À la charge du projet | Portée par Husky |
Husky vaut surtout pour l’activation automatique ; sur un projet Node.js qui utilise déjà npm install, un script prepare avec core.hooksPath donne le même résultat sans dépendance.
Hooks côté serveur
Section intitulée « Hooks côté serveur »Les hooks précédents tournent sur le poste du développeur. Gitea propose aussi des hooks côté serveur (pre-receive, post-receive) et des protections de branche : voir Gitea : dépôts, accès et API. Ce sont eux qui font autorité : un hook local peut être contourné.
Dépannage
Section intitulée « Dépannage »| Symptôme | Piste |
|---|---|
| Le hook ne se lance pas | git config core.hooksPath vide ou incorrect, hook non exécutable (chmod +x), nom du fichier inexact |
Permission denied |
Bit d’exécution perdu : git update-index --chmod=+x .githooks/pre-commit |
env: 'bash\r': No such file or directory |
Fins de ligne Windows (CRLF) : convertir en LF (.gitattributes : *.sh text eol=lf) |
| Le hook bloque un exemple légitime | Marqueur (<mot-de-passe>) dans l’exemple plutôt que --no-verify |
| Réglage non partagé entre clones | core.hooksPath est local : script d’activation |
| Conflit avec Husky | Un seul core.hooksPath à la fois |
Ubuntu 24.04 et 26.04 : différences à connaître
Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »bash et git sont présents sur Ubuntu Server. Pour un hook en sh, rappel : /bin/sh est dash (pas de [[ ]] ni de tableaux) ; utiliser #!/usr/bin/env bash quand on a besoin des fonctions de bash.