Skip to content

Commit cc3feb8

Browse files
Replace deploy-application code
- Use new flow - Intermediary step for testing process
1 parent 5620df4 commit cc3feb8

File tree

6 files changed

+325
-382
lines changed

6 files changed

+325
-382
lines changed

.github/workflows/deploy-application.yml

Lines changed: 211 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ on:
2525
- web
2626
- good-job
2727
default: all
28+
git_sha_to_deploy:
29+
description: The git commit SHA to deploy.
30+
required: false
31+
type: string
2832
workflow_call:
2933
inputs:
3034
environment:
@@ -38,15 +42,21 @@ on:
3842
required: true
3943
type: string
4044

41-
permissions: {}
45+
permissions: { }
4246

4347
concurrency:
44-
group: deploy-application-${{ inputs.environment }}
48+
group: deploy-mavis-${{ inputs.environment }}
4549

4650
env:
4751
aws-role: ${{ inputs.environment == 'production'
4852
&& 'arn:aws:iam::820242920762:role/GithubDeployMavisAndInfrastructure'
4953
|| 'arn:aws:iam::393416225559:role/GithubDeployMavisAndInfrastructure' }}
54+
web_codedeploy_application: mavis-${{ inputs.environment }}
55+
web_codedeploy_group: blue-green-group-${{ inputs.environment }}
56+
web_task_definition: mavis-web-task-definition-${{ inputs.environment }}
57+
cluster_name: mavis-${{ inputs.environment }}
58+
good_job_service: mavis-${{ inputs.environment }}-good-job
59+
good_job_task_definition: mavis-good-job-task-definition-${{ inputs.environment }}
5060

