
Conventional Commits : le chaînon manquant entre vos commits et votre numéro de version
- VersionDude
- guides
- 7 min de lecture
Une forme fixe pour les messages de commit, qui associe fix à PATCH et feat à MINOR, pour que le prochain numéro de version découle du log. Les deux notations de rupture, et ce que la spécification se garde délibérément de faire.
Si vous numérotez déjà vos versions avec le versionnage sémantique et que vous tenez un changelog, il reste entre les deux un vide que la plupart des équipes comblent à la main : quelqu'un doit relire les commits depuis la dernière version et décider si la suivante sera un patch, une mineure ou une majeure. Conventional Commits est une spécification qui comble ce vide en plaçant la réponse dans le message de commit lui-même, au moment où le changement est fait.
La forme du message

La spécification définit une forme fixe pour la première ligne, avec un corps et des pieds de message facultatifs en dessous. Écrite en toutes lettres, la structure est la suivante : le type, puis un scope facultatif entre parenthèses, puis deux-points et une espace, puis une courte description. Une ligne vide sépare le corps, et d'autres lignes vides séparent les éventuels pieds de message.
Ainsi, une correction de bug dans le parseur devient fix(parser): handle empty attribute values, et une nouvelle capacité devient feat(api): add pagination to the search endpoint. Le scope est facultatif, et la description est un résumé plutôt qu'une dissertation. Tout ce qui est plus long a sa place dans le corps, en dessous.
Comment les types se relient a SemVer
La raison d'être de cette forme fixe, c'est ce que les types permettent. La spécification en relie deux directement au versionnage sémantique : un commit fix correspond à PATCH, et un commit feat correspond à MINOR. Un commit qui introduit une rupture d'API correspond à MAJOR.
- Structure : le type, un scope facultatif entre parenthèses, puis deux-points et une espace, puis la description. Le corps et les pieds de message viennent en dessous, séparés chacun par une ligne vide.
- fix correspond à PATCH dans le versionnage sémantique, et feat correspond à MINOR.
- Une rupture d'API correspond à MAJOR, et se signale soit par un pied de message BREAKING CHANGE:, soit par un point d'exclamation avant les deux-points.
- Si le point d'exclamation est utilisé, le pied de message BREAKING CHANGE: peut être omis et la description en tient lieu.
- La spécification est une convention de message, pas un outil de release : elle correspond aux niveaux SemVer mais ne calcule rien par elle-même.
C'est cette seule correspondance qui permet à l'outillage de calculer le prochain numéro de version sans qu'un humain ait à lire le log. Elle déplace aussi la décision vers la personne la mieux placée pour la prendre, c'est-à-dire celle qui a écrit le changement, au moment où elle l'a écrit, plutôt qu'un release manager qui reconstitue l'intention des semaines plus tard.
Deux facons de signaler une rupture
Les ruptures ont deux notations admises, et il vaut mieux connaître les deux car vous rencontrerez les deux dans de vrais dépôts. La première est un pied de message qui commence par BREAKING CHANGE: suivi d'une description. La seconde est un point d'exclamation placé juste avant les deux-points, comme dans feat(api)!: drop support for v1 tokens.
Les deux ne sont pas de simples variantes stylistiques. La spécification indique que si le point d'exclamation est utilisé, le pied de message BREAKING CHANGE: peut être omis, et la description du commit tiendra alors lieu de description de la rupture. Autrement dit, la forme courte se suffit à elle-même, ce qui explique qu'elle soit la plus fréquente en pratique.
Ce que la specification ne fait pas
Il vaut la peine d'être précis sur ce que cette spécification apporte et n'apporte pas. C'est une convention d'écriture des messages, pas un outil de release. La spécification dit que les types correspondent aux niveaux SemVer ; elle ne calcule pas les versions, ne génère pas de changelog et ne publie rien par elle-même. Ce sont les tâches d'un outillage qui lit la convention, et le choix de cet outillage est une décision distincte.
L'avantage qui subsiste même sans aucun outillage est celui que les équipes sous-estiment. Exiger un type sur chaque commit force un petit jugement au moment de l'écriture : est-ce une correction, une fonctionnalité, ou quelque chose qui va casser un consommateur ? Une équipe qui répond à cette question onze cents fois par an a une idée bien plus juste de sa propre surface de release que celle qui y répond quatre fois par an, en préparant une version.
L adopter sans ceremonie
L'adoption ne demande aucune cérémonie. La convention s'applique à partir du commit où vous décidez de l'appliquer, et un dépôt à l'historique mixte fonctionne très bien, parce que l'outillage qui lit la convention ignore simplement ce qu'il ne sait pas analyser. Commencer un lundi, sans migration ni réécriture de l'historique, est une façon parfaitement légitime de l'adopter.
Si vous voulez le garde-fou plutôt que la discipline, un hook sur le message de commit qui rejette une première ligne non conforme suffit, et cela coûte un fichier de configuration. Mais commencez par la convention elle-même. La spécification est courte, les deux correspondances SemVer sont la partie qui rapporte, et la notation par point d'exclamation est le détail que la plupart des gens ratent à la première lecture.



L'avantage qui subsiste même sans aucun outillage est celui que les équipes sous-estiment. Exiger un type sur chaque commit force un petit jugement au moment de l'écriture : est-ce une correction, une fonctionnalité, ou quelque chose qui va casser un consommateur ? Une équipe qui répond à cette question onze cents fois par an a une idée bien plus juste de sa propre surface de release que celle qui y répond quatre fois par an, en préparant une version.