From c72a500f49a7db8f34aa911d134657e511a76cea Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Wed, 3 Jun 2026 10:00:43 +0200 Subject: [PATCH 1/5] Delete previous ECR digest after overwrite_only_existing push When overwriting an existing tag, ECR leaves the old manifest untagged. Remove it with batch-delete-image so hourly mirrors do not accumulate storage. --- src/jobs/mirror_to_ecr.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/jobs/mirror_to_ecr.yml b/src/jobs/mirror_to_ecr.yml index 2941da1..3a4d212 100644 --- a/src/jobs/mirror_to_ecr.yml +++ b/src/jobs/mirror_to_ecr.yml @@ -20,7 +20,7 @@ parameters: type: boolean default: false overwrite_only_existing: - description: If true then overwrite the image only when the target tag already exists in ECR. The job fails if the tag is missing. + description: If true then overwrite the image only when the target tag already exists in ECR. The job fails if the tag is missing. After overwrite, the previous image digest is deleted from ECR. type: boolean default: false aws_profile: @@ -70,9 +70,16 @@ steps: MIRROR="${MIRROR_NAME}:${MIRROR_TAG}" echo "Mirroring ${SRC} to ${ECR_URL}/${MIRROR}..." - REPO_DATA=$(aws ecr describe-images --repository-name=${MIRROR_NAME} --image-ids=imageTag=${MIRROR_TAG} ||: ) + OLD_IMAGE_DIGEST=$(aws ecr describe-images --repository-name=${MIRROR_NAME} --image-ids=imageTag=${MIRROR_TAG} --query 'imageDetails[0].imageDigest' --output text 2>/dev/null ||: ) + IMAGE_EXISTS=false + if [[ -n "${OLD_IMAGE_DIGEST}" && "${OLD_IMAGE_DIGEST}" != "None" ]] ; then + IMAGE_EXISTS=true + if ! << parameters.overwrite_only_existing >> ; then + OLD_IMAGE_DIGEST="" + fi + fi SHOULD_MIRROR=false - if [[ -z $REPO_DATA ]] ; then + if [[ "${IMAGE_EXISTS}" == "false" ]] ; then if << parameters.overwrite_only_existing >> ; then echo "Mirror target ${MIRROR} does not exist in ECR and overwrite_only_existing is enabled." exit 1 @@ -86,6 +93,10 @@ steps: docker pull ${SRC} docker tag ${SRC} ${ECR_URL}/${MIRROR} docker push ${ECR_URL}/${MIRROR} + if [[ -n "${OLD_IMAGE_DIGEST}" ]] ; then + echo "Deleting previous untagged image ${OLD_IMAGE_DIGEST} from ECR..." + aws ecr batch-delete-image --repository-name=${MIRROR_NAME} --image-ids imageDigest=${OLD_IMAGE_DIGEST} + fi else echo "${MIRROR} image is already in ECR" fi From 3ac8c4796d3f26a4376b0a4ce74ad4313ee7c121 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Wed, 3 Jun 2026 10:04:28 +0200 Subject: [PATCH 2/5] refactor: simplify mirror_to_ecr bash decision logic Collapse IMAGE_EXISTS/SHOULD_MIRROR into direct conditions without changing mirror, skip, fail, or digest-delete behavior. --- src/jobs/mirror_to_ecr.yml | 40 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/src/jobs/mirror_to_ecr.yml b/src/jobs/mirror_to_ecr.yml index 3a4d212..b1ad19d 100644 --- a/src/jobs/mirror_to_ecr.yml +++ b/src/jobs/mirror_to_ecr.yml @@ -45,13 +45,13 @@ steps: cmd_name: Mirroring image to ECR repository deploy: true cmd: | - echo "Loging in to Dockerhub..." + echo "Logging in to Dockerhub..." echo $DOCKER_PASS | docker login -u "$DOCKER_USER" --password-stdin export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) export ECR_URL=${ACCOUNT_ID}.dkr.ecr.<< parameters.region >>.amazonaws.com export ECR_PASSWORD=$(aws ecr get-login-password --region << parameters.region >>) - echo "Loging in to ECR: ${ECR_URL}" + echo "Logging in to ECR: ${ECR_URL}" echo ${ECR_PASSWORD} | docker login -u AWS --password-stdin ${ECR_URL} SRC_NAME="<< parameters.source_name >>" @@ -59,41 +59,25 @@ steps: SRC="${SRC_NAME}:${SRC_TAG}" MIRROR_NAME="<< parameters.mirror_name >>" - if [[ -z ${MIRROR_NAME} ]] ; then - MIRROR_NAME="${SRC_NAME}" - fi - + MIRROR_NAME="${MIRROR_NAME:-${SRC_NAME}}" MIRROR_TAG="<< parameters.mirror_tag >>" - if [[ -z ${MIRROR_TAG} ]] ; then - MIRROR_TAG="${SRC_TAG}" - fi + MIRROR_TAG="${MIRROR_TAG:-${SRC_TAG}}" MIRROR="${MIRROR_NAME}:${MIRROR_TAG}" echo "Mirroring ${SRC} to ${ECR_URL}/${MIRROR}..." - OLD_IMAGE_DIGEST=$(aws ecr describe-images --repository-name=${MIRROR_NAME} --image-ids=imageTag=${MIRROR_TAG} --query 'imageDetails[0].imageDigest' --output text 2>/dev/null ||: ) - IMAGE_EXISTS=false - if [[ -n "${OLD_IMAGE_DIGEST}" && "${OLD_IMAGE_DIGEST}" != "None" ]] ; then - IMAGE_EXISTS=true - if ! << parameters.overwrite_only_existing >> ; then - OLD_IMAGE_DIGEST="" - fi - fi - SHOULD_MIRROR=false - if [[ "${IMAGE_EXISTS}" == "false" ]] ; then - if << parameters.overwrite_only_existing >> ; then - echo "Mirror target ${MIRROR} does not exist in ECR and overwrite_only_existing is enabled." - exit 1 - fi - SHOULD_MIRROR=true - elif << parameters.force >> || << parameters.overwrite_only_existing >> ; then - SHOULD_MIRROR=true + OLD_IMAGE_DIGEST=$(aws ecr describe-images --repository-name=${MIRROR_NAME} --image-ids=imageTag=${MIRROR_TAG} --query 'imageDetails[0].imageDigest' --output text 2>/dev/null || true) + [[ -z "${OLD_IMAGE_DIGEST}" || "${OLD_IMAGE_DIGEST}" == "None" ]] && OLD_IMAGE_DIGEST="" + + if << parameters.overwrite_only_existing >> && [[ -z "${OLD_IMAGE_DIGEST}" ]]; then + echo "Mirror target ${MIRROR} does not exist in ECR and overwrite_only_existing is enabled." + exit 1 fi - if [[ "${SHOULD_MIRROR}" == "true" ]] ; then + if [[ -z "${OLD_IMAGE_DIGEST}" ]] || << parameters.force >> || << parameters.overwrite_only_existing >>; then docker pull ${SRC} docker tag ${SRC} ${ECR_URL}/${MIRROR} docker push ${ECR_URL}/${MIRROR} - if [[ -n "${OLD_IMAGE_DIGEST}" ]] ; then + if << parameters.overwrite_only_existing >> && [[ -n "${OLD_IMAGE_DIGEST}" ]]; then echo "Deleting previous untagged image ${OLD_IMAGE_DIGEST} from ECR..." aws ecr batch-delete-image --repository-name=${MIRROR_NAME} --image-ids imageDigest=${OLD_IMAGE_DIGEST} fi From 4205c06ea907e48364205e6d42bc367f3a927647 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Wed, 3 Jun 2026 10:05:35 +0200 Subject: [PATCH 3/5] fix: skip ECR digest deletion when mirror content is unchanged After push, compare the new image digest with the pre-push digest and only batch-delete the old digest when they differ, avoiding removal of the image that still carries the tag. --- src/jobs/mirror_to_ecr.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/jobs/mirror_to_ecr.yml b/src/jobs/mirror_to_ecr.yml index b1ad19d..17676b2 100644 --- a/src/jobs/mirror_to_ecr.yml +++ b/src/jobs/mirror_to_ecr.yml @@ -78,8 +78,14 @@ steps: docker tag ${SRC} ${ECR_URL}/${MIRROR} docker push ${ECR_URL}/${MIRROR} if << parameters.overwrite_only_existing >> && [[ -n "${OLD_IMAGE_DIGEST}" ]]; then - echo "Deleting previous untagged image ${OLD_IMAGE_DIGEST} from ECR..." - aws ecr batch-delete-image --repository-name=${MIRROR_NAME} --image-ids imageDigest=${OLD_IMAGE_DIGEST} + NEW_IMAGE_DIGEST=$(aws ecr describe-images --repository-name=${MIRROR_NAME} --image-ids=imageTag=${MIRROR_TAG} --query 'imageDetails[0].imageDigest' --output text 2>/dev/null || true) + [[ -z "${NEW_IMAGE_DIGEST}" || "${NEW_IMAGE_DIGEST}" == "None" ]] && NEW_IMAGE_DIGEST="" + if [[ -n "${NEW_IMAGE_DIGEST}" ]] && [[ "${OLD_IMAGE_DIGEST}" != "${NEW_IMAGE_DIGEST}" ]]; then + echo "Deleting previous untagged image ${OLD_IMAGE_DIGEST} from ECR..." + aws ecr batch-delete-image --repository-name=${MIRROR_NAME} --image-ids imageDigest=${OLD_IMAGE_DIGEST} + else + echo "Image digest unchanged (${OLD_IMAGE_DIGEST}); skipping deletion." + fi fi else echo "${MIRROR} image is already in ECR" From 7fd078311dfcd52a7fd1d5b5478d2e89caab0ba6 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Wed, 3 Jun 2026 11:04:32 +0200 Subject: [PATCH 4/5] fix: delete all untagged ECR images after overwrite Replace digest-targeted deletion with list-images tagStatus=UNTAGGED cleanup so shared-tag digests are never removed unintentionally. --- src/jobs/mirror_to_ecr.yml | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/jobs/mirror_to_ecr.yml b/src/jobs/mirror_to_ecr.yml index 17676b2..2b8e887 100644 --- a/src/jobs/mirror_to_ecr.yml +++ b/src/jobs/mirror_to_ecr.yml @@ -20,7 +20,7 @@ parameters: type: boolean default: false overwrite_only_existing: - description: If true then overwrite the image only when the target tag already exists in ECR. The job fails if the tag is missing. After overwrite, the previous image digest is deleted from ECR. + description: If true then overwrite the image only when the target tag already exists in ECR. The job fails if the tag is missing. After overwrite, untagged images in the repository are deleted from ECR. type: boolean default: false aws_profile: @@ -77,14 +77,21 @@ steps: docker pull ${SRC} docker tag ${SRC} ${ECR_URL}/${MIRROR} docker push ${ECR_URL}/${MIRROR} - if << parameters.overwrite_only_existing >> && [[ -n "${OLD_IMAGE_DIGEST}" ]]; then - NEW_IMAGE_DIGEST=$(aws ecr describe-images --repository-name=${MIRROR_NAME} --image-ids=imageTag=${MIRROR_TAG} --query 'imageDetails[0].imageDigest' --output text 2>/dev/null || true) - [[ -z "${NEW_IMAGE_DIGEST}" || "${NEW_IMAGE_DIGEST}" == "None" ]] && NEW_IMAGE_DIGEST="" - if [[ -n "${NEW_IMAGE_DIGEST}" ]] && [[ "${OLD_IMAGE_DIGEST}" != "${NEW_IMAGE_DIGEST}" ]]; then - echo "Deleting previous untagged image ${OLD_IMAGE_DIGEST} from ECR..." - aws ecr batch-delete-image --repository-name=${MIRROR_NAME} --image-ids imageDigest=${OLD_IMAGE_DIGEST} + if << parameters.overwrite_only_existing >>; then + UNTAGGED_DIGESTS=$(aws ecr list-images \ + --repository-name="${MIRROR_NAME}" \ + --filter tagStatus=UNTAGGED \ + --query 'imageIds[*].imageDigest' \ + --output text 2>/dev/null || true) + if [[ -n "${UNTAGGED_DIGESTS}" && "${UNTAGGED_DIGESTS}" != "None" ]]; then + echo "Deleting untagged images from ${MIRROR_NAME}..." + IMAGE_IDS=() + for digest in ${UNTAGGED_DIGESTS}; do + IMAGE_IDS+=("imageDigest=${digest}") + done + aws ecr batch-delete-image --repository-name="${MIRROR_NAME}" --image-ids "${IMAGE_IDS[@]}" else - echo "Image digest unchanged (${OLD_IMAGE_DIGEST}); skipping deletion." + echo "No untagged images to delete." fi fi else From eb736fabbab995c16bf55898e04384a2f39abf5f Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Wed, 3 Jun 2026 12:00:50 +0200 Subject: [PATCH 5/5] fix: quote ECR describe-images variables in mirror_to_ecr --- src/jobs/mirror_to_ecr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jobs/mirror_to_ecr.yml b/src/jobs/mirror_to_ecr.yml index 2b8e887..3c88dd6 100644 --- a/src/jobs/mirror_to_ecr.yml +++ b/src/jobs/mirror_to_ecr.yml @@ -65,7 +65,7 @@ steps: MIRROR="${MIRROR_NAME}:${MIRROR_TAG}" echo "Mirroring ${SRC} to ${ECR_URL}/${MIRROR}..." - OLD_IMAGE_DIGEST=$(aws ecr describe-images --repository-name=${MIRROR_NAME} --image-ids=imageTag=${MIRROR_TAG} --query 'imageDetails[0].imageDigest' --output text 2>/dev/null || true) + OLD_IMAGE_DIGEST=$(aws ecr describe-images --repository-name="${MIRROR_NAME}" --image-ids=imageTag="${MIRROR_TAG}" --query 'imageDetails[0].imageDigest' --output text 2>/dev/null || true) [[ -z "${OLD_IMAGE_DIGEST}" || "${OLD_IMAGE_DIGEST}" == "None" ]] && OLD_IMAGE_DIGEST="" if << parameters.overwrite_only_existing >> && [[ -z "${OLD_IMAGE_DIGEST}" ]]; then