From d3d232965361b7d56eb76f0e73bf37f0a289415b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20TR=C3=89BEL=20=28Perso=29?= Date: Thu, 8 Oct 2026 16:11:41 +0200 Subject: [PATCH] feat(release): isolate qualification workflow from main Generated with AI assistance, reviewed by @stephanetrebel Refs #2836 --- .github/workflows/job-release-please.yml | 5 +- .github/workflows/workflow-handle-main.yml | 71 ++----------- .../workflows/workflow-handle-releases.yml | 100 ++++++++++++++++++ RELEASE.md | 98 +++++------------ 4 files changed, 135 insertions(+), 139 deletions(-) create mode 100644 .github/workflows/workflow-handle-releases.yml diff --git a/.github/workflows/job-release-please.yml b/.github/workflows/job-release-please.yml index fe3608474d..3b4f6da25d 100644 --- a/.github/workflows/job-release-please.yml +++ b/.github/workflows/job-release-please.yml @@ -9,7 +9,6 @@ on: workflow_call: outputs: - release-created: description: Has a new release been created ? value: ${{ jobs.prepare.outputs.releases-created && !contains(jobs.prepare.outputs.tag-name, 'rc') }} @@ -57,7 +56,7 @@ jobs: # This job handles (thus the `prepare` name) a prerelease/release MR, # and provides the necessary outputs for the two subsequent jobs. # It will either: - # - Upsert a prerelease MR after a commit has been added to the target branch (namely, `main`) + # - Upsert a prerelease MR after a commit has been added to the target branch (`releases`) # - Create a prerelease (`X.Y.Z-rc`, `X.Y.Z-rc.1`, `X.Y.Z-rc.2`, etc.), if this workflow has been triggered by the related MR merge # - Create a release (`X.Y.Z`), if this workflow has been triggered by the related MR merge # - In both cases "Create" means: @@ -81,6 +80,7 @@ jobs: with: config-file: ".github/prerelease-please-config.json" manifest-file: ".github/prerelease-please-manifest.json" + target-branch: releases # This job only runs when a prerelease has just been created and thus, # will create/updade a subsequent "stable" release MR with the changelog incorporating the prereleases @@ -99,6 +99,7 @@ jobs: with: config-file: .github/release-please-config.json manifest-file: .github/release-please-manifest.json + target-branch: releases token: ${{ secrets.GITHUB_TOKEN }} # This job only runs when a release has just been created and thus, diff --git a/.github/workflows/workflow-handle-main.yml b/.github/workflows/workflow-handle-main.yml index 7b73196e86..91721239d0 100644 --- a/.github/workflows/workflow-handle-main.yml +++ b/.github/workflows/workflow-handle-main.yml @@ -1,11 +1,7 @@ -name: Handle next prerelease or release +name: Build and deploy latest from main permissions: - # Needed for release job - issues: write - contents: write - pull-requests: write - # Needed for build job + contents: read packages: write on: @@ -17,7 +13,6 @@ env: REGISTRY: ghcr.io NAMESPACE: "${{ github.repository }}" BUILD_AMD64: true - BUILD_ARM64: true USE_QEMU: false NODE_VERSION: 26.7.0 @@ -28,78 +23,26 @@ jobs: REGISTRY: ${{ env.REGISTRY }} NAMESPACE: ${{ env.NAMESPACE }} BUILD_AMD64: ${{ env.BUILD_AMD64 }} - BUILD_ARM64: ${{ env.BUILD_ARM64 }} USE_QEMU: ${{ env.USE_QEMU }} NODE_VERSION: ${{ env.NODE_VERSION }} steps: - name: Exposing env vars run: echo "Exposing env vars" - release: - uses: ./.github/workflows/job-release-please.yml - build-amd64: uses: ./.github/workflows/job-build.yml - # We build: - # - Whenever we push a commit on `main` branch, for CI purposes (we automatically push the build on our `cpin-hp` env) - # - Whenever we issue a new release, prerelease (both done on `main`) - if: ${{ github.ref == format('refs/heads/{0}', 'main') }} needs: - expose-vars - - release with: REGISTRY: ${{ needs.expose-vars.outputs.REGISTRY }} NAMESPACE: ${{ needs.expose-vars.outputs.NAMESPACE }} - RELEASE_CREATED: ${{ needs.release.outputs.release-created == 'true' }} - TAG: ${{ needs.release.outputs.tag-name }} - MAJOR_TAG: ${{ needs.release.outputs.major-tag }} - MINOR_TAG: ${{ needs.release.outputs.minor-tag }} - PATCH_TAG: ${{ needs.release.outputs.patch-tag }} + RELEASE_CREATED: false + TAG: '' + MAJOR_TAG: '' + MINOR_TAG: '' + PATCH_TAG: '' BUILD_AMD64: ${{ needs.expose-vars.outputs.BUILD_AMD64 == 'true' }} BUILD_ARM64: false USE_QEMU: ${{ needs.expose-vars.outputs.USE_QEMU == 'true' }} secrets: ARGOCD_TOKEN: ${{ secrets.ARGOCD_TOKEN }} - - build-arm64: - uses: ./.github/workflows/job-build.yml - if: ${{ needs.release.outputs.release-created == 'true' || needs.release.outputs.prerelease-created == 'true' }} - needs: - - expose-vars - - release - with: - REGISTRY: ${{ needs.expose-vars.outputs.REGISTRY }} - NAMESPACE: ${{ needs.expose-vars.outputs.NAMESPACE }} - RELEASE_CREATED: ${{ needs.release.outputs.release-created == 'true' }} - TAG: ${{ needs.release.outputs.tag-name }} - MAJOR_TAG: ${{ needs.release.outputs.major-tag }} - MINOR_TAG: ${{ needs.release.outputs.minor-tag }} - PATCH_TAG: ${{ needs.release.outputs.patch-tag }} - BUILD_AMD64: false - BUILD_ARM64: ${{ needs.expose-vars.outputs.BUILD_ARM64 == 'true' }} - USE_QEMU: ${{ needs.expose-vars.outputs.USE_QEMU == 'true' }} - secrets: - ARGOCD_TOKEN: ${{ secrets.ARGOCD_TOKEN }} - - update-chart: - runs-on: ubuntu-latest - if: ${{ needs.release.outputs.tag-name != '' }} - needs: - - expose-vars - - release - - build-amd64 - steps: - - name: Generate a token - id: generate-token - uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 - with: - app-id: ${{ secrets.GH_APP_ID }} - private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} - owner: ${{ github.repository_owner }} - repositories: helm-charts - - - name: Trigger helm-charts update - env: - GH_TOKEN: ${{ steps.generate-token.outputs.token }} - run: | - gh workflow --repo cloud-pi-native/helm-charts run update-chart.yaml -f APP_VERSION=${{ needs.release.outputs.tag-name }} -f CHART_NAME=dso-console diff --git a/.github/workflows/workflow-handle-releases.yml b/.github/workflows/workflow-handle-releases.yml new file mode 100644 index 0000000000..528648c9bd --- /dev/null +++ b/.github/workflows/workflow-handle-releases.yml @@ -0,0 +1,100 @@ +name: Handle next prerelease or release + +permissions: + issues: write + contents: write + pull-requests: write + packages: write + +on: + push: + branches: + - releases + +env: + REGISTRY: ghcr.io + NAMESPACE: "${{ github.repository }}" + BUILD_AMD64: true + BUILD_ARM64: true + USE_QEMU: false + NODE_VERSION: 26.7.0 + +jobs: + expose-vars: + runs-on: ubuntu-latest + outputs: + REGISTRY: ${{ env.REGISTRY }} + NAMESPACE: ${{ env.NAMESPACE }} + BUILD_AMD64: ${{ env.BUILD_AMD64 }} + BUILD_ARM64: ${{ env.BUILD_ARM64 }} + USE_QEMU: ${{ env.USE_QEMU }} + NODE_VERSION: ${{ env.NODE_VERSION }} + steps: + - name: Exposing env vars + run: echo "Exposing env vars" + + release: + uses: ./.github/workflows/job-release-please.yml + + build-amd64: + uses: ./.github/workflows/job-build.yml + if: ${{ needs.release.outputs.release-created == 'true' || needs.release.outputs.prerelease-created == 'true' }} + needs: + - expose-vars + - release + with: + REGISTRY: ${{ needs.expose-vars.outputs.REGISTRY }} + NAMESPACE: ${{ needs.expose-vars.outputs.NAMESPACE }} + RELEASE_CREATED: ${{ needs.release.outputs.release-created == 'true' }} + TAG: ${{ needs.release.outputs.tag-name }} + MAJOR_TAG: ${{ needs.release.outputs.major-tag }} + MINOR_TAG: ${{ needs.release.outputs.minor-tag }} + PATCH_TAG: ${{ needs.release.outputs.patch-tag }} + BUILD_AMD64: ${{ needs.expose-vars.outputs.BUILD_AMD64 == 'true' }} + BUILD_ARM64: false + USE_QEMU: ${{ needs.expose-vars.outputs.USE_QEMU == 'true' }} + secrets: + ARGOCD_TOKEN: ${{ secrets.ARGOCD_TOKEN }} + + build-arm64: + uses: ./.github/workflows/job-build.yml + if: ${{ needs.release.outputs.release-created == 'true' || needs.release.outputs.prerelease-created == 'true' }} + needs: + - expose-vars + - release + with: + REGISTRY: ${{ needs.expose-vars.outputs.REGISTRY }} + NAMESPACE: ${{ needs.expose-vars.outputs.NAMESPACE }} + RELEASE_CREATED: ${{ needs.release.outputs.release-created == 'true' }} + TAG: ${{ needs.release.outputs.tag-name }} + MAJOR_TAG: ${{ needs.release.outputs.major-tag }} + MINOR_TAG: ${{ needs.release.outputs.minor-tag }} + PATCH_TAG: ${{ needs.release.outputs.patch-tag }} + BUILD_AMD64: false + BUILD_ARM64: ${{ needs.expose-vars.outputs.BUILD_ARM64 == 'true' }} + USE_QEMU: ${{ needs.expose-vars.outputs.USE_QEMU == 'true' }} + secrets: + ARGOCD_TOKEN: ${{ secrets.ARGOCD_TOKEN }} + + update-chart: + runs-on: ubuntu-latest + if: ${{ needs.release.outputs.tag-name != '' }} + needs: + - expose-vars + - release + - build-amd64 + steps: + - name: Generate a token + id: generate-token + uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 + with: + app-id: ${{ secrets.GH_APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: helm-charts + + - name: Trigger helm-charts update + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + run: | + gh workflow --repo cloud-pi-native/helm-charts run update-chart.yaml -f APP_VERSION=${{ needs.release.outputs.tag-name }} -f CHART_NAME=dso-console diff --git a/RELEASE.md b/RELEASE.md index 1cfbd85fed..1f780f1b5d 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -11,7 +11,7 @@ Ce document décrit comment est géré le versionnement de `console`, c'est-à-d Afin d'éviter une confusion entre les correctifs d'urgence ("hotfixes") — historiquement des versions `PATCH` rétroportées sur `main` — et les versions régulières produites par des commits `fix:`, les versions stables adoptent le protocole suivant : -- Les versions régulières sur `main` sont par défaut des `MINOR` (et très rarement des `MAJOR`). +- Les versions régulières sur `releases` sont par défaut des `MINOR` (et très rarement des `MAJOR`). - Une préversion utilise la **prochaine** version stable visée, suffixée par `-rc`, puis `-rc.N` pour les candidates suivantes. Par exemple: - Si la dernière version publiée est la `9.24.5`, la préversion (RC) suivante sera la `9.25.0-rc` - Si la dernière version publiée est la `9.32.0-rc2`, la préversion suivante sera la `9.32.0-rc3` @@ -27,90 +27,42 @@ La structure de versionnement stable de `console` est donc : Les sections suivantes vont expliciter ce schéma: ```mermaid -gitGraph - branch release-please-main - commit id:" " - - %% Initial state: latest tag on main = v1.2.5 - checkout main - commit id: "Bump v1.2.5" tag: "v1.2.5" - - %% Some commits are added to main - commit id: "feat: something" - - %% Job create-or-update-release triggers release-please - %% which will upsert the next release MR, which will be a prerelease - checkout release-please-main - merge main - commit id: "Bump v1.3.0-rc" - - %% Upon merging the release-please MR - %% The new tag is created - checkout main - merge release-please-main tag: "v1.3.0-rc" - %% and the release-please MR for the next stable release is created - checkout release-please-main - merge main - commit id: "Bump v1.3.0" - - %% IF new commits are added BEFORE merging the release-please MR, - %% a new prerelease MR will replace the stable release MR - checkout main - commit id: "fix: something" - checkout release-please-main - merge main - commit id: "Bump v1.3.0-rc1" - - %% Upon merging the release-please MR - %% The new tag is created - checkout main - merge release-please-main tag: "v1.3.0-rc1" - %% and the release-please MR for the next stable release is created - checkout release-please-main - merge main - commit id: "Bump v1.3.0 (updated)" - - %% IF the stable release MR is merged into main - checkout main - merge release-please-main tag: "v1.3.0" - - %% New commits are then pushed to main - %% and the cycle starts anew - commit id: "chore: something" - - %% Job create-or-update-release triggers release-please - %% which will upsert the next release MR, which will be a prerelease - checkout release-please-main - merge main - commit id: "Bump v1.3.1-rc" +flowchart LR + M0["main: version stable N"] --> M1["main: nouvelles MRs fusionnées; build et déploiement latest"] + M0 --> R0["releases créée depuis le périmètre retenu sur main"] + M1 -. "correctif retenu : cherry-pick" .-> R1["releases: commits de qualification"] + R0 --> RP["Release Please cible releases"] + R1 --> RP + RP --> RC["Fusion de la PR RC sur releases; tag et build"] + RC --> ST["PR stable sur releases; fusion, tag et build"] + ST --> BACK["Après la stable : reporter les commits de version nécessaires sur main"] + BACK --> NEXT["Recréer releases depuis main pour le cycle suivant"] ``` ## Versionnement de Console -Le flux de travail [`Handle next prerelease or release`](https://github.com/cloud-pi-native/console/blob/main/.github/workflows/workflow-handle-main.yml) est déclenché à chaque nouveau commit sur `main`. Il utilise le job réutilisable [`Next Prerelease and Release`](https://github.com/cloud-pi-native/console/blob/main/.github/workflows/job-release-please.yml) et notamment la GitHub Action [release-please-action](https://github.com/googleapis/release-please-action). +Le flux [`Build and deploy latest from main`](https://github.com/cloud-pi-native/console/blob/main/.github/workflows/workflow-handle-main.yml) est déclenché à chaque push sur `main`. Il construit la version d'intégration et déploie `latest`; il ne déclenche pas Release Please. + +Le flux [`Handle next prerelease or release`](https://github.com/cloud-pi-native/console/blob/main/.github/workflows/workflow-handle-releases.yml) est déclenché à chaque push sur `releases`. Il appelle le job réutilisable [`Next Prerelease and Release`](https://github.com/cloud-pi-native/console/blob/main/.github/workflows/job-release-please.yml), avec `releases` comme branche cible pour les PR RC et stable. Les tags RC et stables déclenchent les builds d'artefacts et la mise à jour du chart. Le job de déploiement `latest` reste réservé aux événements de `main`. -Le flux de travail [`Handle next hotfix`](https://github.com/cloud-pi-native/console/blob/main/.github/workflows/workflow-handle-hotfix.yml) est déclenché à chaque nouveau commit sur une branche `hotfix/*`. Il utilise le job réutilisable [`Next Hotfix`](https://github.com/cloud-pi-native/console/blob/main/.github/workflows/job-release-please-hotfixes.yml). +Le flux [`Handle next hotfix`](https://github.com/cloud-pi-native/console/blob/main/.github/workflows/workflow-handle-hotfix.yml) est déclenché à chaque nouveau commit sur une branche `hotfix/*`. Il utilise le job réutilisable [`Next Hotfix`](https://github.com/cloud-pi-native/console/blob/main/.github/workflows/job-release-please-hotfixes.yml). Trois configurations isolent les états de version : -- [`.github/prerelease-please-config.json`](./.github/prerelease-please-config.json) et son [manifeste](./.github/prerelease-please-manifest.json) gèrent les préversions (ou "Release Candidates", usuellement dénotées "RC") ; -- [`.github/release-please-config.json`](./.github/release-please-config.json) et son [manifeste](./.github/release-please-manifest.json) gèrent les versions "stables". La version stable met aussi à jour le manifeste de préversion afin de réinitialiser le cycle suivant depuis cette version (si on était à la `-rc14`, on réinitialise à `-rc` pour la prochaine version) ; +- [`.github/prerelease-please-config.json`](./.github/prerelease-please-config.json) et son [manifeste](./.github/prerelease-please-manifest.json) gèrent les préversions (ou "Release Candidates", usuellement dénotées "RC") sur `releases` ; +- [`.github/release-please-config.json`](./.github/release-please-config.json) et son [manifeste](./.github/release-please-manifest.json) gèrent les versions stables sur `releases`. La version stable met aussi à jour le manifeste de préversion afin de réinitialiser le cycle suivant depuis cette version (si on était à la `-rc14`, on réinitialise à `-rc` pour la prochaine version) ; - [`.github/hotfix-release-please-config.json`](./.github/hotfix-release-please-config.json) et le manifeste de version stable gèrent les correctifs d'urgence. Cette configuration n'écrit jamais dans le manifeste de préversion. -À chaque exécution, le job `prepare` finalise d'abord toute requête de fusion de version déjà fusionnée, puis crée ou met à jour la prochaine requête de fusion. -Plus concrètement, le cycle est le suivant : +À chaque push sur `releases`, le job `prepare` finalise d'abord toute PR de version déjà fusionnée, puis crée ou met à jour la prochaine PR Release Please. Le cycle manuel est le suivant : -1. Les commits classiques (`feat`, `chore`, etc.) sur `main` déclenchent les workflows GitHub qui vont créer/mettre à jour une MR de préversion. -2. La fusion de cette MR dans `main` crée effectivement la préversion: - a. Création du tag git `vX.Y.Z-rc`. Les RC suivantes de la même version sont taguées `vX.Y.Z-rc.N`, où `N` vaut `1`, puis `2`, etc. - b. Création des images OCI/docker poussées en tant que "packages" dans l'organisation GitHub `cloud-pi-native` - c. Création de la Release GitHub, avec cependant une étiquette `Pre-release` pour indiquer que ce n'est pas une version "stable". Ce n'est, par exemple, pas la version qui est affichée sur la page de garde du dépôt - d. Création de la MR côté `helm-chart` qui va faire un bump mineur du chart `dso-console` avec comme `appVersion` la version RC nouvellement créée (il faudra alors fusionner cette MR pour avoir un chart à jour) -3. Après la création de la préversion, vu que son commit de "bump" de version a été poussé sur `main`, les workflows sont à nouveaux déclenchés et une MR de version stable `vX.Y.Z` est créée. -4. La fusion de cette MR crée la Release GitHub stable `vX.Y.Z` -5. Tout nouveau commit classique poussé sur `main` déclenchera la création de la prochaine MR de préversion (retour à l'étape 1) +1. Au début d'une qualification, créer `releases` depuis le commit de `main` qui définit le périmètre de la version et pousser cette branche. Pour la bascule du cycle existant, fermer sans la fusionner l'ancienne PR Release Please qui cible `main`, puis créer `releases` depuis l'état de `main` contenant la dernière RC publiée. Release Please crée alors la PR de version sur `releases`. +2. Fusionner les MRs fonctionnelles sur `main` comme d'habitude. Elles continuent à alimenter le build et le déploiement `latest`, sans créer de RC. +3. Pour inclure une correction dans la version en qualification, cherry-picker son commit de `main` sur `releases`. Release Please crée ou met à jour la PR RC ciblant `releases`. +4. Fusionner la PR RC sur `releases` pour créer le tag `vX.Y.Z-rc` ou `vX.Y.Z-rc.N`, la Release GitHub « Pre-release » et les builds d'artefacts correspondants. Le flux existant déclenche également la mise à jour du chart Helm. +5. Après une RC, Release Please crée ou met à jour la PR de version stable `vX.Y.Z`, toujours ciblant `releases`. La fusion de cette PR crée le tag et la Release GitHub stables, les builds d'artefacts et les tags partiels `vX` et `vX.Y`. +6. Après la stable, reporter sur `main` uniquement les commits de version/release nécessaires qui ne s'y trouvent pas déjà. Ne pas fusionner l'ensemble de `releases`, qui contient des cherry-picks de commits déjà présents sur `main`. Supprimer ensuite `releases` et la recréer depuis `main` au début de la qualification suivante. -**⚠️NOTE⚠️**: Si jamais, après l'étape `3` (création de la MR pour la version stable) des commits "classiques" sont poussés sur `main` alors **la MR de la version stable sera automatiquement "convertie" en MR de préversion**. Dit autrement: **On ne peut créer une version "stable" qu'à partir d'une préversion**. Tout commit en plus empêche d'avoir une version stable, il faut forcément qu'il y ait ZERO commit après une préversion pour avoir une MR de version stable. +Les commits qui ne sont pas cherry-pickés sur `releases` restent hors du périmètre de la version en qualification. Le cycle de Release Please régulier ne démarre pas depuis `main`. ## Notes de version