ci(release): only a confirmed 404 excuses a failed draft delete

Greptile and Codex both flagged the same hole in the sweep's error
handling: `gh api` exits 1 for every failure alike, so a rate limit,
permission loss or outage that hit the DELETE would hit the follow-up GET
too — and the GET's nonzero exit was read as proof the draft was already
gone. `failed` stayed 0 and the sweep went green over a draft it never
deleted.

Read the response status instead of the exit code. `gh api -i` prints the
status line even for an error status, so a genuine 404 (the event job
racing the sweep to the same draft) is distinguishable from everything
else; a request that never got a response leaves the status empty, which
is not 404 and so stays a failure.

Verified against the live API: a deleted release re-checks as 404 and is
excused, an existing release re-checks as 200 and fails the job, and an
unreachable host yields no status and fails the job.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5 committed 2026-09-19 20:37:56 +02:00
1 parent a8a3f77c27
commit 3300ed13ea
1 file changed
+19 -2
+19 -2
View File
@@ -166,10 +166,27 @@ jobs:
if gh api -X DELETE "repos/${GITHUB_REPOSITORY}/releases/${release_id}" >/dev/null; then
echo "Deleted ${tag} (release ${release_id}) for closed PR #${pr_number}."
elif ! gh api "repos/${GITHUB_REPOSITORY}/releases/${release_id}" >/dev/null 2>&1; then
continue
fi
# The delete failed. Only a release GitHub confirms is
# gone (404) excuses that — the event job racing us to
# the same draft. `gh api` exits 1 for every failure
# alike, so a rate limit or outage hitting both calls
# would otherwise read as "already deleted" and leave a
# green sweep behind an undeleted draft. Read the status
# line instead: `-i` prints it even on an error status,
# and a request that never got a response leaves it
# empty, which is not 404 and so stays a failure.
recheck_status="$(
gh api -i "repos/${GITHUB_REPOSITORY}/releases/${release_id}" 2>/dev/null |
sed -n '1s#^HTTP/[0-9.]* \([0-9]\{3\}\).*#\1#p' || true
)"
if [ "${recheck_status}" = "404" ]; then
echo "Draft ${tag} (release ${release_id}) was already gone."
else
echo "::error::Failed to delete draft ${tag} (release ${release_id})."
echo "::error::Failed to delete draft ${tag} (release ${release_id}); re-check returned ${recheck_status:-no HTTP status}."
failed=1
fi
done <<< "${drafts}"