Githooks

Les githooks sont des scripts lancés automatiquement par git. Ils prennent des paramètres d'entrée différents en fonction du nom du hook et du contexte d’exécution.

Vous pouvez consulter plusieurs exemples dans le répertoire .git/hooks de chacun de vos repositories.

Il vous suffit de retirer l'extension .sample des fichiers d'exemple pour les activer.

Partager les githooks

Le problème du répertoire .git c'est qu'il est local. Il n'est jamais poussé sur le repo distant. Comment donc partager des hooks ?

La méthode la plus simple consiste a créer un répertoire dédié aux hooks (.githooks par exemple) qui sera poussé avec les sources.

Pour qu'un hook soit exécutable, il faut ... le rendre exécutable:

chmod + x .githooks/commit-msg

Il faudra alors dire a git que ce répertoire contient des hooks pour que git les lance:

la commande serait alors:

git config core.hooksPath .githooks

Automatiser l'initialisation des hooks

La configuration de git étant locale, il faut pouvoir également la partager. Dans l'idéal, cela se fera de manière transparente pour s'assurer que tous les développeurs l'utilisent.

A partir d'ici, cela dépends surtout de votre environnement de development. Par exemple dans un contexte NodeJS, on pourrait utiliser le script pre-install (un hook lui aussi) pour automatiser la configuration lorsqu'un développeur installe les dépendances. Cela donnerait alors un fichier package.json de ce type:

{
    "script": {
        "pre-install": "git config core.hooksPath .githooks"
    }
}

Exemples de githooks

Ne pas permettre de push si les tests sont foireux

Le fichier sera nommé pre-push pour se lancer avant un push afin de le stopper le cas échéant.

#!/bin/sh

# Prevent push when tests fail

# specific params for pre-push hook - see https://git-scm.com/docs/githooks
read local_ref local_sha remote_ref remote_sha

npm run test --workspaces

if [ $? -ne "0" ]; then
    echo "\n\e[1;31m💥 Tests failed. You are not allowed to push ! 💥\033[0m\e[0m\n"

    # exit with an error
    exit 1
fi

Linter les messages de commit

Le fichier sera nommé commit-msg pour se lancer avant un commit afin de le stopper le cas échéant.

#!/usr/bin/env bash

# SETTINGS
SCOPE_MAX_LENGTH=20
TITLE_MAX_LENGTH=60
TYPES=(build ci docs feat fix perf refactor revert style test)

commitRegexp="^("
# add type check
for type in ${TYPES[@]}; do
    commitRegexp="$commitRegexp$type|"
done
commitRegexp="${commitRegexp%|})"
# add optional scope check
commitRegexp="${commitRegexp}(\((.{1,${SCOPE_MAX_LENGTH}})\))?"
# add optional breaking check
commitRegexp="${commitRegexp}!?: "
# add title check
commitRegexp="${commitRegexp}(.{1,$TITLE_MAX_LENGTH})$"

branchNameRegex="(.*)"

mergeRegexp="^(Merge( remote-tracking)? branch '${branchNameRegex}'( of .*)? into ${branchNameRegex})$"

# get first line of the commit message
commit_file=$1
commit_message=$(head -n1 $commit_file)

# run check
if [[ ! $commit_message =~ $commitRegexp && ! $commit_message =~ $mergeRegexp ]]; then
    echo -e "\n\e[1;31m💥 Invalid Commit Message 💥\033[0m\e[0m\n"
    echo -e "\e[37mActual commit message: \e[33m\"$commit_message\"\033[0m\n"
    echo -e "-------- valid message format --------\e[36m"
    echo -e "<type>[optional scope]: <description>\n"
    echo -e "[optional body]\n"
    echo -e "[optional footer(s)]\033[0m"
    echo -e "--------------------------------------\n"
    echo -e "valid types: \e[36m${TYPES[@]}\033[0m"
    echo -e "max length (scope): \e[36m$SCOPE_MAX_LENGTH\033[0m"
    echo -e "max length (title): \e[36m$TITLE_MAX_LENGTH\033[0m\n"
    echo -e "\e[37mregex used: \e[33m$commitRegexp\033[0m"
    echo -e "see https://www.conventionalcommits.org/en/v1.0.0/"

    # exit with an error
    exit 1
fi

Les limites

Les githooks peuvent accomplir de grandes choses. On pourrait par exemple imaginer (sur un petit projet perso) builder et publier notre site a chaque fois que l'on pousse les sources sur Git. Ce serait un peu comme une CI locale.

Les seules limites c'est:

  • vous
  • l'argument --no-verify du CLI Git qui bypass les githooks

Eh oui ! comme c'est du local, si on veut contourner la règle c'est easy. Les githooks c'est bien pour gagner du temps mais ça ne remplace pas une CI.

Aller plus loin

Le site githooks.com est une excellente référence.