5161
jobs:
5262
prepare-deployment:
@@ -65,102 +75,244 @@ jobs:
6575
with:
6676
role-to-assume: ${{ env.aws-role }}
6777
aws-region: eu-west-2
68-
- name: Install terraform
69-
uses: hashicorp/setup-terraform@v3
70-
with:
71-
terraform_version: 1.11.4
72-
- name: Get terraform output
73-
id: terraform-output
74-
working-directory: terraform/app
78+
- name: Get image digest from ECR
79+
id: get-image-digest
80+
run: |
81+
# Get AWS account ID and construct repository URI
82+
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
83+
REPOSITORY_URI="${AWS_ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com/mavis"
84+
85+
# Get the image digest for the git SHA
86+
IMAGE_DIGEST=$(aws ecr describe-images \
87+
--repository-name mavis/webapp \
88+
--image-ids imageTag=${{ inputs.git_sha_to_deploy || github.sha }} \
89+
--query 'imageDetails[0].imageDigest' \
90+
--output text)
91+
92+
NEW_IMAGE_URI="${REPOSITORY_URI}@${IMAGE_DIGEST}"
93+
echo "new-image-uri=${NEW_IMAGE_URI}" >> $GITHUB_OUTPUT
94+
echo "New image URI: ${NEW_IMAGE_URI}"
95+
- name: Get dynamic variables for web
96+
id: get-dynamic-vars-web
97+
run: |
98+
# Fetch SSM parameter for web service
99+
SSM_OUTPUT=$(aws ssm get-parameter --name /${{ inputs.environment }}/ecs/web/container_variables --query Parameter.Value --output text)
100+
101+
# Extract environment variables in the format expected by amazon-ecs-render-task-definition
102+
ENV_VARS=$(echo "$SSM_OUTPUT" | jq -r '.task_envs | map("\(.name): \(.value)") | join("\n")')
103+
104+
# Extract secrets in the format expected by amazon-ecs-render-task-definition
105+
SECRETS=$(echo "$SSM_OUTPUT" | jq -r '.task_secrets | map("\(.name): \(.valueFrom)") | join("\n")')
106+
107+
# Set outputs using multiline format
108+
echo "environment-variables<<EOF" >> $GITHUB_OUTPUT
109+
echo "$ENV_VARS" >> $GITHUB_OUTPUT
110+
echo "EOF" >> $GITHUB_OUTPUT
111+
112+
echo "secrets<<EOF" >> $GITHUB_OUTPUT
113+
echo "$SECRETS" >> $GITHUB_OUTPUT
114+
echo "EOF" >> $GITHUB_OUTPUT
115+
116+
# Also extract role ARNs for potential future use
117+
EXECUTION_ROLE_ARN=$(echo "$SSM_OUTPUT" | jq -r '.execution_role_arn')
118+
TASK_ROLE_ARN=$(echo "$SSM_OUTPUT" | jq -r '.task_role_arn')
119+
120+
echo "execution-role-arn=$EXECUTION_ROLE_ARN" >> $GITHUB_OUTPUT
121+
echo "task-role-arn=$TASK_ROLE_ARN" >> $GITHUB_OUTPUT
122+
123+
echo "Environment variables:"
124+
echo "$ENV_VARS"
125+
echo ""
126+
echo "Secrets:"
127+
echo "$SECRETS"
128+
- name: Get dynamic variables for good-job
129+
id: get-dynamic-vars-good-job
130+
if: inputs.server_types == 'good-job' || inputs.server_types == 'all'
131+
run: |
132+
# Fetch SSM parameter for good-job service
133+
SSM_OUTPUT=$(aws ssm get-parameter --name /${{ inputs.environment }}/ecs/good-job/container_variables --query Parameter.Value --output text)
134+
135+
# Extract environment variables in the format expected by amazon-ecs-render-task-definition
136+
ENV_VARS=$(echo "$SSM_OUTPUT" | jq -r '.task_envs | map("\(.name): \(.value)") | join("\n")')
137+
138+
# Extract secrets in the format expected by amazon-ecs-render-task-definition
139+
SECRETS=$(echo "$SSM_OUTPUT" | jq -r '.task_secrets | map("\(.name): \(.valueFrom)") | join("\n")')
140+
141+
# Set outputs using multiline format
142+
echo "environment-variables<<EOF" >> $GITHUB_OUTPUT
143+
echo "$ENV_VARS" >> $GITHUB_OUTPUT
144+
echo "EOF" >> $GITHUB_OUTPUT
145+
146+
echo "secrets<<EOF" >> $GITHUB_OUTPUT
147+
echo "$SECRETS" >> $GITHUB_OUTPUT
148+
echo "EOF" >> $GITHUB_OUTPUT
149+
150+
# Also extract role ARNs for potential future use
151+
EXECUTION_ROLE_ARN=$(echo "$SSM_OUTPUT" | jq -r '.execution_role_arn')
152+
TASK_ROLE_ARN=$(echo "$SSM_OUTPUT" | jq -r '.task_role_arn')
153+
154+
echo "execution-role-arn=$EXECUTION_ROLE_ARN" >> $GITHUB_OUTPUT
155+
echo "task-role-arn=$TASK_ROLE_ARN" >> $GITHUB_OUTPUT
156+
157+
echo "Environment variables:"
158+
echo "$ENV_VARS"
159+
echo ""
160+
echo "Secrets:"
161+
echo "$SECRETS"
162+
- name: Create web task definition template
163+
if: inputs.server_types == 'web' || inputs.server_types == 'all'
164+
run: |
165+
# Create a web-specific task definition template
166+
cp config/task-definition.json.tpl web-task-definition.json.tpl
167+
sed -i "s/<SERVER_TYPE>/web/g" web-task-definition.json.tpl
168+
sed -i "s/<ENV>/${{ inputs.environment }}/g" web-task-definition.json.tpl
169+
- name: Create good-job task definition template
170+
if: inputs.server_types == 'good-job' || inputs.server_types == 'all'
75171
run: |
76-
set -e
77-
terraform init -backend-config=env/${{ inputs.environment }}-backend.hcl -reconfigure
78-
terraform output -json | jq -r '
79-
"s3_bucket=" + .s3_bucket.value,
80-
"s3_key=" + .s3_key.value,
81-
"application=" + .codedeploy_application_name.value,
82-
"application_group=" + .codedeploy_deployment_group_name.value,
83-
"cluster_name=" + .ecs_variables.value.cluster_name,
84-
"good_job_service=" + .ecs_variables.value.good_job.service_name,
85-
"good_job_task_definition=" + .ecs_variables.value.good_job.task_definition.arn
86-
' > ${{ runner.temp }}/DEPLOYMENT_ENVS
87-
- name: Upload Artifact
172+
# Create a good-job-specific task definition template
173+
cp config/task-definition.json.tpl good-job-task-definition.json.tpl
174+
sed -i "s/<SERVER_TYPE>/good-job/g" good-job-task-definition.json.tpl
175+
sed -i "s/<ENV>/${{ inputs.environment }}/g" good-job-task-definition.json.tpl
176+
- name: Render web task definition
177+
if: inputs.server_types == 'web' || inputs.server_types == 'all'
178+
id: render-web-task-definition
179+
uses: aws-actions/amazon-ecs-render-task-definition@v1
180+
with:
181+
task-definition: web-task-definition.json.tpl
182+
container-name: application
183+
image: ${{ steps.get-image-digest.outputs.new-image-uri }}
184+
environment-variables: ${{ steps.get-dynamic-vars-web.outputs.environment-variables }}
185+
secrets: ${{ steps.get-dynamic-vars-web.outputs.secrets }}
186+
- name: Render good-job task definition
187+
if: inputs.server_types == 'good-job' || inputs.server_types == 'all'
188+
id: render-good-job-task-definition
189+
uses: aws-actions/amazon-ecs-render-task-definition@v1
190+
with:
191+
task-definition: good-job-task-definition.json.tpl
192+
container-name: application
193+
image: ${{ steps.get-image-digest.outputs.new-image-uri }}
194+
environment-variables: ${{ steps.get-dynamic-vars-good-job.outputs.environment-variables }}
195+
secrets: ${{ steps.get-dynamic-vars-good-job.outputs.secrets }}
196+
- name: Upload web task definition artifact
197+
if: inputs.server_types == 'web' || inputs.server_types == 'all'
198+
uses: actions/upload-artifact@v4
199+
with:
200+
name: web-task-definition
201+
path: ${{ steps.render-web-task-definition.outputs.task-definition }}
202+
retention-days: 1
203+
- name: Upload good-job task definition artifact
204+
if: inputs.server_types == 'good-job' || inputs.server_types == 'all'
88205
uses: actions/upload-artifact@v4
89206
with:
90-
name: DEPLOYMENT_ENVS-${{ inputs.environment }}
91-
path: ${{ runner.temp }}/DEPLOYMENT_ENVS
207+
name: good-job-task-definition
208+
path: ${{ steps.render-good-job-task-definition.outputs.task-definition }}
209+
retention-days: 1
210+
outputs:
211+
new-image-uri: ${{ steps.get-image-digest.outputs.new-image-uri }}
212+
web-environment-variables: ${{ steps.get-dynamic-vars-web.outputs.environment-variables }}
213+
web-secrets: ${{ steps.get-dynamic-vars-web.outputs.secrets }}
214+
web-execution-role-arn: ${{ steps.get-dynamic-vars-web.outputs.execution-role-arn }}
215+
web-task-role-arn: ${{ steps.get-dynamic-vars-web.outputs.task-role-arn }}
216+
good-job-environment-variables: ${{ steps.get-dynamic-vars-good-job.outputs.environment-variables }}
217+
good-job-secrets: ${{ steps.get-dynamic-vars-good-job.outputs.secrets }}
218+
good-job-execution-role-arn: ${{ steps.get-dynamic-vars-good-job.outputs.execution-role-arn }}
219+
good-job-task-role-arn: ${{ steps.get-dynamic-vars-good-job.outputs.task-role-arn }}
92220

