From 0c43fa3eb2b7c0d7471d44c501bd20d8cc69f892 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 27 Aug 2026 21:41:14 -0400 Subject: [PATCH] ci: shadow the per-artifact derived versions (milestone 313 step 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every build job now logs the tag, version and revision its artifact would get. Nothing reads them; no `set -e`, and each derivation falls back to UNAVAILABLE, so a broken script cannot fail a build. Same discipline as milestone 271 step 2, which is what made that cutover safe to do in one commit. Also fixes a landmine the plan named but had not checked: build-ml and build-agent were checking out at depth 1. Both now use fetch-depth: 0. That mattered more than it looks. A depth-1 clone sees one commit, so `git log HEAD -- ` either returns that commit's timestamp — plausible, and wrong — or returns nothing. For build-ml on this push it would have returned today's date, because HEAD touches backend/, and nothing downstream would have questioned it. For build-agent it would have returned nothing at all, since no single commit here touches agent/, and artifacts.sh exits non-zero rather than guessing. One direction is silent and one is loud; only the loud one was ever going to get noticed. What to read from the shadow lines over the next few pushes, in order of how badly each would bite: * a push touching the extension must move BOTH the extension and web, because build-web bakes the XPI in. If web does not move, its path set is too narrow and a pinned web image will serve an extension it does not name. * a push touching only agent/ must leave web and ml still. If they move, their sets are too wide and they will rebuild for changes they do not ship. * a docs-only push must move nothing. * dev and main must derive the same values for the same source. Step 3 only lets these values name a tag once those hold. --- .forgejo/workflows/build.yml | 98 ++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index 43ab953..a641f19 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -103,6 +103,27 @@ jobs: # cache hit and holds AMO to one call per extension CHANGE. Only moving # backwards is a failure, so this runs on every path — cache hit # included — rather than only before a sign. + # --- shadow mode (milestone 313, step 2) ----------------------------- + # Informational ONLY. Nothing reads this and it must never fail the + # build — no `set -e`, and every derivation falls back to UNAVAILABLE. + # + # What to watch across pushes, because this is what step 3 will trust: + # * a push touching only agent/ moves the agent and leaves web and ml + # STILL. If web moves, its path set is too wide. + # * a push touching only docs moves nothing. + # * a push touching the extension moves the extension AND web, since + # web bakes in the XPI. If web does not move, its set is too narrow + # — the direction that serves stale bytes on a pin. + # * dev and main derive the same values for the same source. + - name: Shadow — derived artifact version (informational) + run: | + set -u + A=extension + T=$(sh scripts/artifacts.sh tag "$A" 2>&1 || echo UNAVAILABLE) + V=$(sh scripts/artifacts.sh version "$A" 2>&1 || echo UNAVAILABLE) + R=$(sh scripts/artifacts.sh revision "$A" 2>&1 || echo UNAVAILABLE) + echo "shadow: artifact=$A tag=$T version=$V revision=$R sha=$GITHUB_SHA" + - name: Guard — the derived version must never go backwards env: TOKEN: ${{ secrets.RELEASE_TOKEN }} @@ -320,6 +341,27 @@ jobs: # that exists perfectly well under its real name. fetch-depth: 0 + # --- shadow mode (milestone 313, step 2) ----------------------------- + # Informational ONLY. Nothing reads this and it must never fail the + # build — no `set -e`, and every derivation falls back to UNAVAILABLE. + # + # What to watch across pushes, because this is what step 3 will trust: + # * a push touching only agent/ moves the agent and leaves web and ml + # STILL. If web moves, its path set is too wide. + # * a push touching only docs moves nothing. + # * a push touching the extension moves the extension AND web, since + # web bakes in the XPI. If web does not move, its set is too narrow + # — the direction that serves stale bytes on a pin. + # * dev and main derive the same values for the same source. + - name: Shadow — derived artifact version (informational) + run: | + set -u + A=web + T=$(sh scripts/artifacts.sh tag "$A" 2>&1 || echo UNAVAILABLE) + V=$(sh scripts/artifacts.sh version "$A" 2>&1 || echo UNAVAILABLE) + R=$(sh scripts/artifacts.sh revision "$A" 2>&1 || echo UNAVAILABLE) + echo "shadow: artifact=$A tag=$T version=$V revision=$R sha=$GITHUB_SHA" + - 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 @@ -486,6 +528,34 @@ jobs: image: git.fabledsword.com/bvandeusen/ci-python:3.14 steps: - uses: actions/checkout@v4 + with: + # Full history: this job derives its artifact's version from the + # commit its shipped files last changed in (milestone 313). A + # depth-1 clone cannot see that commit — it either derives a wrong, + # too-low value or finds nothing at all, and neither is a failure + # the build would otherwise notice. + fetch-depth: 0 + + # --- shadow mode (milestone 313, step 2) ----------------------------- + # Informational ONLY. Nothing reads this and it must never fail the + # build — no `set -e`, and every derivation falls back to UNAVAILABLE. + # + # What to watch across pushes, because this is what step 3 will trust: + # * a push touching only agent/ moves the agent and leaves web and ml + # STILL. If web moves, its path set is too wide. + # * a push touching only docs moves nothing. + # * a push touching the extension moves the extension AND web, since + # web bakes in the XPI. If web does not move, its set is too narrow + # — the direction that serves stale bytes on a pin. + # * dev and main derive the same values for the same source. + - name: Shadow — derived artifact version (informational) + run: | + set -u + A=ml + T=$(sh scripts/artifacts.sh tag "$A" 2>&1 || echo UNAVAILABLE) + V=$(sh scripts/artifacts.sh version "$A" 2>&1 || echo UNAVAILABLE) + R=$(sh scripts/artifacts.sh revision "$A" 2>&1 || echo UNAVAILABLE) + echo "shadow: artifact=$A tag=$T version=$V revision=$R sha=$GITHUB_SHA" - name: Determine tag id: tag @@ -533,6 +603,34 @@ jobs: image: git.fabledsword.com/bvandeusen/ci-python:3.14 steps: - uses: actions/checkout@v4 + with: + # Full history: this job derives its artifact's version from the + # commit its shipped files last changed in (milestone 313). A + # depth-1 clone cannot see that commit — it either derives a wrong, + # too-low value or finds nothing at all, and neither is a failure + # the build would otherwise notice. + fetch-depth: 0 + + # --- shadow mode (milestone 313, step 2) ----------------------------- + # Informational ONLY. Nothing reads this and it must never fail the + # build — no `set -e`, and every derivation falls back to UNAVAILABLE. + # + # What to watch across pushes, because this is what step 3 will trust: + # * a push touching only agent/ moves the agent and leaves web and ml + # STILL. If web moves, its path set is too wide. + # * a push touching only docs moves nothing. + # * a push touching the extension moves the extension AND web, since + # web bakes in the XPI. If web does not move, its set is too narrow + # — the direction that serves stale bytes on a pin. + # * dev and main derive the same values for the same source. + - name: Shadow — derived artifact version (informational) + run: | + set -u + A=agent + T=$(sh scripts/artifacts.sh tag "$A" 2>&1 || echo UNAVAILABLE) + V=$(sh scripts/artifacts.sh version "$A" 2>&1 || echo UNAVAILABLE) + R=$(sh scripts/artifacts.sh revision "$A" 2>&1 || echo UNAVAILABLE) + echo "shadow: artifact=$A tag=$T version=$V revision=$R sha=$GITHUB_SHA" - name: Determine tag id: tag