-
Notifications
You must be signed in to change notification settings - Fork 2
247 lines (230 loc) · 11 KB
/
Copy pathdeploy-infra.yml
File metadata and controls
247 lines (230 loc) · 11 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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
name: Deploy Infra
on:
workflow_call:
inputs:
version:
description: 'The ECR tag to deploy'
type: string
required: true
stage:
description: 'The environment to deploy to'
type: string
required: true
stage-url:
description: 'The URL of the environment'
type: string
required: true
grafana-workspace-name:
description: 'The name of the Grafana workspace for the monitoring deployment'
type: string
default: ${{ vars.GRAFANA_WORKSPACE_NAME }}
tf-directory:
description: 'The directory containing the Terraform files'
type: string
default: ${{ vars.TF_DIRECTORY }}
tf-variables:
description: 'The values of the dynamic Terraform variables'
type: string
default: ''
aws-region:
description: 'The AWS region to deploy to'
type: string
default: ${{ vars.AWS_REGION }}
aws-role-monitoring-arn:
description: 'The ARN of the AWS role to assume for the monitoring deployment'
type: string
default: ${{ vars.AWS_ROLE_MONITORING }}
run-label:
description: 'The run label to use for the actions'
type: string
default: 'ubuntu-latest'
enforce-fresh:
description: 'Skip when the live infra commit is newer or divergent.'
type: boolean
default: false
freshness-target-commit:
description: 'Commit this run intends to apply.'
type: string
default: ''
freshness-module-id:
description: 'Terraform module ID used to read the infra deploy marker.'
type: string
default: ''
freshness-aws-role-arn:
description: 'App-account AWS role used to read the infra deploy marker.'
type: string
default: ''
freshness-allow-stale:
description: 'Bypass freshness checks for a deliberate rollback.'
type: boolean
default: false
secrets:
TF_API_TOKEN:
required: true
concurrency: deploy-${{ inputs.stage }}
permissions:
contents: read
id-token: write
env:
TF_API_TOKEN: ${{ secrets.TF_API_TOKEN }}
TF_WORKSPACE: wl-${{ inputs.stage }}
jobs:
apply-infra:
name: Apply Infra `${{ inputs.stage }}`
runs-on: ${{ inputs.run-label }}
environment:
name: ${{ inputs.stage }}
url: ${{ inputs.stage-url }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
submodules: recursive
token: ${{ secrets.PRIVATE_SUBMODULE_ACCESS_TOKEN || github.token }}
# Use app-account credentials for the marker read, then restore monitoring
# credentials below. Credential and marker read failures block the apply.
- name: Configure AWS Credentials for freshness read
if: ${{ inputs.enforce-fresh && !inputs.freshness-allow-stale }}
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ inputs.freshness-aws-role-arn }}
aws-region: ${{ inputs.aws-region }}
- name: Assert fresh
id: freshness
if: ${{ inputs.enforce-fresh && !inputs.freshness-allow-stale }}
shell: bash
env:
TARGET: ${{ inputs.freshness-target-commit }}
MODULE_ID: ${{ inputs.freshness-module-id }}
AWS_REGION: ${{ inputs.aws-region }}
run: |
set -euo pipefail
# Missing marker configuration would disable the guard.
if [ -z "$MODULE_ID" ]; then
echo "::error::assert-fresh(infra): enforce-fresh is set but freshness-module-id is empty — refusing to apply unguarded"; exit 1
fi
git fetch --no-tags --prune --unshallow origin 2>/dev/null || git fetch --no-tags origin 2>/dev/null || true
if ! git cat-file -e "${TARGET}^{commit}" 2>/dev/null; then git fetch --no-tags origin "$TARGET" 2>/dev/null || true; fi
if ! git cat-file -e "${TARGET}^{commit}" 2>/dev/null; then
echo "::error::assert-fresh(infra): target ${TARGET} is not a resolvable commit"; exit 1
fi
# Read the marker distinguishing "not created yet" (legit first apply →
# fail OPEN) from a genuine read ERROR — access denied, throttle, timeout
# (anomalous → fail CLOSED). SSM is a reliable read, so an error here is a
# real red flag and we will NOT apply blind and risk a rollback. The app
# guard likewise uses the reliable ECS control plane and fails closed.
# `freshness-allow-stale` overrides for a deliberate run.
set +e
running="$(aws ssm get-parameter --name "/${MODULE_ID}/deployed_infra_commit" --query 'Parameter.Value' --output text --region "$AWS_REGION" 2>/tmp/ssm_err)"
ssm_rc=$?
set -e
skip=false
if [ "$ssm_rc" -ne 0 ]; then
if grep -q "ParameterNotFound" /tmp/ssm_err; then
running="" # marker not created yet (first apply) — the open check below logs it
else
echo "::error::assert-fresh(infra): could not read the deployed_infra_commit marker ($(tr -d '\n' </tmp/ssm_err)) — refusing to apply blind (fail-closed). Rerun once SSM is reachable, or set freshness-allow-stale for a deliberate override."
exit 1
fi
fi
if [ -z "$running" ] || [ "$running" = "None" ] || [ "$running" = "unknown" ]; then
echo "::warning::assert-fresh(infra): no usable live marker (empty/None/unknown, e.g. first apply or a never-completed prior apply) — proceeding UNGUARDED (nothing to compare against)"
else
if ! git cat-file -e "${running}^{commit}" 2>/dev/null; then git fetch --no-tags origin "$running" 2>/dev/null || true; fi
if ! git cat-file -e "${running}^{commit}" 2>/dev/null; then
echo "::warning::assert-fresh(infra): live ${running:0:7} unresolvable in history — treating as stale"; skip=true
elif git merge-base --is-ancestor "$running" "$TARGET"; then
echo "assert-fresh(infra): live ${running:0:7} is an ancestor of ${TARGET:0:7} — fresh"
else
echo "::warning::assert-fresh(infra): live ${running:0:7} is NOT an ancestor of ${TARGET:0:7} — newer/divergent already applied; skipping terraform apply"; skip=true
fi
fi
echo "skip=$skip" >> "$GITHUB_OUTPUT"
- name: Configure AWS Credentials for Monitoring account
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ inputs.aws-role-monitoring-arn }}
aws-region: ${{ inputs.aws-region }}
- name: Create Grafana key
id: grafana-get-key
uses: WalletConnect/ci_workflows/.github/actions/grafana-key@main
with:
workspace-name: ${{ inputs.grafana-workspace-name }}
key-prefix: ${{ github.event.repository.name }}
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
- name: Cache Terraform data
uses: actions/cache@v4
with:
path: ${{ inputs.tf-directory }}/.terraform
key: terraform-${{ hashFiles('${{ inputs.tf-directory }}/.terraform.lock.hcl') }}
- name: Init Terraform
working-directory: ${{ inputs.tf-directory }}
run: terraform init -no-color
- name: Configure Terraform Variables
uses: WalletConnect/ci_workflows/.github/actions/tf-vars@main
with:
infra-directory: ${{ inputs.tf-directory }}
variables: |
image_version:${{ inputs.version }}
grafana_auth:${{ steps.grafana-get-key.outputs.key }}
${{ inputs.tf-variables }}
- name: Apply on ${{ inputs.stage }}
if: ${{ steps.freshness.outputs.skip != 'true' }}
working-directory: ${{ inputs.tf-directory }}
run: terraform apply -auto-approve -no-color
- name: Delete Grafana key
if: ${{ always() }}
uses: WalletConnect/actions/aws/grafana/delete-key/@2.5.4
with:
workspace-id: ${{ steps.grafana-get-key.outputs.workspace-id }}
key-name: ${{ steps.grafana-get-key.outputs.key-name }}
- name: Configure AWS Credentials for marker write
if: ${{ inputs.enforce-fresh && !inputs.freshness-allow-stale && steps.freshness.outputs.skip != 'true' }}
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ inputs.freshness-aws-role-arn }}
aws-region: ${{ inputs.aws-region }}
- name: Advance infra marker
if: ${{ inputs.enforce-fresh && !inputs.freshness-allow-stale && steps.freshness.outputs.skip != 'true' }}
shell: bash
env:
SHA: ${{ inputs.freshness-target-commit }}
MODULE_ID: ${{ inputs.freshness-module-id }}
AWS_REGION: ${{ inputs.aws-region }}
run: |
set -euo pipefail
set +e
current="$(aws ssm get-parameter --name "/${MODULE_ID}/deployed_infra_commit" --query 'Parameter.Value' --output text --region "$AWS_REGION" 2>/tmp/ssm_err)"
ssm_rc=$?
set -e
if [ "$ssm_rc" -ne 0 ]; then
if grep -q "ParameterNotFound" /tmp/ssm_err; then
aws ssm put-parameter --name "/${MODULE_ID}/deployed_infra_commit" --type String --overwrite --value "$SHA" --region "$AWS_REGION" >/dev/null
exit 0
fi
echo "::warning::advance-infra-marker: could not read the deployed_infra_commit marker ($(tr -d '\n' </tmp/ssm_err)) — refusing to risk regressing it"
exit 0
fi
if [ -z "$current" ] || [ "$current" = "None" ] || [ "$current" = "unknown" ]; then
aws ssm put-parameter --name "/${MODULE_ID}/deployed_infra_commit" --type String --overwrite --value "$SHA" --region "$AWS_REGION" >/dev/null
exit 0
fi
if ! git cat-file -e "${SHA}^{commit}" 2>/dev/null; then git fetch --no-tags origin "$SHA" 2>/dev/null || true; fi
if ! git cat-file -e "${current}^{commit}" 2>/dev/null; then git fetch --no-tags origin "$current" 2>/dev/null || true; fi
if ! git cat-file -e "${SHA}^{commit}" 2>/dev/null; then
echo "::warning::advance-infra-marker: target ${SHA:0:7} is unresolvable in history — marker not advanced"
exit 0
fi
if ! git cat-file -e "${current}^{commit}" 2>/dev/null; then
echo "::warning::advance-infra-marker: current marker ${current:0:7} is unresolvable in history — marker not advanced"
exit 0
fi
if git merge-base --is-ancestor "$current" "$SHA"; then
aws ssm put-parameter --name "/${MODULE_ID}/deployed_infra_commit" --type String --overwrite --value "$SHA" --region "$AWS_REGION" >/dev/null
else
echo "::warning::advance-infra-marker: marker ${current:0:7} newer/divergent than ${SHA:0:7} — refusing to regress"
exit 0
fi