Skip to content

Commit a7c3c71

Browse files
authored
Merge pull request #5360 from nhsuk/next
Version 6.3.0
2 parents 6ddfab6 + 129d559 commit a7c3c71

File tree

103 files changed

+4488
-536
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+4488
-536
lines changed

.github/send_slack_notification.sh

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env bash
2+
3+
# Script to send Slack notification about migrations running
4+
# Usage: send_slack_notification.sh <slack_webhook_url> <aws_console_url> <message>
5+
6+
slack_webhook_url="$1"
7+
aws_console_url="$2"
8+
message="$3"
9+
10+
if [[ -z "$slack_webhook_url" ]]; then
11+
echo "Error: Slack webhook URL not provided" >&2
12+
exit 1
13+
fi
14+
15+
if [[ -z "$aws_console_url" ]]; then
16+
echo "Error: AWS console URL not provided" >&2
17+
exit 1
18+
fi
19+
20+
if [[ -z "$message" ]]; then
21+
echo "Error: Message not provided" >&2
22+
exit 1
23+
fi
24+
25+
curl -X POST "$slack_webhook_url" \
26+
-H 'Content-Type: application/json' \
27+
-d @- <<EOF
28+
{
29+
"text": "${message} :gear:",
30+
"blocks": [
31+
{
32+
"type": "section",
33+
"text": {
34+
"type": "mrkdwn",
35+
"text": "${message} :gear:"
36+
}
37+
},
38+
{
39+
"type": "section",
40+
"text": {
41+
"type": "mrkdwn",
42+
"text": "<${aws_console_url}|View live logs in AWS Console>"
43+
}
44+
}
45+
]
46+
}
47+
EOF
48+
49+
if [[ $? -eq 0 ]]; then
50+
echo "Slack notification sent successfully"
51+
else
52+
echo "Failed to send Slack notification" >&2
53+
fi

.github/workflows/deploy-application.yml

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,36 @@ env:
6363
|| 'arn:aws:iam::393416225559:role/GithubDeployECSService' }}
6464
aws_account_id: ${{ inputs.environment == 'production' && '820242920762' || '393416225559' }}
6565
cluster_name: mavis-${{ inputs.environment }}
66+
git_ref_to_deploy: ${{ inputs.git_ref_to_deploy }}
6667
app_version: ${{ inputs.app_version || inputs.git_ref_to_deploy || 'unknown' }}
6768

6869
concurrency:
6970
group: deploy-application-${{ inputs.environment }}
7071

7172
jobs:
73+
notify-deployment-start:
74+
if: ${{ inputs.environment == 'production' }}
75+
runs-on: ubuntu-latest
76+
steps:
77+
- name: Notify deployment start
78+
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a
79+
with:
80+
webhook: ${{ secrets.SLACK_MAVIS_RELEASES_WEBHOOK_URL }}
81+
webhook-type: incoming-webhook
82+
payload: |
83+
text: "Deployment started :rocket:"
84+
blocks:
85+
- type: "section"
86+
text:
87+
type: "mrkdwn"
88+
text: "Deployment of *${{ env.app_version }}* started"
89+
- type: "section"
90+
fields:
91+
- type: "mrkdwn"
92+
text: "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View workflow run>"
93+
- type: "mrkdwn"
94+
text: "*Triggered by:*\n${{ github.actor }}"
95+
7296
determine-git-sha:
7397
runs-on: ubuntu-latest
7498
permissions: { }
@@ -142,17 +166,145 @@ jobs:
142166
name: ${{ inputs.environment }}-${{ matrix.service }}-task-definition
143167
path: ${{ runner.temp }}/${{ matrix.service }}-task-definition.json
144168

