diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 596c247eee..233943268c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -34,6 +34,10 @@ name: Docker image # intact) and the bare version flows straight to the semver parser. On a # real push event value= is empty and the match strips relay-v from the ref. # +# ghcr.io/block/buzz-push-gateway carries the same :main, :sha-<7> and +# :main--<7> tags on main pushes, and publishes only after the +# relay's `qualify` job accepts the same source SHA. +# # :main--<7> names the same main build as :sha-<7>, prefixed # with its UTC commit time so the tag sorts by build order. Kargo's Lexical # strategy picks the newest from the tag list alone; :sha-<7> cannot be @@ -614,7 +618,9 @@ jobs: name: Publish public push gateway image if: github.event_name != 'pull_request' runs-on: ubuntu-24.04 - needs: push-gateway-build + # Staging promotes every published main gateway image automatically, so the + # gateway publishes only from a source the relay's same-SHA CI qualified. + needs: [push-gateway-build, qualify] timeout-minutes: 15 permissions: contents: read @@ -646,6 +652,7 @@ jobs: type=sha,prefix=sha-,format=short,enable=${{ github.event_name != 'workflow_dispatch' || inputs.version == '' }} type=semver,pattern={{version}},match=^relay-v(.*)$,value=${{ inputs.version }} type=semver,pattern={{major}}.{{minor}},match=^relay-v(.*)$,value=${{ inputs.version }} + type=raw,value=main-{{commit_date 'YYYYMMDDHHmmss'}}-{{sha}},enable=${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} - name: Merge and publish manifest id: manifest working-directory: /tmp/gateway-digests diff --git a/scripts/test-relay-image-eligibility-workflow.sh b/scripts/test-relay-image-eligibility-workflow.sh index 40745a8e88..f6adc4c6f3 100755 --- a/scripts/test-relay-image-eligibility-workflow.sh +++ b/scripts/test-relay-image-eligibility-workflow.sh @@ -44,6 +44,20 @@ grep -Fq "actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10" <<<"$merge_ exit 1 } +# Staging auto-promotes the newest main--<7> gateway image, so the gateway +# must publish that tag and only from a source the relay CI qualified. Match +# whole lines (-x) so a commented-out or otherwise inactive copy cannot satisfy +# the contract after the live line regresses. +gateway_merge_job=$(awk '/^ push-gateway-merge:/{f=1} f&&/^ [a-z][a-z_-]*:$/&&!/^ push-gateway-merge:/{exit} f' "$workflow") +grep -Fxq " needs: [push-gateway-build, qualify]" <<<"$gateway_merge_job" || { + echo "push gateway image must publish only after same-SHA CI qualification" >&2 + exit 1 +} +grep -Fxq " type=raw,value=main-{{commit_date 'YYYYMMDDHHmmss'}}-{{sha}},enable=\${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}" <<<"$gateway_merge_job" || { + echo "push gateway image must publish the sortable main--<7> tag on main pushes" >&2 + exit 1 +} + select_run() { jq -r --arg source_sha aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -f "$selector" | jq -r '.id // empty' } diff --git a/scripts/test-release-ref-contract.sh b/scripts/test-release-ref-contract.sh index 5b11a71087..e5903cb46c 100755 --- a/scripts/test-release-ref-contract.sh +++ b/scripts/test-release-ref-contract.sh @@ -52,12 +52,13 @@ fi grep -q 'verify-release-ref\.sh' "$repo_root/.github/workflows/release.yml" grep -q 'verify-release-ref\.sh' "$repo_root/.github/workflows/docker.yml" -# Kargo's buzz-image warehouse selects Lexical over ^main-[0-9]{14}-[0-9a-f]{7}$; -# a drifted format stops Freight discovery silently, so pin both image legs. +# Kargo's buzz-image and buzz-push-gateway-image warehouses select Lexical over +# ^main-[0-9]{14}-[0-9a-f]{7}$; a drifted format stops Freight discovery +# silently, so pin all three image legs (relay, relay-debug, push gateway). sortable_main_tag=" type=raw,value=main-{{commit_date 'YYYYMMDDHHmmss'}}-{{sha}},enable=\${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}" sortable_count="$(grep -Fxc "$sortable_main_tag" "$repo_root/.github/workflows/docker.yml" || true)" -if [ "$sortable_count" != 2 ]; then - echo "docker.yml must publish the sortable main-- tag on both image legs (found $sortable_count)" >&2 +if [ "$sortable_count" != 3 ]; then + echo "docker.yml must publish the sortable main-- tag on all three image legs (found $sortable_count)" >&2 exit 1 fi grep -q 'test-release-ref-contract\.sh' "$repo_root/.github/workflows/ci.yml"