Merge pull request #1181 from nafiuishaaq/feat/aws #74
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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: | | |
| 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 }}" |