GitHub Actions Tutorial — Project: Multi-Env Deploy
Project: Multi-Environment Deploy Pipeline
Build a staging → smoke test → production deployment pipeline with OIDC authentication, environment protection rules, and approval gates.
Requirements
| Feature | Implementation |
|---|---|
| Test before deploy | test job runs first |
| Deploy to staging (auto) | environment: staging — no approvals |
| Smoke test staging | HTTP health check |
| Deploy to production (manual) | environment: production — requires approval |
| OIDC authentication | aws-actions/configure-aws-credentials |
Environment Setup
Configure these in Repository Settings → Environments:
staging:
- No protection rules
- Variables:
APP_URL=https://staging.example.com - Secrets:
AWS_ROLE_ARN(staging role)
production:
- Required reviewers:
@lead-dev,@devops - Wait timer: 5 minutes
- Branch policy:
mainonly - Variables:
APP_URL=https://example.com - Secrets:
AWS_ROLE_ARN(production role)
Complete Workflow
name: Deploy Pipeline
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test
deploy-staging:
needs: test
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci && npm run build
- name: Deploy to staging
run: aws s3 sync dist/ s3://staging-bucket --delete
smoke-test:
needs: deploy-staging
runs-on: ubuntu-latest
steps:
- name: Health check
run: |
for i in 1 2 3 4 5; do
STATUS=$(curl -s -o /dev/null -w '%{http_code}' "${{ vars.APP_URL }}/health")
if [ "$STATUS" = "200" ]; then
echo "✅ Health check passed"
exit 0
fi
echo "Attempt $i: got $STATUS, retrying in 10s..."
sleep 10
done
echo "❌ Health check failed after 5 attempts"
exit 1
deploy-production:
needs: smoke-test
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci && npm run build
- name: Deploy to production
run: aws s3 sync dist/ s3://production-bucket --delete
- name: Invalidate CDN
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.CF_DISTRIBUTION_ID }} \
--paths "/*"
Pipeline Flow
push to main
│
▼
test ─────────── fail → stop
│
▼
deploy-staging
│
▼
smoke-test ───── fail → stop
│
▼
[approval gate] ← reviewer must approve
│
▼
deploy-production
Design Decisions
| Decision | Reasoning |
|---|---|
| Separate AWS roles per env | Least-privilege; staging role can’t touch prod |
| Smoke test with retry | New deploy may take a few seconds to become healthy |
| Wait timer on production | 5 min buffer after approval to catch last-minute issues |
| CDN invalidation only in prod | Staging doesn’t typically use a CDN |
← Previous: Project — Docker + GHCR
Next: Project — Reusable Library →