Qu’est-ce qui est le plus désagréable ?
- Relire une Pull Request principalement composée de changements d’indentation et de formattage ?
- Pousser son code et s’apercevoir après quelques minutes que les tests unitaires ont échoué sur la CI ?
Grâce aux Git Hooks, ne rencontrez plus jamais ces problèmes.
Les avantages de cette solution :
- Git est a priori déjà installé sur votre projet, il n’y a donc pas de nouvel outil ou dépendance à ajouter.
- Ce système fonctionne quels que soient les langages utilisés sur votre projet (tant qu’ils disposent d’outils en ligne de commande)
Les Git Hooks
Les hooks de Git permettent d’exécuter automatiquement des scripts sur certaines actions Git. Par exemple : avant un commit, après un commit, avant un push, avant un rebase… Des exemples sont créés lors de l’initialisation d’un repo Git, dans le dossier .git/hooks
, et vous pouvez aussi trouver la liste complète ici : https://git-scm.com/docs/githooks.
Nous allons nous en servir pour faire tourner automatiquement les formatteurs avant chaque commit, puis linter et tester le code avant chaque push. Prenons l’exemple d’un projet monorepo avec un frontend en React et un backend en Go.
Formatter le code avant chaque commit
Commençons par créer un fichier pre-commit
dans le dossier .git/hooks
#!/bin/bash
echo "Pre commit hook!"
## This will retrieve all of the .go files that have been changed since the last commit
STAGED_GO_FILES=$(git diff --cached --diff-filter=AM --name-only -- '*.go')
echo "Formatting backend files..."
for file in $STAGED_GO_FILES; do
gofmt -l -w -s "$file"
git add "$file"
done
## This will retrieve all of the frontend files that have been changed since the last commit
STAGED_FRONT_FILES=$(git diff --cached --diff-filter=AM --name-only -- '*.ts' '*.tsx' '*.css' '*.js')
echo "Formatting frontend files"
for file in $STAGED_FRONT_FILES; do
./webapp/node_modules/.bin/prettier --write "$file"
git add "$file"
done
Il doit être exécutable, on va donc modifier ses permissions d’accès avec la commande chmod +x pre-commit
.
Pour tester nous pouvons essayer de casser le formattage d’un fichier de notre projet, lors du commit le formatteur devrait le remettre en place.
Exécuter le formatteur avant le commit nous permet de garantir que tout code modifié respecte les règles du projet.
Linter et jouer les tests unitaires avant un git push
Créer un fichier pre-push
dans le dossier .git/hooks
#!/bin/bash
echo "Pre-push hook!"
cd back || exit 1
# Run backend unit tests
echo "Running backend unit tests"
go test -short ./...
status=$?
if test $status -eq 0; then
echo "backend test success"
else
echo "backend test failed"
exit 1
fi
# Run backend linter
if ! command -v golangci-lint &>/dev/null; then
echo "Linter golangci-lint not found in PATH"
exit 1
else
echo "Running Go linter"
golangci-lint run -E gosec -E gofmt -E gocognit
status=$?
if test $status -eq 0; then
echo "Golang lint success"
else
echo "Golang lint failed"
exit 1
fi
fi
# Run frontend linter
echo "Lint webapp"
cd ../webapp || exit 1
npm run lint
status=$?
if test $status -eq 0; then
echo "npm lint success"
else
echo "npm lint failed"
exit 1
fi
Le lint et les tests unitaires sont un peu plus longs à exécuter, on ne le fait donc que sur un push. Cela nous permet néanmoins de vérifier l’absence d’erreurs dans le code avant même qu’il soit envoyé sur le dépôt Git.
En cas d’urgence, il est toujours possible de ne pas effectuer ces vérifications en ajoutant l’option --no-verify
à la commande git.
On pourrait aussi ajouter les validations suivantes :
- utiliser commitlint pour vérifier les conventionnal commit
- utiliser des outils de validation de l’architecture comme dependency-cruiser https://github.com/sverweij/dependency-cruiser
Partager les hooks
Le dossier .git
ne peut pas être commité, pour partager les githooks nous allons donc créer un dossier .githooks
, y copier les hooks créés ci-dessus, puis le committer.
Il suffira ensuite à chaque développeur d’executer la commande suivante pour que Git utilise les hooks présents dans le dossier .githooks
:
git config core.hooksPath .githooks