From b8ad17c68d27890329807adb5c13bd30c6b401b8 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 27 May 2026 13:25:58 -0400 Subject: [PATCH] fix(build): poll for ext- release in tag-push build-web (race fix) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cutting a release fires BOTH the push-to-main workflow AND the push-to-tag workflow in parallel. main-push runs sign-extension (AMO round-trip 1-5min) then publishes the ext- Forgejo release; tag-push skips sign-extension (gated to main) and races straight to build-web's Download XPI step. Tag-push lost every time — got 404 from releases/tags/ext- before main-push had finished signing. v26.05.27.0 hit this: tag-push build-web died on exit 22 because the ext-1.0.4 release wasn't published yet (it arrived ~4min later). Fix: wrap the release lookup in a 20-iteration sleep+retry loop, 30s between attempts (10min total upper bound, generous for AMO). main-push's signing eventually publishes the release; tag-push picks it up on a later poll. No more manual rerun of the failed job after every release cut. Banked the trap as reference_tag_push_main_push_race.md — same shape will recur any time a tag-push workflow consumes a main-push-produced artifact. --- .forgejo/workflows/build.yml | 51 +++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index 407dc24..2bf6248 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -173,24 +173,51 @@ jobs: # same source code as the preceding main-push build but with an # immutable version tag — they need the XPI too, otherwise the # versioned image ships without the signed extension. + # + # Tag-push vs main-push race (operator-flagged 2026-05-27 after + # v26.05.27.0 hit it): a release cut fires BOTH workflows almost + # simultaneously. Main-push runs sign-extension (1-5min AMO round + # trip) before publishing the ext- release; tag-push + # skips sign-extension (gated to main) and races straight to + # this download step. Tag-push lost every time. Fix: poll the + # ext- release endpoint with a sleep+retry loop (30s + # for up to 10min total) before giving up. Main-push's signing + # eventually wins and tag-push picks the release up on a later + # iteration. if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') env: TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | set -eux VERSION=$(grep -E '"version"' extension/package.json | head -1 | sed -E 's/.*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/') - # Look up the ext- release; extract the .xpi asset's - # browser_download_url (Forgejo's /releases/assets/ endpoint - # returns ASSET METADATA, not the binary blob — operator-flagged - # 2026-05-26: my prior code curl'd the metadata endpoint without - # -f and wrote the resulting 404-page-not-found text into - # fabledcurator-*.xpi, which Firefox then rejected as "corrupt"). - # browser_download_url is the canonical binary endpoint and is - # also publicly accessible (no token needed) but we pass the - # token anyway for symmetry with private-repo support. - curl -sf -H "Authorization: token $TOKEN" \ - "https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledCurator/releases/tags/ext-$VERSION" \ - -o release.json + # Poll for the ext- release. main-push's sign-extension + # step (AMO round-trip, 1-5min) needs to finish + upload before + # tag-push can fetch. 30s * 20 = up to 10min wait, then hard-fail. + for attempt in $(seq 1 20); do + STATUS=$(curl -s -o release.json -w "%{http_code}" \ + -H "Authorization: token $TOKEN" \ + "https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledCurator/releases/tags/ext-$VERSION" || echo 000) + if [ "$STATUS" = "200" ]; then + echo "Found ext-$VERSION release on attempt $attempt" + break + fi + if [ "$attempt" = "20" ]; then + echo "ERROR: ext-$VERSION release not available after 10min of polling" + echo "Last HTTP status: $STATUS" + exit 1 + fi + echo "Attempt $attempt: ext-$VERSION not yet published (HTTP $STATUS); sleeping 30s" + sleep 30 + done + # Extract the .xpi asset's browser_download_url (Forgejo's + # /releases/assets/ endpoint returns ASSET METADATA, not + # the binary blob — operator-flagged 2026-05-26: my prior + # code curl'd the metadata endpoint without -f and wrote the + # resulting 404-page-not-found text into fabledcurator-*.xpi, + # which Firefox then rejected as "corrupt"). + # browser_download_url is the canonical binary endpoint and + # is also publicly accessible (no token needed) but we pass + # the token anyway for symmetry with private-repo support. DOWNLOAD_URL=$(python3 -c "import json; r=json.load(open('release.json')); xpis=[a for a in r.get('assets', []) if a.get('name','').endswith('.xpi')]; print(xpis[0]['browser_download_url'])") test -n "$DOWNLOAD_URL" echo "Downloading XPI from: $DOWNLOAD_URL"