From 734676ab7d8d0f526237ca8048077b9315421fca Mon Sep 17 00:00:00 2001 From: codex-bot-andresmgsl Date: Sun, 30 Aug 2026 09:27:08 +0000 Subject: [PATCH] fix: guard tag release publication --- .github/workflows/release.yml | 16 ++++++++++++++++ test/forge-backends.test.sh | 30 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e76b24..7fb9bbf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -363,6 +363,22 @@ jobs: fi changelog_section CHANGELOG.md "$VER" > "$RUNNER_TEMP/notes.md" cat "$RUNNER_TEMP/notes.md" + - name: no published release may exist + env: + GH_TOKEN: ${{ github.token }} + VER: ${{ steps.assert.outputs.ver }} + run: | + # shellcheck source=/dev/null + . "$CEREMONY_DIR/lib/forge.sh" + forge_select "" + if ! exists="$(forge_release_exists "$VER")"; then + echo "could not read whether release '$VER' exists — refusing rather than assuming it does not (#191)." >&2 + exit 1 + fi + if [ "$exists" = yes ]; then + echo "release '$VER' already exists — refusing to re-release, creating nothing." >&2 + exit 1 + fi - name: artifact hook — the consumer's own release-artifact action # After the tag exists (it fired this door), before the publish — # the same contract as the merge door's twin step. diff --git a/test/forge-backends.test.sh b/test/forge-backends.test.sh index 50b7202..d0d0f1c 100644 --- a/test/forge-backends.test.sh +++ b/test/forge-backends.test.sh @@ -1185,6 +1185,36 @@ release_read 200 '{"id":41,"tag_name":"1.2.3","draft":false}' forge_release_create 1.2.3 1.2.3 "$TMP/notes.md" >/dev/null 2>&1 check "a published same-tag release is never deleted" 1 "" grep -q '^DELETE ' "$WRITES" +# The tag door's published-release guard is extracted and executed, like the +# other workflow-shell contracts in this repository. The tag itself is this +# door's premise, so only a published release refuses; adding the merge door's +# ls-remote half here would destroy the documented delete-and-re-push recovery. +TAG_GUARD="$TMP/tag-release-guard.sh" +{ + printf '%s\n' '#!/usr/bin/env bash' 'set -e' + yq -r '.jobs.release-on-tag.steps[] | select(.name == "no published release may exist") | .run' \ + "$ROOT/.github/workflows/release.yml" +} >"$TAG_GUARD" +chmod +x "$TAG_GUARD" +mkdir -p "$TMP/tag-guard-lib/lib" +printf '%s\n' \ + 'forge_select() { :; }' \ + 'forge_release_exists() { case "$RELEASE_EXISTS" in error) return 1 ;; *) echo "$RELEASE_EXISTS" ;; esac; }' \ + >"$TMP/tag-guard-lib/lib/forge.sh" + +tag_guard_extracted() { [ "$(wc -l <"$TAG_GUARD")" -ge 8 ]; } +run_tag_guard() { + CEREMONY_DIR="$TMP/tag-guard-lib" VER=1.2.3 RELEASE_EXISTS="$1" "$TAG_GUARD" +} +tag_door_has_no_tag_exists_guard() { + ! yq -r '.jobs.release-on-tag.steps[].run // ""' "$ROOT/.github/workflows/release.yml" | grep -q 'ls-remote' +} +check "the tag door's published-release guard is extracted" 0 "" tag_guard_extracted +check "the tag door refuses an existing published release" 1 "release '1.2.3' already exists" run_tag_guard yes +check "the tag door proceeds when no published release exists" 0 "" run_tag_guard no +check "the tag door refuses when release state is unreadable" 1 "refusing rather than assuming" run_tag_guard error +check "the tag door carries no tag-exists guard" 0 "" tag_door_has_no_tag_exists_guard + # --- the github twins address their own paths ---------------------------- . "$ROOT/lib/forge-github.sh" GITHUB_REPOSITORY=o/r