Skip to content

Commit 8929503

Browse files
validate before promoting :latest (candidate-tag then promote)
Address the validate-after-publish gap: build+push to an immutable candidate tag (the commit SHA), validate that exact pushed image, then promote :latest onto it via 'docker buildx imagetools create' (a registry-side manifest copy, no rebuild). If validation fails, :latest is left on the last known-good build and only the SHA tag is dirty -- no unvalidated image ever lands on :latest. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d796596 commit 8929503

1 file changed

Lines changed: 26 additions & 11 deletions

File tree

.github/workflows/build-and-push-tutorial-agent.yml

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -181,27 +181,30 @@ jobs:
181181
AGENT_NAME="${{ steps.image-name.outputs.agent_name }}"
182182
REPOSITORY_NAME="${{ github.repository }}/tutorial-agents/${AGENT_NAME}"
183183
184-
# Determine if we should push based on event type
184+
# Determine if we should publish based on event type.
185+
# Publish path: push to an immutable candidate tag (the commit SHA) first,
186+
# validate that exact pushed artifact, then promote :latest onto it. This
187+
# keeps an unvalidated image off :latest — if validation fails, :latest is
188+
# left pointing at the last known-good build, and only the SHA tag is dirty.
185189
if [ "${{ github.event_name }}" = "push" ] || [ "${{ inputs.rebuild_all }}" = "true" ]; then
186190
SHOULD_PUSH=true
187-
VERSION_TAG="latest"
188-
echo "🚀 Building agent (will push after validation): ${{ matrix.agent_path }}"
191+
PROMOTE_LATEST=true
192+
VERSION_TAG="${{ github.sha }}"
193+
echo "🚀 Building agent (push candidate ${VERSION_TAG}, promote :latest after validation): ${{ matrix.agent_path }}"
189194
else
190195
SHOULD_PUSH=false
196+
PROMOTE_LATEST=false
191197
VERSION_TAG="${{ github.sha }}"
192198
echo "🔍 Building agent for validation: ${{ matrix.agent_path }}"
193-
# Set full image name for validation step (local build)
194-
echo "FULL_IMAGE=${REGISTRY}/${REPOSITORY_NAME}:${VERSION_TAG}" >> $GITHUB_ENV
195199
# Skip image validation for PRs since Buildx doesn't load multi-platform images locally
196200
echo "SKIP_VALIDATION=true" >> $GITHUB_ENV
197201
fi
198202
199-
# Build the image. On the publish path, push straight from buildx.
200-
# A multi-platform build cannot be loaded into the local Docker image
201-
# store, so a subsequent `docker push` has no freshly-built image to send
202-
# and ends up re-pushing whatever :latest was pulled during validation —
203-
# i.e. the previous, stale image. Pushing from the build itself is the
204-
# only way the newly-built multi-arch image actually lands on the tag.
203+
# Build the image. On the publish path, push straight from buildx to the
204+
# candidate tag: a multi-platform build cannot be loaded into the local
205+
# Docker store, so it must be pushed by the build itself rather than by a
206+
# later `docker push` (which would have no fresh local image and would
207+
# re-push a stale tag instead).
205208
BUILD_ARGS="--manifest ${{ matrix.agent_path }}/manifest.yaml --registry ${REGISTRY} --tag ${VERSION_TAG} --platforms linux/amd64,linux/arm64 --repository-name ${REPOSITORY_NAME}"
206209
if [ "$SHOULD_PUSH" = "true" ]; then
207210
BUILD_ARGS="$BUILD_ARGS --push"
@@ -212,7 +215,9 @@ jobs:
212215
213216
# Set environment variables for subsequent steps
214217
echo "FULL_IMAGE=${REGISTRY}/${REPOSITORY_NAME}:${VERSION_TAG}" >> $GITHUB_ENV
218+
echo "LATEST_IMAGE=${REGISTRY}/${REPOSITORY_NAME}:latest" >> $GITHUB_ENV
215219
echo "SHOULD_PUSH=${SHOULD_PUSH}" >> $GITHUB_ENV
220+
echo "PROMOTE_LATEST=${PROMOTE_LATEST}" >> $GITHUB_ENV
216221
217222
- name: Validate agent image
218223
if: env.SKIP_VALIDATION != 'true'
@@ -287,6 +292,16 @@ jobs:
287292
288293
echo "✅ All validations passed for: $FULL_IMAGE"
289294
295+
- name: Promote validated image to :latest
296+
if: env.PROMOTE_LATEST == 'true'
297+
run: |
298+
echo "🏷️ Promoting validated ${{ env.FULL_IMAGE }} -> ${{ env.LATEST_IMAGE }}"
299+
# Registry-side manifest copy: no rebuild, preserves the multi-arch
300+
# manifest list, and only runs after validation passed — so :latest never
301+
# points at an unvalidated image.
302+
docker buildx imagetools create --tag "${{ env.LATEST_IMAGE }}" "${{ env.FULL_IMAGE }}"
303+
echo "✅ Promoted to ${{ env.LATEST_IMAGE }}"
304+
290305
deprecate-agents:
291306
name: "Deprecate Removed Agents"
292307
runs-on: ubuntu-latest

0 commit comments

Comments
 (0)