From 055d6c2d006559075e89201db1b5a4837a0dfa43 Mon Sep 17 00:00:00 2001 From: "clagentic-builder[bot]" <290147524+clagentic-builder[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:28:03 -0400 Subject: [PATCH] lr-d4413a: stop beta job from clobbering main's version basis --- .github/workflows/release.yml | 45 +++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 513bb00f..6385b25b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,16 +56,31 @@ jobs: APP_TOKEN: ${{ steps.app-token.outputs.token }} - name: Sync release tags into main + # Tag-reachability only: a beta cut must see any tags/history that landed on + # release (e.g. a stable cut) so semantic-release's tag-range logic behaves, + # but it must NEVER adopt release's version bookkeeping files. Those files + # (package.json version + CHANGELOG boundary) carry the STABLE version, + # which sits above main's own -beta prerelease line. Force-copying them onto + # main resets the version basis semantic-release computes from, burying the + # feature commits that are supposed to drive the next beta below a tag + # boundary that looks newer — silent no-op (lr-d4413a). The merge below + # intentionally leaves package.json/CHANGELOG.md/package-lock.json untouched: + # if the merge itself conflicts on those files, -X theirs already resolves + # them in release's favor at the git level, which is the wrong direction for + # a beta — so explicitly keep main's copy of just those three afterward. run: | git config user.name "clagentic-release-bot[bot]" git config user.email "clagentic-release-bot[bot]@users.noreply.github.com" if git rev-parse --verify origin/release >/dev/null 2>&1; then RELEASE_AHEAD=$(git rev-list --count HEAD..origin/release) if [ "$RELEASE_AHEAD" -gt 0 ]; then - echo "Release branch is $RELEASE_AHEAD commits ahead of main, merging..." + echo "Release branch is $RELEASE_AHEAD commits ahead of main, merging (tag-reachability only, version files stay on main's basis)..." + PRE_MERGE_HEAD=$(git rev-parse HEAD) git merge origin/release --no-edit -X theirs || true - git checkout origin/release -- CHANGELOG.md package.json package-lock.json 2>/dev/null || true - git diff --quiet || git commit -am "chore: resolve merge conflicts from release" + # Restore main's own version files regardless of how the merge landed — + # main's -beta version basis must never be replaced by release's stable one. + git checkout "${PRE_MERGE_HEAD}" -- CHANGELOG.md package.json package-lock.json 2>/dev/null || true + git diff --quiet || git commit -am "chore: resolve merge conflicts from release (keep main's version basis)" git push "https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" main fi fi @@ -81,11 +96,33 @@ jobs: run: npm ci - name: Run semantic-release (beta) - run: npx semantic-release + id: semantic-release-beta + run: | + PRE_RELEASE_HEAD=$(git rev-parse HEAD) + npx semantic-release + POST_RELEASE_HEAD=$(git rev-parse HEAD) + if [ "${PRE_RELEASE_HEAD}" = "${POST_RELEASE_HEAD}" ]; then + echo "published=false" >> "$GITHUB_OUTPUT" + else + echo "published=true" >> "$GITHUB_OUTPUT" + fi env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} NPM_CONFIG_PROVENANCE: true + - name: Fail loud if the dispatched beta published nothing + # An operator explicitly dispatched type=beta expecting a new prerelease. A + # green no-op here previously masked the version-basis-clobber bug + # (lr-d4413a) and left the operator believing the beta had shipped. A + # dispatched beta with no publishable commits is an error the operator must + # see, not a silent success — this is a workflow_dispatch-only invariant, not + # a scheduled/passive run, so failing loud here does not affect any + # unattended trigger. + if: steps.semantic-release-beta.outputs.published == 'false' + run: | + echo "::error::type=beta was dispatched but semantic-release published nothing — no beta-eligible commits were found above the current version basis. This usually means the sync step above reset main's version basis, or there truly are no releasable commits since the last beta/stable. See lr-d4413a." + exit 1 + - name: Generate .betas.json (promotable beta list) # Non-fatal by design: if generation/commit fails for any reason, the beta # publish above has already succeeded and must not be rolled back — the