169+
notify-approval-required:
170+
name: Notify approval required
171+
if: ${{ inputs.environment == 'production' }}
172+
runs-on: ubuntu-latest
173+
needs: prepare-deployment
174+
steps:
175+
- name: Notify approval required
176+
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a
177+
with:
178+
webhook: ${{ secrets.SLACK_MAVIS_RELEASES_WEBHOOK_URL }}
179+
webhook-type: incoming-webhook
180+
payload: |
181+
text: ":hourglass: Approval required :hourglass:"
182+
blocks:
183+
- type: "section"
184+
text:
185+
type: "mrkdwn"
186+
text: "${{ github.workflow }} requires approval"
187+
- type: "section"
188+
fields:
189+
- type: "mrkdwn"
190+
text: "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View workflow run>"
191+
145192
approve-deployments:
146193
name: Wait for approval if required
147194
runs-on: ubuntu-latest
148195
needs: prepare-deployment
149196
environment: ${{ inputs.environment }}
150197
steps:
151198
- run: echo "Proceeding with deployment to $environment"
199+
200+
run-migrations:
201+
name: Run migrations
202+
runs-on: ubuntu-latest
203+
needs: [ approve-deployments ]
204+
if: ${{ inputs.server_types == 'all' || inputs.server_types == 'ops' }}
205+
permissions:
206+
id-token: write
207+
steps:
208+
- name: Checkout code
209+
uses: actions/checkout@v6
210+
- name: Configure AWS Credentials
211+
uses: aws-actions/configure-aws-credentials@v5
212+
with:
213+
role-to-assume: ${{ env.aws_role }}
214+
aws-region: eu-west-2
215+
- name: Download ops task definition artifact
216+
uses: actions/download-artifact@v6
217+
with:
218+
path: ${{ runner.temp }}
219+
name: ${{ inputs.environment }}-ops-task-definition
220+
- name: Register migration task definition
221+
id: register-migration-task-definition
222+
run: |
223+
family_name="mavis-migration-task-definition-$environment"
224+
file_path="${{ runner.temp }}/migration-task-definition.json"
225+
echo "$(jq --arg f "$family_name" '.family = $f' "${{ runner.temp }}/ops-task-definition.json")" > "$file_path"
226+
task_definition_arn=$(aws ecs register-task-definition \
227+
--cli-input-json file://$file_path \
228+
--query 'taskDefinition.taskDefinitionArn' \
229+
--output text
230+
)
231+
echo "task_definition_arn=$task_definition_arn" >> $GITHUB_OUTPUT
232+
- name: Run schema migrations
233+
id: run-schema-migrations
234+
env:
235+
SLACK_MAVIS_RELEASES_WEBHOOK_URL: ${{ secrets.SLACK_MAVIS_RELEASES_WEBHOOK_URL }}
236+
run: |
237+
TASK_DEFINITION_ARN=${{ steps.register-migration-task-definition.outputs.task_definition_arn }}
238+
SUBNET_ID=$(aws ec2 describe-subnets --filters Name=tag:Name,Values=private-subnet-$environment-a --query 'Subnets[0].SubnetId' --output text)
239+
SECURITY_GROUP_ID=$(aws ec2 describe-security-groups --filters Name=group-name,Values=ops-service-$environment --query 'SecurityGroups[0].GroupId' --output text)
240+
241+
MAX_ATTEMPTS=3
242+
ATTEMPT=1
243+
244+
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
245+
TASK_ARN=$(aws ecs run-task \
246+
--cluster $cluster_name \
247+
--task-definition $TASK_DEFINITION_ARN \
248+
--launch-type FARGATE \
249+
--network-configuration "awsvpcConfiguration={subnets=[$SUBNET_ID],securityGroups=[$SECURITY_GROUP_ID]}" \
250+
--overrides '{
251+
"containerOverrides": [{
252+
"name": "application",
253+
"command": ["bin/rails","db:migrate"]
254+
}]
255+
}' \
256+
--query 'tasks[0].taskArn' \
257+
--output text)
258+
259+
echo "Waiting for task to complete: $TASK_ARN"
260+
TASK_ID=$(sed 's:^.*/::' <<< $TASK_ARN)
261+
AWS_CONSOLE_URL="https://eu-west-2.console.aws.amazon.com/ecs/v2/clusters/$cluster_name/tasks/$TASK_ID/logs"
262+
263+
echo "View logs in AWS Console: $AWS_CONSOLE_URL"
264+
if [ $environment = 'production' ]; then
265+
./.github/send_slack_notification.sh "${{ secrets.SLACK_MAVIS_RELEASES_WEBHOOK_URL }}" "$AWS_CONSOLE_URL" "Running schema migrations attempt $ATTEMPT/$MAX_ATTEMPTS"
266+
fi
267+
268+
aws ecs wait tasks-stopped \
269+
--cluster $cluster_name \
270+
--tasks $TASK_ID
271+
272+
EXIT_CODE=$(aws ecs describe-tasks \
273+
--cluster $cluster_name \
274+
--tasks $TASK_ARN \
275+
--query 'tasks[0].containers[0].exitCode' \
276+
--output text)
277+
278+
echo "Container exit code: $EXIT_CODE"
279+
280+
if [ "$EXIT_CODE" = "0" ]; then
281+
echo "Migrations completed"
282+
break
283+
else
284+
echo "ECS task failed with exit code: $EXIT_CODE"
285+
if [ "$ATTEMPT" = "$MAX_ATTEMPTS" ]; then
286+
exit 1
287+
fi
288+
ATTEMPT=$((ATTEMPT+1))
289+
fi
290+
done
291+
- name: Notify migrations completed
292+
if: ${{ env.environment == 'production' }}
293+
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a
294+
with:
295+
webhook: ${{ secrets.SLACK_MAVIS_RELEASES_WEBHOOK_URL }}
296+
webhook-type: incoming-webhook
297+
payload: |
298+
text: "Schema migrations finished successfully :white_check_mark:"
299+
blocks:
300+
- type: "section"
301+
text:
302+
type: "mrkdwn"
303+
text: "Schema migrations finished successfully :white_check_mark:"
152304
deploy-service:
153305
name: Deploy service
154306
runs-on: ubuntu-latest
155-
needs: [ prepare-deployment, approve-deployments ]
307+
needs: [ prepare-deployment, approve-deployments, run-migrations ]
156308
permissions:
157309
id-token: write
158310
strategy:
@@ -194,3 +346,41 @@ jobs:
194346
echo "Task definition arns don't match, likely due to a rollback to the previous version. Deployment failed."
195347
exit 1
196348
fi
349+
notify-deployment-complete:
350+
name: Notify deployment status
351+
runs-on: ubuntu-latest
352+
needs: [ deploy-service, run-migrations ]
353+
if: ${{ !cancelled() }}
354+
steps:
355+
- name: Notify deployment success
356+
if: ${{ env.environment == 'production' && needs.deploy-service.result == 'success' }}
357+
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a
358+
with:
359+
webhook: ${{ secrets.SLACK_MAVIS_RELEASES_WEBHOOK_URL }}
360+
webhook-type: incoming-webhook
361+
payload: |
362+
text: "*${{ env.app_version }}* is deployed :ship:"
363+
blocks:
364+
- type: "section"
365+
text:
366+
type: "mrkdwn"
367+
text: "*${{ env.app_version }}* is deployed :ship:"
368+
- name: Notify deployment failure
369+
if: ${{ env.environment == 'production' && (needs.deploy-service.result == 'failure' || needs.run-migrations.result == 'failure') }}
370+
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a
371+
with:
372+
webhook: ${{ secrets.SLACK_MAVIS_RELEASES_WEBHOOK_URL }}
373+
webhook-type: incoming-webhook
374+
payload: |
375+
text: "Deployment of *${{ env.app_version }}* failed :x:"
376+
blocks:
377+
- type: "section"
378+
text:
379+
type: "mrkdwn"
380+
text: "Deployment of *${{ env.app_version }}* failed :x:"
381+
- type: "section"
382+
fields:
383+
- type: "mrkdwn"
384+
text: "*Failed job:*\n${{ needs.deploy-service.result == 'failure' && 'deploy-service' || 'run-migrations' }}"
385+
- type: "mrkdwn"
386+
text: "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View workflow run>"

