Aller au contenu

Husky : pre-push et tests

Le hook pre-push s’exécute avant l’envoi vers le dépôt distant. Il accepte des vérifications plus longues que pre-commit (tests, types, construction), puisqu’on pousse moins souvent qu’on committe.

Hook Durée visée Contrôles
pre-commit Quelques secondes Formatage et analyse des fichiers indexés (lint-staged)
commit-msg Instantané Format du message (commitlint)
pre-push Jusqu’à une minute Tests, types, construction
Intégration continue Plusieurs minutes Tout, sur une machine propre

L’intégration continue reste la référence : un hook local peut être contourné (--no-verify) ; la CI non.

.husky/pre-push :

Fenêtre de terminal
npm run lint
npm test

Si une commande échoue, le push est refusé.

Pour garder le hook rapide sur un gros projet :

.husky/pre-push
npx vitest run --changed origin/main

--changed origin/main exécute les tests des fichiers modifiés par rapport à la branche principale (Vitest : configuration et intégration continue). Avec le lanceur natif de Node.js, utiliser un script qui cible les dossiers touchés (Tests natifs de Node.js : organisation).

.husky/pre-push
npx tsc --noEmit
npm run build

Selon le projet : npx astro check (Astro), next build (Next.js)… Garder ici ce qui détecte les erreurs que pre-commit ne voit pas (erreurs de types entre fichiers, échec de construction).

Git donne au hook le nom et l’adresse du dépôt distant en paramètres, et les références poussées sur l’entrée standard :

.husky/pre-push
#!/usr/bin/env sh
distant="$1"
adresse="$2"
while read -r ref_locale sha_local ref_distante sha_distant; do
echo "Envoi de $ref_locale vers $distant ($ref_distante)"
done

On peut ainsi appliquer des règles selon la branche.

Empêcher de pousser directement sur main depuis un poste :

.husky/pre-push
branche=$(git rev-parse --abbrev-ref HEAD)
if [ "$branche" = "main" ] || [ "$branche" = "master" ]; then
echo "Poussée directe sur $branche refusée : passer par une branche et une demande de fusion."
exit 1
fi

C’est un garde-fou de confort : la vraie protection se règle sur le serveur Git (branches protégées de Gitea : Paramètres du dépôt > Branches). Voir Gitea : dépôts, accès et API.

.husky/pre-push
if git diff --cached --name-only origin/main..HEAD 2>/dev/null | grep -E '(^|/)\.env($|\.)' | grep -v '\.env\.example'; then
echo "Un fichier .env est sur le point d'être poussé."
exit 1
fi

Un contrôle plus complet s’appuie sur un outil dédié (par exemple gitleaks) : voir Alternative : hooks Git natifs.

Les hooks s’exécutent avec l’environnement de la session qui lance Git. Dans un outil graphique (éditeur, client Git), le PATH peut ne pas contenir Node.js installé avec nvm : voir Dépannage pour charger nvm dans les hooks.

Fenêtre de terminal
# ~/.config/husky/init.sh (exécuté avant chaque hook)
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
Fenêtre de terminal
git push --no-verify
HUSKY=0 git push

Contourner le pre-push doit rester exceptionnel (panne d’un outil, dépôt de secours). Si on le fait souvent, c’est que le hook est trop lent ou trop fragile : l’alléger plutôt que le contourner.

Symptôme Piste
Le push est très long Tests complets dans le hook : --changed, ou déplacer en intégration continue
pre-push échoue seulement dans l’éditeur PATH sans Node.js : ~/.config/husky/init.sh
Le hook ne se lance pas .husky/pre-push absent ou non exécutable, core.hooksPath incorrect
origin/main introuvable Dépôt sans la référence : git fetch origin main
Impossible de pousser une branche de secours --no-verify assumé pour ce cas
Les tests passent en local mais pas en CI Voir Vitest : dépannage (fuseau horaire, variables, casse des fichiers)

Ubuntu 24.04 et 26.04 : différences à connaître

Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »

Les hooks s’exécutent avec sh, qui est dash sur Ubuntu : éviter les syntaxes propres à bash ([[ ]], tableaux) ou ajouter #!/usr/bin/env bash en première ligne du hook.