Créer un tag de release¶
Vous êtes mainteneur·se de Kirexo et vous voulez déclencher une release : créer un tag Git qui enchaîne automatiquement le build des images Docker, le déploiement de la documentation sur GitLab Pages et la création de la release GitLab avec changelog.
Le workflow recommandé met à jour le CHANGELOG.md dans le même mouvement, via le skill /creer-tag. Cette recette présente d'abord ce workflow, puis les trois façons de déclencher la pose du tag à plus bas niveau (Méthodes A, B, C) — utiles si vous voulez taguer sans toucher au changelog.
Workflow recommandé : le skill /creer-tag¶
Le skill /creer-tag est l'enrobage de référence autour de la Méthode C. Il garantit que le commit taggé contient déjà sa propre section de CHANGELOG.md : le nom du tag est calculé d'avance et imposé au job CI (TAG_NAME), au lieu d'être calculé côté serveur au moment de la pose. La section de changelog peut donc être écrite avant la pose du tag.
Le découpage est volontairement en deux scripts — une partie locale sans effet, une partie qui pousse :
prepare.sh → branche release-<tag> + CHANGELOG.md généré (LOCAL, sans effet remote)
release.sh → commit + MR auto-merge sur main + tag:create (REMOTE, lancé par vous)
1. Préparer (local)¶
Depuis main à jour et arbre propre :
Le script calcule TAG_NAME=$(date -u +%Y%m%d%H%M), vérifie qu'au moins un commit existe depuis le dernier tag de release, crée la branche release-<TAG_NAME>, puis lance castor changelog:prepare --name <TAG_NAME> — qui insère la section générée en tête de CHANGELOG.md. La section est affichée ; la dernière ligne de sortie est TAG_NAME=<valeur>. Notez ce TAG_NAME.
La génération du changelog est déterministe : les commits depuis le dernier tag sont classés par rubrique selon leur préfixe Conventional Commits (cf. Convention du CHANGELOG.md). Aucune rédaction par un LLM.
2. Relire (optionnel)¶
La section est déjà affichée par prepare.sh. Pour ajuster un libellé, éditez CHANGELOG.md à la main avant l'étape suivante — la génération est volontairement brute (sujets de commits nettoyés, classés).
3. Ship (remote)¶
Le script enchaîne, dans l'ordre : castor audit (filet CVE) → commit du CHANGELOG.md → push → glab mr create + auto-merge sur main → attente du merge → attente d'une pipeline verte sur main → castor tag:create --name <TAG_NAME>. À la fin, la pipeline $CI_COMMIT_TAG (build images, doc, release GitLab) se déclenche automatiquement, comme pour les autres méthodes.
Pourquoi attendre le vert avant de taguer
Le job tag:create exige une pipeline success sur le dernier commit de main (cf. garde-fou). release.sh attend donc le vert post-merge avant d'appeler tag:create --name — sinon le tag serait refusé.
Le TAG_NAME étant imposé (validé ^[0-9]{12}$ côté Castor et côté job CI), la section de CHANGELOG.md écrite à l'étape 1 porte exactement le nom du tag qui sera posé : pas de décalage entre le contenu commité et le tag.
Le détail bas niveau de la pose du tag (garde-fou, chaîne de jobs déclenchée) est ci-dessous.
Prérequis¶
- Vous avez les droits Maintainer ou Owner sur le projet GitLab.
- La variable CI/CD
GITLAB_TOKENest configurée (cf. Variables CI/CD GitLab) — un Personal Access Token avec le scopewrite_repository(les project access tokens sont réservés à GitLab Premium/Ultimate). - Les images
kirexo-base,kirexo-devetkirexo-prodont déjà été buildées au moins une fois — sinon les jobsdocker:build-*sur tag échoueraient car il n'y a rien à mettre en cache (et la pipeline qualité, qui dépend de ces images, ne tournerait pas non plus). L'imagekirexo-docsne nécessite pas ce bootstrap : son jobdocker:build-docsestneeds: []et tourne automatiquement avantpagessur tag.
Pourquoi passer par une pipeline et pas par git tag en local
git push --tags fonctionnerait, mais ça impose d'avoir une clé SSH ou un PAT configurés sur la machine qui pousse, et ça ne marche pas depuis un environnement où le repo n'est pas cloné en écriture (ex. depuis Claude Code en mode bypassPermissions qui n'a pas la clé de la personne). La pipeline tag:create crée le tag côté serveur via l'API GitLab — l'auteur·rice de la release n'a besoin que de son accès UI ou de glab.
Méthode A — Depuis l'UI GitLab¶
- Aller sur Build → Pipelines → Run pipeline (bouton en haut à droite).
- Choisir la branche
main. - Ajouter une variable :
- Key :
CREATE_TAG - Value :
true - Type :
Variable(pasFile). - Cliquer sur Run pipeline.
La pipeline créée ne contient que le job tag:create (stage tag). Tous les autres jobs sont filtrés par leurs rules: — la pipeline CREATE_TAG=true est volontairement isolée pour ne pas dédoubler la pipeline qualité.
Garde-fou : une pipeline verte est exigée sur le dernier commit de main
Avant de créer le tag, tag:create vérifie qu'au moins une pipeline success existe pour le dernier commit de main ($CI_COMMIT_SHA) — typiquement la pipeline de validation jouée au merge. Si aucune pipeline verte n'est trouvée, le job échoue et le tag n'est pas créé. Le but : ne jamais figer une release sur un commit dont la CI est rouge ou encore en cours.
Pour outrepasser ce garde-fou dans un cas exceptionnel, ajouter une seconde variable FORCE_TAG à true (en plus de CREATE_TAG) :
| Key | Value |
|---|---|
CREATE_TAG |
true |
FORCE_TAG |
true |
Avec FORCE_TAG=true, le statut de pipeline n'est plus vérifié : le tag est créé inconditionnellement. À réserver aux situations où l'on sait que le commit est sain mais que la pipeline n'a pas (re)tourné.
Méthode B — Depuis le CLI avec glab¶
Le retour de glab affiche l'ID et l'URL de la pipeline créée — suivez-la via glab ci get -p <id> ou directement dans l'UI.
Pour outrepasser le garde-fou de pipeline verte (cf. Méthode A), ajouter --variables "FORCE_TAG:true" :
Méthode C — Avec Castor¶
La cible castor tag:create est un raccourci autour de l'appel glab de la Méthode B — c'est la voie la plus courte pour poser un tag à la main. Le workflow recommandé (skill /creer-tag) s'appuie sur cette cible pour la pose finale du tag, après avoir mis à jour le CHANGELOG.md.
Pour outrepasser le garde-fou de pipeline verte (cf. Méthode A), ajouter --force — équivalent de FORCE_TAG=true. La cible affiche alors un warning avant de déclencher la pipeline :
Pour imposer le nom du tag au lieu de le laisser calculer côté serveur, passer --name (12 chiffres) — c'est ce que fait le skill /creer-tag une fois le CHANGELOG.md mergé :
La valeur est transmise au job via la variable CI TAG_NAME (cf. Ce que fait tag:create). Un nom mal formé lève une erreur côté Castor avant même de déclencher la pipeline.
Un simple raccourci, pas une mécanique différente
castor tag:create exécute exactement glab ci run --branch main --variables "CREATE_TAG:true" (plus --variables "TAG_NAME:<nom>" avec --name, et --variables "FORCE_TAG:true" avec --force). Le tag est créé côté serveur GitLab par le job tag:create, à l'identique des méthodes A et B — voir Ce que fait tag:create pour le détail. Prérequis : un glab authentifié (glab auth status), puisque la cible s'appuie sur lui.
Ce que fait tag:create¶
Le job tourne sur alpine:3 avec GIT_STRATEGY: none (pas besoin de cloner le code — l'API agit côté serveur GitLab) et exécute :
- Garde-fou (sauf si
FORCE_TAG=true) : interrogeGET /projects/:id/pipelines?sha=$CI_COMMIT_SHA&status=successet refuse de taguer (exit 1) si aucune pipelinesuccessn'existe pour le dernier commit demain. Le contrôle est fail-safe : si l'appel API échoue (token invalide, réseau), le compteur retombe à0et le job bloque plutôt que de risquer une release non validée. -
Détermine le nom du tag :
- si la variable
TAG_NAMEest fournie au déclenchement (cas du skill/creer-tag/castor tag:create --name), elle est utilisée telle quelle après validation du formatYYYYMMDDHHMM(12 chiffres) — un format invalide fait échouer le job ; - sinon, le nom est calculé côté serveur :
TAG_NAME=$(date -u +%Y%m%d%H%M)— formatYYYYMMDDHHMMen UTC.
Imposer
TAG_NAMEpermet au commit taggé de contenir déjà sa section deCHANGELOG.md, écrite sous ce même nom avant la pose du tag (cf. workflow recommandé). 3. Vérifie l'absence d'un tag homonyme (unGET .../repository/tags/${TAG_NAME}; un200arrête le job pour éviter un409cryptique auPOST). 4. AppellePOST /projects/:id/repository/tagsavec${GITLAB_TOKEN}: - si la variable
-
Affiche la réponse JSON via
jq '{name, target, message}'.
Le --fail de curl fait échouer le job si l'API renvoie un statut HTTP ≥ 400 — typiquement 403 Forbidden si GITLAB_TOKEN n'a pas le scope write_repository, ou 409 Conflict si un tag du même nom existe déjà (très peu probable avec le format YYYYMMDDHHMM).
Ce qui se déclenche ensuite¶
Dès que GitLab détecte le tag créé, il démarre automatiquement une nouvelle pipeline $CI_COMMIT_TAG. Cette pipeline ne rejoue ni les vérifications qualité ni les tests : ceux-ci ont déjà tourné sur le même SHA dans la pipeline main que tag:create a exigée verte (cf. Optimisation pipeline de tag). Seuls les jobs qui produisent un livrable ou dont le résultat dépend de l'instant T du tag s'exécutent :
| Stage | Job | Effet |
|---|---|---|
quality |
quality:composer-audit |
Filet de sécurité rejoué sur le SHA du tag — capture une CVE Composer éventuellement publiée entre le merge et le tag. Si rouge, la pipeline du tag échoue et les jobs docker:* / release:* ne tournent pas. |
docker |
docker:build-base, docker:build-dev, docker:build-prod |
Build + push automatique. kirexo-base et kirexo-dev reçoivent :php8.5 + :latest ; kirexo-prod reçoit en plus le tag de version immuable :$CI_COMMIT_TAG (cf. Déployer Kirexo en production). |
docker |
docker:build-docs, docker:build-supply-chain |
Build + push des images d'outillage CI consommées au stage suivant (mkdocs pour pages, cosign + syft pour docker:sign-prod et release:sbom). |
docker |
docker:sign-prod |
Signature keyless cosign des trois références poussées par docker:build-prod (cf. Supply chain et signatures). |
release |
pages |
Build de la documentation mkdocs en mode strict (via l'image kirexo-docs) et déploiement sur GitLab Pages. |
release |
release:sbom |
Génération du SBOM SPDX + CycloneDX de kirexo-prod:$CI_COMMIT_TAG. |
release |
release:create |
Création de la release GitLab avec changelog des commits depuis le tag précédent + attachement des SBOM en assets. |
maintenance |
container_scanning + security:container-issue |
Scan CVE de kirexo-prod:$CI_COMMIT_TAG fraîchement poussée — signal au moment de la release, pas H+24. Ouvre une issue uniquement si au moins une CVE Critical ou High est remontée (cf. Jobs container_scanning et security:container-issue). |
Le tout sans intervention manuelle supplémentaire. docker:build-docs étant dans le stage docker (avant release), l'image de build de la doc est toujours fraîche au moment où pages la consomme — aucun bootstrap manuel n'est nécessaire pour la doc (contrairement à kirexo-base / kirexo-dev, cf. prérequis).
Pourquoi la qualité ne rejoue pas
tag:create refuse de poser le tag si aucune pipeline success n'existe pour le commit ciblé. Le code du tag est donc, par construction, strictement le même SHA qu'une pipeline qualité verte. Rejouer cs-fix, phpstan, tests unitaires ou E2E donnerait exactement le même résultat — pure duplication de minutes runner. Seul quality:composer-audit est conservé sur tag, parce que la base de vulnérabilités évolue indépendamment du code et qu'une CVE peut être publiée entre le merge et le tag.
Vérifier qu'une release est partie¶
- La pipeline
CREATE_TAG=truedoit être verte. Si le jobtag:createéchoue avec403, vérifier le scopewrite_repositorydu token (cf. Variables CI/CD GitLab). - La pipeline
$CI_COMMIT_TAGapparaît dans la liste des pipelines avec le nom du tag (202605231742) — cliquer dessus pour suivre les jobs Docker et release. - La release apparaît dans Deploy → Releases avec le changelog généré.
- La doc est mise à jour sur
https://doc.kirexo.app/(cf. Configurer le domaine GitLab Pages). - Les images Docker sont visibles dans Deploy → Container Registry :
kirexo-baseetkirexo-devavec les tags:php8.5et:latest;kirexo-prodavec:php8.5,:latestet le tag de version:202605231742(le nom du tag Git) ;kirexo-docsavec le seul tag:latest.
Cas d'erreur¶
Le job tag:create échoue avec 401 Unauthorized¶
Le GITLAB_TOKEN est absent ou expiré. Régénérer le token (Settings → Access Tokens), mettre à jour la variable CI/CD (Settings → CI/CD → Variables) et relancer le job.
Le job tag:create échoue avec 403 Forbidden¶
Le token n'a pas le scope write_repository, ou son rôle est inférieur à Maintainer. Régénérer un token avec les bons droits.
Le job tag:create échoue sans appeler l'API des tags (garde-fou)¶
Le job s'arrête avant la création du tag parce qu'aucune pipeline success n'existe pour le dernier commit de main. Causes typiques : la pipeline de validation est encore en cours, a échoué, ou n'a jamais tourné sur ce commit. Attendre qu'une pipeline passe au vert sur main, puis relancer CREATE_TAG=true. En cas exceptionnel (commit que l'on sait sain mais sans pipeline rejouée), outrepasser avec FORCE_TAG=true (cf. Méthode A / Méthode B) ou castor tag:create --force (cf. Méthode C).
Pipeline $CI_COMMIT_TAG jamais déclenchée¶
Vérifier que le tag a bien été créé : glab repo view --web puis onglet Tags, ou directement via l'API. Si le tag existe mais aucune pipeline n'est partie, vérifier le workflow:rules dans .gitlab-ci.yml — la règle $CI_COMMIT_TAG doit y figurer.
Le job pages échoue en mode strict¶
mkdocs détecte un lien cassé, un snippet manquant ou un avertissement de plugin. Reproduire localement avec castor docs:build (qui passe les mêmes options) pour identifier la cause exacte avant de retagger.
Voir aussi¶
castor changelog:prepareetcastor tag:create— référence des cibles Castor utilisées par le workflow.- Convention du
CHANGELOG.md— versionnement par tag et correspondance préfixe → rubrique. - Pipeline GitLab CI — listing complet des stages, jobs et déclencheurs.
- Variables CI/CD GitLab — détail de
GITLAB_TOKEN. - Configurer le domaine GitLab Pages — étape de configuration du DNS pour
doc.kirexo.app.