Suivre l'avancée de son produit avec des commits de qualité

illustration de l'article

Quel que soit le produit développé ou le langage utilisé, il y a toujours des développeurs et des commits quelque part…

Au travers de l’historique, les commits permettent de retracer toutes les modifications effectuées sur un projet.

Néanmoins, la qualité des messages peut beaucoup varier d’un développeur à l’autre, voire d’un jour à l’autre pour la même personne…

Qu’est-ce qu’un commit de qualité ?

Quand nous regardons l’historique d’un projet versionné sous git, nous constatons parfois une suite de commits peu expressifs…

e60930a9 (HEAD -> my_branch, origin/mybranch) Modif de la nav
49ff1d07 Fix describe bis
49ff1d07 Fix describe
3672f204 Update conf
03d00e2e Types
a5fdbc4b #261 suppress unecessary lines
886fd7af add logo and modify message for loading connections
a06a1272 support logo and redirection
1dd84e9c fix reconnect insert #291
a62f7880 No comment
26f99ee0 ...
d2be491c fix test
bfb2f370 commit final with all fix

Les développeurs arrivant sur le projet, ou pire ceux qui sont déjà sur le projet, peuvent difficilement exploiter un tel historique.

Pour citer quelques problématiques liées à un tel historique :

  • difficile de distinguer les fonctionnalités des correctifs
  • messages trog génériques dont on ne comprend pas les impacts éventuels
  • structure des messages non homogène

Définir un format de message commun

Les développeurs ont tendance à écrire des messages qui ne sont pas assez expressifs. Ils ne décrivent pas toujours les changements opérés ni l’objectif du code ajouté.

Chaque message de commit devrait pouvoir répondre aux questions suivantes :

Quels types de changement suis-je en train de faire ?

Quel périmètre est affecté par mes changements ?

Quelles sont les modifications opérées ?

La spécification “Conventional commit”

Git ne définit pas de format de message. Cela reste du texte simple où chacun est libre de structurer son message comme il le souhaite.

La spécification “conventional Commit” propose d’ajouter une couche de vérification des messages avant de les écrire dans Git.

L’objectif est de créer une convention commune au sein du projet et faciliter ainsi le partage du code ainsi que les résolutions de bugs.

Cette spécification est largement inspirée des bonnes pratiques de contribution du projet Angular

Structure des messages

Les messages sont composés comme suit :

<type>[optional scope]: <subject>
<BLANK LINE>
[optional body]
<BLANK LINE>
[optional footer]

Type

décrit ce que fait votre commit.

Voici les valeurs couramment utilisées :

  • feat: une nouvelle fonctionnalité
  • fix: une correction de bug
  • docs: un changement uniquement dans la documentation
  • style: un changement qui n’affecte pas le sens du code (espace, reformattage, alignements dans le code…)
  • refactor: un changement qui n’est ni une correction de bug ni une évolution
  • perf: un changement qui améliore la performance
  • test: un ajout de tests manquants
  • chore: un changement sur le process de build ou des outils complémentaires

Scope

Décrit le périmètre de la modification (ex: nom du composant, module, packages, etc)

Subject

Description succincte des modifications

  • impératif et temps présent
  • pas de majuscule
  • pas de point

Body (optionnel)

Décrit plus en détail les objectifs et intentions qui ont motivé les changements apportés

Footer (optionnel)

Liens éventuels vers des outils de suivi de bugs

A noter :

En cas de “Breaking change”, il est possible de l’indiquer via un ‘!’ dans le type et dans le body en ajoutant “BREAKING CHANGE:” suivi de l’explication.

Exemples de messages attendus

Fonctionnalité implémentée : Possiblité de passer commande via un bouton

feat(order): add purchase order button

Mise à jour de la documentation pour expliquer les scripts d’initialisation du projet

docs(readme): document how to start project

Arrêt du support Node 6

refactor!: drop support for Node 6

BREAKING CHANGE: refactor to use JavaScript features not available in Node 6.

Vérifier la conformité des messages de commit

Pour vérifier que tout le monde respecte bien le format défini par le projet, il convient de faire valider les messages.

La librairie “Commit Lint” est justement là pour nous aider !

Installation de la librairie

npm install -g @commitlint/cli @commitlint/config-conventional

Rq: il est également possible d’installer localement ou via npx la librairie.

