25
25
- web
26
26
- good-job
27
27
default : all
28
+ git_sha_to_deploy :
29
+ description : The git commit SHA to deploy.
30
+ required : false
31
+ type : string
28
32
workflow_call :
29
33
inputs :
30
34
environment :
38
42
required : true
39
43
type : string
40
44
41
- permissions : {}
45
+ permissions : { }
42
46
43
47
concurrency :
44
- group : deploy-application -${{ inputs.environment }}
48
+ group : deploy-mavis -${{ inputs.environment }}
45
49
46
50
env :
47
51
aws-role : ${{ inputs.environment == 'production'
48
52
&& 'arn:aws:iam::820242920762:role/GithubDeployMavisAndInfrastructure'
49
53
|| '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 }}
50
60
51
61
jobs :
52
62
prepare-deployment :
@@ -58,109 +68,234 @@ jobs:
58
68
steps :
59
69
- name : Checkout code
60
70
uses : actions/checkout@v4
61
- with :
62
- ref : ${{ inputs.git_sha_to_deploy || github.sha }}
63
71
- name : Configure AWS Credentials
64
72
uses : aws-actions/configure-aws-credentials@v4
65
73
with :
66
74
role-to-assume : ${{ env.aws-role }}
67
75
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
76
+ - name : Get image digest from ECR
77
+ id : get-image-digest
78
+ run : |
79
+ # Get AWS account ID and construct repository URI
80
+ AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
81
+ REPOSITORY_URI="${AWS_ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com/mavis"
82
+
83
+ # Get the image digest for the git SHA
84
+ IMAGE_DIGEST=$(aws ecr describe-images \
85
+ --repository-name mavis/webapp \
86
+ --image-ids imageTag=${{ inputs.git_sha_to_deploy || github.sha }} \
87
+ --query 'imageDetails[0].imageDigest' \
88
+ --output text)
89
+
90
+ NEW_IMAGE_URI="${REPOSITORY_URI}@${IMAGE_DIGEST}"
91
+ echo "new-image-uri=${NEW_IMAGE_URI}" >> $GITHUB_OUTPUT
92
+ echo "New image URI: ${NEW_IMAGE_URI}"
93
+ # - name: Get dynamic variables for web
94
+ # id: get-dynamic-vars-web
95
+ # if: inputs.server_types == 'web' || inputs.server_types == 'all'
96
+ # run: |
97
+ # # Fetch SSM parameter for web service
98
+ # SSM_OUTPUT=$(aws ssm get-parameter --name /${{ inputs.environment }}/ecs/web/container_variables --query Parameter.Value --output text)
99
+ #
100
+ # # Extract environment variables in the format expected by amazon-ecs-render-task-definition
101
+ # ENV_VARS=$(echo "$SSM_OUTPUT" | jq -r '.task_envs | map("'"'"'\(.name)=\(.value)'"'"'") | join("\n")')
102
+ #
103
+ # # Extract secrets in the format expected by amazon-ecs-render-task-definition
104
+ # SECRETS=$(echo "$SSM_OUTPUT" | jq -r '.task_secrets | map("'"'"'\(.name)=\(.valueFrom)'"'"'") | join("\n")')
105
+ #
106
+ # # Set outputs using multiline format
107
+ # echo "environment-variables<<EOF" >> $GITHUB_OUTPUT
108
+ # echo "$ENV_VARS" >> $GITHUB_OUTPUT
109
+ # echo "EOF" >> $GITHUB_OUTPUT
110
+ #
111
+ # echo "secrets<<EOF" >> $GITHUB_OUTPUT
112
+ # echo "$SECRETS" >> $GITHUB_OUTPUT
113
+ # echo "EOF" >> $GITHUB_OUTPUT
114
+ #
115
+ # # Also extract role ARNs for potential future use
116
+ # EXECUTION_ROLE_ARN=$(echo "$SSM_OUTPUT" | jq -r '.execution_role_arn')
117
+ # TASK_ROLE_ARN=$(echo "$SSM_OUTPUT" | jq -r '.task_role_arn')
118
+ #
119
+ # echo "execution-role-arn=$EXECUTION_ROLE_ARN" >> $GITHUB_OUTPUT
120
+ # echo "task-role-arn=$TASK_ROLE_ARN" >> $GITHUB_OUTPUT
121
+ #
122
+ # echo "Environment variables:"
123
+ # echo "$ENV_VARS"
124
+ # echo ""
125
+ # echo "Secrets:"
126
+ # echo "$SECRETS"
127
+ # - name: Get dynamic variables for good-job
128
+ # id: get-dynamic-vars-good-job
129
+ # if: inputs.server_types == 'good-job' || inputs.server_types == 'all'
130
+ # run: |
131
+ # # Fetch SSM parameter for good-job service
132
+ # SSM_OUTPUT=$(aws ssm get-parameter --name /${{ inputs.environment }}/ecs/good-job/container_variables --query Parameter.Value --output text)
133
+ #
134
+ # # Extract environment variables in the format expected by amazon-ecs-render-task-definition
135
+ # ENV_VARS=$(echo "$SSM_OUTPUT" | jq -r '.task_envs | map("'"'"'\(.name)=\(.value)'"'"'") | join("\n")')
136
+ #
137
+ # # Extract secrets in the format expected by amazon-ecs-render-task-definition
138
+ # SECRETS=$(echo "$SSM_OUTPUT" | jq -r '.task_secrets | map("'"'"'\(.name)=\(.valueFrom)'"'"'") | join("\n")')
139
+ #
140
+ # # Set outputs using multiline format
141
+ # echo "environment-variables<<EOF" >> $GITHUB_OUTPUT
142
+ # echo "$ENV_VARS" >> $GITHUB_OUTPUT
143
+ # echo "EOF" >> $GITHUB_OUTPUT
144
+ #
145
+ # echo "secrets<<EOF" >> $GITHUB_OUTPUT
146
+ # echo "$SECRETS" >> $GITHUB_OUTPUT
147
+ # echo "EOF" >> $GITHUB_OUTPUT
148
+ #
149
+ # # Also extract role ARNs for potential future use
150
+ # EXECUTION_ROLE_ARN=$(echo "$SSM_OUTPUT" | jq -r '.execution_role_arn')
151
+ # TASK_ROLE_ARN=$(echo "$SSM_OUTPUT" | jq -r '.task_role_arn')
152
+ #
153
+ # echo "execution-role-arn=$EXECUTION_ROLE_ARN" >> $GITHUB_OUTPUT
154
+ # echo "task-role-arn=$TASK_ROLE_ARN" >> $GITHUB_OUTPUT
155
+ #
156
+ # echo "Environment variables:"
157
+ # echo "$ENV_VARS"
158
+ # echo ""
159
+ # echo "Secrets:"
160
+ # echo "$SECRETS"
161
+ - name : Populate web task definition
162
+ if : inputs.server_types == 'web' || inputs.server_types == 'all'
163
+ id : render-web-task-definition
75
164
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
165
+ chmod +x ./script/populate_task_definition.sh
166
+ ./script/populate_task_definition.sh ${{ inputs.environment }} web \
167
+ -i "${{ steps.get-image-digest.outputs.new-image-uri }}" \
168
+ -o web-task-definition.json
169
+ echo "task-definition=web-task-definition.json" >> $GITHUB_OUTPUT
170
+ - name : Populate good-job task definition
171
+ if : inputs.server_types == 'good-job' || inputs.server_types == 'all'
172
+ id : render-good-job-task-definition
173
+ run : |
174
+ chmod +x ./script/populate_task_definition.sh
175
+ ./script/populate_task_definition.sh ${{ inputs.environment }} good-job \
176
+ -i "${{ steps.get-image-digest.outputs.new-image-uri }}" \
177
+ -o good-job-task-definition.json
178
+ echo "task-definition=good-job-task-definition.json" >> $GITHUB_OUTPUT
179
+ - name : Upload web task definition artifact
180
+ if : inputs.server_types == 'web' || inputs.server_types == 'all'
181
+ uses : actions/upload-artifact@v4
182
+ with :
183
+ name : web-task-definition
184
+ path : ${{ steps.render-web-task-definition.outputs.task-definition }}
185
+ retention-days : 1
186
+ - name : Upload good-job task definition artifact
187
+ if : inputs.server_types == 'good-job' || inputs.server_types == 'all'
88
188
uses : actions/upload-artifact@v4
89
189
with :
90
- name : DEPLOYMENT_ENVS-${{ inputs.environment }}
91
- path : ${{ runner.temp }}/DEPLOYMENT_ENVS
190
+ name : good-job-task-definition
191
+ path : ${{ steps.render-good-job-task-definition.outputs.task-definition }}
192
+ retention-days : 1
193
+ outputs :
194
+ new-image-uri : ${{ steps.get-image-digest.outputs.new-image-uri }}
195
+ web-environment-variables : ${{ steps.get-dynamic-vars-web.outputs.environment-variables }}
196
+ web-secrets : ${{ steps.get-dynamic-vars-web.outputs.secrets }}
197
+ web-execution-role-arn : ${{ steps.get-dynamic-vars-web.outputs.execution-role-arn }}
198
+ web-task-role-arn : ${{ steps.get-dynamic-vars-web.outputs.task-role-arn }}
199
+ good-job-environment-variables : ${{ steps.get-dynamic-vars-good-job.outputs.environment-variables }}
200
+ good-job-secrets : ${{ steps.get-dynamic-vars-good-job.outputs.secrets }}
201
+ good-job-execution-role-arn : ${{ steps.get-dynamic-vars-good-job.outputs.execution-role-arn }}
202
+ good-job-task-role-arn : ${{ steps.get-dynamic-vars-good-job.outputs.task-role-arn }}
92
203
93
- create -web-deployment :
94
- name : Create web deployment
204
+ deploy -web :
205
+ name : Deploy web service
95
206
runs-on : ubuntu-latest
96
- needs : prepare-deployment
97
207
if : inputs.server_types == 'web' || inputs.server_types == 'all'
208
+ needs : prepare-deployment
209
+ environment : ${{ inputs.environment }}
98
210
permissions :
99
211
id-token : write
100
212
steps :
101
- - name : Download artifact
102
- uses : actions/download-artifact@v4
103
- with :
104
- name : DEPLOYMENT_ENVS-${{ inputs.environment }}
105
- path : ${{ runner.temp }}
106
213
- name : Configure AWS Credentials
107
214
uses : aws-actions/configure-aws-credentials@v4
108
215
with :
109
216
role-to-assume : ${{ env.aws-role }}
110
217
aws-region : eu-west-2
111
- - name : Trigger CodeDeploy deployment
218
+ - name : Download web task definition artifact
219
+ uses : actions/download-artifact@v4
220
+ with :
221
+ name : web-task-definition
222
+ path : ./artifacts
223
+ - name : Register new task definition
112
224
run : |
113
- set -e
114
- source ${{ runner.temp }}/DEPLOYMENT_ENVS
225
+ # Find the task definition file in the artifacts directory
226
+ TASK_DEF_FILE=$(find ./artifacts -name "*.json" -type f | head -1)
227
+ aws ecs register-task-definition --cli-input-json file://$TASK_DEF_FILE
228
+ - name : Deploy with CodeDeploy
229
+ run : |
230
+ # Create CodeDeploy deployment for blue-green deployment
115
231
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"
232
+ --application-name ${{ env.web_codedeploy_application }} \
233
+ --deployment-group-name ${{ env.web_codedeploy_group }} \
234
+ --deployment-config-name CodeDeployDefault.ECSBlueGreenCanary10Percent5Minutes \
235
+ --description "Deployment from GitHub Actions" \
236
+ | jq -r .deploymentId)
237
+
238
+ echo "CodeDeploy deployment started: $deployment_id"
119
239
echo "deployment_id=$deployment_id" >> $GITHUB_ENV
120
- - name : Wait up to 30 minutes for deployment to complete
240
+ - name : Wait for deployment to complete
121
241
run : |
122
- set -e
242
+ echo "Waiting for CodeDeploy deployment $deployment_id to complete..."
123
243
aws deploy wait deployment-successful --deployment-id "$deployment_id"
124
244
echo "Deployment successful"
125
245
126
- create -good-job-deployment :
127
- name : Create good-job deployment
246
+ deploy -good-job :
247
+ name : Deploy good-job service
128
248
runs-on : ubuntu-latest
129
- needs : prepare-deployment
130
249
if : inputs.server_types == 'good-job' || inputs.server_types == 'all'
250
+ needs : prepare-deployment
251
+ environment : ${{ inputs.environment }}
131
252
permissions :
132
253
id-token : write
133
254
steps :
134
- - name : Download Artifact
135
- uses : actions/download-artifact@v4
136
- with :
137
- name : DEPLOYMENT_ENVS-${{ inputs.environment }}
138
- path : ${{ runner.temp }}
139
255
- name : Configure AWS Credentials
140
256
uses : aws-actions/configure-aws-credentials@v4
141
257
with :
142
258
role-to-assume : ${{ env.aws-role }}
143
259
aws-region : eu-west-2
144
- - name : Trigger ECS Deployment
260
+ - name : Download good-job task definition artifact
261
+ uses : actions/download-artifact@v4
262
+ with :
263
+ name : good-job-task-definition
264
+ path : ./artifacts
265
+ - name : Register new task definition
145
266
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"
267
+ # Find the task definition file in the artifacts directory
268
+ TASK_DEF_FILE=$(find ./artifacts -name "*.json" -type f | head -1)
269
+ TASK_DEFINITION_ARN=$(aws ecs register-task-definition --cli-input-json file://$TASK_DEF_FILE --query 'taskDefinition.taskDefinitionArn' --output text)
270
+ echo "New task definition registered: $TASK_DEFINITION_ARN"
271
+ echo "task_definition_arn=$TASK_DEFINITION_ARN" >> $GITHUB_ENV
272
+ - name : Update ECS service
273
+ run : |
274
+ # Update the good-job service with the new task definition
275
+ DEPLOYMENT_ID=$(aws ecs update-service \
276
+ --cluster ${{ env.cluster_name }} \
277
+ --service ${{ env.good_job_service }} \
278
+ --task-definition "$task_definition_arn" \
279
+ --force-new-deployment \
280
+ --query 'service.deployments[?rolloutState==`IN_PROGRESS`].[id][0]' \
281
+ --output text)
282
+
283
+ echo "ECS deployment started: $DEPLOYMENT_ID"
152
284
echo "deployment_id=$DEPLOYMENT_ID" >> $GITHUB_ENV
153
285
- name : Wait for deployment to complete
154
286
run : |
155
- set -e
156
- source ${{ runner.temp }}/DEPLOYMENT_ENVS
287
+ echo "Waiting for ECS deployment $deployment_id to complete..."
157
288
DEPLOYMENT_STATE=IN_PROGRESS
158
289
while [ "$DEPLOYMENT_STATE" == "IN_PROGRESS" ]; do
159
- echo "Waiting for deployment to complete ..."
290
+ echo "Checking deployment status ..."
160
291
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)"
292
+ DEPLOYMENT_STATE=$(aws ecs describe-services \
293
+ --cluster ${{ env.cluster_name }} \
294
+ --services ${{ env.good_job_service }} \
295
+ --query "services[0].deployments[?id == \`$deployment_id\`].[rolloutState][0]" \
296
+ --output text)
163
297
done
298
+
164
299
if [ "$DEPLOYMENT_STATE" != "COMPLETED" ]; then
165
300
echo "Deployment failed with state: $DEPLOYMENT_STATE"
166
301
exit 1
0 commit comments