Aller au contenu

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.

Fenêtre de terminal
git config core.hooksPath .githooks

Git 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.sh
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).

scripts/installer-hooks.sh
#!/usr/bin/env bash
set -euo pipefail
git config core.hooksPath .githooks
chmod +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.

.githooks/pre-commit :

#!/usr/bin/env bash
set -euo pipefail
# 1. Refuser les fichiers d'environnement
fichiers_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 1
fi
# 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 1
fi
# 3. Refuser les clés privées
if 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 1
fi

Ce 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 :

Fenêtre de terminal
gitleaks protect --staged --redact

.githooks/commit-msg :

#!/usr/bin/env bash
set -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 1
fi

Cette version vérifie le format de la première ligne sans Node.js ; commitlint (Messages de commit avec commitlint) est plus complet et configurable.

Fenêtre de terminal
.githooks/pre-commit
echo $? # 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.

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.

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é.

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.