From 68f747a216078b7fe3404a77d56cae686670fdf5 Mon Sep 17 00:00:00 2001 From: chinmay jain Date: Mon, 17 Aug 2026 15:18:26 +0530 Subject: [PATCH 1/3] feat(version): add force-stable and create-rc-baseline inputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two new opt-in inputs on the reusable version.yml workflow, both defaulting to preserve current behaviour so existing consumers are byte-identical. - force-stable (default false): when true, non-default branches skip the --prerelease --maintained-version flags and cut a stable release the same way the default branch does. Enables a "dev cuts the version, default branch rubber-stamps the deploy" model where the RC line is not maintained. - create-rc-baseline (default true): gates the "Create next RC baseline" step on the default branch. Set to false when the RC line is not maintained (typically alongside force-stable=true on the RC-side workflow), preventing orphan vX.Y.0-rc.1 tags from piling up. Backwards-compatible minor change — no consumer needs to update inputs. --- .github/workflows/version.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index a88325a..3fac6a7 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -45,6 +45,16 @@ on: type: string required: false default: 'dev' + force-stable: + description: 'Cut a stable release even on non-default branches (skips the --prerelease flag). Use when a non-default branch should own version cutting and the default branch acts as a deploy-only rubber stamp.' + type: boolean + required: false + default: false + create-rc-baseline: + description: 'After a stable release on the default branch, auto-create the next-minor RC prerelease tag on rc-branch. Set to false when the RC line is not maintained (e.g. when force-stable is used elsewhere).' + type: boolean + required: false + default: true outputs: version: description: 'The version produced, or empty if no release was cut.' @@ -109,9 +119,10 @@ jobs: RC_LINE: ${{ steps.detect.outputs.rc-line }} WITH_CHANGELOG_DEFAULT: ${{ inputs.changelog-on-default }} WITH_CHANGELOG_RC: ${{ inputs.changelog-on-rc }} + FORCE_STABLE: ${{ inputs.force-stable }} run: | set -- --token "$GH_TOKEN" --version-file --allow-no-changes - if [ "$CURRENT_REF" != "$DEFAULT_BRANCH" ] && [ -n "$RC_LINE" ]; then + if [ "$CURRENT_REF" != "$DEFAULT_BRANCH" ] && [ -n "$RC_LINE" ] && [ "$FORCE_STABLE" != "true" ]; then set -- "$@" --prerelease --maintained-version "$RC_LINE" fi if { [ "$CURRENT_REF" = "$DEFAULT_BRANCH" ] && [ "$WITH_CHANGELOG_DEFAULT" = "true" ]; } || \ @@ -141,7 +152,9 @@ jobs: fi - name: Create next RC baseline - if: github.ref_name == github.event.repository.default_branch + if: | + inputs.create-rc-baseline && + github.ref_name == github.event.repository.default_branch env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} RC_BRANCH: ${{ inputs.rc-branch }} From eb2296917522d811f55deb920f0091bf7c4c5272 Mon Sep 17 00:00:00 2001 From: chinmay jain Date: Mon, 17 Aug 2026 16:05:56 +0530 Subject: [PATCH 2/3] fix(version): force-stable needs to override CI-condition defaultBranch The condition-github plugin refuses a non-default branch with "configured to only publish from " unless the config passed to it says otherwise. Semrel's main.go forwards repoInfo.DefaultBranch (fetched from the GitHub API) as the ci-condition's defaultBranch by default; the previous force-stable path didn't override it, so semrel exited with code 66 before cutting the tag. Fix: pass --ci-condition-opt "defaultBranch=$CURRENT_REF" when force-stable=true on a non-default branch. This makes the condition see branch==defaultBranch and proceed. The RC prerelease path was implicitly relying on --maintained-version setting defaultBranch to "*" inside semrel core, so it didn't need this workaround. Verified failure: ago-web-frontend run 32020117652 exited at the ci condition step with the "only publish from main" error. --- .github/workflows/version.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 3fac6a7..9f2b49f 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -122,8 +122,16 @@ jobs: FORCE_STABLE: ${{ inputs.force-stable }} run: | set -- --token "$GH_TOKEN" --version-file --allow-no-changes - if [ "$CURRENT_REF" != "$DEFAULT_BRANCH" ] && [ -n "$RC_LINE" ] && [ "$FORCE_STABLE" != "true" ]; then - set -- "$@" --prerelease --maintained-version "$RC_LINE" + if [ "$CURRENT_REF" != "$DEFAULT_BRANCH" ]; then + if [ "$FORCE_STABLE" = "true" ]; then + # go-semantic-release's condition-github plugin refuses a non-default + # branch with "configured to only publish from " unless the + # config says otherwise. Override defaultBranch so the condition sees + # branch==defaultBranch and proceeds. + set -- "$@" --ci-condition-opt "defaultBranch=$CURRENT_REF" + elif [ -n "$RC_LINE" ]; then + set -- "$@" --prerelease --maintained-version "$RC_LINE" + fi fi if { [ "$CURRENT_REF" = "$DEFAULT_BRANCH" ] && [ "$WITH_CHANGELOG_DEFAULT" = "true" ]; } || \ { [ "$CURRENT_REF" != "$DEFAULT_BRANCH" ] && [ "$WITH_CHANGELOG_RC" = "true" ]; }; then From 0bdf9ba6900d2b8ebf6450538ab3335287da9fe3 Mon Sep 17 00:00:00 2001 From: chinmay jain Date: Mon, 17 Aug 2026 16:41:02 +0530 Subject: [PATCH 3/3] feat(version): add expose-latest-on-noop input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the rubber-stamp release model, the default branch merges an already-tagged branch and semrel correctly exits with "no change" (the release exists via GH API lookup, no new bump needed). Downstream build/deploy/announce steps that fall back to \`git describe\` when outputs.version is empty can pick the WRONG tag: rev-list distance between HEAD and the actual latest tag can be huge across a divergent merge (all the docs: changelog auto-commits + hotfix merges on the default branch that aren't reachable from the tag's commit). Fix: opt-in input that populates outputs.version from GH's release list on no-op, using the same source semrel uses when it looks up the "latest release". Callers set expose-latest-on-noop: true on the default-branch's version job to guarantee outputs.version reflects the actual latest stable release, not whatever git describe returns. Default false preserves existing behaviour for all consumers. Verified failure: AmericanGunOwners/ago-web-frontend PR #645 merge — main pipeline no-oped correctly, but git describe returned v1.17.0 (6 commits away via first-parent) instead of v1.18.0 (58 commits away by rev-list distance, but only 1 hop via second-parent). Prod /version.json ended up stamped 1.17.0 despite v1.18.0 being the actual latest release. --- .github/workflows/version.yml | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 9f2b49f..2d59fdd 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -55,9 +55,14 @@ on: type: boolean required: false default: true + expose-latest-on-noops: + description: 'When semrel exits without cutting a new tag (rubber-stamp merges into a branch where the release already exists), still expose the latest stable release as outputs.version. Prevents downstream build/deploy/announce steps from falling back to a stale git-describe result across a divergent merge.' + type: boolean + required: false + default: false outputs: version: - description: 'The version produced, or empty if no release was cut.' + description: 'The version produced, or empty if no release was cut (unless expose-latest-on-noops is true, in which case the latest stable tag is exposed).' value: ${{ jobs.release.outputs.version }} jobs: @@ -141,8 +146,27 @@ jobs: - name: Expose version id: version + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + EXPOSE_LATEST_ON_NOOP: ${{ inputs.expose-latest-on-noops }} run: | - if [ -f .version ]; then echo "version=$(cat .version)" >> "$GITHUB_OUTPUT"; fi + if [ -f .version ]; then + echo "version=$(cat .version)" >> "$GITHUB_OUTPUT" + elif [ "$EXPOSE_LATEST_ON_NOOP" = "true" ]; then + # Rubber-stamp path: semrel found no bump because the tag already + # exists on a merged branch. `git describe` on divergent histories + # can pick the wrong tag by rev-list distance, so read from GH + # releases instead — same source semrel itself used. + latest=$(gh release list \ + --repo "$GITHUB_REPOSITORY" \ + --exclude-pre-releases \ + --limit 1 \ + --json tagName \ + --jq '.[0].tagName' | sed 's/^v//') + if [ -n "$latest" ]; then + echo "version=$latest" >> "$GITHUB_OUTPUT" + fi + fi - name: Commit changelog if: |