Un check de changelog pour GitHub Actions
6 min de lecture
Chaque équipe qui tient un changelog à la main a eu la même conversation après le même incident : une release est sortie sans entrée, quelqu’un demande pourquoi, et la réponse honnête est que la personne qui l’aurait écrite allait vite et l’étape du changelog ne vivait que dans la mémoire. L’automatisation du changelog couvre ce qu’un pipeline peut automatiser en sécurité et ce qui a encore besoin d’une personne ; un check de changelog en CI est l’autre moitié de ce problème, parce qu’automatiser l’écriture n’aide pas si personne n’est obligé de la déclencher en premier lieu. GitHub Actions est là où la plupart des équipes font déjà tourner leurs checks de pull request, donc c’est là que celui-ci vit aussi.
Pourquoi « on demande aux gens d’ajouter une entrée » échoue-t-il selon un schéma prévisible ?
Parce que ça rivalise pour l’attention avec tout le reste dans une pull request, et c’est la seule partie sans conséquence immédiate à la sauter. Les tests échouent bruyamment et bloquent le merge. Une entrée de changelog manquante ne bloque rien, donc elle perd dès que quelqu’un est pressé, ce qui en pratique est la plupart du temps. Une politique imposée par la mémoire se dégrade exactement au rythme attendu : bien pendant les premières semaines après l’accord de tous, puis silencieusement abandonnée dès que la personne qui s’en souciait part en vacances ou change d’équipe.
Que vérifie vraiment un check CI pour une entrée de changelog ?
Pas la qualité de la rédaction, seulement qu’une entrée existe et qu’elle est bien formée, ce qui est le bon périmètre pour un check de changelog qui tourne en CI plutôt que dans la tête de quelqu’un. Une forme courante : le check regarde le diff de la PR et exige soit un nouveau fichier dans un répertoire de changesets (le modèle qu’utilisent Changesets et des outils similaires) soit une ligne modifiée dans un fichier de changelog, et fait échouer le build si aucun des deux n’existe. La revue de ce que dit vraiment l’entrée continue de se passer là où elle s’est toujours passée, en code review, parce que ce jugement n’a pas sa place dans un script.
| Ce que vérifie le check CI | Ce qu’il ne vérifie pas |
|---|---|
| Un changeset ou une ligne de changelog existe dans le diff | Si la formulation est claire |
| L’entrée référence le bon package, dans un monorepo | Si le changement mérite vraiment une entrée |
| Le fichier est syntaxiquement valide (front matter, forme JSON) | Si l’entrée est honnête sur l’impact |
Chaque PR en a-t-elle besoin, ou certains changements sont-ils exemptés ?
Certains sont exemptés, et la liste des exemptions est là où ces systèmes se construisent ou
s’abandonnent vraiment. Une montée de dépendance sans effet visible, un changement uniquement de
tests, un refactor interne sans changement de comportement : aucun de ceux-là ne devrait forcer
une contributrice à inventer une entrée de changelog pour quelque chose dont personne lisant le
changelog ne se soucie. Le modèle qui fonctionne est une étiquette ou un flag qu’une contributrice
peut appliquer (no-changelog-needed) et qui satisfait le check CI sans fichier, revu par qui
approuve la PR, pour que l’exemption elle-même passe le même contrôle qu’une entrée.
Que se passe-t-il pour les exceptions légitimes, comme un hotfix urgent ?
Le gate appartient au merge, pas au déploiement : un hotfix sous vraie pression de temps peut merger avec une entrée provisoire ou un ticket de suivi, pourvu que le check CI soit satisfait par l’intention plutôt que seulement par un paragraphe fini ; certaines équipes acceptent un stub d’une ligne qu’une mainteneuse peaufine avant la prochaine coupe de release. Ce que le gate ne devrait jamais permettre, c’est de sauter l’étape en silence, parce qu’un stub qu’on oublie est un échec plus petit qu’une entrée qui n’a jamais existé, et un stub laisse au moins une trace que quelqu’un peut retrouver plus tard.
# .github/workflows/changelog-check.yml
on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
jobs:
changelog:
if: >-
!contains(github.event.pull_request.labels.*.name,
'no-changelog-needed')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # le diff a besoin de la branche de base
- name: Require changelog entry
run: |
base="origin/${{ github.base_ref }}"
if ! git diff --name-only "$base"...HEAD \
| grep -q '^\.changeset/'; then
echo "No changeset. Add one, or have a maintainer"
echo "apply the no-changelog-needed label."
exit 1
fi
Comment savoir que le check lui-même est correct avant qu’il ne bloque de vraies PR ?
Ouvrez d’abord une pull request de test contre une branche jetable : une avec un changeset, une
sans, et une portant l’étiquette d’exemption, et confirmez que les trois obtiennent le résultat
attendu avant que le check ne s’applique au travail de quelqu’un d’autre. Un check de changelog qui
échoue ouvert, laissant passer chaque PR parce qu’une condition a été écrite à l’envers, est pire
que l’absence de check, parce que ça ressemble à une couverture qui n’existe pas.
workflow_dispatch sur le même fichier, exécuté manuellement contre quelques PR récemment
mergées, attrape la plupart de ces erreurs sans avoir besoin d’une vraie pull request.
La même idée fonctionne-t-elle en dehors de GitHub Actions ?
La forme se transpose, seule la syntaxe change. GitLab CI exprime la même règle comme un bloc
rules de job qui vérifie $CI_MERGE_REQUEST_LABELS au lieu d’un if GitHub Actions, et une
approbation de merge request obligatoire peut remplacer l’étape de revue d’exemption. Le check que
cet article décrit est GitHub Actions parce que c’est la plateforme sur laquelle la plupart des
lectrices sont déjà, mais l’exigence sous-jacente, un gate vérifié par une machine plutôt qu’une
convention qu’on se contente de demander, est la même partout où une CI tourne avant un merge.
Est-ce que ça fonctionne pareil dans un monorepo ?
Il faut une pièce de plus : pour quel package est l’entrée. Les changelogs de monorepo couvre pourquoi un seul fichier pour tout le repo cesse de fonctionner dès que les packages sont livrés indépendamment ; le check CI hérite de la même exigence ; un changeset qui ne nomme pas de package n’est pas une preuve utile que le bon changelog sera mis à jour, seulement qu’un fichier a changé quelque part dans le diff. Les outils construits pour ça (Changesets est celui courant dans l’écosystème JavaScript) demandent à la contributrice de choisir le package concerné et un bump de semver au moment même où le changeset est créé, donc le check CI obtient les deux pièces gratuitement au lieu de les inférer plus tard.
FAQ
Le check CI devrait-il bloquer le merge, ou juste avertir ? Bloquer. Un avertissement est fonctionnellement identique à demander gentiment, ce qui a déjà échoué. L’étiquette d’exemption existe précisément pour qu’un cas d’avertissement authentique ait quand même un chemin légitime à travers le même gate strict.
Qui vérifie qu’une étiquette d’exemption a été appliquée correctement ? Qui que ce soit qui approuve la pull request, dans le cadre de la revue qu’elle fait déjà de toute façon. L’étiquette ne devrait jamais être auto-appliquée sans revue, sinon elle devient le même contournement silencieux que le gate était censé fermer.
Exiger ça en CI remplace-t-il le besoin d’un pipeline d’automatisation de changelog ? Non, ça l’alimente. L’automatisation du changelog couvre comment transformer des entrées structurées en une page, un flux et un email ; le check CI est ce qui garantit que ces entrées structurées existent pour être automatisées en premier lieu.
Quelle est la plus petite version de ça qui vaut la peine d’être construite en premier ? Un seul check qui échoue si aucun fichier n’a changé sous un répertoire de changelog désigné, avec une étiquette d’exemption. Le routage par package et l’inférence de semver pour un monorepo peuvent venir plus tard ; l’habitude centrale, une entrée existe ou quelqu’un a explicitement dit qu’elle n’était pas nécessaire, vaut la peine d’être là dès le premier jour.
Les affirmations techniques de cet article n'ont pas été vérifiées de façon indépendante. Si quelque chose est faux, dis-le-nous et nous le corrigerons.