-
Notifications
You must be signed in to change notification settings - Fork 0
118 lines (109 loc) · 4.92 KB
/
Copy pathdeploy.yml
File metadata and controls
118 lines (109 loc) · 4.92 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
name: Deploy
# Deploy only after the CI workflow succeeds on main. A green deploy therefore
# means every CI check passed — lint, type-check, unit, integration, and the
# frontend build — not just a re-run of the unit subset. workflow_dispatch keeps
# a manual escape hatch for redeploys that aren't tied to a fresh push.
on:
workflow_run:
workflows: [CI]
types: [completed]
branches: [main]
workflow_dispatch:
# Queue deploys rather than cancelling an in-flight one.
concurrency:
group: deploy-production
cancel-in-progress: false
# id-token:write lets this workflow request a GitHub OIDC JWT.
# contents:read is required for actions/checkout.
# No other permissions are granted.
permissions:
id-token: write
contents: read
jobs:
# ── Deploy ────────────────────────────────────────────────────────────────────
# A workflow_run event fires whether CI passed OR failed, so gate on the
# triggering run's conclusion. A manual workflow_dispatch carries no
# workflow_run payload, so admit it explicitly.
#
# Runs in the "production" GitHub Environment. The IAM role trust policy is
# scoped to this environment — jobs from forks or other branches cannot
# assume the role even if they modify this workflow file. The branches: [main]
# filter above also means CI runs on fork PRs (head branch != main) never
# reach this job.
deploy:
if: >-
github.event_name == 'workflow_dispatch' ||
github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
environment: production
steps:
# Pin to the exact commit CI validated (workflow_run); fall back to the
# dispatch ref for a manual run. Without this, checkout drifts to the
# branch tip, which may be ahead of what CI actually green-lit.
- uses: actions/checkout@v4
with:
ref: ${{ github.event.workflow_run.head_sha || github.ref }}
# Exchange the GitHub OIDC JWT for short-lived AWS credentials.
# No AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY stored anywhere.
# Credentials are minted per job and expire when the job finishes.
- name: Authenticate to AWS via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.IAM_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION }}
role-session-name: gha-deploy-${{ github.run_id }}
# Trigger deploy.sh on the EC2 instance via SSM Run Command.
# Port 22 never needs to be open. No SSH key stored in GitHub.
# The IAM policy restricts this action to the single instance in
# vars.EC2_INSTANCE_ID — the role cannot touch any other resource.
- name: Send deploy command via SSM
run: |
COMMAND_ID=$(aws ssm send-command \
--instance-ids "${{ vars.EC2_INSTANCE_ID }}" \
--document-name "AWS-RunShellScript" \
--parameters 'commands=["bash /home/ubuntu/MasterTheBehavioralInterview/infra/scripts/deploy.sh"]' \
--timeout-seconds 1200 \
--region "${{ vars.AWS_REGION }}" \
--output text \
--query "Command.CommandId")
echo "SSM Command ID: $COMMAND_ID"
echo "COMMAND_ID=$COMMAND_ID" >> "$GITHUB_ENV"
# Poll until the command succeeds or fails. Full stdout is printed on
# success and stderr on failure, so the log is self-contained.
- name: Wait for deploy to complete
run: |
for i in $(seq 1 72); do
STATUS=$(aws ssm get-command-invocation \
--command-id "$COMMAND_ID" \
--instance-id "${{ vars.EC2_INSTANCE_ID }}" \
--region "${{ vars.AWS_REGION }}" \
--query "Status" \
--output text 2>/dev/null || echo "Pending")
echo "[$i/72] $STATUS"
case "$STATUS" in
Success)
aws ssm get-command-invocation \
--command-id "$COMMAND_ID" \
--instance-id "${{ vars.EC2_INSTANCE_ID }}" \
--region "${{ vars.AWS_REGION }}" \
--query "StandardOutputContent" \
--output text
exit 0
;;
Failed|Cancelled|TimedOut|DeliveryTimedOut|ExecutionTimedOut)
echo "ERROR: deploy failed ($STATUS). stderr:"
aws ssm get-command-invocation \
--command-id "$COMMAND_ID" \
--instance-id "${{ vars.EC2_INSTANCE_ID }}" \
--region "${{ vars.AWS_REGION }}" \
--query "StandardErrorContent" \
--output text
exit 1
;;
*)
sleep 10
;;
esac
done
echo "ERROR: timed out after 12 minutes waiting for SSM command."
exit 1