diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index a592488c7..85b0f63af 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -75,34 +75,43 @@ jobs: NEXT: ${{ steps.version.outputs.next }} run: node .github/scripts/release-milestone-migrate.mjs - - name: Bump package.json to pre-release version - env: - PRERELEASE: ${{ steps.version.outputs.prerelease }} - run: | - set -euo pipefail - sed -i -E "s|(\"version\"[[:space:]]*:[[:space:]]*\")[^\"]*(\")|\1${PRERELEASE}\2|" package.json - echo "package.json version:" - grep '"version"' package.json - - - name: Commit package.json bump on a release branch + - name: Create or reuse the release branch and bump package.json env: TOKEN: ${{ secrets.OPENSCREEN_RELEASE_TOKEN }} PRERELEASE: ${{ steps.version.outputs.prerelease }} + NEXT: ${{ steps.version.outputs.next }} run: | set -euo pipefail git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - # The release branch is FROZEN between RC cut and stable promotion. The bump - # commit lives on release/v${PRERELEASE} only; nothing is merged into main - # until promote.yml publishes the stable tag, so any features merged into - # main after this step are NOT in the RC build. - BRANCH="release/v${PRERELEASE}" - # Delete remote branch first so the push below is always fast-forward (idempotent on rerun). - git push "https://x-access-token:${TOKEN}@github.com/${GITHUB_REPOSITORY}.git" ":${BRANCH}" 2>/dev/null || true - git checkout -b "$BRANCH" + # ONE release branch per STABLE version (release/vX.Y.Z), created at rc.1 and + # FROZEN until promote publishes the stable tag. Later RCs (rc.2, rc.3, ...) are + # cut from this same branch, so they carry the rc.1 snapshot plus any + # cherry-picked bugfixes and NOT whatever has landed on main since. + # + # The name must stay in sync with promote.yml, which resolves + # release/v${STABLE_VERSION}. Naming this branch release/v${PRERELEASE} + # (with the -rc.N suffix) breaks promote and makes every re-cut a fresh + # branch off main, which silently defeats the freeze. + BRANCH="release/v${NEXT}" + REMOTE="https://x-access-token:${TOKEN}@github.com/${GITHUB_REPOSITORY}.git" + if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then + # Re-cut (rc.2+): build on the frozen branch. Never delete it — it carries the + # cherry-picks that make this RC differ from the previous one. + echo "Reusing frozen release branch ${BRANCH}" + git fetch origin "$BRANCH" + git checkout -B "$BRANCH" "origin/${BRANCH}" + else + # First cut (rc.1): branch from the dispatched ref (main). + echo "Creating release branch ${BRANCH} from ${GITHUB_REF_NAME}" + git checkout -b "$BRANCH" + fi + sed -i -E "s|(\"version\"[[:space:]]*:[[:space:]]*\")[^\"]*(\")|\1${PRERELEASE}\2|" package.json + echo "package.json version:" + grep '"version"' package.json git add package.json - git commit -m "chore(release): bump to ${PRERELEASE} [skip ci]" - git push "https://x-access-token:${TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "$BRANCH" + git commit -m "chore(release): bump to ${PRERELEASE} [skip ci]" || echo "(version already at ${PRERELEASE})" + git push "$REMOTE" "$BRANCH" - name: Push RC tag on the release branch env: @@ -111,10 +120,10 @@ jobs: # Note: GITHUB_TOKEN tag pushes do NOT trigger build.yml in this org's setup, # so we explicitly trigger it via gh workflow run right after. RC_TAG: ${{ steps.version.outputs.rc_tag }} - PRERELEASE: ${{ steps.version.outputs.prerelease }} + NEXT: ${{ steps.version.outputs.next }} run: | set -euo pipefail - BRANCH="release/v${PRERELEASE}" + BRANCH="release/v${NEXT}" git fetch origin "$BRANCH" git checkout "$BRANCH" git reset --hard "origin/${BRANCH}" @@ -133,7 +142,14 @@ jobs: # GITHUB_TOKEN tag pushes don't fire the build.yml trigger in this setup, # so dispatch it explicitly. The PAT ensures the build's release creation # propagates to Tier 3 (homebrew/winget/nix/aur) via release: published. + # + # --ref is REQUIRED: the RC version bump lives ONLY on the release branch / + # RC tag, never on the default branch. Without --ref the build runs on main + # (still the previous stable version), so build.yml's publish-release step + # fails its guard ("package.json version X does not match from tag"). + # Pin the build to the RC tag so checkout gets the bumped package.json + code. gh workflow run build.yml \ + --ref "${RC_TAG}" \ -f release_tag="${RC_TAG}" \ -f arch=both \ --repo "$GITHUB_REPOSITORY" diff --git a/.github/workflows/promote.yml b/.github/workflows/promote.yml index ac4cfc079..3e8214b9c 100644 --- a/.github/workflows/promote.yml +++ b/.github/workflows/promote.yml @@ -142,7 +142,13 @@ jobs: run: | set -euo pipefail # GITHUB_TOKEN tag pushes don't fire build.yml in this setup, dispatch it. + # + # --ref pins the build to the stable tag (the frozen release-branch tip). The + # prior main-merge step usually leaves main at the stable version already, but + # relying on that is fragile; building the tag guarantees checkout has the + # matching package.json + code and enables signing/notarization (tag has no '-'). gh workflow run build.yml \ + --ref "${STABLE_TAG}" \ -f release_tag="${STABLE_TAG}" \ -f arch=both \ --repo "$GITHUB_REPOSITORY"