Skip to content

Commit 6ee54ae

Browse files
committed
feat(ci): rewrite deploy workflow with OIDC, dev/prod branching, inline task def
1 parent a956228 commit 6ee54ae

1 file changed

Lines changed: 149 additions & 30 deletions

File tree

Lines changed: 149 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,181 @@
1-
name: Deploy Backend
1+
name: Deploy Backend to AWS ECS
22

33
on:
44
push:
5-
branches: [main]
5+
branches:
6+
- main
7+
- develop
68
paths:
79
- "backend/**"
810
- ".github/workflows/deploy-backend.yml"
11+
workflow_dispatch:
912

1013
env:
1114
AWS_REGION: us-east-1
12-
ECR_REPOSITORY: kickwatch-backend
13-
ECS_CLUSTER: kickwatch-cluster
14-
ECS_SERVICE: kickwatch-backend-service
15-
CONTAINER_NAME: kickwatch-backend
1615

1716
jobs:
18-
deploy:
17+
build-and-deploy:
18+
name: Build and Deploy
1919
runs-on: ubuntu-latest
20-
defaults:
21-
run:
22-
working-directory: backend
20+
permissions:
21+
contents: read
22+
id-token: write
23+
24+
env:
25+
IS_PROD: ${{ github.ref == 'refs/heads/main' }}
26+
2327
steps:
28+
- name: Set environment variables
29+
run: |
30+
if [ "${{ env.IS_PROD }}" = "true" ]; then
31+
echo "ECR_REPOSITORY=kickwatch-api" >> $GITHUB_ENV
32+
echo "ECS_CLUSTER=kickwatch-cluster" >> $GITHUB_ENV
33+
echo "ECS_SERVICE=kickwatch-api-service" >> $GITHUB_ENV
34+
echo "CONTAINER_NAME=kickwatch-api" >> $GITHUB_ENV
35+
echo "DEPLOY_ENV=production" >> $GITHUB_ENV
36+
echo "SECRET_PREFIX=kickwatch" >> $GITHUB_ENV
37+
echo "LOG_GROUP=/ecs/kickwatch-api" >> $GITHUB_ENV
38+
echo "GIN_MODE=release" >> $GITHUB_ENV
39+
else
40+
echo "ECR_REPOSITORY=kickwatch-api-dev" >> $GITHUB_ENV
41+
echo "ECS_CLUSTER=kickwatch-cluster-dev" >> $GITHUB_ENV
42+
echo "ECS_SERVICE=kickwatch-api-dev-service" >> $GITHUB_ENV
43+
echo "CONTAINER_NAME=kickwatch-api-dev" >> $GITHUB_ENV
44+
echo "DEPLOY_ENV=development" >> $GITHUB_ENV
45+
echo "SECRET_PREFIX=kickwatch-dev" >> $GITHUB_ENV
46+
echo "LOG_GROUP=/ecs/kickwatch-api-dev" >> $GITHUB_ENV
47+
echo "GIN_MODE=debug" >> $GITHUB_ENV
48+
fi
49+
2450
- uses: actions/checkout@v4
2551

2652
- uses: actions/setup-go@v5
2753
with:
28-
go-version: "1.24"
54+
go-version-file: backend/go.mod
2955
cache-dependency-path: backend/go.sum
3056

31-
- run: go test ./...
57+
- name: Run tests and vet
58+
working-directory: backend
59+
run: |
60+
go vet ./... &
61+
VET_PID=$!
62+
go test ./... &
63+
TEST_PID=$!
64+
wait $VET_PID || exit 1
65+
wait $TEST_PID || exit 1
66+
67+
- name: Build Go binary
68+
working-directory: backend
69+
run: CGO_ENABLED=0 GOOS=linux go build -o api ./cmd/api
3270

33-
- name: Configure AWS credentials
71+
- name: Configure AWS credentials (OIDC)
3472
uses: aws-actions/configure-aws-credentials@v4
3573
with:
36-
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
37-
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
74+
role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
3875
aws-region: ${{ env.AWS_REGION }}
3976

40-
- name: Login to ECR
77+
- name: Login to Amazon ECR
4178
id: login-ecr
4279
uses: aws-actions/amazon-ecr-login@v2
4380

