From bc8c647928f3ec15d869f35342bd11263501d52b Mon Sep 17 00:00:00 2001 From: Kirill Plis Date: Mon, 17 Nov 2025 12:32:16 +0100 Subject: [PATCH 1/9] feat: preserve preview images for the preview PR lifetime --- ...e-to-ecr.yaml => preview.build-image.yaml} | 17 ++++++-- .github/workflows/preview.remove-tag.yaml | 40 +++++++++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) rename .github/workflows/{build-and-push-image-to-ecr.yaml => preview.build-image.yaml} (75%) create mode 100644 .github/workflows/preview.remove-tag.yaml diff --git a/.github/workflows/build-and-push-image-to-ecr.yaml b/.github/workflows/preview.build-image.yaml similarity index 75% rename from .github/workflows/build-and-push-image-to-ecr.yaml rename to .github/workflows/preview.build-image.yaml index 5704a32..b777bf3 100644 --- a/.github/workflows/build-and-push-image-to-ecr.yaml +++ b/.github/workflows/preview.build-image.yaml @@ -52,9 +52,18 @@ jobs: role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} - name: Create ECR repository if it doesn't exist run: | - aws ecr describe-repositories --repository-names ${{ inputs.APPLICATION_NAME }} || \ - aws ecr create-repository --repository-name ${{ inputs.APPLICATION_NAME }} - LIFECYCLE_POLICY='{"rules":[{"rulePriority":1,"description":"Keep last 500 images","selection":{"tagStatus":"any","countType":"imageCountMoreThan","countNumber":500},"action":{"type":"expire"}}]}' + if ! aws ecr describe-repositories --repository-names ${{ inputs.APPLICATION_NAME }} 2>/dev/null; then + echo "Repository ${{ inputs.APPLICATION_NAME }} does not exist, creating it..." + aws ecr create-repository --repository-name ${{ inputs.APPLICATION_NAME }} + echo "Setting lifecycle policy..." + else + echo "Repository ${{ inputs.APPLICATION_NAME }} already exists, skipping creation" + fi + + LIFECYCLE_POLICY='{"rules":[ + {"rulePriority":1,"description":"Protect preview images from deletion","selection":{"tagStatus":"preview-*","countType":"imageCountMoreThan","countNumber":5000},"action":{"type":"expire"}}, + {"rulePriority":2,"description":"Keep last 500 images","selection":{"tagStatus":"any","countType":"imageCountMoreThan","countNumber":500},"action":{"type":"expire"}} + ]}' aws ecr put-lifecycle-policy --repository-name ${{ inputs.APPLICATION_NAME }} --lifecycle-policy-text "$LIFECYCLE_POLICY" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 @@ -70,7 +79,7 @@ jobs: provenance: false push: true tags: | - ${{ steps.login-ecr.outputs.registry }}/${{ inputs.APPLICATION_NAME }}:preview + ${{ steps.login-ecr.outputs.registry }}/${{ inputs.APPLICATION_NAME }}:preview-${{ github.event.pull_request.number }} ${{ steps.login-ecr.outputs.registry }}/${{ inputs.APPLICATION_NAME }}:${{ github.event.pull_request.head.sha }} comment-pr: if: ${{ inputs.GHA_TRIGGER_EVENT != 'synchronize' }} diff --git a/.github/workflows/preview.remove-tag.yaml b/.github/workflows/preview.remove-tag.yaml new file mode 100644 index 0000000..d9aea40 --- /dev/null +++ b/.github/workflows/preview.remove-tag.yaml @@ -0,0 +1,40 @@ +name: Remove preview tag from ECR + +on: + workflow_call: + inputs: + APPLICATION_NAME: + description: The name of the application + required: true + type: string + secrets: + AWS_ROLE_TO_ASSUME: + required: true + description: AWS OIDC role for GitHub to assume + +jobs: + remove-preview-tag: + permissions: + id-token: write + contents: read + runs-on: ubuntu-latest + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-region: eu-central-1 + role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + - name: Remove preview tag from ECR + run: | + # Check if repository exists + if aws ecr describe-repositories --repository-names ${{ inputs.APPLICATION_NAME }} 2>/dev/null; then + echo "Repository ${{ inputs.APPLICATION_NAME }} exists, attempting to remove preview-${{ github.event.pull_request.number }} tag..." + + # Remove the preview tag + aws ecr batch-delete-image \ + --repository-name ${{ inputs.APPLICATION_NAME }} \ + --image-ids imageTag=preview-${{ github.event.pull_request.number }} || \ + echo "Tag preview-${{ github.event.pull_request.number }} not found or already removed" + else + echo "Repository ${{ inputs.APPLICATION_NAME }} does not exist, nothing to remove" + fi From 655096cc3d0896c2ed4636888a5bd5668a362076 Mon Sep 17 00:00:00 2001 From: Kirill Plis Date: Tue, 18 Nov 2025 08:21:41 +0100 Subject: [PATCH 2/9] fix the release workflow --- .github/workflows/ci.release.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.release.yaml b/.github/workflows/ci.release.yaml index bf40b4e..f0014de 100644 --- a/.github/workflows/ci.release.yaml +++ b/.github/workflows/ci.release.yaml @@ -1,14 +1,12 @@ -name: Github workflows +name: Update major tag and release on: push: branches: - main paths: - ".github/workflows/*.yaml" - pull_request: - paths: - - ".github/workflows/*.yaml" workflow_dispatch: + jobs: update-major-tag: runs-on: ubuntu-latest From 577031477b99b47ffba602d9df860a4bfd7c9249 Mon Sep 17 00:00:00 2001 From: Kirill Plis Date: Tue, 18 Nov 2025 09:32:01 +0100 Subject: [PATCH 3/9] fix policy --- .github/workflows/preview.build-image.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/preview.build-image.yaml b/.github/workflows/preview.build-image.yaml index b777bf3..1e551b2 100644 --- a/.github/workflows/preview.build-image.yaml +++ b/.github/workflows/preview.build-image.yaml @@ -61,8 +61,8 @@ jobs: fi LIFECYCLE_POLICY='{"rules":[ - {"rulePriority":1,"description":"Protect preview images from deletion","selection":{"tagStatus":"preview-*","countType":"imageCountMoreThan","countNumber":5000},"action":{"type":"expire"}}, - {"rulePriority":2,"description":"Keep last 500 images","selection":{"tagStatus":"any","countType":"imageCountMoreThan","countNumber":500},"action":{"type":"expire"}} + {"rulePriority":1,"description":"Protect preview images from deletion","selection":{"tagStatus":"tagged","countType":"imageCountMoreThan","countNumber":5000},"action":{"type":"expire"}}, + {"rulePriority":2,"description":"Keep last 500 images","selection":{"tagStatus":"any","tagPatternList":"preview-*","countType":"imageCountMoreThan","countNumber":500},"action":{"type":"expire"}} ]}' aws ecr put-lifecycle-policy --repository-name ${{ inputs.APPLICATION_NAME }} --lifecycle-policy-text "$LIFECYCLE_POLICY" - name: Set up Docker Buildx From 1f57fdb840ee50b91a731c4282a901cfe4628623 Mon Sep 17 00:00:00 2001 From: Kirill Plis Date: Tue, 18 Nov 2025 09:45:17 +0100 Subject: [PATCH 4/9] use list --- .github/workflows/preview.build-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preview.build-image.yaml b/.github/workflows/preview.build-image.yaml index 1e551b2..ba7830c 100644 --- a/.github/workflows/preview.build-image.yaml +++ b/.github/workflows/preview.build-image.yaml @@ -62,7 +62,7 @@ jobs: LIFECYCLE_POLICY='{"rules":[ {"rulePriority":1,"description":"Protect preview images from deletion","selection":{"tagStatus":"tagged","countType":"imageCountMoreThan","countNumber":5000},"action":{"type":"expire"}}, - {"rulePriority":2,"description":"Keep last 500 images","selection":{"tagStatus":"any","tagPatternList":"preview-*","countType":"imageCountMoreThan","countNumber":500},"action":{"type":"expire"}} + {"rulePriority":2,"description":"Keep last 500 images","selection":{"tagStatus":"any","tagPatternList":["preview-*"],"countType":"imageCountMoreThan","countNumber":500},"action":{"type":"expire"}} ]}' aws ecr put-lifecycle-policy --repository-name ${{ inputs.APPLICATION_NAME }} --lifecycle-policy-text "$LIFECYCLE_POLICY" - name: Set up Docker Buildx From e59cdf2923d189c09d040df74d9869fbe385f34a Mon Sep 17 00:00:00 2001 From: Kirill Plis Date: Tue, 18 Nov 2025 10:03:14 +0100 Subject: [PATCH 5/9] add tagPrefixList --- .github/workflows/preview.build-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preview.build-image.yaml b/.github/workflows/preview.build-image.yaml index ba7830c..62447b8 100644 --- a/.github/workflows/preview.build-image.yaml +++ b/.github/workflows/preview.build-image.yaml @@ -61,7 +61,7 @@ jobs: fi LIFECYCLE_POLICY='{"rules":[ - {"rulePriority":1,"description":"Protect preview images from deletion","selection":{"tagStatus":"tagged","countType":"imageCountMoreThan","countNumber":5000},"action":{"type":"expire"}}, + {"rulePriority":1,"description":"Protect preview images from deletion","selection":{"tagStatus":"tagged","tagPrefixList":[""],"countType":"imageCountMoreThan","countNumber":5000},"action":{"type":"expire"}}, {"rulePriority":2,"description":"Keep last 500 images","selection":{"tagStatus":"any","tagPatternList":["preview-*"],"countType":"imageCountMoreThan","countNumber":500},"action":{"type":"expire"}} ]}' aws ecr put-lifecycle-policy --repository-name ${{ inputs.APPLICATION_NAME }} --lifecycle-policy-text "$LIFECYCLE_POLICY" From f2183b57eb644e7cd01799ae97d52e946780f28d Mon Sep 17 00:00:00 2001 From: Kirill Plis Date: Tue, 18 Nov 2025 10:16:36 +0100 Subject: [PATCH 6/9] fix policy --- .github/workflows/preview.build-image.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/preview.build-image.yaml b/.github/workflows/preview.build-image.yaml index 62447b8..4898ab6 100644 --- a/.github/workflows/preview.build-image.yaml +++ b/.github/workflows/preview.build-image.yaml @@ -60,9 +60,9 @@ jobs: echo "Repository ${{ inputs.APPLICATION_NAME }} already exists, skipping creation" fi - LIFECYCLE_POLICY='{"rules":[ - {"rulePriority":1,"description":"Protect preview images from deletion","selection":{"tagStatus":"tagged","tagPrefixList":[""],"countType":"imageCountMoreThan","countNumber":5000},"action":{"type":"expire"}}, - {"rulePriority":2,"description":"Keep last 500 images","selection":{"tagStatus":"any","tagPatternList":["preview-*"],"countType":"imageCountMoreThan","countNumber":500},"action":{"type":"expire"}} + LIFECYCLE_POLICY='='{"rules":[ + {"rulePriority":1,"description":"Protect preview images from deletion","selection":{"tagStatus":"tagged","tagPatternList":["preview-*"],"countType":"imageCountMoreThan","countNumber":5000},"action":{"type":"expire"}}, + {"rulePriority":2,"description":"Keep last 500 images","selection":{"tagStatus":"any","countType":"imageCountMoreThan","countNumber":500},"action":{"type":"expire"}} ]}' aws ecr put-lifecycle-policy --repository-name ${{ inputs.APPLICATION_NAME }} --lifecycle-policy-text "$LIFECYCLE_POLICY" - name: Set up Docker Buildx From b61790a5d70b574c6926e88937bbffd40f2bcb18 Mon Sep 17 00:00:00 2001 From: Kirill Plis Date: Tue, 18 Nov 2025 10:26:17 +0100 Subject: [PATCH 7/9] remove duplicate = --- .github/workflows/preview.build-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preview.build-image.yaml b/.github/workflows/preview.build-image.yaml index 4898ab6..436817a 100644 --- a/.github/workflows/preview.build-image.yaml +++ b/.github/workflows/preview.build-image.yaml @@ -60,7 +60,7 @@ jobs: echo "Repository ${{ inputs.APPLICATION_NAME }} already exists, skipping creation" fi - LIFECYCLE_POLICY='='{"rules":[ + LIFECYCLE_POLICY='{"rules":[ {"rulePriority":1,"description":"Protect preview images from deletion","selection":{"tagStatus":"tagged","tagPatternList":["preview-*"],"countType":"imageCountMoreThan","countNumber":5000},"action":{"type":"expire"}}, {"rulePriority":2,"description":"Keep last 500 images","selection":{"tagStatus":"any","countType":"imageCountMoreThan","countNumber":500},"action":{"type":"expire"}} ]}' From 376b2d988c0dfe91bb2cd7d53fb0eceba80c9906 Mon Sep 17 00:00:00 2001 From: Kirill Plis Date: Wed, 19 Nov 2025 10:06:23 +0100 Subject: [PATCH 8/9] set preview image expiration to 365 days --- .github/workflows/preview.build-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preview.build-image.yaml b/.github/workflows/preview.build-image.yaml index 436817a..b336a39 100644 --- a/.github/workflows/preview.build-image.yaml +++ b/.github/workflows/preview.build-image.yaml @@ -61,7 +61,7 @@ jobs: fi LIFECYCLE_POLICY='{"rules":[ - {"rulePriority":1,"description":"Protect preview images from deletion","selection":{"tagStatus":"tagged","tagPatternList":["preview-*"],"countType":"imageCountMoreThan","countNumber":5000},"action":{"type":"expire"}}, + {"rulePriority":1,"description":"Protect preview images from deletion","selection":{"tagStatus":"tagged","tagPatternList":["preview-*"],"countType":"imageCountMoreThan","countNumber":365},"action":{"type":"expire"}}, {"rulePriority":2,"description":"Keep last 500 images","selection":{"tagStatus":"any","countType":"imageCountMoreThan","countNumber":500},"action":{"type":"expire"}} ]}' aws ecr put-lifecycle-policy --repository-name ${{ inputs.APPLICATION_NAME }} --lifecycle-policy-text "$LIFECYCLE_POLICY" From bd7535d3909c3bd73b6e7e6afbd916d48164f17c Mon Sep 17 00:00:00 2001 From: Kirill Plis Date: Wed, 19 Nov 2025 12:12:17 +0100 Subject: [PATCH 9/9] use latest policies --- .github/workflows/preview.build-image.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/preview.build-image.yaml b/.github/workflows/preview.build-image.yaml index b336a39..aa07910 100644 --- a/.github/workflows/preview.build-image.yaml +++ b/.github/workflows/preview.build-image.yaml @@ -60,9 +60,11 @@ jobs: echo "Repository ${{ inputs.APPLICATION_NAME }} already exists, skipping creation" fi + echo "Applying lifecycle policies" LIFECYCLE_POLICY='{"rules":[ - {"rulePriority":1,"description":"Protect preview images from deletion","selection":{"tagStatus":"tagged","tagPatternList":["preview-*"],"countType":"imageCountMoreThan","countNumber":365},"action":{"type":"expire"}}, - {"rulePriority":2,"description":"Keep last 500 images","selection":{"tagStatus":"any","countType":"imageCountMoreThan","countNumber":500},"action":{"type":"expire"}} + {"rulePriority":1,"description":"Preserve preview images","selection":{"tagStatus":"tagged","tagPatternList":["preview-*"],"countType":"sinceImagePushed","countNumber":365},"action":{"type":"expire"}}, + {"rulePriority":2,"description":"Preserve production images","selection":{"tagStatus":"tagged","tagPatternList":["v*"],"countType":"imageCountMoreThan","countNumber":50},"action":{"type":"expire"}} + {"rulePriority":3,"description":"Remove untagged images","selection":{"tagStatus":"untagged","countType":"sinceImagePushed","countNumber":7},"action":{"type":"expire"}} ]}' aws ecr put-lifecycle-policy --repository-name ${{ inputs.APPLICATION_NAME }} --lifecycle-policy-text "$LIFECYCLE_POLICY" - name: Set up Docker Buildx