.github/workflows/test.yml

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ jobs:
4040
outputs:
4141
run-rails-tests: ${{ steps.check-rails-relevant-changes-failure.outputs.RUN_RAILS_TESTS }}
4242

43-
rails:
44-
name: Rails
43+
rspec:
44+
name: RSpec
4545
runs-on: ubuntu-latest
4646
needs: pre-rails-tests
4747
if: needs.pre-rails-tests.outputs.run-rails-tests == 'true'
@@ -71,15 +71,47 @@ jobs:
7171
- uses: ruby/setup-ruby@v1
7272
with:
7373
bundler-cache: true
74-
- name: Setup Database
74+
- name: Precompile assets
75+
run: bin/rails assets:precompile
76+
- name: Setup parallel database
7577
run: |
7678
bin/rails parallel:create
7779
bin/rails parallel:load_schema
78-
- name: Precompile assets
79-
run: bin/rails assets:precompile
80-
- name: Run rspec
80+
- name: Run tests
8181
run: bin/rails parallel:spec
8282

83+
seeds:
84+
name: Seeds
85+
runs-on: ubuntu-latest
86+
needs: pre-rails-tests
87+
if: needs.pre-rails-tests.outputs.run-rails-tests == 'true'
88+
services:
89+
postgres:
90+
image: postgres:17.2
91+
env:
92+
POSTGRES_PASSWORD: postgres
93+
options: >-
94+
--health-cmd pg_isready
95+
--health-interval 10s
96+
--health-timeout 5s
97+
--health-retries 5
98+
ports:
99+
- 5432:5432
100+
env:
101+
RAILS_ENV: development
102+
DATABASE_URL: postgres://postgres:postgres@localhost:5432/manage_vaccinations_development
103+
steps:
104+
- uses: actions/checkout@v6
105+
- uses: actions/setup-node@v6
106+
with:
107+
node-version-file: .tool-versions
108+
cache: yarn
109+
- uses: ruby/setup-ruby@v1
110+
with:
111+
bundler-cache: true
112+
- name: Check seeds run
113+
run: bin/rails db:prepare
114+
83115
jest:
84116
name: Jest
85117
runs-on: ubuntu-latest
@@ -93,7 +125,7 @@ jobs:
93125
- run: yarn test
94126

95127
terraform:
96-
name: Terraform Validate
128+
name: Terraform
97129
runs-on: ubuntu-latest
98130
defaults:
99131
run:

0 commit comments

Comments
 (0)