Skip to content

Commit 5620df4

Browse files
first_ideas
1 parent cadfd6c commit 5620df4

File tree

5 files changed

+351
-28
lines changed

5 files changed

+351
-28
lines changed

.github/workflows/deploy-mavis.yml

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
name: Deploy Mavis
2+
run-name: Deploy Mavis to ${{ inputs.environment }}
3+
4+
on:
5+
workflow_dispatch:
6+
inputs:
7+
environment:
8+
description: Deployment environment
9+
required: true
10+
type: choice
11+
options:
12+
- qa
13+
- test
14+
- preview
15+
- training
16+
- production
17+
- sandbox-alpha
18+
- sandbox-beta
19+
server_types:
20+
description: Server types to deploy
21+
required: true
22+
type: choice
23+
options:
24+
- all
25+
- web
26+
- good-job
27+
default: all
28+
workflow_call:
29+
inputs:
30+
environment:
31+
required: true
32+
type: string
33+
server_types:
34+
required: true
35+
type: string
36+
git_sha_to_deploy:
37+
description: The git commit SHA to deploy.
38+
required: true
39+
type: string
40+
41+
permissions: { }
42+
43+
concurrency:
44+
group: deploy-mavis-${{ inputs.environment }}
45+
46+
env:
47+
aws-role: ${{ inputs.environment == 'production'
48+
&& 'arn:aws:iam::820242920762:role/GithubDeployMavisAndInfrastructure'
49+
|| 'arn:aws:iam::393416225559:role/GithubDeployMavisAndInfrastructure' }}
50+
web_codedeploy_application: mavis-${{ inputs.environment }}
51+
web_codedeploy_group: blue-green-group-${{ inputs.environment }}
52+
web_task_definition: mavis-web-task-definition-${{ inputs.environment }}
53+
cluster_name: mavis-${{ inputs.environment }}
54+
good_job_service: mavis-${{ inputs.environment }}-good-job
55+
good_job_task_definition: mavis-good-job-task-definition-${{ inputs.environment }}
56+
57+
jobs:
58+
prepare-web-deployment:
59+
name: Prepare web service deployment
60+
runs-on: ubuntu-latest
61+
if: inputs.server_types == 'web' || inputs.server_types == 'all'
62+
environment: ${{ inputs.environment }}
63+
permissions:
64+
id-token: write
65+
outputs:
66+
task-definition-arn: ${{ steps.update-task-definition.outputs.task-definition-arn }}
67+
steps:
68+
- name: Checkout code
69+
uses: actions/checkout@v4
70+
with:
71+
ref: ${{ inputs.git_sha_to_deploy || github.sha }}
72+
- name: Configure AWS Credentials
73+
uses: aws-actions/configure-aws-credentials@v4
74+
with:
75+
role-to-assume: ${{ env.aws-role }}
76+
aws-region: eu-west-2
77+
- name: Get image digest from ECR
78+
id: get-image-digest
79+
run: |
80+
# Get AWS account ID and construct repository URI
81+
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
82+
REPOSITORY_URI="${AWS_ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com/mavis"
83+
84+
# Get the image digest for the git SHA
85+
IMAGE_DIGEST=$(aws ecr describe-images \
86+
--repository-name mavis \
87+
--image-ids imageTag=${{ inputs.git_sha_to_deploy || github.sha }} \
88+
--query 'imageDetails[0].imageDigest' \
89+
--output text)
90+
91+
NEW_IMAGE_URI="${REPOSITORY_URI}@${IMAGE_DIGEST}"
92+
echo "new-image-uri=${NEW_IMAGE_URI}" >> $GITHUB_OUTPUT
93+
echo "New image URI: ${NEW_IMAGE_URI}"
94+
- name: Update task definition with new image
95+
id: update-task-definition
96+
uses: aws-actions/amazon-ecs-render-task-definition@v1
97+
with:
98+
task-definition: config/task-definitions/web-task-definition.json
99+
container-name: web
100+
image: ${{ steps.get-image-digest.outputs.new-image-uri }}
101+
- name: Create appspec.yml from template
102+
run: |
103+
# Read the template and substitute variables
104+
TASK_DEFINITION_ARN="${{ steps.update-task-definition.outputs.task-definition-arn }}"
105+
CONTAINER_NAME="web"
106+
CONTAINER_PORT="4000"
107+
108+
# Create appspec.yml by substituting template variables
109+
sed -e "s|\${task_definition_arn}|${TASK_DEFINITION_ARN}|g" \
110+
-e "s|\${container_name}|${CONTAINER_NAME}|g" \
111+
-e "s|\${container_port}|${CONTAINER_PORT}|g" \
112+
terraform/app/templates/appspec.yaml.tpl > appspec.yml
113+
114+
echo "Generated appspec.yml:"
115+
cat appspec.yml
116+
- name: Upload deployment artifacts
117+
uses: actions/upload-artifact@v4
118+
with:
119+
name: web-deployment-artifacts-${{ inputs.environment }}
120+
path: |
121+
task-definition.json
122+
appspec.yml
123+
124+
125+
deploy-web:
126+
name: Deploy web service via CodeDeploy
127+
runs-on: ubuntu-latest
128+
needs: prepare-web-deployment
129+
if: inputs.server_types == 'web' || inputs.server_types == 'all'
130+
environment: ${{ inputs.environment }}
131+
permissions:
132+
id-token: write
133+
steps:
134+
- name: Download deployment artifacts
135+
uses: actions/download-artifact@v4
136+
with:
137+
name: web-deployment-artifacts-${{ inputs.environment }}
138+
path: ${{ runner.temp }}
139+
- name: Configure AWS Credentials
140+
uses: aws-actions/configure-aws-credentials@v4
141+
with:
142+
role-to-assume: ${{ env.aws-role }}
143+
aws-region: eu-west-2
144+
- name: Install terraform
145+
uses: hashicorp/setup-terraform@v3
146+
with:
147+
terraform_version: 1.11.4
148+
- name: Get terraform output for S3 bucket
149+
id: terraform-output
150+
working-directory: terraform/app
151+
run: |
152+
set -e
153+
terraform init -backend-config=env/${{ inputs.environment }}-backend.hcl -reconfigure
154+
S3_BUCKET=$(terraform output -raw s3_bucket)
155+
echo "s3-bucket=${S3_BUCKET}" >> $GITHUB_OUTPUT
156+
echo "S3 bucket: ${S3_BUCKET}"
157+
- name: Register new task definition
158+
run: |
159+
aws ecs register-task-definition \
160+
--cli-input-json file://${{ runner.temp }}/task-definition.json
161+
- name: Create CodeDeploy deployment
162+
id: create-deployment
163+
run: |
164+
# Create a deployment bundle with appspec.yml
165+
cd ${{ runner.temp }}
166+
zip -r deployment-bundle.zip appspec.yml
167+
168+
# Upload to S3 using bucket from Terraform outputs
169+
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
170+
S3_KEY="deployments/mavis-${{ inputs.environment }}-${TIMESTAMP}.zip"
171+
S3_BUCKET="${{ steps.terraform-output.outputs.s3-bucket }}"
172+
173+
aws s3 cp deployment-bundle.zip s3://${S3_BUCKET}/${S3_KEY}
174+
175+
# Create CodeDeploy deployment
176+
DEPLOYMENT_ID=$(aws deploy create-deployment \
177+
--application-name ${{ env.web_codedeploy_application }} \
178+
--deployment-group-name ${{ env.web_codedeploy_group }} \
179+
--s3-location bucket=${S3_BUCKET},key=${S3_KEY},bundleType=zip \
180+
--query 'deploymentId' \
181+
--output text)
182+
183+
echo "deployment-id=${DEPLOYMENT_ID}" >> $GITHUB_OUTPUT
184+
echo "CodeDeploy deployment started: ${DEPLOYMENT_ID}"
185+
- name: Wait for deployment to complete
186+
run: |
187+
echo "Waiting for deployment ${{ steps.create-deployment.outputs.deployment-id }} to complete..."
188+
aws deploy wait deployment-successful \
189+
--deployment-id ${{ steps.create-deployment.outputs.deployment-id }}
190+
echo "Deployment completed successfully!"
191+
192+
193+
deploy-good-job-service:
194+
name: Deploy good-job service via ECS
195+
runs-on: ubuntu-latest
196+
if: inputs.server_types == 'good-job' || inputs.server_types == 'all'
197+
environment: ${{ inputs.environment }}
198+
permissions:
199+
id-token: write
200+
steps:
201+
- name: Checkout code
202+
uses: actions/checkout@v4
203+
with:
204+
ref: ${{ inputs.git_sha_to_deploy || github.sha }}
205+
- name: Configure AWS Credentials
206+
uses: aws-actions/configure-aws-credentials@v4
207+
with:
208+
role-to-assume: ${{ env.aws-role }}
209+
aws-region: eu-west-2
210+
- name: Load task definition template
211+
run: |
212+
cp config/task-definitions/good-job-task-definition.json current-task-definition.json
213+
- name: Get image digest from ECR
214+
id: get-image-digest
215+
run: |
216+
# Get AWS account ID and construct repository URI
217+
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
218+
REPOSITORY_URI="${AWS_ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com/mavis"
219+
220+
# Get the image digest for the git SHA
221+
IMAGE_DIGEST=$(aws ecr describe-images \
222+
--repository-name mavis \
223+
--image-ids imageTag=${{ inputs.git_sha_to_deploy || github.sha }} \
224+
--query 'imageDetails[0].imageDigest' \
225+
--output text)
226+
227+
NEW_IMAGE_URI="${REPOSITORY_URI}@${IMAGE_DIGEST}"
228+
echo "new-image-uri=${NEW_IMAGE_URI}" >> $GITHUB_OUTPUT
229+
echo "New image URI: ${NEW_IMAGE_URI}"
230+
- name: Update task definition with new image
231+
id: update-task-definition
232+
uses: aws-actions/amazon-ecs-render-task-definition@v1
233+
with:
234+
task-definition: current-task-definition.json
235+
container-name: good-job
236+
image: ${{ steps.get-image-digest.outputs.new-image-uri }}
237+
- name: Deploy to ECS
238+
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
239+
with:
240+
task-definition: ${{ steps.update-task-definition.outputs.task-definition }}
241+
service: ${{ env.good_job_service }}
242+
cluster: ${{ env.cluster_name }}
243+
wait-for-service-stability: true
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"family": "mavis-good-job-task-definition",
3+
"networkMode": "awsvpc",
4+
"requiresCompatibilities": ["FARGATE"],
5+
"cpu": "256",
6+
"memory": "512",
7+
"executionRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ecsTaskExecutionRole",
8+
"taskRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ecsTaskRole",
9+
"containerDefinitions": [
10+
{
11+
"name": "good-job",
12+
"image": "REPOSITORY_URI:latest",
13+
"essential": true,
14+
"logConfiguration": {
15+
"logDriver": "awslogs",
16+
"options": {
17+
"awslogs-group": "/ecs/mavis-good-job",
18+
"awslogs-region": "eu-west-2",
19+
"awslogs-stream-prefix": "ecs"
20+
}
21+
},
22+
"environment": [
23+
{
24+
"name": "RAILS_ENV",
25+
"value": "production"
26+
}
27+
]
28+
}
29+
]
30+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"family": "mavis-web-task-definition",
3+
"networkMode": "awsvpc",
4+
"requiresCompatibilities": ["FARGATE"],
5+
"cpu": "256",
6+
"memory": "512",
7+
"executionRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ecsTaskExecutionRole",
8+
"taskRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ecsTaskRole",
9+
"containerDefinitions": [
10+
{
11+
"name": "web",
12+
"image": "REPOSITORY_URI",
13+
"portMappings": [
14+
{
15+
"containerPort": 4000,
16+
"protocol": "tcp"
17+
}
18+
],
19+
"essential": true,
20+
"logConfiguration": {
21+
"logDriver": "awslogs",
22+
"options": {
23+
"awslogs-group": "/ecs/mavis-web",
24+
"awslogs-region": "eu-west-2",
25+
"awslogs-stream-prefix": "ecs"
26+
}
27+
},
28+
"environment": [
29+
{
30+
"name": "RAILS_ENV",
31+
"value": "production"
32+
}
33+
],
34+
"secrets": [
35+
{
36+
"name": "DATABASE_URL",
37+
"valueFrom": "arn:aws:ssm:eu-west-2:ACCOUNT_ID:parameter/mavis/database_url"
38+
},
39+
{
40+
"name": "REDIS_URL",
41+
"valueFrom": "arn:aws:ssm:eu-west-2:ACCOUNT_ID:parameter/mavis/redis_url"
42+
},
43+
{
44+
"name": "SECRET_KEY_BASE",
45+
"valueFrom": "arn:aws:ssm:eu-west-2:ACCOUNT_ID:parameter/mavis/secret_key_base"
46+
}
47+
]
48+
}
49+
]
50+
}

