Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 26 additions & 12 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -532,22 +532,36 @@ jobs:
process.stdout.write(`${lines.join("\n")}\n`);
' > "$notes_file"

tag_response=''
if tag_response="$(gh api \
--method POST \
"repos/$GITHUB_REPOSITORY/git/refs" \
--field "ref=refs/tags/$tag" \
--field "sha=$RELEASE_COMMIT" 2>&1)"; then
CREATED_TAG_RESPONSE="$tag_response" EXPECTED_TAG="refs/tags/$tag" EXPECTED_SHA="$RELEASE_COMMIT" \
existing_tag_response=''
if existing_tag_response="$(gh api \
"repos/$GITHUB_REPOSITORY/git/ref/tags/$tag" 2>&1)"; then
EXISTING_TAG_RESPONSE="$existing_tag_response" EXPECTED_TAG="refs/tags/$tag" EXPECTED_SHA="$RELEASE_COMMIT" \
node --input-type=module -e '
import assert from "node:assert/strict";
const created = JSON.parse(process.env.CREATED_TAG_RESPONSE);
assert.equal(created.ref, process.env.EXPECTED_TAG);
assert.equal(created.object?.type, "commit");
assert.equal(created.object?.sha, process.env.EXPECTED_SHA);
const existing = JSON.parse(process.env.EXISTING_TAG_RESPONSE);
assert.equal(existing.ref, process.env.EXPECTED_TAG);
assert.equal(existing.object?.type, "commit");
assert.equal(existing.object?.sha, process.env.EXPECTED_SHA);
'
else
[[ "$tag_response" == *"HTTP 422"* ]]
[[ "$existing_tag_response" == *"HTTP 404"* ]]
tag_response=''
if tag_response="$(gh api \
--method POST \
"repos/$GITHUB_REPOSITORY/git/refs" \
--field "ref=refs/tags/$tag" \
--field "sha=$RELEASE_COMMIT" 2>&1)"; then
CREATED_TAG_RESPONSE="$tag_response" EXPECTED_TAG="refs/tags/$tag" EXPECTED_SHA="$RELEASE_COMMIT" \
node --input-type=module -e '
import assert from "node:assert/strict";
const created = JSON.parse(process.env.CREATED_TAG_RESPONSE);
assert.equal(created.ref, process.env.EXPECTED_TAG);
assert.equal(created.object?.type, "commit");
assert.equal(created.object?.sha, process.env.EXPECTED_SHA);
'
else
[[ "$tag_response" == *"HTTP 422"* ]]
fi
fi
tag_commit="$(gh api "repos/$GITHUB_REPOSITORY/commits/$tag" --jq '.sha')"
test "$tag_commit" = "$RELEASE_COMMIT"
Expand Down
14 changes: 14 additions & 0 deletions scripts/check-release-workflows.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,20 @@ assert.match(
);
assert.match(release, /gh release create/, 'release must create the reviewed GitHub release');
assert.match(release, /gh release view/, 'GitHub release creation must be safe to rerun');
assert.match(
release,
/if existing_tag_response="\$\(gh api \\\n\s+"repos\/\$GITHUB_REPOSITORY\/git\/ref\/tags\/\$tag" 2>&1\)"; then/,
'release recovery must read and verify an existing exact tag before attempting creation',
);
assert.ok(
release.indexOf('git/ref/tags/$tag') < release.indexOf('--method POST'),
'the existing-tag lookup must happen before the atomic tag-creation request',
);
assert.match(
release,
/\[\[ "\$existing_tag_response" == \*"HTTP 404"\* \]\]/,
'release may attempt tag creation only after an explicit not-found response',
);
assert.match(
release,
/--method POST \\\n\s+"repos\/\$GITHUB_REPOSITORY\/git\/refs"/,
Expand Down
Loading