44-
- name: Build, tag, push image
81+
- name: Ensure ECR repository exists
82+
run: |
83+
aws ecr describe-repositories --repository-names $ECR_REPOSITORY --region $AWS_REGION 2>/dev/null || \
84+
aws ecr create-repository --repository-name $ECR_REPOSITORY --region $AWS_REGION \
85+
--image-scanning-configuration scanOnPush=true
86+
87+
- name: Set up Docker Buildx
88+
uses: docker/setup-buildx-action@v3
89+
90+
- name: Build and push Docker image
4591
id: build-image
92+
uses: docker/build-push-action@v6
93+
with:
94+
context: backend
95+
file: backend/Dockerfile.ci
96+
push: true
97+
tags: |
98+
${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}
99+
${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:latest
100+
cache-from: type=gha
101+
cache-to: type=gha,mode=max
102+
provenance: false
103+
104+
- name: Resolve Secrets Manager ARNs
105+
id: secrets
46106
env:
47-
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
48-
IMAGE_TAG: ${{ github.sha }}
107+
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
49108
run: |
50-
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
51-
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
52-
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
109+
get_arn() { aws secretsmanager describe-secret --secret-id "$1" --region $AWS_REGION --query "ARN" --output text; }
110+
echo "db_arn=$(get_arn ${SECRET_PREFIX}/database-url)" >> $GITHUB_OUTPUT
111+
echo "apns_key_id_arn=$(get_arn ${SECRET_PREFIX}/apns-key-id)" >> $GITHUB_OUTPUT
112+
echo "apns_team_id_arn=$(get_arn ${SECRET_PREFIX}/apns-team-id)" >> $GITHUB_OUTPUT
113+
echo "apns_bundle_id_arn=$(get_arn ${SECRET_PREFIX}/apns-bundle-id)" >> $GITHUB_OUTPUT
114+
echo "apns_key_arn=$(get_arn ${SECRET_PREFIX}/apns-key)" >> $GITHUB_OUTPUT
53115
54-
- name: Download ECS task definition
116+
- name: Generate ECS task definition
117+
env:
118+
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
55119
run: |
56-
aws ecs describe-task-definition \
57-
--task-definition kickwatch-backend \
58-
--query taskDefinition \
59-
> task-definition.json
120+
cat > /tmp/task-definition.json <<EOF
121+
{
122+
"family": "${{ env.CONTAINER_NAME }}",
123+
"networkMode": "awsvpc",
124+
"requiresCompatibilities": ["FARGATE"],
125+
"cpu": "256",
126+
"memory": "512",
127+
"executionRoleArn": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecsTaskExecutionRole",
128+
"taskRoleArn": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/kickwatch-task-role",
129+
"containerDefinitions": [
130+
{
131+
"name": "${{ env.CONTAINER_NAME }}",
132+
"image": "placeholder",
133+
"essential": true,
134+
"portMappings": [
135+
{ "containerPort": 8080, "protocol": "tcp" }
136+
],
137+
"environment": [
138+
{ "name": "PORT", "value": "8080" },
139+
{ "name": "GIN_MODE", "value": "${{ env.GIN_MODE }}" },
140+
{ "name": "APP_ENV", "value": "${{ env.DEPLOY_ENV }}" },
141+
{ "name": "APNS_ENV", "value": "${{ env.IS_PROD == 'true' && 'production' || 'sandbox' }}" },
142+
{ "name": "APNS_KEY_PATH","value": "/secrets/apns.p8" }
143+
],
144+
"secrets": [
145+
{ "name": "DATABASE_URL", "valueFrom": "${{ steps.secrets.outputs.db_arn }}" },
146+
{ "name": "APNS_KEY_ID", "valueFrom": "${{ steps.secrets.outputs.apns_key_id_arn }}" },
147+
{ "name": "APNS_TEAM_ID", "valueFrom": "${{ steps.secrets.outputs.apns_team_id_arn }}" },
148+
{ "name": "APNS_BUNDLE_ID", "valueFrom": "${{ steps.secrets.outputs.apns_bundle_id_arn }}" }
149+
],
150+
"readonlyRootFilesystem": true,
151+
"linuxParameters": { "initProcessEnabled": true },
152+
"healthCheck": {
153+
"command": ["CMD-SHELL", "wget -q -O /dev/null http://localhost:8080/api/health || exit 1"],
154+
"interval": 15,
155+
"timeout": 5,
156+
"retries": 3,
157+
"startPeriod": 10
158+
},
159+
"logConfiguration": {
160+
"logDriver": "awslogs",
161+
"options": {
162+
"awslogs-group": "${{ env.LOG_GROUP }}",
163+
"awslogs-region": "${{ env.AWS_REGION }}",
164+
"awslogs-stream-prefix": "ecs"
165+
}
166+
}
167+
}
168+
]
169+
}
170+
EOF
60171
61-
- name: Update ECS task definition with new image
172+
- name: Fill image into task definition
62173
id: task-def
63174
uses: aws-actions/amazon-ecs-render-task-definition@v1
64175
with:
65-
task-definition: backend/task-definition.json
176+
task-definition: /tmp/task-definition.json
66177
container-name: ${{ env.CONTAINER_NAME }}
67-
image: ${{ steps.build-image.outputs.image }}
178+
image: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}
68179

69180
- name: Deploy to ECS
70181
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
@@ -73,3 +184,11 @@ jobs:
73184
service: ${{ env.ECS_SERVICE }}
74185
cluster: ${{ env.ECS_CLUSTER }}
75186
wait-for-service-stability: true
187+
188+
- name: Deployment summary
189+
run: |
190+
echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY
191+
echo "- **Environment**: ${{ env.DEPLOY_ENV }}" >> $GITHUB_STEP_SUMMARY
192+
echo "- **Cluster**: ${{ env.ECS_CLUSTER }}" >> $GITHUB_STEP_SUMMARY
193+
echo "- **Service**: ${{ env.ECS_SERVICE }}" >> $GITHUB_STEP_SUMMARY
194+
echo "- **Image**: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY

0 commit comments

Comments
 (0)