terraform/app/codedeploy.tf

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -111,17 +111,17 @@ resource "aws_s3_bucket_public_access_block" "s3_bucket_access" {
111111
restrict_public_buckets = true
112112
}
113113

114-
resource "aws_s3_object" "appspec_object" {
115-
bucket = aws_s3_bucket.code_deploy_bucket.bucket
116-
key = "appspec.yaml"
117-
acl = "private"
118-
content = templatefile("templates/appspec.yaml.tpl", {
119-
task_definition_arn = module.web_service.task_definition.arn
120-
container_name = module.web_service.task_definition.container_name
121-
container_port = aws_lb_target_group.blue.port
122-
})
123-
124-
tags = {
125-
UseWithCodeDeploy = true
126-
}
127-
}
114+
# resource "aws_s3_object" "appspec_object" {
115+
# bucket = aws_s3_bucket.code_deploy_bucket.bucket
116+
# key = "appspec.yaml"
117+
# acl = "private"
118+
# content = templatefile("templates/appspec.yaml.tpl", {
119+
# task_definition_arn = module.web_service.task_definition.arn
120+
# container_name = module.web_service.task_definition.container_name
121+
# container_port = aws_lb_target_group.blue.port
122+
# })
123+
#
124+
# tags = {
125+
# UseWithCodeDeploy = true
126+
# }
127+
# }

