diff --git a/.github/.env b/.github/.env index cc47acd96..23ea90212 100644 --- a/.github/.env +++ b/.github/.env @@ -1,4 +1,5 @@ ACR_LOGIN_SERVER=acrsswwebsite.azurecr.io +ACR_NAME=acrsswwebsite APP_SERVICE_NAME=app-sswwebsite-9eb3 AZURE_RESOURCE_GROUP=ssw.com.au AZURE_RESOURCE_GROUP_LOCATION=australiaeast diff --git a/.github/workflows/pr-close-delete-env.yml b/.github/workflows/pr-close-delete-env.yml index 8477efb37..10ed5c24d 100644 --- a/.github/workflows/pr-close-delete-env.yml +++ b/.github/workflows/pr-close-delete-env.yml @@ -33,15 +33,13 @@ jobs: outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - - name: Converting slots into JSON Formatted Array + - name: Get slot ids from input id: set-matrix run: | - # TODO: Tech Debt - need to use it as JSON instead of space delimited - Github issue - https://github.com/SSWConsulting/SSW.Website/issues/1228 - $slotIDs = '${{ env.SLOT_NAME }}' -split ' ' - $slotIDsInJSonArray = ConvertTo-Json -Compress @($slotIDs) - echo "matrix=$slotIDsInJSonArray" >> $env:GITHUB_OUTPUT + $slotIDs = '${{ env.SLOT_NAME }}' + echo "matrix=$slotIDs" >> $env:GITHUB_OUTPUT - delete-slot: + delete-slot-and-acr-cleanup: runs-on: ubuntu-latest needs: setting-up-slot-ids strategy: @@ -63,7 +61,7 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - name: Cleanup ACR Pull & Key Vault permission + - name: Cleanup ACR and Key Vault Permissions run: | $acrId = az acr show ` --resource-group ${{ env.AZURE_RESOURCE_GROUP }} ` @@ -109,7 +107,15 @@ jobs: Write-Host '❌ Key Vault not found' } - - name: Delete slot on staging site + - name: ACR - Delete image + run: | + $imageTagWithPrefix = '${{ env.SLOT_PREFIX }}${{ matrix.SLOT_NAME}}' + + az acr repository delete --name ${{ env.ACR_NAME }} ` + --image ${{ env.IMAGE_NAME }}:$imageTagWithPrefix --yes + Write-Output "✅ ACR - ${{ env.IMAGE_NAME }}:$imageTagWithPrefix image deleted successfully." + + - name: Delete slot run: | az webapp deployment slot delete ` --resource-group ${{ env.AZURE_RESOURCE_GROUP }} ` diff --git a/.github/workflows/template-build.yml b/.github/workflows/template-build.yml index e09d4fc0f..2a12d09b8 100644 --- a/.github/workflows/template-build.yml +++ b/.github/workflows/template-build.yml @@ -123,5 +123,4 @@ jobs: NEXT_PUBLIC_CHATBASE_BOT_ID=${{ env.NEXT_PUBLIC_CHATBASE_BOT_ID }} SITE_URL=https://www.ssw.com.au tags: | - ${{ env.ACR_LOGIN_SERVER }}/${{ env.IMAGE_NAME }}:${{ github.sha }} ${{ env.ACR_LOGIN_SERVER }}/${{ env.IMAGE_NAME }}:${{ inputs.tag }} diff --git a/.github/workflows/template-delete-acr-image.yml b/.github/workflows/template-delete-acr-image.yml new file mode 100644 index 000000000..efb791bb9 --- /dev/null +++ b/.github/workflows/template-delete-acr-image.yml @@ -0,0 +1,72 @@ +name: Delete - Docker image from ACR + +on: + workflow_call: + inputs: + imageTags: + type: string + description: "Image Tags or PR numbers" + required: true + workflow_dispatch: + inputs: + imageTag: + description: "Image Tag or PR number" + required: true + +defaults: + run: + shell: pwsh + +env: + IMAGE_TAGS: ${{ inputs.imageTags || inputs.imageTag }} + PREFIX: pr- +permissions: + id-token: write + contents: read + +jobs: + setting-up-img-tags: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Get image tags from input + id: set-matrix + run: | + $imageTags = '${{ env.IMAGE_TAGS }}' + echo "matrix=$imageTags" >> $env:GITHUB_OUTPUT + + delete-acr-image: + runs-on: ubuntu-latest + needs: setting-up-img-tags + strategy: + matrix: + IMAGE_TAG: ${{ fromJson(needs.setting-up-img-tags.outputs.matrix) }} + + steps: + - uses: actions/checkout@v4 + + - name: Load .env file + uses: xom9ikk/dotenv@v2 + with: + path: ./.github + + - name: Azure CLI - Login + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: ACR - Delete image + run: | + if('${{ matrix.IMAGE_TAG }}'.Length -le 4) # PR tags consist of 4 digits (i.e pr-xxxx) + { + $imageTagWithPrefix = '${{ env.PREFIX }}${{ matrix.IMAGE_TAG}}' + }else{ + $imageTagWithPrefix = '${{ matrix.IMAGE_TAG }}' + } + + az acr repository delete --name ${{ env.ACR_NAME }} ` + --image ${{ env.IMAGE_NAME }}:$imageTagWithPrefix --yes + Write-Output "✅ ACR - ${{ env.IMAGE_NAME }}:$imageTagWithPrefix image deleted successfully." diff --git a/.github/workflows/weekly-acr-images-cleanup.yml b/.github/workflows/weekly-acr-images-cleanup.yml new file mode 100644 index 000000000..f28b49ff1 --- /dev/null +++ b/.github/workflows/weekly-acr-images-cleanup.yml @@ -0,0 +1,103 @@ +name: Weekly ACR images cleanup + +on: + schedule: + # Monday at 2 PM UTC - https://cron.help/#0_14_*_*_MON + - cron: "0 14 * * MON" + workflow_dispatch: + +env: + GH_TOKEN: ${{ github.token }} + +defaults: + run: + shell: pwsh + +permissions: + id-token: write + contents: read + +jobs: + check-acr-images: + runs-on: ubuntu-latest + outputs: + imageTagList: ${{ steps.comparison.outputs.imageTagList }} + steps: + - name: Checking out + uses: actions/checkout@v4 + + - name: Load .env file + uses: xom9ikk/dotenv@v2 + with: + path: ./.github + + - name: Azure CLI - Login + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Delete all untagged images + run: | + $untaggedDigests = az acr manifest list-metadata -r ${{ env.ACR_NAME }} -n ` + ${{ env.IMAGE_NAME }} --query "[?tags==null].digest" -o tsv | ` + %{ az acr repository delete -n ${{ env.ACR_NAME }} -t ${{ env.IMAGE_NAME }}@$_ --yes} + Write-Host "✅ All untagged images have been deleted" + + - name: Get list of active PRs + id: prList + run: | + $active_prs=$(gh pr list --state open --json number | jq -r '.[].number') + echo "active_prs=$active_prs" >> $env:GITHUB_OUTPUT + + - name: Get list of ACR image tags + id: imageTags + run: | + $images = az acr repository show-tags ` + --name ${{ env.ACR_NAME }} --repository ${{ env.IMAGE_NAME }} ` + --top 250 --orderby time_asc --output tsv # Limiting to 250 tags because of the GitHub action matrix limit + + # Filter tags that start with "pr-" + $filteredTags = $images | Where-Object { $_ -like "pr-*" } + + # Remove the "pr-" prefix from filtered tags + $filteredTagsWithoutPrefix = $filteredTags -replace "^pr-", "" + + echo "filteredTags=$filteredTagsWithoutPrefix" >> $env:GITHUB_OUTPUT + + - name: Compare active PRs with existing ACR images + id: comparison + run: | + # Comparing the number of images and PRs + $prList = "${{ steps.PRList.outputs.active_prs }}" -split ' ' + $imageTags = "${{ steps.imageTags.outputs.filteredTags }}" -split ' ' + + $imagesExistThatRequireDeletion = $imageTags | Where-Object { $_ -notin $prList } + $imagesNeedDeletion = $imagesExistThatRequireDeletion.Length -gt 0 + + if ( ! $imagesNeedDeletion ) { + echo "✅ - Number of docker images are equal to number of active PRs - 🏃 Skipping next step" + } + else { + echo "❌ - Number of docker images are not equal to number of active PRs" + Write-Host "⚡- These images need to be deleted : $imagesExistThatRequireDeletion" + } + # Convert string into Array + $tags = $imagesExistThatRequireDeletion -split ' ' + $imageTagList = ConvertTo-Json -Compress @($tags) + + echo "imageTagList=$imageTagList" >> $env:GITHUB_OUTPUT + + invokeDeleteImage: + name: Invoking delete-acr-image + needs: + - check-acr-images #Adding second check to avoid running this flow + if: needs.check-acr-images.outputs.imageTagList != '[]' + uses: ./.github/workflows/template-delete-acr-image.yml + with: + imageTags: ${{ needs.check-acr-images.outputs.imageTagList }} + permissions: + id-token: write + contents: read + secrets: inherit diff --git a/.github/workflows/weekly-slots-cleanup.yml b/.github/workflows/weekly-slots-cleanup.yml index 70023b9c0..1cb603354 100644 --- a/.github/workflows/weekly-slots-cleanup.yml +++ b/.github/workflows/weekly-slots-cleanup.yml @@ -21,7 +21,7 @@ jobs: check-pr-slots: runs-on: ubuntu-latest outputs: - slotsExistThatRequireDeletion: ${{ steps.comparision.outputs.slotsExistThatRequireDeletion }} + slotList: ${{ steps.comparision.outputs.slotList }} steps: - name: Checking out @@ -74,16 +74,20 @@ jobs: echo "❌ - Number of slots are not equal to number of active PRs" Write-Host "⚡- These slots need to be deleted : $slotsExistThatRequireDeletion" } - echo "slotsExistThatRequireDeletion=$slotsExistThatRequireDeletion" >> $env:GITHUB_OUTPUT + # Convert string into Array + $slots = $slotsExistThatRequireDeletion -split ' ' + $slotList = ConvertTo-Json -Compress @($slots) + + echo "slotList=$slotList" >> $env:GITHUB_OUTPUT invokeDeleteSlot: name: Invoking PR Close/Delete needs: - check-pr-slots #Adding second check to avoid running this flow - if: needs.check-pr-slots.outputs.slotsExistThatRequireDeletion != '' + if: needs.check-pr-slots.outputs.slotList != '[]' uses: ./.github/workflows/pr-close-delete-env.yml with: - slotIDs: ${{ needs.check-pr-slots.outputs.slotsExistThatRequireDeletion }} + slotIDs: ${{ needs.check-pr-slots.outputs.slotList }} permissions: id-token: write contents: read