Nous allons aujourd'hui aborder le thème des crochets Git. Personne n'appelle ça des crochets, tout le monde utilise le terme anglais Hook. Mais "crochet" c'est sympa.
Cet article fait partie d'une série d'articles visant à énumérer les différents éléments de qualité à mettre en place au démarrage d'un projet.
Les hooks de pré-commit de Git sont une fonctionnalité qui permet aux développeurs de définir des scripts personnalisés qui s'exécutent avant que les modifications ne soient enregistrées dans Git. Ils sont en général utilisés pour faire des vérifications automatisées.
Dans ce dossier sur la qualité, on a déjà parlé de code styling, de linting, de tests unitaires. Les hooks de pré-commit sont un moyen de vérifier que le code qui va être poussé respecte ces règles. C'est un moyen de graver ces règles dans le marbre. De s'assurer que le contrat de qualité que nous passons, en tant que contributeur du projet, sera respecté.
C'est un confort pour les développeurs qui peuvent repérer des erreurs avant que leur code ait été proposé sur le dépôt externe. En effet, rien de plus frustrant pour un développeur de réaliser qu'il avait oublié une instruction de debug après avoir poussé son code.
C'est donc important de les mettre en place dès le démarrage du projet. Mais allons voir comment ça fonctionne.
Les hooks de pré-commit de Git sont simples à utiliser et peuvent être configurés pour chaque projet.
Chaque projet Git est doté, à sa racine, d'un répertoire .git
dans lequel nous allons retrouver toute la configuration du projet (et plus encore, mais ce n'est pas le sujet). Le dossier .git/hooks/
contient des modèles de hook (fichiers .sample
) qui peuvent être utilisés comme point de départ pour créer des hooks de pré-commit personnalisés.
Pour créer un script de pre-commit, il faut créer un fichier pre-commit
(sans extension) dans le dossier .git/hooks/
et lui donner les droits d'exécution. Ensuite, nous pouvons ajouter le code nécessaire pour effectuer les vérifications souhaitées. Les scripts peuvent être écrits dans n'importe quel langage, mais les scripts shell sont les plus couramment utilisés.
Voici un exemple de script shell qui lancera la commande pylint
sur tout le code source (contenu dans le dossier src
) du projet.
#!/bin/bash
# On lance pylint sur tout le code source contenu dans le dossier 'src'
pylint src
Vous pouvez ajouter autant de vérifications que nécessaire en modifiant le script. Vous pouvez également le personnaliser pour ne lancer pylint
que sur les fichiers qui ont été modifiés.
Le principal problème des hooks de pré-commit de Git est qu'ils ne sont pas partagés entre les développeurs d'un projet.
Si un développeur ajoute un hook de pré-commit à son environnement de développement local, il ne sera pas automatiquement appliqué aux autres développeurs travaillant sur le même projet. En effet, le contenu du dossier .git/hooks/
n'est pas versionné et n'est pas partagé entre les développeurs.
C'est donc une discipline que l'on s'impose à soit-même, mais qui n'est d'aucune garantie au niveau du projet. Cela peut entraîner des problèmes de cohérence et de qualité du code, puisque cette étape de vérification peut être totalement absente sur le poste d'un développeur. C'est pour cela qu'on utilise des outils d'Intégration Continue (que nous allons aborder dans un autre article).
Une solution simple pourrait-être de versionner le fichier sur le dépôt, charge aux développeurs de copier/coller le fichier dans le répertoire .git/hooks/
. Il pourrait y avoir des adaptations à faire, mais ça serait un bon compromis. Heureusement, il existe des outils qui offrent des solutions mieux adaptées.
Le package Python "pre-commit" est une solution pratique qui permet de partager facilement des hooks de pré-commit entre les développeurs travaillant sur un même projet. Ce package fournit une bibliothèque de hooks préconfigurés qui peuvent être facilement intégrés dans un projet. De plus, il permet de définir des hooks personnalisés qui seront partagés entre les développeurs.
Pour un utilisateur, il s'agit d'une dépendance Python à installer, et d'un fichier de configuration à rajouter au dépôt. Voyons comment cela fonctionne.
Pour utiliser le package "pre-commit", il suffit d'installer le package avec pip :
pip install pre-commit
Ensuite, il faut créer un fichier de configuration .pre-commit-config.yaml
à la racine du projet pour définir les hooks à utiliser. Ce fichier de configuration peut inclure des hooks fournis par le package ou des hooks personnalisés définis par les développeurs. Vous pouvez aussi ajouter vos propres commandes.
Les commandes peuvent prendre le nom des fichiers modifiés en paramètre. Cela permet de lancer certaines commandes sur les fichiers modifiés. On peut aussi appliquer certains hooks à certains types de fichiers. Ainsi, par exemple, on ne lancera pas les tests unitaires python si on a seulement modifié un README.md
.
Voici un exemple de fichier .pre-commit-config.yaml
:
fail_fast: true
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v1.2.3
hooks:
- id: check-merge-conflict
- id: debug-statements
- id: check-json
- id: trailing-whitespace
files: (Dockerfile|Jenkinsfile|rc|\.(cfg|css|csv|feature|gitignore|html|inc|ini|info|install|java|js|json|md|module|php|pp|properties|py|sh|txt|xml|ya?ml))$
args:
- --no-markdown-linebreak-ext
- repo: https://github.com/pre-commit/mirrors-autopep8
rev: v1.4.3
hooks:
- id: autopep8
- repo: local
hooks:
- id: pylint
name: pylint
entry: python -m pylint.__main__
language: system
types: [python]
args:
- --rcfile=setup.cfg
- id: tests
name: run tests
entry: coverage run --source='tominardi' tominardi/manage.py test tominardi
language: system
types: [python]
pass_filenames: false
- id: coverage
name: check coverage
entry: coverage html --fail-under=100
language: system
types: [python]
pass_filenames: false
Dans cet exemple, nous déclarons 5 hooks que nous installons à partir du dépôt https://github.com/pre-commit/pre-commit-hooks, un hook à partir du dépôt https://github.com/pre-commit/mirrors-autopep8, et 3 hooks qui sont des commandes personnalisées (le dépôt local
).
Dans cet exemple, nous lançons, à chaque commit :
pdb
oublié par exemple)json
soient bien formatéspep8
Une fois que le fichier de configuration est en place, il suffit de lancer la commande pre-commit install
pour installer les hooks de pré-commit pour le projet.
À partir de ce moment, à chaque fois qu'on fera un commit, les hooks seront déclenchés.
Les hooks de pré-commit sont devenus un indispensable dans les projets. C'est un moyen simple et efficace d'éviter les oublis.
Pour le développeur habitué à commiter souvent, et pour qui un commit est instantané, c'est une habitude à prendre. En effet, les hooks peuvent prendre quelques secondes à s'executer. Les tests unitaires peuvent également prendre un certain temps. Si l'execution devient trop longue, il faudra soit les supprimer, soit se contenter de jouer une suite de test "essentielle". Les hooks ne devraient pas prendre plus de quelques secondes avant de s'executer.
La prochaine fois, on parlera de la CI, qui sera entre autres utilisé pour s'assurer que le contrat de qualité est rempli.
2022 - tominardi.fr