Skip to content

Commit d5966ed

Browse files
EtienneLescotclaude
andcommitted
fix(release): name the release branch per stable version, reuse it across RCs
prerelease.yml created release/v${PRERELEASE} (e.g. release/v1.7.0-rc.1) but promote.yml resolves release/v${STABLE_VERSION} (release/v1.7.0), so promote would die at `git fetch origin release/v1.7.0`. v1.7.0-rc.1 is the first RC cut under the release-branch freeze, so this path had never run. Worse, each re-cut created a *new* branch off main and force-deleted the remote branch first, so cutting rc.2 would both sweep in whatever had landed on main and destroy the cherry-picks on the rc.1 branch. That silently defeats the freeze the contract is supposed to guarantee. Use one branch per stable version (release/vX.Y.Z), created at rc.1 and reused for later RCs: fetch and check it out when it exists, branch from the dispatched ref when it doesn't, and never delete it. Fold the package.json bump into the same step so the version is written after the correct branch is checked out. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent f62162b commit d5966ed

1 file changed

Lines changed: 31 additions & 22 deletions

File tree

.github/workflows/prerelease.yml

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -75,34 +75,43 @@ jobs:
7575
NEXT: ${{ steps.version.outputs.next }}
7676
run: node .github/scripts/release-milestone-migrate.mjs
7777

78-
- name: Bump package.json to pre-release version
79-
env:
80-
PRERELEASE: ${{ steps.version.outputs.prerelease }}
81-
run: |
82-
set -euo pipefail
83-
sed -i -E "s|(\"version\"[[:space:]]*:[[:space:]]*\")[^\"]*(\")|\1${PRERELEASE}\2|" package.json
84-
echo "package.json version:"
85-
grep '"version"' package.json
86-
87-
- name: Commit package.json bump on a release branch
78+
- name: Create or reuse the release branch and bump package.json
8879
env:
8980
TOKEN: ${{ secrets.OPENSCREEN_RELEASE_TOKEN }}
9081
PRERELEASE: ${{ steps.version.outputs.prerelease }}
82+
NEXT: ${{ steps.version.outputs.next }}
9183
run: |
9284
set -euo pipefail
9385
git config user.name "github-actions[bot]"
9486
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
95-
# The release branch is FROZEN between RC cut and stable promotion. The bump
96-
# commit lives on release/v${PRERELEASE} only; nothing is merged into main
97-
# until promote.yml publishes the stable tag, so any features merged into
98-
# main after this step are NOT in the RC build.
99-
BRANCH="release/v${PRERELEASE}"
100-
# Delete remote branch first so the push below is always fast-forward (idempotent on rerun).
101-
git push "https://x-access-token:${TOKEN}@github.com/${GITHUB_REPOSITORY}.git" ":${BRANCH}" 2>/dev/null || true
102-
git checkout -b "$BRANCH"
87+
# ONE release branch per STABLE version (release/vX.Y.Z), created at rc.1 and
88+
# FROZEN until promote publishes the stable tag. Later RCs (rc.2, rc.3, ...) are
89+
# cut from this same branch, so they carry the rc.1 snapshot plus any
90+
# cherry-picked bugfixes and NOT whatever has landed on main since.
91+
#
92+
# The name must stay in sync with promote.yml, which resolves
93+
# release/v${STABLE_VERSION}. Naming this branch release/v${PRERELEASE}
94+
# (with the -rc.N suffix) breaks promote and makes every re-cut a fresh
95+
# branch off main, which silently defeats the freeze.
96+
BRANCH="release/v${NEXT}"
97+
REMOTE="https://x-access-token:${TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
98+
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
99+
# Re-cut (rc.2+): build on the frozen branch. Never delete it — it carries the
100+
# cherry-picks that make this RC differ from the previous one.
101+
echo "Reusing frozen release branch ${BRANCH}"
102+
git fetch origin "$BRANCH"
103+
git checkout -B "$BRANCH" "origin/${BRANCH}"
104+
else
105+
# First cut (rc.1): branch from the dispatched ref (main).
106+
echo "Creating release branch ${BRANCH} from ${GITHUB_REF_NAME}"
107+
git checkout -b "$BRANCH"
108+
fi
109+
sed -i -E "s|(\"version\"[[:space:]]*:[[:space:]]*\")[^\"]*(\")|\1${PRERELEASE}\2|" package.json
110+
echo "package.json version:"
111+
grep '"version"' package.json
103112
git add package.json
104-
git commit -m "chore(release): bump to ${PRERELEASE} [skip ci]"
105-
git push "https://x-access-token:${TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "$BRANCH"
113+
git commit -m "chore(release): bump to ${PRERELEASE} [skip ci]" || echo "(version already at ${PRERELEASE})"
114+
git push "$REMOTE" "$BRANCH"
106115
107116
- name: Push RC tag on the release branch
108117
env:
@@ -111,10 +120,10 @@ jobs:
111120
# Note: GITHUB_TOKEN tag pushes do NOT trigger build.yml in this org's setup,
112121
# so we explicitly trigger it via gh workflow run right after.
113122
RC_TAG: ${{ steps.version.outputs.rc_tag }}
114-
PRERELEASE: ${{ steps.version.outputs.prerelease }}
123+
NEXT: ${{ steps.version.outputs.next }}
115124
run: |
116125
set -euo pipefail
117-
BRANCH="release/v${PRERELEASE}"
126+
BRANCH="release/v${NEXT}"
118127
git fetch origin "$BRANCH"
119128
git checkout "$BRANCH"
120129
git reset --hard "origin/${BRANCH}"

0 commit comments

Comments
 (0)