terraform/app/outputs.tf

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
output "s3_uri" {
2-
description = "S3 uri for appspec.yaml needed for CodeDeploy"
3-
value = "s3://${aws_s3_bucket.code_deploy_bucket.bucket}/${aws_s3_object.appspec_object.key}"
4-
}
5-
6-
output "s3_bucket" {
7-
description = "The name of the S3 bucket that stores the appspec.yaml for CodeDeploy"
8-
value = aws_s3_bucket.code_deploy_bucket.bucket
9-
}
10-
11-
output "s3_key" {
12-
description = "The key of the S3 CodeDeploy appspec object"
13-
value = aws_s3_object.appspec_object.key
14-
}
1+
# output "s3_uri" {
2+
# description = "S3 uri for appspec.yaml needed for CodeDeploy"
3+
# value = "s3://${aws_s3_bucket.code_deploy_bucket.bucket}/${aws_s3_object.appspec_object.key}"
4+
# }
5+
#
6+
# output "s3_bucket" {
7+
# description = "The name of the S3 bucket that stores the appspec.yaml for CodeDeploy"
8+
# value = aws_s3_bucket.code_deploy_bucket.bucket
9+
# }
10+
#
11+
# output "s3_key" {
12+
# description = "The key of the S3 CodeDeploy appspec object"
13+
# value = aws_s3_object.appspec_object.key
14+
# }
1515

1616
output "codedeploy_application_name" {
1717
description = "The name of the CodeDeploy application"

0 commit comments

Comments
 (0)