From 5587a76606988c357a9084d451d3115e4a696d19 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 25 May 2026 23:14:11 -0400 Subject: [PATCH] =?UTF-8?q?fix(ext-ci):=20replace=20jq=20with=20python3=20?= =?UTF-8?q?(jq=20not=20in=20ci-python=20image)=20+=20bump=20ext=201.0.2?= =?UTF-8?q?=E2=86=921.0.3=20(escape=20AMO=20'version=20already=20exists'?= =?UTF-8?q?=20from=20prior=20partial-failure=20run)=20+=20add=20rollback?= =?UTF-8?q?=20to=20prevent=20empty=20cache-release=20tombstones=20?= =?UTF-8?q?=E2=80=94=20Co-Authored-By:=20Claude=20Opus=204.7=20(1M=20conte?= =?UTF-8?q?xt)=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .forgejo/workflows/build.yml | 49 ++++++++++++++++++++++++++++++------ extension/manifest.json | 2 +- extension/package.json | 2 +- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index 837b093..838efcf 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -49,9 +49,13 @@ jobs: -H "Authorization: token $TOKEN" \ "https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledCurator/releases/tags/ext-$VERSION" || echo 000) echo "Tag lookup HTTP status: $STATUS" + # JSON parsing via python (ci-python:3.14 has stdlib json; jq is + # not in the image and adding it per ci-requirements.md is not + # warranted for a single consumer — operator-flagged 2026-05-26 + # after a sign job failed with `jq: not found`). if [ "$STATUS" = "200" ]; then - ASSET_ID=$(jq -r '.assets[]? | select(.name | test("\\.xpi$")) | .id' release.json | head -1) - if [ -n "$ASSET_ID" ] && [ "$ASSET_ID" != "null" ]; then + ASSET_ID=$(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]['id'] if xpis else '')") + if [ -n "$ASSET_ID" ]; then echo "cached=true" >> "$GITHUB_OUTPUT" echo "asset_id=$ASSET_ID" >> "$GITHUB_OUTPUT" echo "Cached XPI exists at ext-$VERSION (asset id $ASSET_ID); skipping AMO sign" @@ -98,21 +102,50 @@ jobs: SIGNED=$(ls extension/web-ext-artifacts/*.xpi | head -1) XPI="extension/web-ext-artifacts/fabledcurator-$VERSION.xpi" cp "$SIGNED" "$XPI" - # Find-or-create the ext- release. + # Find-or-create the ext- release. Track whether WE + # created it so an upload failure below can roll back (don't + # leave an empty release tombstone that the next run's + # cache-check mistakes for a partial-failure state). 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 + if [ "$STATUS" = "200" ]; then + CREATED_BY_US=false + else curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \ -d "{\"tag_name\":\"ext-$VERSION\",\"name\":\"Extension $VERSION (signed XPI cache)\",\"body\":\"Internal cache for the signed XPI consumed by build.yml's build-web job. Not a user-facing FC release.\",\"target_commitish\":\"main\"}" \ -o release.json \ "https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledCurator/releases" + CREATED_BY_US=true fi - RELEASE_ID=$(jq -r '.id' release.json) - test -n "$RELEASE_ID" && test "$RELEASE_ID" != "null" - curl -s -X POST -H "Authorization: token $TOKEN" \ + RELEASE_ID=$(python3 -c "import json; print(json.load(open('release.json'))['id'])") + test -n "$RELEASE_ID" + # Rollback-on-failure: if the asset upload fails AND we just + # created the release in this run, delete it. Prevents an empty + # ext- release from poisoning the next workflow run + # (operator-flagged 2026-05-26 — without rollback the next run + # saw 'release exists, no asset → cache miss → sign' which AMO + # then rejected with 409 'Version already exists'). + rollback_if_we_created() { + if [ "$CREATED_BY_US" = "true" ]; then + echo "Rolling back: deleting just-created release $RELEASE_ID" + curl -s -X DELETE -H "Authorization: token $TOKEN" \ + "https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledCurator/releases/$RELEASE_ID" || true + curl -s -X DELETE -H "Authorization: token $TOKEN" \ + "https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledCurator/tags/ext-$VERSION" || true + fi + } + trap 'rollback_if_we_created' EXIT + HTTP_CODE=$(curl -s -X POST -H "Authorization: token $TOKEN" \ -F "attachment=@$XPI" \ - "https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledCurator/releases/$RELEASE_ID/assets?name=fabledcurator-$VERSION.xpi" + -o /dev/null -w "%{http_code}" \ + "https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledCurator/releases/$RELEASE_ID/assets?name=fabledcurator-$VERSION.xpi") + if [ "$HTTP_CODE" != "201" ] && [ "$HTTP_CODE" != "200" ]; then + echo "Asset upload failed with HTTP $HTTP_CODE" + exit 1 + fi + # Upload succeeded — clear the rollback trap. + trap - EXIT echo "Uploaded fabledcurator-$VERSION.xpi to ext-$VERSION release" - name: Upload XPI as Actions artifact (handoff to build-web) diff --git a/extension/manifest.json b/extension/manifest.json index a9d1220..217f0a0 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "FabledCurator", - "version": "1.0.2", + "version": "1.0.3", "description": "Export cookies from supported platforms to FabledCurator and add creators as sources in one click.", "browser_specific_settings": { diff --git a/extension/package.json b/extension/package.json index 9f8e814..db94a39 100644 --- a/extension/package.json +++ b/extension/package.json @@ -1,6 +1,6 @@ { "name": "fabledcurator-extension", - "version": "1.0.2", + "version": "1.0.3", "private": true, "description": "Firefox extension for FabledCurator", "scripts": {