93-
create-web-deployment:
94-
name: Create web deployment
221+
deploy-web:
222+
name: Deploy web service
95223
runs-on: ubuntu-latest
96-
needs: prepare-deployment
97224
if: inputs.server_types == 'web' || inputs.server_types == 'all'
225+
needs: prepare-deployment
226+
environment: ${{ inputs.environment }}
98227
permissions:
99228
id-token: write
100229
steps:
101-
- name: Download artifact
102-
uses: actions/download-artifact@v4
103-
with:
104-
name: DEPLOYMENT_ENVS-${{ inputs.environment }}
105-
path: ${{ runner.temp }}
106230
- name: Configure AWS Credentials
107231
uses: aws-actions/configure-aws-credentials@v4
108232
with:
109233
role-to-assume: ${{ env.aws-role }}
110234
aws-region: eu-west-2
111-
- name: Trigger CodeDeploy deployment
235+
- name: Download web task definition artifact
236+
uses: actions/download-artifact@v4
237+
with:
238+
name: web-task-definition
239+
path: ./artifacts
240+
- name: Register new task definition
241+
run: |
242+
# Find the task definition file in the artifacts directory
243+
TASK_DEF_FILE=$(find ./artifacts -name "*.json" -type f | head -1)
244+
aws ecs register-task-definition --cli-input-json file://$TASK_DEF_FILE
245+
- name: Deploy with CodeDeploy
112246
run: |
113-
set -e
114-
source ${{ runner.temp }}/DEPLOYMENT_ENVS
247+
# Create CodeDeploy deployment for blue-green deployment
115248
deployment_id=$(aws deploy create-deployment \
116-
--application-name "$application" --deployment-group-name "$application_group" \
117-
--s3-location bucket="$s3_bucket",key="$s3_key",bundleType=yaml | jq -r .deploymentId)
118-
echo "Deployment started: $deployment_id"
249+
--application-name ${{ env.web_codedeploy_application }} \
250+
--deployment-group-name ${{ env.web_codedeploy_group }} \
251+
--deployment-config-name CodeDeployDefault.ECSBlueGreenCanary10Percent5Minutes \
252+
--description "Deployment from GitHub Actions" \
253+
| jq -r .deploymentId)
254+
255+
echo "CodeDeploy deployment started: $deployment_id"
119256
echo "deployment_id=$deployment_id" >> $GITHUB_ENV
120-
- name: Wait up to 30 minutes for deployment to complete
257+
- name: Wait for deployment to complete
121258
run: |
122-
set -e
259+
echo "Waiting for CodeDeploy deployment $deployment_id to complete..."
123260
aws deploy wait deployment-successful --deployment-id "$deployment_id"
124261
echo "Deployment successful"
125262
126-
create-good-job-deployment:
127-
name: Create good-job deployment
263+
deploy-good-job:
264+
name: Deploy good-job service
128265
runs-on: ubuntu-latest
129-
needs: prepare-deployment
130266
if: inputs.server_types == 'good-job' || inputs.server_types == 'all'
267+
needs: prepare-deployment
268+
environment: ${{ inputs.environment }}
131269
permissions:
132270
id-token: write
133271
steps:
134-
- name: Download Artifact
135-
uses: actions/download-artifact@v4
136-
with:
137-
name: DEPLOYMENT_ENVS-${{ inputs.environment }}
138-
path: ${{ runner.temp }}
139272
- name: Configure AWS Credentials
140273
uses: aws-actions/configure-aws-credentials@v4
141274
with:
142275
role-to-assume: ${{ env.aws-role }}
143276
aws-region: eu-west-2
144-
- name: Trigger ECS Deployment
277+
- name: Download good-job task definition artifact
278+
uses: actions/download-artifact@v4
279+
with:
280+
name: good-job-task-definition
281+
path: ./artifacts
282+
- name: Register new task definition
145283
run: |
146-
set -e
147-
source ${{ runner.temp }}/DEPLOYMENT_ENVS
148-
DEPLOYMENT_ID=$(aws ecs update-service --cluster $cluster_name --service $good_job_service \
149-
--task-definition $good_job_task_definition --force-new-deployment \
150-
--query 'service.deployments[?rolloutState==`IN_PROGRESS`].[id][0]' --output text)
151-
echo "Deployment started: $DEPLOYMENT_ID"
284+
# Find the task definition file in the artifacts directory
285+
TASK_DEF_FILE=$(find ./artifacts -name "*.json" -type f | head -1)
286+
TASK_DEFINITION_ARN=$(aws ecs register-task-definition --cli-input-json file://$TASK_DEF_FILE --query 'taskDefinition.taskDefinitionArn' --output text)
287+
echo "New task definition registered: $TASK_DEFINITION_ARN"
288+
echo "task_definition_arn=$TASK_DEFINITION_ARN" >> $GITHUB_ENV
289+
- name: Update ECS service
290+
run: |
291+
# Update the good-job service with the new task definition
292+
DEPLOYMENT_ID=$(aws ecs update-service \
293+
--cluster ${{ env.cluster_name }} \
294+
--service ${{ env.good_job_service }} \
295+
--task-definition "$task_definition_arn" \
296+
--force-new-deployment \
297+
--query 'service.deployments[?rolloutState==`IN_PROGRESS`].[id][0]' \
298+
--output text)
299+
300+
echo "ECS deployment started: $DEPLOYMENT_ID"
152301
echo "deployment_id=$DEPLOYMENT_ID" >> $GITHUB_ENV
153302
- name: Wait for deployment to complete
154303
run: |
155-
set -e
156-
source ${{ runner.temp }}/DEPLOYMENT_ENVS
304+
echo "Waiting for ECS deployment $deployment_id to complete..."
157305
DEPLOYMENT_STATE=IN_PROGRESS
158306
while [ "$DEPLOYMENT_STATE" == "IN_PROGRESS" ]; do
159-
echo "Waiting for deployment to complete..."
307+
echo "Checking deployment status..."
160308
sleep 30
161-
DEPLOYMENT_STATE="$(aws ecs describe-services --cluster $cluster_name --services $good_job_service \
162-
--query "services[0].deployments[?id == \`$deployment_id\`].[rolloutState][0]" --output text)"
309+
DEPLOYMENT_STATE=$(aws ecs describe-services \
310+
--cluster ${{ env.cluster_name }} \
311+
--services ${{ env.good_job_service }} \
312+
--query "services[0].deployments[?id == \`$deployment_id\`].[rolloutState][0]" \
313+
--output text)
163314
done
315+
164316
if [ "$DEPLOYMENT_STATE" != "COMPLETED" ]; then
165317
echo "Deployment failed with state: $DEPLOYMENT_STATE"
166318
exit 1

0 commit comments

Comments
 (0)