ci: a release names a build, it does not make one (milestone 318 step 2)
Build images / sign-extension (push) Successful in 4s
CI / lint (push) Successful in 3s
CI / extension-version (push) Successful in 4s
Build images / build-ml (push) Successful in 4s
Build images / build-agent (push) Successful in 5s
Build images / build-web (push) Successful in 4s
CI / frontend-build (push) Successful in 20s
extension / lint (push) Successful in 26s
CI / backend-lint-and-test (push) Successful in 31s
CI / integration (push) Successful in 3m50s
Build images / sign-extension (push) Successful in 4s
CI / lint (push) Successful in 3s
CI / extension-version (push) Successful in 4s
Build images / build-ml (push) Successful in 4s
Build images / build-agent (push) Successful in 5s
Build images / build-web (push) Successful in 4s
CI / frontend-build (push) Successful in 20s
extension / lint (push) Successful in 26s
CI / backend-lint-and-test (push) Successful in 31s
CI / integration (push) Successful in 3m50s
Removes the `v*` image-build trigger. A release tag names a commit `main` already built and published; rebuilding it produced the same source under the same names and RE-PUSHED `:c-<sha>` — which rule 145 forbids even when the bytes match, because image configs carry timestamps and "same source" does not mean "same manifest". The tag build was publishing nothing new and violating an immutability rule to do it. Three constructs go with it, all of which existed only to manage that build: The 10-minute XPI polling loop. A release cut fired the tag build and the main build together; the tag build skipped sign-extension and raced straight to the download, losing every time (operator-flagged 2026-05-27 after v26.05.27.0). Polling was the fix for a build that should not have run. It is now a single fetch, and a 404 means the two jobs derived different versions for one commit — which should fail loudly rather than be slept through. The IS_TAG_PUSH carve-out from milestone 313 step 4, which stopped a tag build from claiming an identity tag it would have re-pointed at fresh bytes. build-web's `always() && (success || skipped)` gate, which existed to let a SKIPPED sign-extension through on a tag push while still blocking a failed one. sign-extension now always runs, so a plain `needs` gives exactly the wanted behaviour. Its own branch condition goes too: main and dev are the only triggers, so a gate naming both matched everything, and a condition that is always true reads as though some path avoids it. Releases still happen — rule 148 is untouched and tags are still cut on explicit request per rule 2. They stop building images and start carrying a changelog (step 7). Net 115 lines deleted, 73 added, most of that comments explaining races that can no longer occur. Nothing in the repo referenced the tag build, so no doc changes were needed; the broader doc pass is step 9.
This commit is contained in:
+76
-119
@@ -14,13 +14,16 @@ on:
|
|||||||
# with a stale `:dev` ml or agent is a worse trap than no dev channel at
|
# with a stale `:dev` ml or agent is a worse trap than no dev channel at
|
||||||
# all, since the mismatch only shows up as a runtime failure.
|
# all, since the mismatch only shows up as a runtime failure.
|
||||||
branches: [main, dev]
|
branches: [main, dev]
|
||||||
# Tag-push triggers an immutable per-version image build (e.g.
|
#
|
||||||
# `:v26.05.26.5`) — gives a real rollback story alongside the floating
|
# NO tag trigger (milestone 318 step 2). A `v*` tag names a commit `main`
|
||||||
# `:main` / `:latest`. Layer reuse keeps the registry-storage cost
|
# already built and published; rebuilding it produces the same source under
|
||||||
# negligible per tag. Doesn't overlap with the push-to-main build (that
|
# the same names and RE-PUSHES `:c-<sha>`, which rule 145 forbids even when
|
||||||
# one publishes `:main` + `:latest`; the tag-push build publishes only
|
# the bytes match — image configs carry timestamps, so "same source" does
|
||||||
# `:<tag>`).
|
# not mean "same manifest". The release build was publishing nothing new
|
||||||
tags: ['v*']
|
# and violating an immutability rule to do it.
|
||||||
|
#
|
||||||
|
# Releases still happen (rule 148, on explicit request per rule 2). They
|
||||||
|
# produce a changelog, not an image.
|
||||||
|
|
||||||
# Requires repo secret RELEASE_TOKEN — a Forgejo PAT with scopes:
|
# Requires repo secret RELEASE_TOKEN — a Forgejo PAT with scopes:
|
||||||
# - write:package, read:package (for docker push to git.fabledsword.com)
|
# - write:package, read:package (for docker push to git.fabledsword.com)
|
||||||
@@ -54,10 +57,11 @@ jobs:
|
|||||||
# have hit the existing ext-1.0.11 cache and bundled MAIN's stale XPI into
|
# have hit the existing ext-1.0.11 cache and bundled MAIN's stale XPI into
|
||||||
# `:dev` — a dev channel confidently serving old code.
|
# `:dev` — a dev channel confidently serving old code.
|
||||||
#
|
#
|
||||||
# Tags stay excluded: the tag path deliberately skips signing and polls for
|
# Unconditional since milestone 318 step 2: main and dev are now the only
|
||||||
# the release instead (see build-web's race note, 2026-05-27).
|
# triggers, so the branch gate that used to exclude tag pushes matched
|
||||||
|
# everything. A condition that is always true reads as if some path avoids
|
||||||
|
# it, which is worse than no condition.
|
||||||
sign-extension:
|
sign-extension:
|
||||||
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev'
|
|
||||||
runs-on: python-ci
|
runs-on: python-ci
|
||||||
container:
|
container:
|
||||||
image: git.fabledsword.com/bvandeusen/ci-python:3.14
|
image: git.fabledsword.com/bvandeusen/ci-python:3.14
|
||||||
@@ -323,12 +327,12 @@ jobs:
|
|||||||
# to. Same source of truth; no double-store.
|
# to. Same source of truth; no double-store.
|
||||||
|
|
||||||
build-web:
|
build-web:
|
||||||
|
# A plain `needs` — no `always()`. That expression existed to let a
|
||||||
|
# SKIPPED sign-extension through on a tag push while still blocking a
|
||||||
|
# FAILED one. With no tag trigger, sign-extension always runs, so the
|
||||||
|
# default behaviour is exactly what we want: a failed sign skips build-web
|
||||||
|
# rather than shipping an image without its XPI.
|
||||||
needs: [sign-extension]
|
needs: [sign-extension]
|
||||||
# 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
|
runs-on: python-ci
|
||||||
container:
|
container:
|
||||||
image: git.fabledsword.com/bvandeusen/ci-python:3.14
|
image: git.fabledsword.com/bvandeusen/ci-python:3.14
|
||||||
@@ -373,15 +377,7 @@ jobs:
|
|||||||
- name: Determine tag
|
- name: Determine tag
|
||||||
id: tag
|
id: tag
|
||||||
run: |
|
run: |
|
||||||
# Three trigger shapes:
|
# Two trigger shapes:
|
||||||
# refs/tags/v… → tag-push: opt-in milestone label (vYY.MM.DD,
|
|
||||||
# plus `.N` when the day already carries a tag —
|
|
||||||
# family rule 148, amended 2026-08-24 after a
|
|
||||||
# same-day tag was retargeted and a release
|
|
||||||
# deleted to make room, note 2813).
|
|
||||||
# Publish ONLY the immutable version tag;
|
|
||||||
# don't touch :latest (the main-push build
|
|
||||||
# for the merge commit already did that).
|
|
||||||
# refs/heads/main → push to main: publish :main + :latest
|
# refs/heads/main → push to main: publish :main + :latest
|
||||||
# (floating) AND :c-<short_sha> (immutable
|
# (floating) AND :c-<short_sha> (immutable
|
||||||
# per-commit rollback substrate, per family
|
# per-commit rollback substrate, per family
|
||||||
@@ -411,14 +407,8 @@ jobs:
|
|||||||
# reads 2026.8.27 — and the reuse step below turns that into a
|
# reads 2026.8.27 — and the reuse step below turns that into a
|
||||||
# skipped build rather than a rebuild of bytes that already exist.
|
# skipped build rather than a rebuild of bytes that already exist.
|
||||||
# `channel` is baked into the image as FC_CHANNEL and reported by
|
# `channel` is baked into the image as FC_CHANNEL and reported by
|
||||||
# /api/extension/manifest (milestone 271 step 7). A tag-push counts as
|
# /api/extension/manifest (milestone 271 step 7).
|
||||||
# `main`: a vYY.MM.DD tag is cut from main, so that image is a
|
if [ "${GITHUB_REF##*/}" = "main" ]; then
|
||||||
# main-channel artifact wearing an immutable name.
|
|
||||||
if [ "${GITHUB_REF#refs/tags/}" != "${GITHUB_REF}" ]; then
|
|
||||||
TAG_NAME="${GITHUB_REF#refs/tags/}"
|
|
||||||
echo "tags=git.fabledsword.com/bvandeusen/fabledcurator:${TAG_NAME}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "channel=main" >> "$GITHUB_OUTPUT"
|
|
||||||
elif [ "${GITHUB_REF##*/}" = "main" ]; then
|
|
||||||
CALVER=$(sh scripts/artifacts.sh tag web)
|
CALVER=$(sh scripts/artifacts.sh tag web)
|
||||||
# Guarded, and computed only on this path. There is no `set -e` in
|
# Guarded, and computed only on this path. There is no `set -e` in
|
||||||
# this step, so a failed derivation would otherwise leave CALVER
|
# this step, so a failed derivation would otherwise leave CALVER
|
||||||
@@ -493,22 +483,16 @@ jobs:
|
|||||||
IMAGE: git.fabledsword.com/bvandeusen/fabledcurator
|
IMAGE: git.fabledsword.com/bvandeusen/fabledcurator
|
||||||
CHANNEL: ${{ steps.tag.outputs.channel }}
|
CHANNEL: ${{ steps.tag.outputs.channel }}
|
||||||
TAGS: ${{ steps.tag.outputs.tags }}
|
TAGS: ${{ steps.tag.outputs.tags }}
|
||||||
IS_TAG_PUSH: ${{ startsWith(github.ref, 'refs/tags/') }}
|
|
||||||
run: |
|
run: |
|
||||||
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"
|
||||||
|
|
||||||
# A tag-push builds a revision that main already published, so it
|
# Every build that runs now claims the identity. The carve-out here
|
||||||
# must NOT claim the identity: image configs are not bit-reproducible
|
# existed only for tag pushes, which rebuilt an already-published
|
||||||
# (embedded timestamps), so re-pushing r-<rev> would point an
|
# revision and so had to be stopped from re-pointing an immutable
|
||||||
# immutable tag at fresh bytes — rule 145's exact prohibition. It
|
# tag at fresh bytes. No tag trigger, nothing to carve out.
|
||||||
# publishes only its own :v… label and otherwise reuses.
|
echo "build_tags=$TAGS,$IMAGE:$ID" >> "$GITHUB_OUTPUT"
|
||||||
if [ "$IS_TAG_PUSH" = "true" ]; then
|
|
||||||
echo "build_tags=$TAGS" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "build_tags=$TAGS,$IMAGE:$ID" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if docker buildx imagetools inspect "$IMAGE:$ID" >/dev/null 2>&1; then
|
if docker buildx imagetools inspect "$IMAGE:$ID" >/dev/null 2>&1; then
|
||||||
echo "hit=true" >> "$GITHUB_OUTPUT"
|
echo "hit=true" >> "$GITHUB_OUTPUT"
|
||||||
@@ -519,28 +503,26 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Download signed XPI from Forgejo release asset
|
- name: Download signed XPI from Forgejo release asset
|
||||||
# Fires on every trigger shape. dev and main each bundle the XPI their
|
# dev and main each bundle the XPI their own sign-extension just
|
||||||
# own sign-extension just published — that is the whole point of the
|
# published — the point of the channel work (milestone 271 step 6): the
|
||||||
# channel work (milestone 271 step 6): the dev image carries the
|
# dev image carries the extension being developed, rather than
|
||||||
# extension being developed, rather than requiring a merge to try it.
|
# 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
|
# The 10-minute polling loop that used to live here is gone with the
|
||||||
# v26.05.27.0 hit it): a release cut fires BOTH workflows almost
|
# tag trigger (milestone 318 step 2). It existed for one shape only: a
|
||||||
# simultaneously. Main-push runs sign-extension (1-5min AMO round
|
# release cut fired the tag build and the main build together, the tag
|
||||||
# trip) before publishing the ext-<version> release; tag-push
|
# build skipped sign-extension and raced straight here, and it lost
|
||||||
# skips sign-extension (gated to main) and races straight to
|
# every time (operator-flagged 2026-05-27 after v26.05.27.0). Polling
|
||||||
# this download step. Tag-push lost every time. Fix: poll the
|
# was the fix for a build that should not have been running.
|
||||||
# ext-<version> release endpoint with a sleep+retry loop (30s
|
#
|
||||||
# for up to 10min total) before giving up. Main-push's signing
|
# sign-extension is a `needs` dependency and it succeeded, so the
|
||||||
# eventually wins and tag-push picks the release up on a later
|
# release exists. A single fetch is correct, and a 404 now means a real
|
||||||
# iteration.
|
# disagreement about the derived version rather than a race — which is
|
||||||
# Gated on the reuse miss as well: if the image is already published it
|
# exactly what should fail loudly instead of being slept through.
|
||||||
# already contains its XPI, so this would download (and on a tag-push,
|
#
|
||||||
# poll up to 10 minutes for) a file nothing then reads.
|
# Still gated on the reuse miss: a published image already contains its
|
||||||
if: steps.reuse.outputs.hit != 'true' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/'))
|
# XPI, so this would fetch a file nothing then reads.
|
||||||
|
if: steps.reuse.outputs.hit != 'true'
|
||||||
env:
|
env:
|
||||||
TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
@@ -552,25 +534,21 @@ jobs:
|
|||||||
# didn't, this download 404s and the build fails loudly instead of
|
# didn't, this download 404s and the build fails loudly instead of
|
||||||
# shipping a stale XPI.
|
# shipping a stale XPI.
|
||||||
VERSION=$(sh extension/scripts/packaging.sh version)
|
VERSION=$(sh extension/scripts/packaging.sh version)
|
||||||
# Poll for the ext-<version> release. main-push's sign-extension
|
# One fetch, no retry. sign-extension ran to success in this same
|
||||||
# step (AMO round-trip, 1-5min) needs to finish + upload before
|
# workflow and published ext-$VERSION; both jobs derive $VERSION from
|
||||||
# tag-push can fetch. 30s * 20 = up to 10min wait, then hard-fail.
|
# the same commit, so they agree by construction. A 404 here means
|
||||||
for attempt in $(seq 1 20); do
|
# they did NOT agree, and sleeping on that would only delay the
|
||||||
STATUS=$(curl -s -o release.json -w "%{http_code}" \
|
# report.
|
||||||
-H "Authorization: token $TOKEN" \
|
STATUS=$(curl -s -o release.json -w "%{http_code}" \
|
||||||
"https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledCurator/releases/tags/ext-$VERSION" || echo 000)
|
-H "Authorization: token $TOKEN" \
|
||||||
if [ "$STATUS" = "200" ]; then
|
"https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledCurator/releases/tags/ext-$VERSION" || echo 000)
|
||||||
echo "Found ext-$VERSION release on attempt $attempt"
|
if [ "$STATUS" != "200" ]; then
|
||||||
break
|
echo "ERROR: ext-$VERSION release not found (HTTP $STATUS)."
|
||||||
fi
|
echo "sign-extension succeeded in this run, so it published some"
|
||||||
if [ "$attempt" = "20" ]; then
|
echo "other version — the two jobs derived different values for one"
|
||||||
echo "ERROR: ext-$VERSION release not available after 10min of polling"
|
echo "commit. Check that both checked out with fetch-depth: 0."
|
||||||
echo "Last HTTP status: $STATUS"
|
exit 1
|
||||||
exit 1
|
fi
|
||||||
fi
|
|
||||||
echo "Attempt $attempt: ext-$VERSION not yet published (HTTP $STATUS); sleeping 30s"
|
|
||||||
sleep 30
|
|
||||||
done
|
|
||||||
# Extract the .xpi asset's browser_download_url (Forgejo's
|
# Extract the .xpi asset's browser_download_url (Forgejo's
|
||||||
# /releases/assets/<id> endpoint returns ASSET METADATA, not
|
# /releases/assets/<id> endpoint returns ASSET METADATA, not
|
||||||
# the binary blob — operator-flagged 2026-05-26: my prior
|
# the binary blob — operator-flagged 2026-05-26: my prior
|
||||||
@@ -687,10 +665,9 @@ jobs:
|
|||||||
- name: Determine tag
|
- name: Determine tag
|
||||||
id: tag
|
id: tag
|
||||||
run: |
|
run: |
|
||||||
# Mirrors build-web's three-shape logic (tag-push / main-push /
|
# Mirrors build-web's two-shape logic (main-push / dev-push),
|
||||||
# safety-net dev) including the per-commit :c-<short_sha> tag
|
# including the per-commit :c-<short_sha> tag on main — the rollback
|
||||||
# on main-push per the family release-posture rule. The -ml
|
# unit per rule 145. The -ml image follows the same cadence as web.
|
||||||
# image follows the same release cadence as the web image.
|
|
||||||
# POSIX-safe substring (the runner shell is dash/BusyBox sh, not
|
# POSIX-safe substring (the runner shell is dash/BusyBox sh, not
|
||||||
# bash — `${var:0:7}` errors with "Bad substitution"; cut works
|
# bash — `${var:0:7}` errors with "Bad substitution"; cut works
|
||||||
# everywhere). Operator-flagged 2026-06-01 after first :c-<sha>
|
# everywhere). Operator-flagged 2026-06-01 after first :c-<sha>
|
||||||
@@ -706,11 +683,7 @@ jobs:
|
|||||||
# the tag it already had: the agent reads 2026.7.17 today while web
|
# the tag it already had: the agent reads 2026.7.17 today while web
|
||||||
# reads 2026.8.27 — and the reuse step below turns that into a
|
# reads 2026.8.27 — and the reuse step below turns that into a
|
||||||
# skipped build rather than a rebuild of bytes that already exist.
|
# skipped build rather than a rebuild of bytes that already exist.
|
||||||
if [ "${GITHUB_REF#refs/tags/}" != "${GITHUB_REF}" ]; then
|
if [ "${GITHUB_REF##*/}" = "main" ]; then
|
||||||
TAG_NAME="${GITHUB_REF#refs/tags/}"
|
|
||||||
echo "tags=git.fabledsword.com/bvandeusen/fabledcurator-ml:${TAG_NAME}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "channel=main" >> "$GITHUB_OUTPUT"
|
|
||||||
elif [ "${GITHUB_REF##*/}" = "main" ]; then
|
|
||||||
CALVER=$(sh scripts/artifacts.sh tag ml)
|
CALVER=$(sh scripts/artifacts.sh tag ml)
|
||||||
# Guarded, and computed only on this path. There is no `set -e` in
|
# Guarded, and computed only on this path. There is no `set -e` in
|
||||||
# this step, so a failed derivation would otherwise leave CALVER
|
# this step, so a failed derivation would otherwise leave CALVER
|
||||||
@@ -768,22 +741,16 @@ jobs:
|
|||||||
IMAGE: git.fabledsword.com/bvandeusen/fabledcurator-ml
|
IMAGE: git.fabledsword.com/bvandeusen/fabledcurator-ml
|
||||||
CHANNEL: ${{ steps.tag.outputs.channel }}
|
CHANNEL: ${{ steps.tag.outputs.channel }}
|
||||||
TAGS: ${{ steps.tag.outputs.tags }}
|
TAGS: ${{ steps.tag.outputs.tags }}
|
||||||
IS_TAG_PUSH: ${{ startsWith(github.ref, 'refs/tags/') }}
|
|
||||||
run: |
|
run: |
|
||||||
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"
|
||||||
|
|
||||||
# A tag-push builds a revision that main already published, so it
|
# Every build that runs now claims the identity. The carve-out here
|
||||||
# must NOT claim the identity: image configs are not bit-reproducible
|
# existed only for tag pushes, which rebuilt an already-published
|
||||||
# (embedded timestamps), so re-pushing r-<rev> would point an
|
# revision and so had to be stopped from re-pointing an immutable
|
||||||
# immutable tag at fresh bytes — rule 145's exact prohibition. It
|
# tag at fresh bytes. No tag trigger, nothing to carve out.
|
||||||
# publishes only its own :v… label and otherwise reuses.
|
echo "build_tags=$TAGS,$IMAGE:$ID" >> "$GITHUB_OUTPUT"
|
||||||
if [ "$IS_TAG_PUSH" = "true" ]; then
|
|
||||||
echo "build_tags=$TAGS" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "build_tags=$TAGS,$IMAGE:$ID" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if docker buildx imagetools inspect "$IMAGE:$ID" >/dev/null 2>&1; then
|
if docker buildx imagetools inspect "$IMAGE:$ID" >/dev/null 2>&1; then
|
||||||
echo "hit=true" >> "$GITHUB_OUTPUT"
|
echo "hit=true" >> "$GITHUB_OUTPUT"
|
||||||
@@ -889,11 +856,7 @@ jobs:
|
|||||||
# the tag it already had: the agent reads 2026.7.17 today while web
|
# the tag it already had: the agent reads 2026.7.17 today while web
|
||||||
# reads 2026.8.27 — and the reuse step below turns that into a
|
# reads 2026.8.27 — and the reuse step below turns that into a
|
||||||
# skipped build rather than a rebuild of bytes that already exist.
|
# skipped build rather than a rebuild of bytes that already exist.
|
||||||
if [ "${GITHUB_REF#refs/tags/}" != "${GITHUB_REF}" ]; then
|
if [ "${GITHUB_REF##*/}" = "main" ]; then
|
||||||
TAG_NAME="${GITHUB_REF#refs/tags/}"
|
|
||||||
echo "tags=git.fabledsword.com/bvandeusen/fabledcurator-agent:${TAG_NAME}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "channel=main" >> "$GITHUB_OUTPUT"
|
|
||||||
elif [ "${GITHUB_REF##*/}" = "main" ]; then
|
|
||||||
CALVER=$(sh scripts/artifacts.sh tag agent)
|
CALVER=$(sh scripts/artifacts.sh tag agent)
|
||||||
# Guarded, and computed only on this path. There is no `set -e` in
|
# Guarded, and computed only on this path. There is no `set -e` in
|
||||||
# this step, so a failed derivation would otherwise leave CALVER
|
# this step, so a failed derivation would otherwise leave CALVER
|
||||||
@@ -951,22 +914,16 @@ jobs:
|
|||||||
IMAGE: git.fabledsword.com/bvandeusen/fabledcurator-agent
|
IMAGE: git.fabledsword.com/bvandeusen/fabledcurator-agent
|
||||||
CHANNEL: ${{ steps.tag.outputs.channel }}
|
CHANNEL: ${{ steps.tag.outputs.channel }}
|
||||||
TAGS: ${{ steps.tag.outputs.tags }}
|
TAGS: ${{ steps.tag.outputs.tags }}
|
||||||
IS_TAG_PUSH: ${{ startsWith(github.ref, 'refs/tags/') }}
|
|
||||||
run: |
|
run: |
|
||||||
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"
|
||||||
|
|
||||||
# A tag-push builds a revision that main already published, so it
|
# Every build that runs now claims the identity. The carve-out here
|
||||||
# must NOT claim the identity: image configs are not bit-reproducible
|
# existed only for tag pushes, which rebuilt an already-published
|
||||||
# (embedded timestamps), so re-pushing r-<rev> would point an
|
# revision and so had to be stopped from re-pointing an immutable
|
||||||
# immutable tag at fresh bytes — rule 145's exact prohibition. It
|
# tag at fresh bytes. No tag trigger, nothing to carve out.
|
||||||
# publishes only its own :v… label and otherwise reuses.
|
echo "build_tags=$TAGS,$IMAGE:$ID" >> "$GITHUB_OUTPUT"
|
||||||
if [ "$IS_TAG_PUSH" = "true" ]; then
|
|
||||||
echo "build_tags=$TAGS" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "build_tags=$TAGS,$IMAGE:$ID" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if docker buildx imagetools inspect "$IMAGE:$ID" >/dev/null 2>&1; then
|
if docker buildx imagetools inspect "$IMAGE:$ID" >/dev/null 2>&1; then
|
||||||
echo "hit=true" >> "$GITHUB_OUTPUT"
|
echo "hit=true" >> "$GITHUB_OUTPUT"
|
||||||
|
|||||||
Reference in New Issue
Block a user