tominardi.fr

La qualité dans un projet web, Partie 10 - Hooks de pre-commit

06 avril 2023

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.

Le dossier

Rappel du contexte

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.

Définition des hooks de pré-commit de Git

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.

Utilisation des hooks de pré-commit de Git

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.

Problème des hooks de pré-commit de Git

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.

Solution avec le package Python "pre-commit"

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.

Il existe une multitude de packages qui réalisent ce travail. Nous pouvons citer par exemple husky, en javascript. Il est courant d'utiliser un outil qui partage le même environnement que le projet sur lequel on travaille. Mais ce n'est pas une nécessité. Bien qu'il soit écrit en python, pre-commit peut très bien vérifier d'autres langages.

Utilisation de "pre-commit"

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 :

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.

Il faut noter que l'utilisation de cet outil n'est toujours pas une garantie que les scripts seront joués à chaque commit. En effet, l'activation de ces hooks est à réaliser par le développeur, qui peut très bien passer cette étape. De plus, Git dispose d'une option qui permet de ne pas jouer les hooks. Une CI est donc obligatoire pour s'assurer que les règles soient respectées.

Conclusion

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.

Tumblr
Pinterest
LinkedIn
WhatsApp

<< La qualité dans un projet web, Partie 9 - Versionning >> La qualité dans un projet web, Partie 11 - Intégration Continue

2022 - tominardi.fr