From f0af24db7015ae9df4bd046bbcc2a7109e32f3ff Mon Sep 17 00:00:00 2001 From: Tom Whitwell Date: Thu, 22 Jan 2026 11:25:15 +0000 Subject: [PATCH 1/3] Only run one review-apps workflow at a time This prevents us attempting to run multiple instance of terraform at the same time. Instead, the current running workflow will complete before the next one starts. Only 1 running and 1 pending workflow is allowed - any further workflows will supercede the pending one. --- .github/workflows/review_apps_on_pr_change.yml | 4 ++++ .github/workflows/review_apps_on_pr_close.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/review_apps_on_pr_change.yml b/.github/workflows/review_apps_on_pr_change.yml index 2ddb58a31..743fdf708 100644 --- a/.github/workflows/review_apps_on_pr_change.yml +++ b/.github/workflows/review_apps_on_pr_change.yml @@ -5,6 +5,10 @@ on: # matches the docs for the default types # https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request types: [opened, reopened, synchronize] + +concurrency: + group: "review-apps-forms-admin-pr-${{ github.event.pull_request.number }}" + cancel-in-progress: false jobs: update-review-app: # this references a codebuild project configured in forms-deploy diff --git a/.github/workflows/review_apps_on_pr_close.yml b/.github/workflows/review_apps_on_pr_close.yml index e0b49d516..700019957 100644 --- a/.github/workflows/review_apps_on_pr_close.yml +++ b/.github/workflows/review_apps_on_pr_close.yml @@ -3,6 +3,10 @@ on: pull_request: # only run when a PR is closed or merged types: [closed] + +concurrency: + group: "review-apps-forms-admin-pr-${{ github.event.pull_request.number }}" + cancel-in-progress: false env: IMAGE_TAG: "842676007477.dkr.ecr.eu-west-2.amazonaws.com/forms-admin:pr-${{github.event.pull_request.number}}-${{github.event.pull_request.head.ref}}" jobs: From 83cb6bef30a040a4475aaadbfd4202bd8ccb3243 Mon Sep 17 00:00:00 2001 From: Tom Whitwell Date: Thu, 19 Mar 2026 13:19:32 +0000 Subject: [PATCH 2/3] Deploy / destroy review apps with CodeBuild Instead of running Terraform directly in the GitHub Actions runners, we now trigger AWS CodeBuild projects to handle the deployment and destruction of review apps. This means that the repository no longer needs extensive AWS permissions in GitHub Actions, and the actual available AWS operations are limited. --- .../workflows/review_apps_on_pr_change.yml | 110 ++---------------- .github/workflows/review_apps_on_pr_close.yml | 43 ++----- 2 files changed, 22 insertions(+), 131 deletions(-) diff --git a/.github/workflows/review_apps_on_pr_change.yml b/.github/workflows/review_apps_on_pr_change.yml index 743fdf708..5d58d49a7 100644 --- a/.github/workflows/review_apps_on_pr_change.yml +++ b/.github/workflows/review_apps_on_pr_change.yml @@ -1,108 +1,20 @@ name: "Review apps: on PR change" on: pull_request: - # being explicit about what to trigger on. - # matches the docs for the default types - # https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request types: [opened, reopened, synchronize] concurrency: - group: "review-apps-forms-admin-pr-${{ github.event.pull_request.number }}" + group: "review-apps-pr-${{ github.event.pull_request.number }}" cancel-in-progress: false -jobs: - update-review-app: - # this references a codebuild project configured in forms-deploy - # see: https://docs.aws.amazon.com/codebuild/latest/userguide/action-runner.html - runs-on: codebuild-review-forms-admin-gha-runner-${{github.run_id}}-${{github.run_attempt}} - - permissions: - pull-requests: write - - steps: - - name: Generate container image URI - run: | - echo "CONTAINER_IMAGE_URI=842676007477.dkr.ecr.eu-west-2.amazonaws.com/forms-admin:pr-${{github.event.pull_request.number}}-${{github.event.pull_request.head.sha}}-$(date +%s)" >> "$GITHUB_ENV" - - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Build container - run: | - # Docker credentials are configured in CodeBuild - # CodeBuild retrieves the credentials from ParameterStore - echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin - docker build \ - --tag "${{env.CONTAINER_IMAGE_URI}}" \ - . - - - name: Push container - id: build-container - run: | - aws ecr get-login-password --region eu-west-2 \ - | docker login --username AWS --password-stdin 842676007477.dkr.ecr.eu-west-2.amazonaws.com - - echo "Pushing container image" - echo "${{env.CONTAINER_IMAGE_URI}}" - - docker push "${CONTAINER_IMAGE_URI}" - - - name: Determine Terraform version - id: terraform-version - run: | - TF_VERSION=$(< .review_apps/.terraform-version) - printf "TF_VERSION=%s\n" "$TF_VERSION" >> "$GITHUB_OUTPUT" - - - uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # v4.0.0 - with: - terraform_version: ${{steps.terraform-version.outputs.TF_VERSION}} - - name: Deploy review app - id: deploy - run: | - cd .review_apps/ +permissions: + id-token: write + contents: read + pull-requests: write - terraform init -backend-config="key=review-apps/forms-admin/pr-${{github.event.pull_request.number}}.tfstate" - - terraform apply \ - -var "pull_request_number=${{github.event.pull_request.number}}" \ - -var "forms_admin_container_image=${{env.CONTAINER_IMAGE_URI}}" \ - -no-color \ - -auto-approve - REVIEW_APP_URL=$(terraform output -raw review_app_url) - ECS_CLUSTER_ID=$(terraform output -raw review_app_ecs_cluster_id) - ECS_SERVICE_NAME=$(terraform output -raw review_app_ecs_service_name) - { - printf 'REVIEW_APP_URL=%s\n' "$REVIEW_APP_URL" - printf 'ECS_CLUSTER_ID=%s\n' "$ECS_CLUSTER_ID" - printf 'ECS_SERVICE_NAME=%s\n' "$ECS_SERVICE_NAME" - } >> "$GITHUB_OUTPUT" - - - name: Wait for AWS ECS deployments to finish - run: | - aws ecs wait services-stable \ - --cluster "${{steps.deploy.outputs.ECS_CLUSTER_ID}}" \ - --services "${{steps.deploy.outputs.ECS_SERVICE_NAME}}" - - - name: Comment on PR - env: - COMMENT_MARKER: - GH_TOKEN: ${{ github.token }} - run: | - cat < "${{runner.temp}}/pr-comment.md" - :tada: A review copy of this PR has been deployed! You can reach it at: ${{steps.deploy.outputs.REVIEW_APP_URL}} - - It may take 5 minutes or so for the application to be fully deployed and working. If it still isn't ready - after 5 minutes, there may be something wrong with the ECS task. You will need to go to the integration AWS account - to debug, or otherwise ask an infrastructure person. - - For the sign in details and more information, [see the review apps wiki page](https://github.com/alphagov/forms-team/wiki/Review-apps). - - $COMMENT_MARKER - EOF - - old_comment_ids=$(gh api "repos/{owner}/{repo}/issues/${{github.event.pull_request.number}}/comments" --jq "map(select((.user.login == \"github-actions[bot]\") and (.body | endswith(env.COMMENT_MARKER + \"\n\")))) | .[].id") - for comment_id in $old_comment_ids; do - gh api -X DELETE "repos/{owner}/{repo}/issues/comments/${comment_id}" - done - - gh pr comment "${{github.event.pull_request.html_url}}" --body-file "${{runner.temp}}/pr-comment.md" +jobs: + update-review-app: + name: Update review app + uses: alphagov/forms-deploy/.github/workflows/reusable-review_apps_on_pr_change.yml@main + with: + app-name: forms-admin diff --git a/.github/workflows/review_apps_on_pr_close.yml b/.github/workflows/review_apps_on_pr_close.yml index 700019957..f73baf21e 100644 --- a/.github/workflows/review_apps_on_pr_close.yml +++ b/.github/workflows/review_apps_on_pr_close.yml @@ -1,41 +1,20 @@ name: "Review apps: on PR close" on: pull_request: - # only run when a PR is closed or merged types: [closed] concurrency: - group: "review-apps-forms-admin-pr-${{ github.event.pull_request.number }}" + group: "review-apps-pr-${{ github.event.pull_request.number }}" cancel-in-progress: false -env: - IMAGE_TAG: "842676007477.dkr.ecr.eu-west-2.amazonaws.com/forms-admin:pr-${{github.event.pull_request.number}}-${{github.event.pull_request.head.ref}}" -jobs: - delete-review-app: - # this references a codebuild project configured in forms-deploy - # see: https://docs.aws.amazon.com/codebuild/latest/userguide/action-runner.html - runs-on: codebuild-review-forms-admin-gha-runner-${{github.run_id}}-${{github.run_attempt}} - - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Determine Terraform version - id: terraform-version - run: | - TF_VERSION=$(< .review_apps/.terraform-version) - printf "TF_VERSION=%s\n" "$TF_VERSION" >> "$GITHUB_OUTPUT" - - uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # v4.0.0 - with: - terraform_version: ${{steps.terraform-version.outputs.TF_VERSION}} +permissions: + id-token: write + contents: read + pull-requests: write - - name: Delete review app - run: | - cd .review_apps/ - - terraform init -backend-config="key=review-apps/forms-admin/pr-${{github.event.pull_request.number}}.tfstate" - terraform destroy \ - -var "pull_request_number=${{github.event.pull_request.number}}" \ - -var "forms_admin_container_image=${{env.IMAGE_TAG}}" \ - -no-color \ - -auto-approve +jobs: + delete-review-app: + name: Delete review app + uses: alphagov/forms-deploy/.github/workflows/reusable-review_apps_on_pr_close.yml@main + with: + app-name: forms-admin From 10c16464df45a909a9b9fdf8202756393b34706d Mon Sep 17 00:00:00 2001 From: Tom Whitwell Date: Thu, 19 Mar 2026 13:28:28 +0000 Subject: [PATCH 3/3] BAU: add missing fields to the review app task definition These fields are automatically added by AWS when creating a task definition. If we don't include them in our task definition, Terraform tries to remove them on every apply, which causes unnecessary changes to the task definition (and thus noise in our Terraform plan output). --- .review_apps/ecs_task_definition.tf | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/.review_apps/ecs_task_definition.tf b/.review_apps/ecs_task_definition.tf index 4ac7d1624..8a3434c2c 100644 --- a/.review_apps/ecs_task_definition.tf +++ b/.review_apps/ecs_task_definition.tf @@ -70,11 +70,16 @@ resource "aws_ecs_task_definition" "task" { portMappings = [ { containerPort = 3000 + hostPort = 3000 protocol = "tcp" appProtocol = "http" } ] + mountPoints = [] + systemControls = [] + volumesFrom = [] + logConfiguration = { logDriver = "awslogs" options = { @@ -89,6 +94,7 @@ resource "aws_ecs_task_definition" "task" { interval = 30 retries = 5 startPeriod = 180 + timeout = 5 } dependsOn = [ @@ -106,7 +112,17 @@ resource "aws_ecs_task_definition" "task" { command = [] essential = true - portMappings = [{ containerPort = 5432 }] + portMappings = [ + { + containerPort = 5432 + hostPort = 5432 + protocol = "tcp" + } + ] + + mountPoints = [] + systemControls = [] + volumesFrom = [] environment = [ { name = "POSTGRES_PASSWORD", value = "postgres" } @@ -122,7 +138,10 @@ resource "aws_ecs_task_definition" "task" { } healthCheck = { - command = ["CMD-SHELL", "psql -h localhost -p 5432 -U postgres -c \"SELECT current_timestamp - pg_postmaster_start_time();\""] + command = ["CMD-SHELL", "psql -h localhost -p 5432 -U postgres -c \"SELECT current_timestamp - pg_postmaster_start_time();\""] + interval = 30 + retries = 3 + timeout = 5 } }, @@ -135,6 +154,10 @@ resource "aws_ecs_task_definition" "task" { environment = local.forms_admin_env_vars readonlyRootFilesystem = true + mountPoints = [] + systemControls = [] + volumesFrom = [] + logConfiguration = { logDriver = "awslogs" options = {