release, nightly: publish the staged release by id, refuse duplicate drafts, resume a complete staged release
Summary
This PR ports the v0.65.2 duplicate-draft fix (manaflow-ai/cmux#21137, which is still open on main) to the feat-cmux-next universal-app release.yml. It also fixes the two follow-ups: beads cx-sqadk (the guard cannot see drafts) and cx-9ol90 (nightly uses a softprops tag lookup).
Incident: v0.65.2 was published with 0 assets. publish-release-assets.py staged draft 414419116 and uploaded 12 assets to it. Then softprops/action-gh-release looked the release up by tag. That lookup returns 404 for drafts, so the action created release 414419301 and published it with 0 assets (job log of run 38621170537, attempt 5).
Changes
scripts/ci/publish-release-assets.py: before any upload, the script refuses when the tag has more than one release object, draft or published. The new--github-outputflag reportsrelease_id,asset_countandasset_names.- New
scripts/ci/finalize-release.py:publishpublishes the given release id. It refuses when any other release exists for the tag. It clears prerelease and sets the metadata first, then setsdraft=falseandmake_latest. It then checks that the tag resolves to the same id, that the release is not a draft, and that the expected assets are there (exact count when one is given).downloadtakes assets from a staged draft and checks the digest.
release.yml:- The guard now lists drafts too, and it refuses duplicate release objects (cx-sqadk).
- When the existing release (draft or published) already has every immutable asset, the run does not rebuild, because a rebuild makes new signed bytes. It resumes instead: it downloads the release's own
appcast.xml, publishes the release by id and uploads the appcast to R2. Before, a failure between upload and publish could not be fixed by a rerun, and a missed R2 upload stayed missed. Publish verified releasenow usesfinalize-release.pywith the upload step's id and asset count. softprops is removed.
nightly.yml:Publish verified nightly release metadataandRecord verified nightly publicationnow publish the staged channel release by id with the same release body and token, in place of the softprops tag lookup (cx-9ol90).tests/test_github_release_publication.py: the existing workflow-text assertions are changed to match the new steps. One existing case is changed to have a single draft, because two drafts for one tag are now refused. No new tests (behavior-test rule).
Changelog
none
Verification
actionlintpasses onrelease.yml. Onnightly.ymlit reports only a pre-existing SC2129 at line 1597, which is not in this diff.finalize-release.py publish/download --helpload correctly, andpy_compilepasses.- Per the no-tests-on-laptop rule, the Python tests were not run locally. The CI
ci-guardsandr2-upload-testsjobs run them. - The real release and nightly paths run only on a tag push or the nightly schedule, so this PR does not exercise them.
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes release and nightly publication so the staged release is published by id, preventing releases from being published with 0 assets (v0.65.2 shipped empty this way).
- The upload script refuses when more than one release object exists for the tag and reports the staged release id, asset count, and asset names.
- New
finalize-release.pypublishes a release by id, refuses other releases for the same tag, and verifies the published release holds the expected assets; its download command fetches staged assets by id without sending the token on GitHub's redirect. release.yml: the guard lists drafts, refuses duplicate release objects, and skips the build when a complete staged draft exists; the rerun then resumes publication by id with the exact asset count the guard saw, without rebuilding, so signed bytes stay intact. A rerun of an already-published release is a no-op.nightly.ymlpublishes the staged release by id in place of thesoftpropstag lookup.- CI now proves
finalize-release.pyloads.