From 7585ecaf306ba3db3052e8dc28b6c54ad6eadb2d Mon Sep 17 00:00:00 2001 From: Strycher Date: Thu, 10 Sep 2026 02:05:05 -0400 Subject: [PATCH] fix(#654): derive the release prerelease flag from the tag Part of #652. The release job hardcoded --prerelease on both gh release create and the retry branch's gh release edit, with no look at the tag. Every v* tag therefore published as a prerelease. All 37 releases carry the flag, GET /releases/latest 404s, and v1.3.0 (first Play production release) and v1.4.0 (current stable) are both mislabelled. The flag is now derived from GITHUB_REF_NAME: a bare vMAJOR.MINOR.PATCH is a full release, anything else is a prerelease. The unrecognised shape defaults to prerelease deliberately, so the legacy tags (observer-g2-rc3, v1.1.2-queuediag) stay where they belong and an unknown tag can never publish itself as stable. The resolved flag is echoed into the run log. It is passed explicitly, including the =false form, on every gh call that publishes, so both branches declare the complete final state and a re-run can correct a stale flag rather than preserve it. --latest stays unset; gh documents its default as automatic by date and version, and forcing it would promote a back-ported patch tagged after a newer minor. Verified: gh 2.86.0 accepts --prerelease=false on both create and edit (probed, failed on tag resolution not flag parsing). Classifier run against all 37 existing tags plus edge cases; exactly the 5 bare tags resolve to a full release. The step's run block was executed under bash -e against a stubbed gh, both branches, 6/6. YAML re-parsed. Gemini review (gemini-2.5-pro, standards#145) raised one finding: the create branch's final publish edit did not restate the flag, leaving publication dependent on create+draft persistence that was never verified. Accepted and applied. Owner verification on the next real tag cut stays on #652. Backfilling the 5 already-published releases is #653. Agent: TealGlen (session c61ce804) Co-Authored-By: Claude Opus 5 --- .github/workflows/release-signed.yml | 36 +++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-signed.yml b/.github/workflows/release-signed.yml index 1529db2..f26b1ec 100644 --- a/.github/workflows/release-signed.yml +++ b/.github/workflows/release-signed.yml @@ -292,17 +292,47 @@ jobs: run: | set -eu TAG="${GITHUB_REF_NAME}" + + # Whether a tag publishes as a full release or a prerelease is DERIVED + # from the tag, never hardcoded (#652). A bare vMAJOR.MINOR.PATCH is a + # full release; anything else is a prerelease. + # + # Defaulting the unrecognised shape to prerelease is deliberate. The + # legacy tags (observer-g2-rc3, v1.1.2-queuediag, v1.1.2-rc.1-b54-rxfed) + # belong there, and a tag whose shape we do not recognise can never + # publish itself as stable by accident. + # + # The flag is passed EXPLICITLY in both branches, including the + # =false form. gh only changes what it is told to change, so a retry + # that omitted the flag was what left a stale prerelease in place. + # + # --latest is deliberately NOT passed: `gh release create --help` + # documents the default as "automatic based on date and version", + # which is what we want. Forcing it would promote a back-ported patch + # tagged after a newer minor. + if printf '%s' "${TAG}" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then + PRERELEASE_FLAG="--prerelease=false" + else + PRERELEASE_FLAG="--prerelease" + fi + echo "Tag ${TAG} resolves to ${PRERELEASE_FLAG}" + if gh release view "${TAG}" >/dev/null 2>&1; then # Retry / re-run: refresh assets and notes, then ensure published. gh release upload "${TAG}" out/* --clobber - gh release edit "${TAG}" --notes-file "${NOTES}" --prerelease --draft=false + gh release edit "${TAG}" --notes-file "${NOTES}" "${PRERELEASE_FLAG}" --draft=false else gh release create "${TAG}" \ --draft \ --title "Offband Meshcore ${VERSION}" \ --notes-file "${NOTES}" \ - --prerelease + "${PRERELEASE_FLAG}" gh release upload "${TAG}" out/* - gh release edit "${TAG}" --draft=false + # The prerelease flag is restated here, not just on create, so both + # branches converge on ONE publish call that declares the complete + # final state. Relying on create+draft to have persisted the flag + # would make publication depend on behaviour we have not verified, + # which is the same class of assumption that caused #652. + gh release edit "${TAG}" "${PRERELEASE_FLAG}" --draft=false fi echo "Release ${TAG} published with $(ls out | wc -l) assets."