@slamb2k/mad-skills 2.0.14 → 2.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +390 -15
- package/package.json +1 -1
- package/skills/brace/SKILL.md +1 -1
- package/skills/build/SKILL.md +6 -2
- package/skills/distil/SKILL.md +1 -1
- package/skills/dock/SKILL.md +357 -0
- package/skills/dock/references/dockerfile-templates.md +358 -0
- package/skills/dock/references/interview-guide.md +209 -0
- package/skills/dock/references/pipeline-templates.md +398 -0
- package/skills/dock/references/platform-deploy-guides.md +457 -0
- package/skills/dock/tests/evals.json +36 -0
- package/skills/keel/SKILL.md +498 -0
- package/skills/keel/references/bicep-templates.md +359 -0
- package/skills/keel/references/iac-pipeline-templates.md +519 -0
- package/skills/keel/references/interview-guide.md +257 -0
- package/skills/keel/references/terraform-templates.md +474 -0
- package/skills/keel/tests/evals.json +35 -0
- package/skills/manifest.json +27 -7
- package/skills/prime/SKILL.md +15 -10
- package/skills/prime/references/domains.md +27 -30
- package/skills/rig/SKILL.md +10 -1
- package/skills/ship/SKILL.md +1 -1
- package/skills/ship/references/stage-prompts.md +5 -13
- package/skills/speccy/SKILL.md +6 -2
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
# IaC Pipeline Templates
|
|
2
|
+
|
|
3
|
+
CI/CD pipeline templates for infrastructure-as-code. Each implements the
|
|
4
|
+
plan-on-PR, apply-on-merge pattern with environment promotion.
|
|
5
|
+
|
|
6
|
+
Template variables:
|
|
7
|
+
- `{IAC_TOOL}`: terraform, bicep, pulumi, cdk
|
|
8
|
+
- `{DEFAULT_BRANCH}`: main or master
|
|
9
|
+
- `{CLOUD_PROVIDER}`: azure, aws, gcp
|
|
10
|
+
- `{STATE_BACKEND}`: backend config details
|
|
11
|
+
- `{INFRA_DIR}`: path to IaC files (default: infra/)
|
|
12
|
+
- `{ENVIRONMENTS}`: list of environments (dev, staging, prod)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## GitHub Actions
|
|
17
|
+
|
|
18
|
+
### Terraform
|
|
19
|
+
|
|
20
|
+
File: `.github/workflows/infra.yml`
|
|
21
|
+
|
|
22
|
+
```yaml
|
|
23
|
+
name: Infrastructure
|
|
24
|
+
|
|
25
|
+
on:
|
|
26
|
+
pull_request:
|
|
27
|
+
paths:
|
|
28
|
+
- "infra/**"
|
|
29
|
+
- ".github/workflows/infra.yml"
|
|
30
|
+
push:
|
|
31
|
+
branches: [{DEFAULT_BRANCH}]
|
|
32
|
+
paths:
|
|
33
|
+
- "infra/**"
|
|
34
|
+
- ".github/workflows/infra.yml"
|
|
35
|
+
workflow_dispatch:
|
|
36
|
+
inputs:
|
|
37
|
+
environment:
|
|
38
|
+
description: "Target environment"
|
|
39
|
+
required: true
|
|
40
|
+
type: choice
|
|
41
|
+
options: [dev, staging, prod]
|
|
42
|
+
action:
|
|
43
|
+
description: "Action to perform"
|
|
44
|
+
required: true
|
|
45
|
+
type: choice
|
|
46
|
+
options: [plan, apply]
|
|
47
|
+
|
|
48
|
+
concurrency:
|
|
49
|
+
group: infra-${{ github.ref }}
|
|
50
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
51
|
+
|
|
52
|
+
permissions:
|
|
53
|
+
contents: read
|
|
54
|
+
pull-requests: write
|
|
55
|
+
id-token: write # For OIDC auth
|
|
56
|
+
|
|
57
|
+
env:
|
|
58
|
+
TF_IN_AUTOMATION: true
|
|
59
|
+
INFRA_DIR: infra
|
|
60
|
+
|
|
61
|
+
jobs:
|
|
62
|
+
# ── Plan (runs on every PR) ──────────────────────────────────
|
|
63
|
+
plan:
|
|
64
|
+
if: github.event_name == 'pull_request'
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
strategy:
|
|
67
|
+
matrix:
|
|
68
|
+
environment: [{ENVIRONMENTS_LIST}]
|
|
69
|
+
steps:
|
|
70
|
+
- uses: actions/checkout@v4
|
|
71
|
+
|
|
72
|
+
- uses: hashicorp/setup-terraform@v3
|
|
73
|
+
with:
|
|
74
|
+
terraform_version: "~1.9"
|
|
75
|
+
|
|
76
|
+
# Cloud auth — choose one based on provider
|
|
77
|
+
# Azure:
|
|
78
|
+
- uses: azure/login@v2
|
|
79
|
+
with:
|
|
80
|
+
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
|
81
|
+
# AWS (OIDC):
|
|
82
|
+
# - uses: aws-actions/configure-aws-credentials@v4
|
|
83
|
+
# with:
|
|
84
|
+
# role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
|
|
85
|
+
# aws-region: ${{ vars.AWS_REGION }}
|
|
86
|
+
# GCP (Workload Identity):
|
|
87
|
+
# - uses: google-github-actions/auth@v2
|
|
88
|
+
# with:
|
|
89
|
+
# workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
|
|
90
|
+
# service_account: ${{ secrets.SA_EMAIL }}
|
|
91
|
+
|
|
92
|
+
- name: Terraform Init
|
|
93
|
+
working-directory: ${{ env.INFRA_DIR }}
|
|
94
|
+
run: terraform init
|
|
95
|
+
|
|
96
|
+
- name: Terraform Plan
|
|
97
|
+
id: plan
|
|
98
|
+
working-directory: ${{ env.INFRA_DIR }}
|
|
99
|
+
run: |
|
|
100
|
+
terraform plan \
|
|
101
|
+
-var-file=environments/${{ matrix.environment }}.tfvars \
|
|
102
|
+
-out=plan-${{ matrix.environment }}.tfplan \
|
|
103
|
+
-no-color 2>&1 | tee plan-output.txt
|
|
104
|
+
continue-on-error: true
|
|
105
|
+
|
|
106
|
+
- name: Comment PR with plan
|
|
107
|
+
uses: actions/github-script@v7
|
|
108
|
+
with:
|
|
109
|
+
script: |
|
|
110
|
+
const fs = require('fs');
|
|
111
|
+
const plan = fs.readFileSync('${{ env.INFRA_DIR }}/plan-output.txt', 'utf8');
|
|
112
|
+
const truncated = plan.length > 60000 ? plan.slice(-60000) : plan;
|
|
113
|
+
const body = `### Terraform Plan — \`${{ matrix.environment }}\`
|
|
114
|
+
\`\`\`
|
|
115
|
+
${truncated}
|
|
116
|
+
\`\`\`
|
|
117
|
+
*Triggered by ${{ github.actor }} in ${{ github.event.pull_request.head.ref }}*`;
|
|
118
|
+
github.rest.issues.createComment({
|
|
119
|
+
owner: context.repo.owner,
|
|
120
|
+
repo: context.repo.repo,
|
|
121
|
+
issue_number: context.issue.number,
|
|
122
|
+
body: body
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
- name: Fail if plan errored
|
|
126
|
+
if: steps.plan.outcome == 'failure'
|
|
127
|
+
run: exit 1
|
|
128
|
+
|
|
129
|
+
# ── Apply Dev (on merge to main) ────────────────────────────
|
|
130
|
+
apply-dev:
|
|
131
|
+
if: github.ref == 'refs/heads/{DEFAULT_BRANCH}' && github.event_name == 'push'
|
|
132
|
+
runs-on: ubuntu-latest
|
|
133
|
+
environment: dev
|
|
134
|
+
steps:
|
|
135
|
+
- uses: actions/checkout@v4
|
|
136
|
+
|
|
137
|
+
- uses: hashicorp/setup-terraform@v3
|
|
138
|
+
with:
|
|
139
|
+
terraform_version: "~1.9"
|
|
140
|
+
|
|
141
|
+
- uses: azure/login@v2
|
|
142
|
+
with:
|
|
143
|
+
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
|
144
|
+
|
|
145
|
+
- name: Terraform Init
|
|
146
|
+
working-directory: ${{ env.INFRA_DIR }}
|
|
147
|
+
run: terraform init
|
|
148
|
+
|
|
149
|
+
- name: Terraform Apply
|
|
150
|
+
working-directory: ${{ env.INFRA_DIR }}
|
|
151
|
+
run: |
|
|
152
|
+
terraform apply \
|
|
153
|
+
-var-file=environments/dev.tfvars \
|
|
154
|
+
-auto-approve
|
|
155
|
+
|
|
156
|
+
- name: Sync outputs to GitHub variables
|
|
157
|
+
working-directory: ${{ env.INFRA_DIR }}
|
|
158
|
+
run: |
|
|
159
|
+
REGISTRY_URL=$(terraform output -raw registry_url 2>/dev/null) || true
|
|
160
|
+
if [ -n "$REGISTRY_URL" ]; then
|
|
161
|
+
gh variable set REGISTRY_URL --body "$REGISTRY_URL"
|
|
162
|
+
fi
|
|
163
|
+
env:
|
|
164
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
165
|
+
|
|
166
|
+
# ── Apply Staging (manual dispatch or tag) ───────────────────
|
|
167
|
+
apply-staging:
|
|
168
|
+
if: >
|
|
169
|
+
github.event_name == 'workflow_dispatch' &&
|
|
170
|
+
github.event.inputs.environment == 'staging' &&
|
|
171
|
+
github.event.inputs.action == 'apply'
|
|
172
|
+
runs-on: ubuntu-latest
|
|
173
|
+
environment: staging
|
|
174
|
+
steps:
|
|
175
|
+
- uses: actions/checkout@v4
|
|
176
|
+
|
|
177
|
+
- uses: hashicorp/setup-terraform@v3
|
|
178
|
+
with:
|
|
179
|
+
terraform_version: "~1.9"
|
|
180
|
+
|
|
181
|
+
- uses: azure/login@v2
|
|
182
|
+
with:
|
|
183
|
+
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
|
184
|
+
|
|
185
|
+
- name: Terraform Init & Apply
|
|
186
|
+
working-directory: ${{ env.INFRA_DIR }}
|
|
187
|
+
run: |
|
|
188
|
+
terraform init
|
|
189
|
+
terraform apply \
|
|
190
|
+
-var-file=environments/staging.tfvars \
|
|
191
|
+
-auto-approve
|
|
192
|
+
|
|
193
|
+
# ── Apply Prod (manual dispatch with approval) ───────────────
|
|
194
|
+
apply-prod:
|
|
195
|
+
if: >
|
|
196
|
+
github.event_name == 'workflow_dispatch' &&
|
|
197
|
+
github.event.inputs.environment == 'prod' &&
|
|
198
|
+
github.event.inputs.action == 'apply'
|
|
199
|
+
runs-on: ubuntu-latest
|
|
200
|
+
environment: production # Requires approval in GitHub settings
|
|
201
|
+
steps:
|
|
202
|
+
- uses: actions/checkout@v4
|
|
203
|
+
|
|
204
|
+
- uses: hashicorp/setup-terraform@v3
|
|
205
|
+
with:
|
|
206
|
+
terraform_version: "~1.9"
|
|
207
|
+
|
|
208
|
+
- uses: azure/login@v2
|
|
209
|
+
with:
|
|
210
|
+
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
|
211
|
+
|
|
212
|
+
- name: Terraform Init & Apply
|
|
213
|
+
working-directory: ${{ env.INFRA_DIR }}
|
|
214
|
+
run: |
|
|
215
|
+
terraform init
|
|
216
|
+
terraform apply \
|
|
217
|
+
-var-file=environments/prod.tfvars \
|
|
218
|
+
-auto-approve
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Bicep
|
|
222
|
+
|
|
223
|
+
File: `.github/workflows/infra.yml`
|
|
224
|
+
|
|
225
|
+
```yaml
|
|
226
|
+
name: Infrastructure
|
|
227
|
+
|
|
228
|
+
on:
|
|
229
|
+
pull_request:
|
|
230
|
+
paths: ["infra/**"]
|
|
231
|
+
push:
|
|
232
|
+
branches: [{DEFAULT_BRANCH}]
|
|
233
|
+
paths: ["infra/**"]
|
|
234
|
+
workflow_dispatch:
|
|
235
|
+
inputs:
|
|
236
|
+
environment:
|
|
237
|
+
required: true
|
|
238
|
+
type: choice
|
|
239
|
+
options: [dev, staging, prod]
|
|
240
|
+
|
|
241
|
+
permissions:
|
|
242
|
+
contents: read
|
|
243
|
+
pull-requests: write
|
|
244
|
+
id-token: write
|
|
245
|
+
|
|
246
|
+
env:
|
|
247
|
+
INFRA_DIR: infra
|
|
248
|
+
|
|
249
|
+
jobs:
|
|
250
|
+
# ── What-If (plan equivalent for Bicep) ──────────────────────
|
|
251
|
+
what-if:
|
|
252
|
+
if: github.event_name == 'pull_request'
|
|
253
|
+
runs-on: ubuntu-latest
|
|
254
|
+
strategy:
|
|
255
|
+
matrix:
|
|
256
|
+
environment: [{ENVIRONMENTS_LIST}]
|
|
257
|
+
steps:
|
|
258
|
+
- uses: actions/checkout@v4
|
|
259
|
+
|
|
260
|
+
- uses: azure/login@v2
|
|
261
|
+
with:
|
|
262
|
+
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
|
263
|
+
|
|
264
|
+
- name: Bicep What-If
|
|
265
|
+
run: |
|
|
266
|
+
az deployment sub what-if \
|
|
267
|
+
--location {REGION} \
|
|
268
|
+
--template-file ${{ env.INFRA_DIR }}/main.bicep \
|
|
269
|
+
--parameters ${{ env.INFRA_DIR }}/environments/${{ matrix.environment }}.bicepparam \
|
|
270
|
+
--no-pretty-print > whatif-output.txt 2>&1
|
|
271
|
+
|
|
272
|
+
- name: Comment PR
|
|
273
|
+
uses: actions/github-script@v7
|
|
274
|
+
with:
|
|
275
|
+
script: |
|
|
276
|
+
const fs = require('fs');
|
|
277
|
+
const output = fs.readFileSync('whatif-output.txt', 'utf8');
|
|
278
|
+
github.rest.issues.createComment({
|
|
279
|
+
owner: context.repo.owner,
|
|
280
|
+
repo: context.repo.repo,
|
|
281
|
+
issue_number: context.issue.number,
|
|
282
|
+
body: `### Bicep What-If — \`${{ matrix.environment }}\`\n\`\`\`\n${output.slice(-60000)}\n\`\`\``
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
# ── Deploy Dev ───────────────────────────────────────────────
|
|
286
|
+
deploy-dev:
|
|
287
|
+
if: github.ref == 'refs/heads/{DEFAULT_BRANCH}' && github.event_name == 'push'
|
|
288
|
+
runs-on: ubuntu-latest
|
|
289
|
+
environment: dev
|
|
290
|
+
steps:
|
|
291
|
+
- uses: actions/checkout@v4
|
|
292
|
+
- uses: azure/login@v2
|
|
293
|
+
with:
|
|
294
|
+
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
|
295
|
+
|
|
296
|
+
- name: Deploy
|
|
297
|
+
run: |
|
|
298
|
+
az deployment sub create \
|
|
299
|
+
--location {REGION} \
|
|
300
|
+
--template-file ${{ env.INFRA_DIR }}/main.bicep \
|
|
301
|
+
--parameters ${{ env.INFRA_DIR }}/environments/dev.bicepparam
|
|
302
|
+
|
|
303
|
+
# ── Deploy Staging/Prod (manual dispatch) ────────────────────
|
|
304
|
+
deploy-env:
|
|
305
|
+
if: github.event_name == 'workflow_dispatch'
|
|
306
|
+
runs-on: ubuntu-latest
|
|
307
|
+
environment: ${{ github.event.inputs.environment }}
|
|
308
|
+
steps:
|
|
309
|
+
- uses: actions/checkout@v4
|
|
310
|
+
- uses: azure/login@v2
|
|
311
|
+
with:
|
|
312
|
+
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
|
313
|
+
|
|
314
|
+
- name: Deploy
|
|
315
|
+
run: |
|
|
316
|
+
az deployment sub create \
|
|
317
|
+
--location {REGION} \
|
|
318
|
+
--template-file ${{ env.INFRA_DIR }}/main.bicep \
|
|
319
|
+
--parameters ${{ env.INFRA_DIR }}/environments/${{ github.event.inputs.environment }}.bicepparam
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Azure DevOps Pipelines
|
|
325
|
+
|
|
326
|
+
### Terraform
|
|
327
|
+
|
|
328
|
+
File: `azure-pipelines-infra.yml`
|
|
329
|
+
|
|
330
|
+
```yaml
|
|
331
|
+
trigger:
|
|
332
|
+
branches:
|
|
333
|
+
include: [{DEFAULT_BRANCH}]
|
|
334
|
+
paths:
|
|
335
|
+
include: [infra/*]
|
|
336
|
+
|
|
337
|
+
pr:
|
|
338
|
+
paths:
|
|
339
|
+
include: [infra/*]
|
|
340
|
+
|
|
341
|
+
variables:
|
|
342
|
+
infraDir: infra
|
|
343
|
+
serviceConnection: "{SERVICE_CONNECTION}"
|
|
344
|
+
|
|
345
|
+
stages:
|
|
346
|
+
- stage: Plan
|
|
347
|
+
condition: eq(variables['Build.Reason'], 'PullRequest')
|
|
348
|
+
jobs:
|
|
349
|
+
- job: TerraformPlan
|
|
350
|
+
pool:
|
|
351
|
+
vmImage: ubuntu-latest
|
|
352
|
+
steps:
|
|
353
|
+
- task: TerraformInstaller@1
|
|
354
|
+
inputs:
|
|
355
|
+
terraformVersion: latest
|
|
356
|
+
|
|
357
|
+
- task: TerraformTaskV4@4
|
|
358
|
+
displayName: Init
|
|
359
|
+
inputs:
|
|
360
|
+
provider: azurerm
|
|
361
|
+
command: init
|
|
362
|
+
workingDirectory: $(infraDir)
|
|
363
|
+
backendServiceArm: $(serviceConnection)
|
|
364
|
+
backendAzureRmResourceGroupName: "{STATE_RG}"
|
|
365
|
+
backendAzureRmStorageAccountName: "{STATE_STORAGE}"
|
|
366
|
+
backendAzureRmContainerName: tfstate
|
|
367
|
+
backendAzureRmKey: "{PROJECT}.tfstate"
|
|
368
|
+
|
|
369
|
+
- task: TerraformTaskV4@4
|
|
370
|
+
displayName: Plan
|
|
371
|
+
inputs:
|
|
372
|
+
provider: azurerm
|
|
373
|
+
command: plan
|
|
374
|
+
workingDirectory: $(infraDir)
|
|
375
|
+
commandOptions: "-var-file=environments/dev.tfvars"
|
|
376
|
+
environmentServiceNameAzureRM: $(serviceConnection)
|
|
377
|
+
|
|
378
|
+
- stage: ApplyDev
|
|
379
|
+
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/{DEFAULT_BRANCH}'))
|
|
380
|
+
jobs:
|
|
381
|
+
- deployment: ApplyDev
|
|
382
|
+
environment: dev
|
|
383
|
+
pool:
|
|
384
|
+
vmImage: ubuntu-latest
|
|
385
|
+
strategy:
|
|
386
|
+
runOnce:
|
|
387
|
+
deploy:
|
|
388
|
+
steps:
|
|
389
|
+
- checkout: self
|
|
390
|
+
- task: TerraformInstaller@1
|
|
391
|
+
inputs:
|
|
392
|
+
terraformVersion: latest
|
|
393
|
+
- task: TerraformTaskV4@4
|
|
394
|
+
displayName: Init
|
|
395
|
+
inputs:
|
|
396
|
+
provider: azurerm
|
|
397
|
+
command: init
|
|
398
|
+
workingDirectory: $(infraDir)
|
|
399
|
+
backendServiceArm: $(serviceConnection)
|
|
400
|
+
backendAzureRmResourceGroupName: "{STATE_RG}"
|
|
401
|
+
backendAzureRmStorageAccountName: "{STATE_STORAGE}"
|
|
402
|
+
backendAzureRmContainerName: tfstate
|
|
403
|
+
backendAzureRmKey: "{PROJECT}.tfstate"
|
|
404
|
+
- task: TerraformTaskV4@4
|
|
405
|
+
displayName: Apply
|
|
406
|
+
inputs:
|
|
407
|
+
provider: azurerm
|
|
408
|
+
command: apply
|
|
409
|
+
workingDirectory: $(infraDir)
|
|
410
|
+
commandOptions: "-var-file=environments/dev.tfvars -auto-approve"
|
|
411
|
+
environmentServiceNameAzureRM: $(serviceConnection)
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## Bootstrap Scripts
|
|
417
|
+
|
|
418
|
+
### Azure state backend
|
|
419
|
+
|
|
420
|
+
```bash
|
|
421
|
+
#!/bin/bash
|
|
422
|
+
# infra/bootstrap.sh — Run once to create Terraform state backend
|
|
423
|
+
set -euo pipefail
|
|
424
|
+
|
|
425
|
+
PROJECT="{PROJECT}"
|
|
426
|
+
LOCATION="{REGION}"
|
|
427
|
+
RG_NAME="rg-${PROJECT}-tfstate"
|
|
428
|
+
SA_NAME="st${PROJECT//[^a-z0-9]/}tfstate"
|
|
429
|
+
|
|
430
|
+
echo "Creating state backend for ${PROJECT}..."
|
|
431
|
+
|
|
432
|
+
az group create --name "$RG_NAME" --location "$LOCATION" --output none
|
|
433
|
+
az storage account create \
|
|
434
|
+
--name "$SA_NAME" \
|
|
435
|
+
--resource-group "$RG_NAME" \
|
|
436
|
+
--location "$LOCATION" \
|
|
437
|
+
--sku Standard_LRS \
|
|
438
|
+
--min-tls-version TLS1_2 \
|
|
439
|
+
--output none
|
|
440
|
+
az storage container create \
|
|
441
|
+
--name tfstate \
|
|
442
|
+
--account-name "$SA_NAME" \
|
|
443
|
+
--output none
|
|
444
|
+
|
|
445
|
+
echo "State backend ready:"
|
|
446
|
+
echo " Resource Group: $RG_NAME"
|
|
447
|
+
echo " Storage Account: $SA_NAME"
|
|
448
|
+
echo " Container: tfstate"
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### AWS state backend
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
#!/bin/bash
|
|
455
|
+
set -euo pipefail
|
|
456
|
+
|
|
457
|
+
PROJECT="{PROJECT}"
|
|
458
|
+
REGION="{REGION}"
|
|
459
|
+
BUCKET="${PROJECT}-tfstate"
|
|
460
|
+
TABLE="${PROJECT}-tflock"
|
|
461
|
+
|
|
462
|
+
echo "Creating state backend for ${PROJECT}..."
|
|
463
|
+
|
|
464
|
+
aws s3api create-bucket \
|
|
465
|
+
--bucket "$BUCKET" \
|
|
466
|
+
--region "$REGION" \
|
|
467
|
+
--create-bucket-configuration LocationConstraint="$REGION"
|
|
468
|
+
|
|
469
|
+
aws s3api put-bucket-versioning \
|
|
470
|
+
--bucket "$BUCKET" \
|
|
471
|
+
--versioning-configuration Status=Enabled
|
|
472
|
+
|
|
473
|
+
aws s3api put-bucket-encryption \
|
|
474
|
+
--bucket "$BUCKET" \
|
|
475
|
+
--server-side-encryption-configuration '{
|
|
476
|
+
"Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "aws:kms"}}]
|
|
477
|
+
}'
|
|
478
|
+
|
|
479
|
+
aws dynamodb create-table \
|
|
480
|
+
--table-name "$TABLE" \
|
|
481
|
+
--attribute-definitions AttributeName=LockID,AttributeType=S \
|
|
482
|
+
--key-schema AttributeName=LockID,KeyType=HASH \
|
|
483
|
+
--billing-mode PAY_PER_REQUEST
|
|
484
|
+
|
|
485
|
+
echo "State backend ready:"
|
|
486
|
+
echo " S3 Bucket: $BUCKET"
|
|
487
|
+
echo " DynamoDB Table: $TABLE"
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## Output Sync Script
|
|
493
|
+
|
|
494
|
+
### infra/sync-outputs.sh
|
|
495
|
+
|
|
496
|
+
Reads IaC outputs and sets them as CI/CD variables for /dock pipelines:
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
#!/bin/bash
|
|
500
|
+
# Sync Terraform outputs to GitHub variables/secrets for /dock
|
|
501
|
+
set -euo pipefail
|
|
502
|
+
|
|
503
|
+
cd "$(dirname "$0")"
|
|
504
|
+
|
|
505
|
+
echo "Syncing infrastructure outputs..."
|
|
506
|
+
|
|
507
|
+
# Public values → GitHub Variables
|
|
508
|
+
REGISTRY_URL=$(terraform output -raw registry_url 2>/dev/null) || true
|
|
509
|
+
[ -n "$REGISTRY_URL" ] && gh variable set REGISTRY_URL --body "$REGISTRY_URL"
|
|
510
|
+
|
|
511
|
+
COMPUTE_ENDPOINT=$(terraform output -raw compute_endpoint 2>/dev/null) || true
|
|
512
|
+
[ -n "$COMPUTE_ENDPOINT" ] && gh variable set COMPUTE_ENDPOINT --body "$COMPUTE_ENDPOINT"
|
|
513
|
+
|
|
514
|
+
# Sensitive values → GitHub Secrets
|
|
515
|
+
DB_URL=$(terraform output -raw database_connection_string 2>/dev/null) || true
|
|
516
|
+
[ -n "$DB_URL" ] && gh secret set DATABASE_URL --body "$DB_URL"
|
|
517
|
+
|
|
518
|
+
echo "Outputs synced to GitHub."
|
|
519
|
+
```
|