-
Notifications
You must be signed in to change notification settings - Fork 116
166 lines (141 loc) · 6.09 KB
/
Copy pathdeploy-staging.yml
File metadata and controls
166 lines (141 loc) · 6.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
name: Deploy to Staging
on:
push:
branches: [main]
permissions:
contents: read
packages: write # Needed to push Docker images to GHCR
id-token: write # Required for OIDC-based AWS authentication
jobs:
test:
name: Run Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.3.0
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Audit dependencies
run: npm audit --audit-level=high
- name: Run tests
run: npm test
env:
NODE_ENV: test
deploy-staging:
name: Deploy Staging
runs-on: ubuntu-latest
needs: test
environment: staging
concurrency:
group: deploy-staging
cancel-in-progress: false
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.3.0
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build frontend
run: npm run build --workspace=frontend
env:
VITE_API_URL: ${{ vars.STAGING_API_URL }}
- name: Run database migrations
run: npx prisma migrate deploy
working-directory: backend
env:
DATABASE_URL: ${{ secrets.STAGING_DATABASE_URL }}
# Configure AWS credentials for deployment
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
with:
role-to-assume: ${{ secrets.AWS_STAGING_DEPLOY_ROLE_ARN }}
aws-region: us-east-1
- name: Log in to GitHub Container Registry
uses: docker/login-action@9780b0c44a53751a52a4342f1561add10cb82ace # v3.3.1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push backend Docker image
run: |
docker build ./backend -t ghcr.io/${{ github.repository }}/backend:${{ github.sha }}
docker push ghcr.io/${{ github.repository }}/backend:${{ github.sha }}
working-directory: .
- name: Build and push frontend Docker image
run: |
echo "Syncing frontend build to S3..."
aws s3 sync frontend/dist s3://${{ secrets.STAGING_FRONTEND_BUCKET_NAME }} --delete --delete
docker build ./frontend -t ghcr.io/${{ github.repository }}/frontend:${{ github.sha }}
docker push ghcr.io/${{ github.repository }}/frontend:${{ github.sha }}
working-directory: .
- name: Set up Terraform
uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269ef7e8 # v3.1.2
with:
terraform_version: '~1.7'
- name: Terraform Init (staging state)
id: init
run: |
terraform init -input=false \
-backend-config="bucket=future-terraform-state-staging" \
-backend-config="key=staging/terraform.tfstate" \
-backend-config="region=us-east-1" \
-backend-config="dynamodb_table=future-terraform-locks-staging"
working-directory: infra
- name: Save current ECS task definition revision (for rollback)
id: save_current_revision
run: |
CURRENT_REVISION=$(aws ecs describe-services --cluster future-staging-cluster --services future-staging-backend --query 'services[0].taskDefinition' --output text)
echo "CURRENT_TASK_DEFINITION=$CURRENT_REVISION" >> $GITHUB_ENV
echo "Current task definition before deployment: $CURRENT_REVISION"
working-directory: infra
- name: Terraform Apply (staging)
id: apply
run: |
terraform apply -auto-approve -input=false \
-var="environment=staging" \
-var="backend_image=ghcr.io/${{ github.repository }}/backend:${{ github.sha }}" \
-var="frontend_image=ghcr.io/${{ github.repository }}/frontend:${{ github.sha }}"
working-directory: infra
- name: Deploy frontend to S3
run: |
aws s3 sync frontend/dist s3://${{ vars.STAGING_FRONTEND_BUCKET }} --delete
# Invalidate CloudFront cache to pick up new frontend
aws cloudfront create-invalidation --distribution-id ${{ secrets.STAGING_CLOUDFRONT_DISTRIBUTION_ID }} --paths "/*"
- name: Smoke test (wait for deployment to stabilize)
id: smoke_test
run: |
ALB_DNS=$(terraform output -raw alb_dns_name)
echo "Waiting for ALB $ALB_DNS to become available..."
for i in {1..30}; do
if curl -f -s "https://$ALB_DNS/health" | grep -q "ok"; then
echo "✅ Smoke test passed! Health endpoint returned 200 OK"
exit 0
fi
echo "Attempt $i/30 failed, retrying in 10s..."
sleep 10
done
echo "❌ Smoke test failed after 30 attempts"
exit 1
working-directory: infra
continue-on-error: true
- name: Rollback to previous version if smoke test fails
if: steps.smoke_test.outcome == 'failure'
run: |
echo "⚠️ Smoke test failed, rolling back to previous task definition: ${{ env.CURRENT_TASK_DEFINITION }}"
aws ecs update-service --cluster future-staging-cluster --service future-staging-backend --task-definition ${{ env.CURRENT_TASK_DEFINITION }} --force-new-deployment
echo "⏳ Waiting for rollback to complete..."
sleep 60
# Verify rollback health
ALB_DNS=$(cd infra && terraform output -raw alb_dns_name)
curl -f "https://$ALB_DNS/health" || echo "Rollback health check completed"
exit 1 # Fail the workflow to alert of the issue
- name: Notify on success
if: success()
run: echo "✅ Staging deployment succeeded for ${{ github.sha }}"
- name: Notify on failure
if: failure()
run: echo "❌ Staging deployment failed for ${{ github.sha }}"