diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index 828cf9d..c1c1176 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -487,6 +487,11 @@ jobs: set -eu ID=$(sh scripts/artifacts.sh identity web "$CHANNEL") echo "identity=$ID" >> "$GITHUB_OUTPUT" + # The value step 3 will key on once the label mechanism is proven. + # Emitted now so the build below can stamp it — stamping is harmless + # and is what gives the shadow read something to find next push. + REV=$(sh scripts/artifacts.sh revision web) + echo "revision=$REV" >> "$GITHUB_OUTPUT" # Every build that runs now claims the identity. The carve-out here # existed only for tag pushes, which rebuilt an already-published @@ -502,6 +507,66 @@ jobs: echo "reuse: $IMAGE:$ID is not published — building" fi + # --- shadow: can we read a label off the channel tag? (milestone 318 step 3) + # Informational ONLY — no `set -e`, every probe falls back to + # UNAVAILABLE, and nothing downstream reads this. It must never fail a + # build; that is the whole reason it runs before the mechanism does. + # + # The question: can the reuse check be keyed on an image LABEL read off + # the channel tag, instead of on a separate r- tag? If yes, nothing + # is minted per build, the -main/-dev identity split disappears, and the + # base-refresh clobber (#3154) stops existing. + # + # Two questions, answered at different times — worth separating, because + # reading "no value" as "the mechanism does not work" would abandon a + # working design: + # + # 1. Do the TEMPLATE MECHANICS work here? Answered on the very first + # run, by probes B and C: if they print a labels map (even an empty + # one) or a config JSON, `--format` over .Image resolves on this + # buildx and this registry. That is the actual gate for step 3. + # 2. Does a stamped value ROUND-TRIP? Only answerable once an artifact + # genuinely rebuilds. A push that changes no shipped file reuses, + # the build is skipped, and nothing gets stamped — so an artifact + # keeps reporting no label until its own source next moves. + # + # That second point is not a defect, it is the migration path: under the + # real mechanism "no label" reads as a MISS, so the artifact rebuilds + # once and is stamped from then on. One extra build per artifact, ever, + # in the safe direction. `imagetools create` cannot add the label on the + # reuse path — it copies a manifest and config labels are not manifest + # annotations — so there is no way to backfill, and no need to. + # + # Several probes on purpose: `--format` templating over .Image varies by + # buildx version, and one push should tell us which spelling works + # rather than costing a round trip per guess. + - name: Shadow — read fc.revision off the channel tag (informational) + env: + IMAGE: git.fabledsword.com/bvandeusen/fabledcurator + CHANNEL: ${{ steps.tag.outputs.channel }} + DERIVED: ${{ steps.reuse.outputs.revision }} + run: | + set -u + # The moving tag this channel publishes. `main` is asked about + # :latest, which is what step 4 keeps; :main is on its way out. + if [ "$CHANNEL" = "main" ]; then T=latest; else T=dev; fi + REF="$IMAGE:$T" + echo "label-shadow: ref=$REF derived=$DERIVED" + + A=$(docker buildx imagetools inspect "$REF" \ + --format '{{ index .Image.Config.Labels "fc.revision" }}' 2>&1 || echo FAILED) + echo "label-shadow: probe-index = $A" + + B=$(docker buildx imagetools inspect "$REF" \ + --format '{{ .Image.Config.Labels }}' 2>&1 | head -c 300 || echo FAILED) + echo "label-shadow: probe-labels = $B" + + C=$(docker buildx imagetools inspect "$REF" \ + --format '{{ json .Image }}' 2>&1 | head -c 400 || echo FAILED) + echo "label-shadow: probe-json = $C" + + echo "label-shadow: buildx = $(docker buildx version 2>&1 || echo UNAVAILABLE)" + - name: Download signed XPI from Forgejo release asset # dev and main each bundle the XPI their own sign-extension just # published — the point of the channel work (milestone 271 step 6): the @@ -586,6 +651,11 @@ jobs: file: Dockerfile push: true tags: ${{ steps.reuse.outputs.build_tags }} + # Stamped now, read later. Additive and inert this step: no + # decision depends on it until the shadow above proves the + # label can actually be read back (milestone 318 step 3). + labels: | + fc.revision=${{ steps.reuse.outputs.revision }} # Only the web image carries a channel: it is the one that serves # /api/extension/manifest. The ml and agent images have nothing to # report it to. @@ -745,6 +815,11 @@ jobs: set -eu ID=$(sh scripts/artifacts.sh identity ml "$CHANNEL") echo "identity=$ID" >> "$GITHUB_OUTPUT" + # The value step 3 will key on once the label mechanism is proven. + # Emitted now so the build below can stamp it — stamping is harmless + # and is what gives the shadow read something to find next push. + REV=$(sh scripts/artifacts.sh revision ml) + echo "revision=$REV" >> "$GITHUB_OUTPUT" # Every build that runs now claims the identity. The carve-out here # existed only for tag pushes, which rebuilt an already-published @@ -760,6 +835,66 @@ jobs: echo "reuse: $IMAGE:$ID is not published — building" fi + # --- shadow: can we read a label off the channel tag? (milestone 318 step 3) + # Informational ONLY — no `set -e`, every probe falls back to + # UNAVAILABLE, and nothing downstream reads this. It must never fail a + # build; that is the whole reason it runs before the mechanism does. + # + # The question: can the reuse check be keyed on an image LABEL read off + # the channel tag, instead of on a separate r- tag? If yes, nothing + # is minted per build, the -main/-dev identity split disappears, and the + # base-refresh clobber (#3154) stops existing. + # + # Two questions, answered at different times — worth separating, because + # reading "no value" as "the mechanism does not work" would abandon a + # working design: + # + # 1. Do the TEMPLATE MECHANICS work here? Answered on the very first + # run, by probes B and C: if they print a labels map (even an empty + # one) or a config JSON, `--format` over .Image resolves on this + # buildx and this registry. That is the actual gate for step 3. + # 2. Does a stamped value ROUND-TRIP? Only answerable once an artifact + # genuinely rebuilds. A push that changes no shipped file reuses, + # the build is skipped, and nothing gets stamped — so an artifact + # keeps reporting no label until its own source next moves. + # + # That second point is not a defect, it is the migration path: under the + # real mechanism "no label" reads as a MISS, so the artifact rebuilds + # once and is stamped from then on. One extra build per artifact, ever, + # in the safe direction. `imagetools create` cannot add the label on the + # reuse path — it copies a manifest and config labels are not manifest + # annotations — so there is no way to backfill, and no need to. + # + # Several probes on purpose: `--format` templating over .Image varies by + # buildx version, and one push should tell us which spelling works + # rather than costing a round trip per guess. + - name: Shadow — read fc.revision off the channel tag (informational) + env: + IMAGE: git.fabledsword.com/bvandeusen/fabledcurator-ml + CHANNEL: ${{ steps.tag.outputs.channel }} + DERIVED: ${{ steps.reuse.outputs.revision }} + run: | + set -u + # The moving tag this channel publishes. `main` is asked about + # :latest, which is what step 4 keeps; :main is on its way out. + if [ "$CHANNEL" = "main" ]; then T=latest; else T=dev; fi + REF="$IMAGE:$T" + echo "label-shadow: ref=$REF derived=$DERIVED" + + A=$(docker buildx imagetools inspect "$REF" \ + --format '{{ index .Image.Config.Labels "fc.revision" }}' 2>&1 || echo FAILED) + echo "label-shadow: probe-index = $A" + + B=$(docker buildx imagetools inspect "$REF" \ + --format '{{ .Image.Config.Labels }}' 2>&1 | head -c 300 || echo FAILED) + echo "label-shadow: probe-labels = $B" + + C=$(docker buildx imagetools inspect "$REF" \ + --format '{{ json .Image }}' 2>&1 | head -c 400 || echo FAILED) + echo "label-shadow: probe-json = $C" + + echo "label-shadow: buildx = $(docker buildx version 2>&1 || echo UNAVAILABLE)" + - name: Build and push ml image if: steps.reuse.outputs.hit != 'true' uses: docker/build-push-action@v5 @@ -768,6 +903,11 @@ jobs: file: Dockerfile.ml push: true tags: ${{ steps.reuse.outputs.build_tags }} + # Stamped now, read later. Additive and inert this step: no + # decision depends on it until the shadow above proves the + # label can actually be read back (milestone 318 step 3). + labels: | + fc.revision=${{ steps.reuse.outputs.revision }} # Registry-side manifest copy: no layer transfer, no local daemon, no # rebuild. Each -t becomes another reference to the SAME manifest the @@ -918,6 +1058,11 @@ jobs: set -eu ID=$(sh scripts/artifacts.sh identity agent "$CHANNEL") echo "identity=$ID" >> "$GITHUB_OUTPUT" + # The value step 3 will key on once the label mechanism is proven. + # Emitted now so the build below can stamp it — stamping is harmless + # and is what gives the shadow read something to find next push. + REV=$(sh scripts/artifacts.sh revision agent) + echo "revision=$REV" >> "$GITHUB_OUTPUT" # Every build that runs now claims the identity. The carve-out here # existed only for tag pushes, which rebuilt an already-published @@ -933,6 +1078,66 @@ jobs: echo "reuse: $IMAGE:$ID is not published — building" fi + # --- shadow: can we read a label off the channel tag? (milestone 318 step 3) + # Informational ONLY — no `set -e`, every probe falls back to + # UNAVAILABLE, and nothing downstream reads this. It must never fail a + # build; that is the whole reason it runs before the mechanism does. + # + # The question: can the reuse check be keyed on an image LABEL read off + # the channel tag, instead of on a separate r- tag? If yes, nothing + # is minted per build, the -main/-dev identity split disappears, and the + # base-refresh clobber (#3154) stops existing. + # + # Two questions, answered at different times — worth separating, because + # reading "no value" as "the mechanism does not work" would abandon a + # working design: + # + # 1. Do the TEMPLATE MECHANICS work here? Answered on the very first + # run, by probes B and C: if they print a labels map (even an empty + # one) or a config JSON, `--format` over .Image resolves on this + # buildx and this registry. That is the actual gate for step 3. + # 2. Does a stamped value ROUND-TRIP? Only answerable once an artifact + # genuinely rebuilds. A push that changes no shipped file reuses, + # the build is skipped, and nothing gets stamped — so an artifact + # keeps reporting no label until its own source next moves. + # + # That second point is not a defect, it is the migration path: under the + # real mechanism "no label" reads as a MISS, so the artifact rebuilds + # once and is stamped from then on. One extra build per artifact, ever, + # in the safe direction. `imagetools create` cannot add the label on the + # reuse path — it copies a manifest and config labels are not manifest + # annotations — so there is no way to backfill, and no need to. + # + # Several probes on purpose: `--format` templating over .Image varies by + # buildx version, and one push should tell us which spelling works + # rather than costing a round trip per guess. + - name: Shadow — read fc.revision off the channel tag (informational) + env: + IMAGE: git.fabledsword.com/bvandeusen/fabledcurator-agent + CHANNEL: ${{ steps.tag.outputs.channel }} + DERIVED: ${{ steps.reuse.outputs.revision }} + run: | + set -u + # The moving tag this channel publishes. `main` is asked about + # :latest, which is what step 4 keeps; :main is on its way out. + if [ "$CHANNEL" = "main" ]; then T=latest; else T=dev; fi + REF="$IMAGE:$T" + echo "label-shadow: ref=$REF derived=$DERIVED" + + A=$(docker buildx imagetools inspect "$REF" \ + --format '{{ index .Image.Config.Labels "fc.revision" }}' 2>&1 || echo FAILED) + echo "label-shadow: probe-index = $A" + + B=$(docker buildx imagetools inspect "$REF" \ + --format '{{ .Image.Config.Labels }}' 2>&1 | head -c 300 || echo FAILED) + echo "label-shadow: probe-labels = $B" + + C=$(docker buildx imagetools inspect "$REF" \ + --format '{{ json .Image }}' 2>&1 | head -c 400 || echo FAILED) + echo "label-shadow: probe-json = $C" + + echo "label-shadow: buildx = $(docker buildx version 2>&1 || echo UNAVAILABLE)" + - name: Build and push agent image if: steps.reuse.outputs.hit != 'true' uses: docker/build-push-action@v5 @@ -941,6 +1146,11 @@ jobs: file: agent/Dockerfile push: true tags: ${{ steps.reuse.outputs.build_tags }} + # Stamped now, read later. Additive and inert this step: no + # decision depends on it until the shadow above proves the + # label can actually be read back (milestone 318 step 3). + labels: | + fc.revision=${{ steps.reuse.outputs.revision }} # Registry-side manifest copy: no layer transfer, no local daemon, no # rebuild. Each -t becomes another reference to the SAME manifest the