Stop minting identifiers nothing reads: moving channel tags, :c-<sha> on main, and a label-keyed build cache #239
@@ -487,6 +487,11 @@ jobs:
|
|||||||
set -eu
|
set -eu
|
||||||
ID=$(sh scripts/artifacts.sh identity web "$CHANNEL")
|
ID=$(sh scripts/artifacts.sh identity web "$CHANNEL")
|
||||||
echo "identity=$ID" >> "$GITHUB_OUTPUT"
|
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
|
# Every build that runs now claims the identity. The carve-out here
|
||||||
# existed only for tag pushes, which rebuilt an already-published
|
# existed only for tag pushes, which rebuilt an already-published
|
||||||
@@ -502,6 +507,66 @@ jobs:
|
|||||||
echo "reuse: $IMAGE:$ID is not published — building"
|
echo "reuse: $IMAGE:$ID is not published — building"
|
||||||
fi
|
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-<rev> 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
|
- name: Download signed XPI from Forgejo release asset
|
||||||
# dev and main each bundle the XPI their own sign-extension just
|
# dev and main each bundle the XPI their own sign-extension just
|
||||||
# published — the point of the channel work (milestone 271 step 6): the
|
# published — the point of the channel work (milestone 271 step 6): the
|
||||||
@@ -586,6 +651,11 @@ jobs:
|
|||||||
file: Dockerfile
|
file: Dockerfile
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.reuse.outputs.build_tags }}
|
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
|
# Only the web image carries a channel: it is the one that serves
|
||||||
# /api/extension/manifest. The ml and agent images have nothing to
|
# /api/extension/manifest. The ml and agent images have nothing to
|
||||||
# report it to.
|
# report it to.
|
||||||
@@ -745,6 +815,11 @@ jobs:
|
|||||||
set -eu
|
set -eu
|
||||||
ID=$(sh scripts/artifacts.sh identity ml "$CHANNEL")
|
ID=$(sh scripts/artifacts.sh identity ml "$CHANNEL")
|
||||||
echo "identity=$ID" >> "$GITHUB_OUTPUT"
|
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
|
# Every build that runs now claims the identity. The carve-out here
|
||||||
# existed only for tag pushes, which rebuilt an already-published
|
# existed only for tag pushes, which rebuilt an already-published
|
||||||
@@ -760,6 +835,66 @@ jobs:
|
|||||||
echo "reuse: $IMAGE:$ID is not published — building"
|
echo "reuse: $IMAGE:$ID is not published — building"
|
||||||
fi
|
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-<rev> 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
|
- name: Build and push ml image
|
||||||
if: steps.reuse.outputs.hit != 'true'
|
if: steps.reuse.outputs.hit != 'true'
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
@@ -768,6 +903,11 @@ jobs:
|
|||||||
file: Dockerfile.ml
|
file: Dockerfile.ml
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.reuse.outputs.build_tags }}
|
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
|
# Registry-side manifest copy: no layer transfer, no local daemon, no
|
||||||
# rebuild. Each -t becomes another reference to the SAME manifest the
|
# rebuild. Each -t becomes another reference to the SAME manifest the
|
||||||
@@ -918,6 +1058,11 @@ jobs:
|
|||||||
set -eu
|
set -eu
|
||||||
ID=$(sh scripts/artifacts.sh identity agent "$CHANNEL")
|
ID=$(sh scripts/artifacts.sh identity agent "$CHANNEL")
|
||||||
echo "identity=$ID" >> "$GITHUB_OUTPUT"
|
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
|
# Every build that runs now claims the identity. The carve-out here
|
||||||
# existed only for tag pushes, which rebuilt an already-published
|
# existed only for tag pushes, which rebuilt an already-published
|
||||||
@@ -933,6 +1078,66 @@ jobs:
|
|||||||
echo "reuse: $IMAGE:$ID is not published — building"
|
echo "reuse: $IMAGE:$ID is not published — building"
|
||||||
fi
|
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-<rev> 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
|
- name: Build and push agent image
|
||||||
if: steps.reuse.outputs.hit != 'true'
|
if: steps.reuse.outputs.hit != 'true'
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
@@ -941,6 +1146,11 @@ jobs:
|
|||||||
file: agent/Dockerfile
|
file: agent/Dockerfile
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.reuse.outputs.build_tags }}
|
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
|
# Registry-side manifest copy: no layer transfer, no local daemon, no
|
||||||
# rebuild. Each -t becomes another reference to the SAME manifest the
|
# rebuild. Each -t becomes another reference to the SAME manifest the
|
||||||
|
|||||||
Reference in New Issue
Block a user