diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index f63df7d..ca758ff 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -33,16 +33,31 @@ jobs: # Forgejo release exists yet, otherwise downloads the cached signed XPI. # Result is uploaded as an Actions artifact for build-web to consume. # - # Why this lives in build.yml (not a separate workflow): the merge-commit's - # docker image tagged `:latest` MUST carry the XPI. A separate sign workflow - # racing build.yml leaves `:latest` without the XPI for ~5min (until the - # commit-back triggers another build). Inline ordering eliminates the race. + # Why this lives in build.yml (not a separate workflow): the image a push + # publishes MUST carry the XPI. A separate sign workflow racing build.yml + # leaves that image without one for ~5min (until the commit-back triggers + # another build). Inline ordering eliminates the race. # Cache strategy: Forgejo Release Assets — picked 2026-05-25 over Generic # Packages (cleaner API surface) and commit-back-to-side-branch (no extra # branch to manage). AMO blocks re-signing the same version (returns 409), - # so signing is intentionally one-shot per version bump. + # so signing is intentionally one-shot per version. + # + # BOTH branches sign (milestone 271 step 6, 2026-08-27). Not two signatures: + # the version is the commit TIME of the newest packaged-extension change, so + # dev and main derive the SAME number for the same extension source. A dev + # push that changes the extension signs it; the merge to main then finds the + # ext- release already there, hits the cache, and bundles the + # byte-identical XPI into `:latest` with no second AMO call. One signature + # per extension CHANGE, shared by both channels — that is what makes two + # channels affordable, and it is why step 4 (derived version) had to land + # first. Ungating this while the version was still the hand-set 1.0.11 would + # have hit the existing ext-1.0.11 cache and bundled MAIN's stale XPI into + # `:dev` — a dev channel confidently serving old code. + # + # Tags stay excluded: the tag path deliberately skips signing and polls for + # the release instead (see build-web's race note, 2026-05-27). sign-extension: - if: github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' runs-on: python-ci container: image: git.fabledsword.com/bvandeusen/ci-python:3.14 @@ -233,6 +248,11 @@ jobs: # 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). + # + # target_commitish is the signing commit, not a branch name: since + # step 6 either branch can create this release, and hard-coding + # `main` would tag a dev-signed XPI against a main commit that may + # not even contain the extension source it was built from. 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) @@ -240,7 +260,7 @@ jobs: 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\"}" \ + -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\":\"$GITHUB_SHA\"}" \ -o release.json \ "https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledCurator/releases" CREATED_BY_US=true @@ -283,7 +303,10 @@ jobs: build-web: needs: [sign-extension] - # sign-extension is main-only; on dev it's skipped, build-web still runs. + # sign-extension runs on main and dev, and is skipped on a tag push (which + # polls for the release instead). Either is fine to build on; a FAILED sign + # is not — this condition lets success and skipped through, so a failure + # skips build-web rather than shipping an image without the XPI. if: always() && (needs.sign-extension.result == 'success' || needs.sign-extension.result == 'skipped') runs-on: python-ci container: @@ -297,11 +320,14 @@ jobs: # that exists perfectly well under its real name. fetch-depth: 0 - - name: Download signed XPI from Forgejo release asset (main + tags) - # Fires on main-push AND on tag-push. Tag-push builds re-package the - # 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. + - name: Download signed XPI from Forgejo release asset + # Fires on every trigger shape. dev and main each bundle the XPI their + # own sign-extension just published — that is the whole point of the + # channel work (milestone 271 step 6): the dev image carries the + # extension being developed, rather than requiring a merge to try it. + # Tag-push builds re-package the same source 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 @@ -313,7 +339,7 @@ jobs: # 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/') + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/') env: TOKEN: ${{ secrets.RELEASE_TOKEN }} run: |