From 3c41a7f388f7e766de784d77ea48d407fece7ba2 Mon Sep 17 00:00:00 2001 From: Stephan Butler Date: Wed, 16 Sep 2026 09:46:35 +0200 Subject: [PATCH 1/3] ci: registration of v1.x release channel --- .github/workflows/e2e.yml | 1 + .github/workflows/release.yml | 33 +++++++++++++++++++++++-------- release.config.js | 37 ++++++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 7ada0057b..367317fe4 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - v1.x types: - opened - synchronize diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7272577f7..43d65ca65 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,8 +22,8 @@ jobs: - name: Check branch is releasable run: | BRANCH="${{ github.ref_name }}" - if [ "$BRANCH" != "main" ] && ! echo "$BRANCH" | grep -qE '^release/v[0-9]+\.[0-9]+$'; then - echo "::error::Releases can only be created from 'main' or 'release/vX.Y' branches. Current branch: $BRANCH" + if [ "$BRANCH" != "main" ] && [ "$BRANCH" != "v1.x" ] && ! echo "$BRANCH" | grep -qE '^release/v[0-9]+\.[0-9]+$'; then + echo "::error::Releases can only be created from 'main', 'v1.x' or 'release/vX.Y' branches. Current branch: $BRANCH" exit 1 fi echo "Branch '$BRANCH' is valid for release." @@ -95,14 +95,14 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20.19.5' + node-version: '24.13.1' - name: Install semantic-release run: | npm install -g \ - semantic-release@24 \ + semantic-release@25 \ @semantic-release/commit-analyzer@13 \ @semantic-release/release-notes-generator@14 \ - @semantic-release/github@11 + @semantic-release/github@12 - name: Run semantic-release id: semantic run: | @@ -188,6 +188,25 @@ jobs: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Compute image tags + id: image_tags + env: + IMAGE: ghcr.io/${{ github.repository_owner }}/${{ matrix.package.name }} + VERSION: v${{ needs.release.outputs.new_release_version }} + BRANCH: ${{ github.ref_name }} + run: | + { + echo 'tags<> "$GITHUB_OUTPUT" + - name: Docker build and push uses: docker/build-push-action@v6 with: @@ -205,6 +224,4 @@ jobs: ${{ matrix.package.identifier == 'wallet:frontend' && format('NEXT_PUBLIC_AUTH_HOST={0}', matrix.package.auth_host) || '' }} ${{ matrix.package.identifier == 'wallet:frontend' && format('NEXT_PUBLIC_GATEHUB_ENV={0}', matrix.package.gatehub_env) || '' }} ${{ matrix.package.identifier == 'wallet:frontend' && format('NEXT_PUBLIC_THEME={0}', matrix.package.theme) || '' }} - tags: | - ghcr.io/${{ github.repository_owner }}/${{ matrix.package.name }}:v${{ needs.release.outputs.new_release_version }} - ghcr.io/${{ github.repository_owner }}/${{ matrix.package.name }}:latest + tags: ${{ steps.image_tags.outputs.tags }} diff --git a/release.config.js b/release.config.js index b02e0460e..18ed17c46 100644 --- a/release.config.js +++ b/release.config.js @@ -1,10 +1,45 @@ +// Release branches, in ascending version order. semantic-release derives the +// version range each branch may publish from the tags on the branches after it: +// +// - main The 0.x production line. When v1.x holds a v1.0.0 tag, main is +// clamped to ">=0.x <1.0.0" and accepts only patch and minor. A +// breaking change merged here then fails the release with +// EINVALIDNEXTVERSION. That failure is the guardrail, not a defect: +// the commit belongs on v1.x. +// - v1.x The next-major development line. It publishes on its own "v1.x" +// channel, so its GitHub releases are marked pre-release and never +// take the "Latest" badge away from main. +// +// Both branches are listed on every run, not only the branch being released. +// semantic-release can clamp main only if it can also read the v1.x tags. +// Listing v1.x before that branch exists costs nothing: semantic-release drops +// configured branches that have no matching remote branch. +// +// Keep the "v" prefix. semantic-release classifies a branch named "1.x" as a +// *maintenance* branch, which is for versions below the default branch and is +// the opposite of what this line does. "v1.x" is classified as a release branch. +// +// v1.x is spelled out as an object to make its channel explicit. Note that the +// channel is not what marks its GitHub releases as pre-release: that comes from +// the branch being a release branch that is not the first one in this array +// (see is-prerelease.js in @semantic-release/github). Do not add +// "prerelease: false" here by analogy with the maintenance entry below — that +// flag is checked first and would turn the pre-release marking off, letting a +// v1.x release compete with main for the "Latest" badge. +// // Maintenance branches (release/vX.Y) are added dynamically so semantic-release // accepts whichever branch the workflow is triggered against, without needing // to hardcode every future maintenance branch in advance. const branch = process.env.GITHUB_REF_NAME || 'main' const maintenanceMatch = branch.match(/^release\/v(\d+)\.(\d+)$/) -const branches = ['main'] +const branches = [ + 'main', + { + name: 'v1.x', + channel: 'v1.x' + } +] if (maintenanceMatch) { const [, major, minor] = maintenanceMatch branches.push({ From 3e110deab497c9b8a54483732e743643bde91956 Mon Sep 17 00:00:00 2001 From: Stephan Butler Date: Wed, 16 Sep 2026 09:53:53 +0200 Subject: [PATCH 2/3] docs: cleanup --- release.config.js | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/release.config.js b/release.config.js index 18ed17c46..00160b98d 100644 --- a/release.config.js +++ b/release.config.js @@ -10,26 +10,11 @@ // channel, so its GitHub releases are marked pre-release and never // take the "Latest" badge away from main. // -// Both branches are listed on every run, not only the branch being released. -// semantic-release can clamp main only if it can also read the v1.x tags. -// Listing v1.x before that branch exists costs nothing: semantic-release drops -// configured branches that have no matching remote branch. +// You might ask, why are we not just using release/v1.0.x instead? The reason +// is because we might want multiple feature versions of v1.x deployed in +// various environments simultaneously, and using release/v1.0.x would tie us to a +// single patch line, which is less flexible. // -// Keep the "v" prefix. semantic-release classifies a branch named "1.x" as a -// *maintenance* branch, which is for versions below the default branch and is -// the opposite of what this line does. "v1.x" is classified as a release branch. -// -// v1.x is spelled out as an object to make its channel explicit. Note that the -// channel is not what marks its GitHub releases as pre-release: that comes from -// the branch being a release branch that is not the first one in this array -// (see is-prerelease.js in @semantic-release/github). Do not add -// "prerelease: false" here by analogy with the maintenance entry below — that -// flag is checked first and would turn the pre-release marking off, letting a -// v1.x release compete with main for the "Latest" badge. -// -// Maintenance branches (release/vX.Y) are added dynamically so semantic-release -// accepts whichever branch the workflow is triggered against, without needing -// to hardcode every future maintenance branch in advance. const branch = process.env.GITHUB_REF_NAME || 'main' const maintenanceMatch = branch.match(/^release\/v(\d+)\.(\d+)$/) From b656dc4929d74f3fb891d86bd2f439c56d063f1f Mon Sep 17 00:00:00 2001 From: Stephan Butler Date: Wed, 16 Sep 2026 10:22:14 +0200 Subject: [PATCH 3/3] fix: formatting correction --- release.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release.config.js b/release.config.js index 00160b98d..d4e2a9039 100644 --- a/release.config.js +++ b/release.config.js @@ -11,7 +11,7 @@ // take the "Latest" badge away from main. // // You might ask, why are we not just using release/v1.0.x instead? The reason -// is because we might want multiple feature versions of v1.x deployed in +// is because we might want multiple feature versions of v1.x deployed in // various environments simultaneously, and using release/v1.0.x would tie us to a // single patch line, which is less flexible. //