From 8fcadc4126e82738ab24ede55cf73bc6cc37bb7d Mon Sep 17 00:00:00 2001 From: M4n5ter Date: Tue, 18 Aug 2026 18:14:31 +0800 Subject: [PATCH] fix(release): resume draft CLI releases by ID GitHub's release-by-tag REST endpoint does not return draft releases, so a successful draft creation was followed by a false 404. Resolve both draft and published releases through gh, bind the result to the expected repository API URL, and perform subsequent reads by stable release ID.\n\nGenerated-by: Codex --- .github/workflows/release-cli-finalize.yml | 20 ++++++++++++++++---- scripts/release-cli-workflow-policy.test.mjs | 3 +++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-cli-finalize.yml b/.github/workflows/release-cli-finalize.yml index 3049954114..825a8fe049 100644 --- a/.github/workflows/release-cli-finalize.yml +++ b/.github/workflows/release-cli-finalize.yml @@ -194,7 +194,6 @@ jobs: exit 1 fi - release_endpoint="repos/$GITHUB_REPOSITORY/releases/tags/$RELEASE_TAG" release_json="$RUNNER_TEMP/github-release.json" release_assets=( "$RELEASE_DIRECTORY/$RELEASE_TARBALL_NAME" @@ -203,7 +202,12 @@ jobs: "$RELEASE_DIRECTORY/release.json" ) - if ! gh api "$release_endpoint" > "$release_json" 2>/dev/null; then + # The REST release-by-tag endpoint does not return drafts. Resolve both draft and + # published releases through gh, then use the immutable API URL for exact reads. + if ! release_api_url="$(gh release view "$RELEASE_TAG" \ + --repo "$GITHUB_REPOSITORY" \ + --json apiUrl \ + --jq .apiUrl 2>/dev/null)"; then if ! gh release create "$RELEASE_TAG" \ --repo "$GITHUB_REPOSITORY" \ --verify-tag \ @@ -213,8 +217,16 @@ jobs: --notes-file "$RELEASE_DIRECTORY/release-notes.md"; then echo "GitHub Release creation did not confirm success; inspecting remote state" >&2 fi - gh api "$release_endpoint" > "$release_json" + release_api_url="$(gh release view "$RELEASE_TAG" \ + --repo "$GITHUB_REPOSITORY" \ + --json apiUrl \ + --jq .apiUrl)" + fi + if [[ ! "$release_api_url" =~ ^https://api\.github\.com/repos/$GITHUB_REPOSITORY/releases/[1-9][0-9]*$ ]]; then + echo "GitHub Release API URL is not bound to the expected repository: $release_api_url" >&2 + exit 1 fi + gh api "$release_api_url" > "$release_json" release_draft="$(RELEASE_JSON="$release_json" node -e ' const fs = require("node:fs"); @@ -243,7 +255,7 @@ jobs: --notes-file "$RELEASE_DIRECTORY/release-notes.md" fi - gh api "$release_endpoint" > "$release_json" + gh api "$release_api_url" > "$release_json" node scripts/release-cli-publication.mjs validate-github-release \ "$RELEASE_DIRECTORY" \ "$release_json" diff --git a/scripts/release-cli-workflow-policy.test.mjs b/scripts/release-cli-workflow-policy.test.mjs index a0e2693c89..19b3882e54 100644 --- a/scripts/release-cli-workflow-policy.test.mjs +++ b/scripts/release-cli-workflow-policy.test.mjs @@ -84,6 +84,9 @@ test('finalize propagates verified artifacts and idempotently publishes an exact assert.match(publish, /gh release create[\s\S]*?--draft/u); assert.match(publish, /gh release upload[\s\S]*?--clobber/u); assert.match(publish, /gh release edit[\s\S]*?--draft=false/u); + assert.match(publish, /gh release view[\s\S]*?--json apiUrl/u); + assert.match(publish, /gh api "\$release_api_url"/u); + assert.doesNotMatch(publish, /releases\/tags\/\$RELEASE_TAG/u); assert.match(publish, /--prerelease/u); assert.match(publish, /--latest=false/u); assert.match(publish, /validate-github-release/u);