diff --git a/.github/scripts/promote-app-image.sh b/.github/scripts/promote-app-image.sh index 39cb88f23ff..86cb8a6d77d 100644 --- a/.github/scripts/promote-app-image.sh +++ b/.github/scripts/promote-app-image.sh @@ -5,11 +5,12 @@ REGISTRY="${1:?registry required}" REPOSITORY="${2:?repository required}" SOURCE_TAG="${3:?source tag required}" DEPLOY_TAG="${4:?deploy tag required}" +RELEASE_TAG="${5:-}" : "${GITHUB_OUTPUT:?GitHub output file required}" SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) PREVIOUS=$(bash "$SCRIPT_DIR/get-ecr-image-digest.sh" "$REPOSITORY" "$DEPLOY_TAG" --allow-missing) EPOCH=$(date +%s) -docker buildx imagetools create -t "$REGISTRY/$REPOSITORY:$DEPLOY_TAG" "$REGISTRY/$REPOSITORY:$SOURCE_TAG" +bash "$SCRIPT_DIR/promote-image.sh" "$REGISTRY" "$REPOSITORY" "$SOURCE_TAG" "$DEPLOY_TAG" "$RELEASE_TAG" DIGEST=$(bash "$SCRIPT_DIR/get-ecr-image-digest.sh" "$REPOSITORY" "$DEPLOY_TAG") CHANGED=true if [ "$DIGEST" = "$PREVIOUS" ]; then CHANGED=false; fi diff --git a/.github/scripts/promote-image.sh b/.github/scripts/promote-image.sh new file mode 100755 index 00000000000..b3fc6525bce --- /dev/null +++ b/.github/scripts/promote-image.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# Point a deploy tag (and, for production, its release- alias) at a built image. +# One imagetools call writes both tags, so they always share one list digest. +set -euo pipefail +REGISTRY="${1:?registry required}" +REPOSITORY="${2:?repository required}" +SOURCE_TAG="${3:?source tag required}" +DEPLOY_TAG="${4:?deploy tag required}" +RELEASE_TAG="${5:-}" +TAG_ARGS=(-t "$REGISTRY/$REPOSITORY:$DEPLOY_TAG") +if [ -n "$RELEASE_TAG" ]; then TAG_ARGS+=(-t "$REGISTRY/$REPOSITORY:$RELEASE_TAG"); fi +docker buildx imagetools create "${TAG_ARGS[@]}" "$REGISTRY/$REPOSITORY:$SOURCE_TAG" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5de7794781..6482b395ea2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -520,8 +520,11 @@ jobs: run: | REGISTRY="${{ steps.login-ecr.outputs.registry }}" + RELEASE_TAG="" if [ "${{ github.ref }}" = "refs/heads/main" ]; then ECR_TAG="latest" + # ECR lifecycle retains release- aliases as prod rollback targets. + RELEASE_TAG="release-${SOURCE_TAG}" elif [ "${{ github.ref }}" = "refs/heads/dev" ]; then ECR_TAG="dev" else @@ -540,13 +543,11 @@ jobs: # Move the app last so a preceding tag failure cannot start app rollout # and then skip the downstream Trigger promotion job. for repo in $ECR_REPOS; do - echo "🚀 Promoting ${repo}:${SOURCE_TAG} to ${ECR_TAG}" + echo "🚀 Promoting ${repo}:${SOURCE_TAG} to ${ECR_TAG}${RELEASE_TAG:+ and ${RELEASE_TAG}}" if [ "$repo" = "$APP_REPO" ]; then - bash .github/scripts/promote-app-image.sh "$REGISTRY" "$APP_REPO" "$SOURCE_TAG" "$ECR_TAG" + bash .github/scripts/promote-app-image.sh "$REGISTRY" "$APP_REPO" "$SOURCE_TAG" "$ECR_TAG" "$RELEASE_TAG" else - docker buildx imagetools create \ - -t "${REGISTRY}/${repo}:${ECR_TAG}" \ - "${REGISTRY}/${repo}:${SOURCE_TAG}" + bash .github/scripts/promote-image.sh "$REGISTRY" "$repo" "$SOURCE_TAG" "$ECR_TAG" "$RELEASE_TAG" fi done