Configuration des règles

Création du fichier de config commitlint.config.js lue par la librairie

echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

Les types autorisés par cette configuration sont : ‘build’, ‘ci’, ‘chore’, ‘docs’, ‘feat’, ‘fix’, ‘perf’, ‘refactor’, ‘revert’, ‘style’, ‘test’

Consultez la documentation pour les autres règles prédéfinies

Exemple d’erreur lors de la validation d’un message

# Lint from stdin
echo 'foo: bar' | commitlint
⧗   input: foo: bar
✖   type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, 
style,test] [type-enum]

✖   found 1 problems, 0 warnings
ⓘ   Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint

La personnalisation des règles de validation

Il est par exemple possible de forcer l’usage des scopes et d’en restreindre le choix à une liste prédéfinie

//commitlint.config.js
module.exports = {
    extends: ['@commitlint/config-conventional'],
    rules: {
        'scope-empty': [2, 'never'],
        'scope-enum': [2, 'always', [
            'logging',
            'services',
            'docs',
            'dependencies',
            'profiles',
            'users',
            'api',
            'segments',
            'configuration'
        ]]
    }
};

Pour aller plus loin, consultez la liste des règles de commitlint

Exemple d’historique git avec des commits structurés et expressifs

86f80528 (HEAD -> master, origin/master, origin/HEAD) fix(changelog): display scope in breaking change messages
6b578392 fix(CLI): show symlinked themes in `omz theme list`
64cb1530 chore: add Konfekt as universalarchive maintainer
492f712d feat(plugins): add `universalarchive` plugin to conveniently compress files (#6846)
2118d35e fix(vi-mode)!: add back edit-command-line key binding as 'vv' (#9573)
79980b00 fix(vi-mode): hide cursor-change logic behind `VI_MODE_SET_CURSOR` setting
94ce46d4 docs(vi-mode): revamp README and document settings
66e0438d fix(archlinux): update URL and key server in `pacmanallkeys` (#9569)
9e5f280f feat(CLI): add `plugin info` subcommand (#9452)

Logs extraits du projet OhMyzsh

Automatiser la vérification des messages

Pour garantir la structure des messages de commit et ne pas ralentir le développeur, l’idéal est de vérifier automatiquement le format à la création du commit.

Git permet justement d’exécuter des scripts personnalisés au déclenchement de certaines actions (documentation sur les Git Hooks)

Le hook qui nous intéresse est “commit-msg”

Installation manuelle du hook

Créer un fichier .git/hooks/commit-msg

#!/bin/sh
commitlint > $1
status=$?
if [ $status -gt 0 ]
then
  echo "✖ Commit message does not follow conventional commit message spec"
  echo 'ⓘ  Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint'
fi

exit $status

Installation automatique avec Husky (node project)

Husky est une dépendance npm qui nous aide à facilement configurer les “Git Hooks”

npm install --save-dev husky

Configuration du hook ‘commit-msg’ pour exécuter le linter

// package.json
{
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  }
}

Exemple de vérification automatique des messages lors du commit

$ git commit -m “foo”

husky > commit-msg (node v12.19.0)
⧗   input: foo
✖   subject may not be empty [subject-empty]
✖   type may not be empty [type-empty]

✖   found 2 problems, 0 warnings
ⓘ   Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint

husky > commit-msg hook failed (add --no-verify to bypass)

Conclusion

Appliquer cette rigueur peut être perturbant au début mais cela devient très vite un réel atout.

L’historique des commits n’est plus une liste inexploitable que personne ne consulte. Il devient une véritable aide à la compréhension des modifications opérées dans le projet.

En soignant ses messages de commit, le développeur se pose également plus de questions sur ce qu’il est en train de faire et si les changements apportés dans le commit sont bien adaptés (voir les bonnes pratiques des commits atomiques)

En cas de problème d’historique, ne pas oublier le “rebase interactif” :-)

Pour aller plus loin

Avoir des messages expressifs et structurés permet également d’automatiser certaines tâches.

Dans un prochain article nous verrons comment automatiser la création d’un changelog lors de la livraison d’une nouvelle version.

Liens utiles

Crédits

Photo de couverture par Yancy Min sur Unsplash

Commentaires

comments powered by Disqus

Date

Auteur

Avatar Julien ROLLIN

Julien ROLLIN

CTO Talan Labs