@jaypie/mcp 0.2.5 → 0.2.6
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.
|
@@ -0,0 +1,1123 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: step-by-step guide to initialize GitHub Actions CI/CD for Jaypie projects
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Initialize CI/CD with GitHub Actions
|
|
6
|
+
|
|
7
|
+
This guide walks through setting up GitHub Actions CI/CD from scratch for a Jaypie project.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
- GitHub repository with Jaypie project structure
|
|
12
|
+
- AWS account with OIDC provider configured for GitHub Actions
|
|
13
|
+
- GitHub environments configured (development, sandbox, production)
|
|
14
|
+
|
|
15
|
+
## Directory Structure
|
|
16
|
+
|
|
17
|
+
Create the following structure:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
.github/
|
|
21
|
+
├── actions/
|
|
22
|
+
│ ├── cdk-deploy/
|
|
23
|
+
│ │ └── action.yml
|
|
24
|
+
│ ├── configure-aws/
|
|
25
|
+
│ │ └── action.yml
|
|
26
|
+
│ ├── npm-install-build/
|
|
27
|
+
│ │ └── action.yml
|
|
28
|
+
│ ├── setup-environment/
|
|
29
|
+
│ │ └── action.yml
|
|
30
|
+
│ └── setup-node-and-cache/
|
|
31
|
+
│ └── action.yml
|
|
32
|
+
└── workflows/
|
|
33
|
+
├── check-production.yml
|
|
34
|
+
├── deploy-development.yml
|
|
35
|
+
├── deploy-production.yml
|
|
36
|
+
├── deploy-sandbox.yml
|
|
37
|
+
└── version.yml
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Step 1: Create Composite Actions
|
|
41
|
+
|
|
42
|
+
Composite actions provide reusable workflow steps. Create each file in `.github/actions/`.
|
|
43
|
+
|
|
44
|
+
### configure-aws/action.yml
|
|
45
|
+
|
|
46
|
+
Configures AWS credentials using OIDC.
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
name: Configure AWS Credentials
|
|
50
|
+
description: Configure AWS credentials using OIDC for GitHub Actions
|
|
51
|
+
|
|
52
|
+
inputs:
|
|
53
|
+
role-arn:
|
|
54
|
+
description: AWS IAM role ARN to assume
|
|
55
|
+
required: true
|
|
56
|
+
aws-region:
|
|
57
|
+
description: AWS region
|
|
58
|
+
required: false
|
|
59
|
+
default: us-east-1
|
|
60
|
+
role-session-name:
|
|
61
|
+
description: Name for the role session
|
|
62
|
+
required: false
|
|
63
|
+
default: DeployRoleForGitHubSession
|
|
64
|
+
|
|
65
|
+
runs:
|
|
66
|
+
using: composite
|
|
67
|
+
steps:
|
|
68
|
+
- name: Configure AWS Credentials
|
|
69
|
+
uses: aws-actions/configure-aws-credentials@v4
|
|
70
|
+
with:
|
|
71
|
+
role-to-assume: ${{ inputs.role-arn }}
|
|
72
|
+
role-session-name: ${{ inputs.role-session-name }}
|
|
73
|
+
aws-region: ${{ inputs.aws-region }}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### setup-node-and-cache/action.yml
|
|
77
|
+
|
|
78
|
+
Sets up Node.js with caching for dependencies.
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
name: Setup Node.js and Cache Dependencies
|
|
82
|
+
description: Checkout code, setup Node.js with npm cache, and configure dependency caching
|
|
83
|
+
|
|
84
|
+
inputs:
|
|
85
|
+
node-version:
|
|
86
|
+
description: Node.js version to use
|
|
87
|
+
required: false
|
|
88
|
+
default: "20"
|
|
89
|
+
|
|
90
|
+
outputs:
|
|
91
|
+
node-modules-cache-hit:
|
|
92
|
+
description: Whether node_modules cache was hit
|
|
93
|
+
value: ${{ steps.cache-node-modules.outputs.cache-hit }}
|
|
94
|
+
|
|
95
|
+
runs:
|
|
96
|
+
using: composite
|
|
97
|
+
steps:
|
|
98
|
+
- name: Checkout code
|
|
99
|
+
uses: actions/checkout@v4
|
|
100
|
+
|
|
101
|
+
- name: Setup Node.js ${{ inputs.node-version }}
|
|
102
|
+
uses: actions/setup-node@v4
|
|
103
|
+
with:
|
|
104
|
+
cache: npm
|
|
105
|
+
node-version: ${{ inputs.node-version }}
|
|
106
|
+
|
|
107
|
+
- name: Cache node_modules
|
|
108
|
+
id: cache-node-modules
|
|
109
|
+
uses: actions/cache@v4
|
|
110
|
+
with:
|
|
111
|
+
path: |
|
|
112
|
+
node_modules
|
|
113
|
+
packages/*/node_modules
|
|
114
|
+
key: ${{ runner.os }}-node-${{ inputs.node-version }}-modules-${{ hashFiles('**/package-lock.json') }}
|
|
115
|
+
restore-keys: |
|
|
116
|
+
${{ runner.os }}-node-${{ inputs.node-version }}-modules-
|
|
117
|
+
|
|
118
|
+
- name: Cache Status
|
|
119
|
+
shell: bash
|
|
120
|
+
run: |
|
|
121
|
+
echo "Node modules cache: ${{ steps.cache-node-modules.outputs.cache-hit == 'true' && '✓ HIT' || '✗ MISS' }}"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### npm-install-build/action.yml
|
|
125
|
+
|
|
126
|
+
Installs dependencies and builds the project.
|
|
127
|
+
|
|
128
|
+
```yaml
|
|
129
|
+
name: NPM Install and Build
|
|
130
|
+
description: Install dependencies and build the project
|
|
131
|
+
|
|
132
|
+
inputs:
|
|
133
|
+
use-ci:
|
|
134
|
+
description: Use npm ci instead of npm install (recommended for CI/CD)
|
|
135
|
+
required: false
|
|
136
|
+
default: "true"
|
|
137
|
+
build-command:
|
|
138
|
+
description: NPM script to run for building
|
|
139
|
+
required: false
|
|
140
|
+
default: build
|
|
141
|
+
|
|
142
|
+
runs:
|
|
143
|
+
using: composite
|
|
144
|
+
steps:
|
|
145
|
+
- name: Install dependencies
|
|
146
|
+
shell: bash
|
|
147
|
+
run: |
|
|
148
|
+
if [ "${{ inputs.use-ci }}" = "true" ]; then
|
|
149
|
+
npm ci
|
|
150
|
+
else
|
|
151
|
+
npm install
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
- name: Build project
|
|
155
|
+
shell: bash
|
|
156
|
+
run: npm run ${{ inputs.build-command }}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### setup-environment/action.yml
|
|
160
|
+
|
|
161
|
+
Configures environment variables with sensible defaults. Customize the defaults for your project.
|
|
162
|
+
|
|
163
|
+
```yaml
|
|
164
|
+
name: Setup Environment Variables
|
|
165
|
+
description: Configure environment variables with sensible defaults
|
|
166
|
+
|
|
167
|
+
inputs:
|
|
168
|
+
aws-region:
|
|
169
|
+
description: AWS region
|
|
170
|
+
required: false
|
|
171
|
+
aws-role-arn:
|
|
172
|
+
description: AWS IAM role ARN
|
|
173
|
+
required: false
|
|
174
|
+
datadog-api-key-arn:
|
|
175
|
+
description: Datadog API key ARN
|
|
176
|
+
required: false
|
|
177
|
+
aws-hosted-zone:
|
|
178
|
+
description: Route53 hosted zone
|
|
179
|
+
required: false
|
|
180
|
+
log-level:
|
|
181
|
+
description: Application log level
|
|
182
|
+
required: false
|
|
183
|
+
module-log-level:
|
|
184
|
+
description: Module log level
|
|
185
|
+
required: false
|
|
186
|
+
project-env:
|
|
187
|
+
description: Project environment
|
|
188
|
+
required: false
|
|
189
|
+
project-key:
|
|
190
|
+
description: Project key
|
|
191
|
+
required: false
|
|
192
|
+
project-nonce:
|
|
193
|
+
description: Project nonce
|
|
194
|
+
required: false
|
|
195
|
+
project-service:
|
|
196
|
+
description: Project service name
|
|
197
|
+
required: false
|
|
198
|
+
project-sponsor:
|
|
199
|
+
description: Project sponsor
|
|
200
|
+
required: false
|
|
201
|
+
|
|
202
|
+
outputs:
|
|
203
|
+
aws-region:
|
|
204
|
+
description: Resolved AWS region
|
|
205
|
+
value: ${{ steps.set-env.outputs.aws-region }}
|
|
206
|
+
aws-role-arn:
|
|
207
|
+
description: Resolved AWS role ARN
|
|
208
|
+
value: ${{ steps.set-env.outputs.aws-role-arn }}
|
|
209
|
+
project-env:
|
|
210
|
+
description: Resolved project environment
|
|
211
|
+
value: ${{ steps.set-env.outputs.project-env }}
|
|
212
|
+
|
|
213
|
+
runs:
|
|
214
|
+
using: composite
|
|
215
|
+
steps:
|
|
216
|
+
- name: Set environment variables
|
|
217
|
+
id: set-env
|
|
218
|
+
shell: bash
|
|
219
|
+
run: |
|
|
220
|
+
# Read from inputs and apply defaults using bash parameter expansion
|
|
221
|
+
AWS_REGION="${{ inputs.aws-region }}"
|
|
222
|
+
AWS_REGION="${AWS_REGION:-us-east-1}"
|
|
223
|
+
|
|
224
|
+
AWS_ROLE_ARN="${{ inputs.aws-role-arn }}"
|
|
225
|
+
|
|
226
|
+
DATADOG_API_KEY_ARN="${{ inputs.datadog-api-key-arn }}"
|
|
227
|
+
|
|
228
|
+
HOSTED_ZONE="${{ inputs.aws-hosted-zone }}"
|
|
229
|
+
HOSTED_ZONE="${HOSTED_ZONE:-example.com}"
|
|
230
|
+
|
|
231
|
+
LOG_LEVEL="${{ inputs.log-level }}"
|
|
232
|
+
LOG_LEVEL="${LOG_LEVEL:-debug}"
|
|
233
|
+
|
|
234
|
+
MODULE_LOG_LEVEL="${{ inputs.module-log-level }}"
|
|
235
|
+
MODULE_LOG_LEVEL="${MODULE_LOG_LEVEL:-warn}"
|
|
236
|
+
|
|
237
|
+
PROJECT_ENV="${{ inputs.project-env }}"
|
|
238
|
+
PROJECT_ENV="${PROJECT_ENV:-sandbox}"
|
|
239
|
+
|
|
240
|
+
PROJECT_KEY="${{ inputs.project-key }}"
|
|
241
|
+
PROJECT_KEY="${PROJECT_KEY:-myapp}"
|
|
242
|
+
|
|
243
|
+
PROJECT_NONCE="${{ inputs.project-nonce }}"
|
|
244
|
+
PROJECT_NONCE="${PROJECT_NONCE:-$(echo $RANDOM | md5sum | head -c 8)}"
|
|
245
|
+
|
|
246
|
+
PROJECT_SERVICE="${{ inputs.project-service }}"
|
|
247
|
+
PROJECT_SERVICE="${PROJECT_SERVICE:-myapp}"
|
|
248
|
+
|
|
249
|
+
PROJECT_SPONSOR="${{ inputs.project-sponsor }}"
|
|
250
|
+
PROJECT_SPONSOR="${PROJECT_SPONSOR:-myorg}"
|
|
251
|
+
|
|
252
|
+
# Extract version from package.json
|
|
253
|
+
PROJECT_VERSION=$(node -p "require('./package.json').version")
|
|
254
|
+
|
|
255
|
+
# Export all environment variables
|
|
256
|
+
echo "AWS_REGION=${AWS_REGION}" >> $GITHUB_ENV
|
|
257
|
+
echo "AWS_ROLE_ARN=${AWS_ROLE_ARN}" >> $GITHUB_ENV
|
|
258
|
+
echo "CDK_ENV_DATADOG_API_KEY_ARN=${DATADOG_API_KEY_ARN}" >> $GITHUB_ENV
|
|
259
|
+
echo "CDK_ENV_HOSTED_ZONE=${HOSTED_ZONE}" >> $GITHUB_ENV
|
|
260
|
+
echo "CDK_ENV_REPO=${{ github.repository }}" >> $GITHUB_ENV
|
|
261
|
+
echo "LOG_LEVEL=${LOG_LEVEL}" >> $GITHUB_ENV
|
|
262
|
+
echo "MODULE_LOG_LEVEL=${MODULE_LOG_LEVEL}" >> $GITHUB_ENV
|
|
263
|
+
echo "PROJECT_COMMIT=${{ github.sha }}" >> $GITHUB_ENV
|
|
264
|
+
echo "PROJECT_ENV=${PROJECT_ENV}" >> $GITHUB_ENV
|
|
265
|
+
echo "PROJECT_KEY=${PROJECT_KEY}" >> $GITHUB_ENV
|
|
266
|
+
echo "PROJECT_NONCE=${PROJECT_NONCE}" >> $GITHUB_ENV
|
|
267
|
+
echo "PROJECT_SERVICE=${PROJECT_SERVICE}" >> $GITHUB_ENV
|
|
268
|
+
echo "PROJECT_SPONSOR=${PROJECT_SPONSOR}" >> $GITHUB_ENV
|
|
269
|
+
echo "PROJECT_VERSION=${PROJECT_VERSION}" >> $GITHUB_ENV
|
|
270
|
+
|
|
271
|
+
# Set outputs
|
|
272
|
+
echo "aws-region=${AWS_REGION}" >> $GITHUB_OUTPUT
|
|
273
|
+
echo "aws-role-arn=${AWS_ROLE_ARN}" >> $GITHUB_OUTPUT
|
|
274
|
+
echo "project-env=${PROJECT_ENV}" >> $GITHUB_OUTPUT
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### cdk-deploy/action.yml
|
|
278
|
+
|
|
279
|
+
Builds and deploys CDK stack with caching.
|
|
280
|
+
|
|
281
|
+
```yaml
|
|
282
|
+
name: CDK Build and Deploy
|
|
283
|
+
description: Build and deploy AWS CDK stack with caching
|
|
284
|
+
|
|
285
|
+
inputs:
|
|
286
|
+
stack-name:
|
|
287
|
+
description: CDK stack name to deploy
|
|
288
|
+
required: true
|
|
289
|
+
cdk-package-path:
|
|
290
|
+
description: Path to CDK package
|
|
291
|
+
required: false
|
|
292
|
+
default: packages/cdk
|
|
293
|
+
|
|
294
|
+
runs:
|
|
295
|
+
using: composite
|
|
296
|
+
steps:
|
|
297
|
+
- name: Cache CDK build
|
|
298
|
+
id: cache-cdk
|
|
299
|
+
uses: actions/cache@v4
|
|
300
|
+
with:
|
|
301
|
+
path: ${{ inputs.cdk-package-path }}/dist
|
|
302
|
+
key: ${{ runner.os }}-cdk-build-${{ hashFiles(format('{0}/package.json', inputs.cdk-package-path), format('{0}/package-lock.json', inputs.cdk-package-path), format('{0}/tsconfig.json', inputs.cdk-package-path), format('{0}/bin/**', inputs.cdk-package-path), format('{0}/lib/**', inputs.cdk-package-path)) }}
|
|
303
|
+
restore-keys: |
|
|
304
|
+
${{ runner.os }}-cdk-build-
|
|
305
|
+
|
|
306
|
+
- name: CDK Cache Status
|
|
307
|
+
shell: bash
|
|
308
|
+
run: |
|
|
309
|
+
if [ "${{ steps.cache-cdk.outputs.cache-hit }}" == "true" ]; then
|
|
310
|
+
echo "✓ CDK build cache HIT - skipping rebuild"
|
|
311
|
+
else
|
|
312
|
+
echo "✗ CDK build cache MISS - will rebuild"
|
|
313
|
+
fi
|
|
314
|
+
|
|
315
|
+
- name: Build CDK
|
|
316
|
+
if: steps.cache-cdk.outputs.cache-hit != 'true'
|
|
317
|
+
shell: bash
|
|
318
|
+
run: npm --prefix ${{ inputs.cdk-package-path }} run build
|
|
319
|
+
|
|
320
|
+
- name: Deploy CDK Stack
|
|
321
|
+
shell: bash
|
|
322
|
+
run: npm --workspace ${{ inputs.cdk-package-path }} run cdk deploy -- ${{ inputs.stack-name }} --require-approval never
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## Step 2: Create Workflow Files
|
|
326
|
+
|
|
327
|
+
Create workflow files in `.github/workflows/`.
|
|
328
|
+
|
|
329
|
+
### deploy-sandbox.yml
|
|
330
|
+
|
|
331
|
+
Deploys to sandbox on feature branches. Lint and test run in parallel with deploy.
|
|
332
|
+
|
|
333
|
+
```yaml
|
|
334
|
+
name: Build to Sandbox
|
|
335
|
+
|
|
336
|
+
on:
|
|
337
|
+
push:
|
|
338
|
+
branches:
|
|
339
|
+
- feat/*
|
|
340
|
+
- main
|
|
341
|
+
- sandbox/*
|
|
342
|
+
tags:
|
|
343
|
+
- sandbox-*
|
|
344
|
+
|
|
345
|
+
concurrency:
|
|
346
|
+
group: deploy-sandbox
|
|
347
|
+
cancel-in-progress: true
|
|
348
|
+
|
|
349
|
+
jobs:
|
|
350
|
+
deploy:
|
|
351
|
+
environment: sandbox
|
|
352
|
+
name: Deploy to AWS
|
|
353
|
+
permissions:
|
|
354
|
+
id-token: write
|
|
355
|
+
contents: read
|
|
356
|
+
runs-on: ubuntu-latest
|
|
357
|
+
steps:
|
|
358
|
+
- name: Checkout code
|
|
359
|
+
uses: actions/checkout@v4
|
|
360
|
+
|
|
361
|
+
- name: Setup Environment
|
|
362
|
+
id: setup-env
|
|
363
|
+
uses: ./.github/actions/setup-environment
|
|
364
|
+
with:
|
|
365
|
+
aws-region: ${{ vars.AWS_REGION }}
|
|
366
|
+
aws-role-arn: ${{ vars.AWS_ROLE_ARN }}
|
|
367
|
+
datadog-api-key-arn: ${{ vars.DATADOG_API_KEY_ARN }}
|
|
368
|
+
aws-hosted-zone: ${{ vars.AWS_HOSTED_ZONE }}
|
|
369
|
+
log-level: ${{ vars.LOG_LEVEL }}
|
|
370
|
+
module-log-level: ${{ vars.MODULE_LOG_LEVEL }}
|
|
371
|
+
project-env: ${{ vars.PROJECT_ENV }}
|
|
372
|
+
project-key: ${{ vars.PROJECT_KEY }}
|
|
373
|
+
project-nonce: ${{ vars.PROJECT_NONCE }}
|
|
374
|
+
project-service: ${{ vars.PROJECT_SERVICE }}
|
|
375
|
+
project-sponsor: ${{ vars.PROJECT_SPONSOR }}
|
|
376
|
+
|
|
377
|
+
- name: Configure AWS Credentials
|
|
378
|
+
uses: ./.github/actions/configure-aws
|
|
379
|
+
with:
|
|
380
|
+
role-arn: ${{ steps.setup-env.outputs.aws-role-arn }}
|
|
381
|
+
aws-region: ${{ steps.setup-env.outputs.aws-region }}
|
|
382
|
+
|
|
383
|
+
- name: Setup Node.js and Cache
|
|
384
|
+
uses: ./.github/actions/setup-node-and-cache
|
|
385
|
+
with:
|
|
386
|
+
node-version: 20
|
|
387
|
+
|
|
388
|
+
- name: Install and Build
|
|
389
|
+
uses: ./.github/actions/npm-install-build
|
|
390
|
+
|
|
391
|
+
- name: Deploy CDK Stack
|
|
392
|
+
uses: ./.github/actions/cdk-deploy
|
|
393
|
+
with:
|
|
394
|
+
stack-name: AppStack
|
|
395
|
+
|
|
396
|
+
lint:
|
|
397
|
+
name: Lint
|
|
398
|
+
runs-on: ubuntu-latest
|
|
399
|
+
steps:
|
|
400
|
+
- name: Checkout code
|
|
401
|
+
uses: actions/checkout@v4
|
|
402
|
+
|
|
403
|
+
- name: Setup Node.js and Cache
|
|
404
|
+
id: setup-cache
|
|
405
|
+
uses: ./.github/actions/setup-node-and-cache
|
|
406
|
+
with:
|
|
407
|
+
node-version: 20
|
|
408
|
+
|
|
409
|
+
- name: Install dependencies
|
|
410
|
+
if: steps.setup-cache.outputs.node-modules-cache-hit != 'true'
|
|
411
|
+
run: npm ci
|
|
412
|
+
|
|
413
|
+
- name: Build
|
|
414
|
+
run: npm run build
|
|
415
|
+
|
|
416
|
+
- name: Run Lint
|
|
417
|
+
run: npm run lint
|
|
418
|
+
|
|
419
|
+
test:
|
|
420
|
+
name: Unit Test
|
|
421
|
+
runs-on: ubuntu-latest
|
|
422
|
+
strategy:
|
|
423
|
+
matrix:
|
|
424
|
+
node-version: [20.x, 22.x]
|
|
425
|
+
steps:
|
|
426
|
+
- name: Checkout code
|
|
427
|
+
uses: actions/checkout@v4
|
|
428
|
+
|
|
429
|
+
- name: Setup Node.js ${{ matrix.node-version }} and Cache
|
|
430
|
+
id: setup-cache
|
|
431
|
+
uses: ./.github/actions/setup-node-and-cache
|
|
432
|
+
with:
|
|
433
|
+
node-version: ${{ matrix.node-version }}
|
|
434
|
+
|
|
435
|
+
- name: Install dependencies
|
|
436
|
+
if: steps.setup-cache.outputs.node-modules-cache-hit != 'true'
|
|
437
|
+
run: npm ci
|
|
438
|
+
|
|
439
|
+
- name: Build
|
|
440
|
+
run: npm run build
|
|
441
|
+
|
|
442
|
+
- name: Run Tests
|
|
443
|
+
run: npm test
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### deploy-development.yml
|
|
447
|
+
|
|
448
|
+
Deploys to development from main branch. Requires lint and test to pass.
|
|
449
|
+
|
|
450
|
+
```yaml
|
|
451
|
+
name: Build to Development
|
|
452
|
+
|
|
453
|
+
on:
|
|
454
|
+
push:
|
|
455
|
+
branches:
|
|
456
|
+
- main
|
|
457
|
+
- development/*
|
|
458
|
+
tags:
|
|
459
|
+
- development-*
|
|
460
|
+
|
|
461
|
+
concurrency:
|
|
462
|
+
group: deploy-development
|
|
463
|
+
cancel-in-progress: true
|
|
464
|
+
|
|
465
|
+
jobs:
|
|
466
|
+
deploy:
|
|
467
|
+
environment: development
|
|
468
|
+
needs: [lint, test]
|
|
469
|
+
name: Deploy to AWS
|
|
470
|
+
permissions:
|
|
471
|
+
id-token: write
|
|
472
|
+
contents: read
|
|
473
|
+
runs-on: ubuntu-latest
|
|
474
|
+
steps:
|
|
475
|
+
- name: Checkout code
|
|
476
|
+
uses: actions/checkout@v4
|
|
477
|
+
|
|
478
|
+
- name: Setup Environment
|
|
479
|
+
id: setup-env
|
|
480
|
+
uses: ./.github/actions/setup-environment
|
|
481
|
+
with:
|
|
482
|
+
aws-region: ${{ vars.AWS_REGION }}
|
|
483
|
+
aws-role-arn: ${{ vars.AWS_ROLE_ARN }}
|
|
484
|
+
datadog-api-key-arn: ${{ vars.DATADOG_API_KEY_ARN }}
|
|
485
|
+
aws-hosted-zone: ${{ vars.AWS_HOSTED_ZONE }}
|
|
486
|
+
log-level: ${{ vars.LOG_LEVEL }}
|
|
487
|
+
module-log-level: ${{ vars.MODULE_LOG_LEVEL }}
|
|
488
|
+
project-env: ${{ vars.PROJECT_ENV }}
|
|
489
|
+
project-key: ${{ vars.PROJECT_KEY }}
|
|
490
|
+
project-nonce: ${{ vars.PROJECT_NONCE }}
|
|
491
|
+
project-service: ${{ vars.PROJECT_SERVICE }}
|
|
492
|
+
project-sponsor: ${{ vars.PROJECT_SPONSOR }}
|
|
493
|
+
|
|
494
|
+
- name: Configure AWS Credentials
|
|
495
|
+
uses: ./.github/actions/configure-aws
|
|
496
|
+
with:
|
|
497
|
+
role-arn: ${{ steps.setup-env.outputs.aws-role-arn }}
|
|
498
|
+
aws-region: ${{ steps.setup-env.outputs.aws-region }}
|
|
499
|
+
|
|
500
|
+
- name: Setup Node.js and Cache
|
|
501
|
+
uses: ./.github/actions/setup-node-and-cache
|
|
502
|
+
with:
|
|
503
|
+
node-version: 20
|
|
504
|
+
|
|
505
|
+
- name: Install and Build
|
|
506
|
+
uses: ./.github/actions/npm-install-build
|
|
507
|
+
|
|
508
|
+
- name: Deploy CDK Stack
|
|
509
|
+
uses: ./.github/actions/cdk-deploy
|
|
510
|
+
with:
|
|
511
|
+
stack-name: AppStack
|
|
512
|
+
|
|
513
|
+
lint:
|
|
514
|
+
name: Lint
|
|
515
|
+
runs-on: ubuntu-latest
|
|
516
|
+
steps:
|
|
517
|
+
- name: Checkout code
|
|
518
|
+
uses: actions/checkout@v4
|
|
519
|
+
|
|
520
|
+
- name: Setup Node.js and Cache
|
|
521
|
+
id: setup-cache
|
|
522
|
+
uses: ./.github/actions/setup-node-and-cache
|
|
523
|
+
with:
|
|
524
|
+
node-version: 20
|
|
525
|
+
|
|
526
|
+
- name: Install dependencies
|
|
527
|
+
if: steps.setup-cache.outputs.node-modules-cache-hit != 'true'
|
|
528
|
+
run: npm ci
|
|
529
|
+
|
|
530
|
+
- name: Build
|
|
531
|
+
run: npm run build
|
|
532
|
+
|
|
533
|
+
- name: Run Lint
|
|
534
|
+
run: npm run lint
|
|
535
|
+
|
|
536
|
+
test:
|
|
537
|
+
name: Unit Test
|
|
538
|
+
runs-on: ubuntu-latest
|
|
539
|
+
strategy:
|
|
540
|
+
matrix:
|
|
541
|
+
node-version: [20.x, 22.x]
|
|
542
|
+
steps:
|
|
543
|
+
- name: Checkout code
|
|
544
|
+
uses: actions/checkout@v4
|
|
545
|
+
|
|
546
|
+
- name: Setup Node.js ${{ matrix.node-version }} and Cache
|
|
547
|
+
id: setup-cache
|
|
548
|
+
uses: ./.github/actions/setup-node-and-cache
|
|
549
|
+
with:
|
|
550
|
+
node-version: ${{ matrix.node-version }}
|
|
551
|
+
|
|
552
|
+
- name: Install dependencies
|
|
553
|
+
if: steps.setup-cache.outputs.node-modules-cache-hit != 'true'
|
|
554
|
+
run: npm ci
|
|
555
|
+
|
|
556
|
+
- name: Build
|
|
557
|
+
run: npm run build
|
|
558
|
+
|
|
559
|
+
- name: Run Tests
|
|
560
|
+
run: npm test
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### deploy-production.yml
|
|
564
|
+
|
|
565
|
+
Deploys to production from version tags. Requires lint and test to pass. Does not cancel in-progress builds.
|
|
566
|
+
|
|
567
|
+
```yaml
|
|
568
|
+
name: Build to Production
|
|
569
|
+
|
|
570
|
+
on:
|
|
571
|
+
push:
|
|
572
|
+
tags:
|
|
573
|
+
- 'production-*'
|
|
574
|
+
- 'v0.*'
|
|
575
|
+
- 'v1.*'
|
|
576
|
+
|
|
577
|
+
concurrency:
|
|
578
|
+
group: deploy-production
|
|
579
|
+
cancel-in-progress: false
|
|
580
|
+
|
|
581
|
+
jobs:
|
|
582
|
+
deploy:
|
|
583
|
+
environment: production
|
|
584
|
+
needs: [lint, test]
|
|
585
|
+
if: |
|
|
586
|
+
always() &&
|
|
587
|
+
needs.lint.result == 'success' &&
|
|
588
|
+
needs.test.result == 'success'
|
|
589
|
+
name: Deploy to AWS
|
|
590
|
+
permissions:
|
|
591
|
+
id-token: write
|
|
592
|
+
contents: read
|
|
593
|
+
runs-on: ubuntu-latest
|
|
594
|
+
steps:
|
|
595
|
+
- name: Checkout code
|
|
596
|
+
uses: actions/checkout@v4
|
|
597
|
+
|
|
598
|
+
- name: Display deployment version
|
|
599
|
+
run: |
|
|
600
|
+
VERSION=$(node -p "require('./package.json').version")
|
|
601
|
+
echo "::notice::Deploying version $VERSION to production"
|
|
602
|
+
echo "DEPLOY_VERSION=$VERSION" >> $GITHUB_ENV
|
|
603
|
+
|
|
604
|
+
- name: Setup Environment
|
|
605
|
+
id: setup-env
|
|
606
|
+
uses: ./.github/actions/setup-environment
|
|
607
|
+
with:
|
|
608
|
+
aws-region: ${{ vars.AWS_REGION }}
|
|
609
|
+
aws-role-arn: ${{ vars.AWS_ROLE_ARN }}
|
|
610
|
+
datadog-api-key-arn: ${{ vars.DATADOG_API_KEY_ARN }}
|
|
611
|
+
aws-hosted-zone: ${{ vars.AWS_HOSTED_ZONE }}
|
|
612
|
+
log-level: ${{ vars.LOG_LEVEL }}
|
|
613
|
+
module-log-level: ${{ vars.MODULE_LOG_LEVEL }}
|
|
614
|
+
project-env: ${{ vars.PROJECT_ENV }}
|
|
615
|
+
project-key: ${{ vars.PROJECT_KEY }}
|
|
616
|
+
project-nonce: ${{ vars.PROJECT_NONCE }}
|
|
617
|
+
project-service: ${{ vars.PROJECT_SERVICE }}
|
|
618
|
+
project-sponsor: ${{ vars.PROJECT_SPONSOR }}
|
|
619
|
+
|
|
620
|
+
- name: Configure AWS Credentials
|
|
621
|
+
uses: ./.github/actions/configure-aws
|
|
622
|
+
with:
|
|
623
|
+
role-arn: ${{ steps.setup-env.outputs.aws-role-arn }}
|
|
624
|
+
aws-region: ${{ steps.setup-env.outputs.aws-region }}
|
|
625
|
+
|
|
626
|
+
- name: Setup Node.js and Cache
|
|
627
|
+
uses: ./.github/actions/setup-node-and-cache
|
|
628
|
+
with:
|
|
629
|
+
node-version: 20
|
|
630
|
+
|
|
631
|
+
- name: Install and Build
|
|
632
|
+
uses: ./.github/actions/npm-install-build
|
|
633
|
+
|
|
634
|
+
- name: Deploy CDK Stack
|
|
635
|
+
uses: ./.github/actions/cdk-deploy
|
|
636
|
+
with:
|
|
637
|
+
stack-name: AppStack
|
|
638
|
+
|
|
639
|
+
lint:
|
|
640
|
+
name: Lint
|
|
641
|
+
runs-on: ubuntu-latest
|
|
642
|
+
steps:
|
|
643
|
+
- name: Checkout code
|
|
644
|
+
uses: actions/checkout@v4
|
|
645
|
+
|
|
646
|
+
- name: Setup Node.js and Cache
|
|
647
|
+
id: setup-cache
|
|
648
|
+
uses: ./.github/actions/setup-node-and-cache
|
|
649
|
+
with:
|
|
650
|
+
node-version: 20
|
|
651
|
+
|
|
652
|
+
- name: Install dependencies
|
|
653
|
+
if: steps.setup-cache.outputs.node-modules-cache-hit != 'true'
|
|
654
|
+
run: npm ci
|
|
655
|
+
|
|
656
|
+
- name: Build
|
|
657
|
+
run: npm run build
|
|
658
|
+
|
|
659
|
+
- name: Run Lint
|
|
660
|
+
run: npm run lint
|
|
661
|
+
|
|
662
|
+
test:
|
|
663
|
+
name: Unit Test
|
|
664
|
+
runs-on: ubuntu-latest
|
|
665
|
+
strategy:
|
|
666
|
+
matrix:
|
|
667
|
+
node-version: [20.x, 22.x]
|
|
668
|
+
steps:
|
|
669
|
+
- name: Checkout code
|
|
670
|
+
uses: actions/checkout@v4
|
|
671
|
+
|
|
672
|
+
- name: Setup Node.js ${{ matrix.node-version }} and Cache
|
|
673
|
+
id: setup-cache
|
|
674
|
+
uses: ./.github/actions/setup-node-and-cache
|
|
675
|
+
with:
|
|
676
|
+
node-version: ${{ matrix.node-version }}
|
|
677
|
+
|
|
678
|
+
- name: Install dependencies
|
|
679
|
+
if: steps.setup-cache.outputs.node-modules-cache-hit != 'true'
|
|
680
|
+
run: npm ci
|
|
681
|
+
|
|
682
|
+
- name: Build
|
|
683
|
+
run: npm run build
|
|
684
|
+
|
|
685
|
+
- name: Run Tests
|
|
686
|
+
run: npm test
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### check-production.yml
|
|
690
|
+
|
|
691
|
+
Runs checks on production branches without deploying.
|
|
692
|
+
|
|
693
|
+
```yaml
|
|
694
|
+
name: Check Production Build
|
|
695
|
+
|
|
696
|
+
on:
|
|
697
|
+
push:
|
|
698
|
+
branches:
|
|
699
|
+
- production
|
|
700
|
+
- production-*
|
|
701
|
+
- production/*
|
|
702
|
+
|
|
703
|
+
concurrency:
|
|
704
|
+
group: check-production
|
|
705
|
+
cancel-in-progress: true
|
|
706
|
+
|
|
707
|
+
jobs:
|
|
708
|
+
lint:
|
|
709
|
+
name: Lint
|
|
710
|
+
runs-on: ubuntu-latest
|
|
711
|
+
steps:
|
|
712
|
+
- name: Checkout code
|
|
713
|
+
uses: actions/checkout@v4
|
|
714
|
+
|
|
715
|
+
- name: Setup Node.js and Cache
|
|
716
|
+
id: setup-cache
|
|
717
|
+
uses: ./.github/actions/setup-node-and-cache
|
|
718
|
+
with:
|
|
719
|
+
node-version: 20
|
|
720
|
+
|
|
721
|
+
- name: Install dependencies
|
|
722
|
+
if: steps.setup-cache.outputs.node-modules-cache-hit != 'true'
|
|
723
|
+
run: npm ci
|
|
724
|
+
|
|
725
|
+
- name: Build
|
|
726
|
+
run: npm run build
|
|
727
|
+
|
|
728
|
+
- name: Run Lint
|
|
729
|
+
run: npm run lint
|
|
730
|
+
|
|
731
|
+
test:
|
|
732
|
+
name: Unit Test
|
|
733
|
+
runs-on: ubuntu-latest
|
|
734
|
+
strategy:
|
|
735
|
+
matrix:
|
|
736
|
+
node-version: [20.x, 22.x]
|
|
737
|
+
steps:
|
|
738
|
+
- name: Checkout code
|
|
739
|
+
uses: actions/checkout@v4
|
|
740
|
+
|
|
741
|
+
- name: Setup Node.js ${{ matrix.node-version }} and Cache
|
|
742
|
+
id: setup-cache
|
|
743
|
+
uses: ./.github/actions/setup-node-and-cache
|
|
744
|
+
with:
|
|
745
|
+
node-version: ${{ matrix.node-version }}
|
|
746
|
+
|
|
747
|
+
- name: Install dependencies
|
|
748
|
+
if: steps.setup-cache.outputs.node-modules-cache-hit != 'true'
|
|
749
|
+
run: npm ci
|
|
750
|
+
|
|
751
|
+
- name: Build
|
|
752
|
+
run: npm run build
|
|
753
|
+
|
|
754
|
+
- name: Run Tests
|
|
755
|
+
run: npm test
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
### version.yml
|
|
759
|
+
|
|
760
|
+
Updates version across monorepo packages.
|
|
761
|
+
|
|
762
|
+
```yaml
|
|
763
|
+
name: Update Version
|
|
764
|
+
|
|
765
|
+
on:
|
|
766
|
+
workflow_dispatch:
|
|
767
|
+
inputs:
|
|
768
|
+
version_type:
|
|
769
|
+
description: 'Version update type'
|
|
770
|
+
required: true
|
|
771
|
+
type: choice
|
|
772
|
+
options:
|
|
773
|
+
- patch
|
|
774
|
+
- minor
|
|
775
|
+
- major
|
|
776
|
+
- custom
|
|
777
|
+
default: patch
|
|
778
|
+
custom_version:
|
|
779
|
+
description: 'Custom version (e.g., 1.2.3) - only used if version_type is custom'
|
|
780
|
+
required: false
|
|
781
|
+
type: string
|
|
782
|
+
workflow_call:
|
|
783
|
+
inputs:
|
|
784
|
+
version_type:
|
|
785
|
+
description: 'Version update type'
|
|
786
|
+
required: false
|
|
787
|
+
type: string
|
|
788
|
+
default: patch
|
|
789
|
+
custom_version:
|
|
790
|
+
description: 'Custom version (e.g., 1.2.3) - only used if version_type is custom'
|
|
791
|
+
required: false
|
|
792
|
+
type: string
|
|
793
|
+
outputs:
|
|
794
|
+
new_version:
|
|
795
|
+
description: 'The new version number'
|
|
796
|
+
value: ${{ jobs.version.outputs.new_version }}
|
|
797
|
+
|
|
798
|
+
jobs:
|
|
799
|
+
version:
|
|
800
|
+
name: Update and Sync Version
|
|
801
|
+
runs-on: ubuntu-latest
|
|
802
|
+
permissions:
|
|
803
|
+
contents: write
|
|
804
|
+
outputs:
|
|
805
|
+
new_version: ${{ steps.bump.outputs.new_version }}
|
|
806
|
+
steps:
|
|
807
|
+
- name: Checkout code
|
|
808
|
+
uses: actions/checkout@v4
|
|
809
|
+
with:
|
|
810
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
811
|
+
|
|
812
|
+
- name: Setup Node.js
|
|
813
|
+
uses: actions/setup-node@v4
|
|
814
|
+
with:
|
|
815
|
+
node-version: 20
|
|
816
|
+
|
|
817
|
+
- name: Configure Git
|
|
818
|
+
run: |
|
|
819
|
+
git config user.name "github-actions[bot]"
|
|
820
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
821
|
+
|
|
822
|
+
- name: Update root version
|
|
823
|
+
id: bump
|
|
824
|
+
run: |
|
|
825
|
+
VERSION_TYPE="${{ inputs.version_type }}"
|
|
826
|
+
CUSTOM_VERSION="${{ inputs.custom_version }}"
|
|
827
|
+
|
|
828
|
+
if [[ "$VERSION_TYPE" == "custom" ]]; then
|
|
829
|
+
if [[ -z "$CUSTOM_VERSION" ]]; then
|
|
830
|
+
echo "Error: custom_version is required when version_type is 'custom'"
|
|
831
|
+
exit 1
|
|
832
|
+
fi
|
|
833
|
+
npm version "$CUSTOM_VERSION" --no-git-tag-version --allow-same-version
|
|
834
|
+
else
|
|
835
|
+
npm version "$VERSION_TYPE" --no-git-tag-version
|
|
836
|
+
fi
|
|
837
|
+
|
|
838
|
+
NEW_VERSION=$(node -p "require('./package.json').version")
|
|
839
|
+
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
|
840
|
+
echo "Updated to version $NEW_VERSION"
|
|
841
|
+
|
|
842
|
+
- name: Sync package versions
|
|
843
|
+
run: |
|
|
844
|
+
NEW_VERSION="${{ steps.bump.outputs.new_version }}"
|
|
845
|
+
echo "Syncing all packages to version $NEW_VERSION"
|
|
846
|
+
|
|
847
|
+
for pkg in packages/*/package.json; do
|
|
848
|
+
if [ -f "$pkg" ]; then
|
|
849
|
+
echo "Updating $pkg"
|
|
850
|
+
node -e "
|
|
851
|
+
const fs = require('fs');
|
|
852
|
+
const pkg = JSON.parse(fs.readFileSync('$pkg', 'utf8'));
|
|
853
|
+
pkg.version = '$NEW_VERSION';
|
|
854
|
+
fs.writeFileSync('$pkg', JSON.stringify(pkg, null, 2) + '\n');
|
|
855
|
+
"
|
|
856
|
+
fi
|
|
857
|
+
done
|
|
858
|
+
|
|
859
|
+
- name: Commit and push changes
|
|
860
|
+
run: |
|
|
861
|
+
git add package.json package-lock.json packages/*/package.json
|
|
862
|
+
git commit -m "chore: version: ${{ steps.bump.outputs.new_version }}"
|
|
863
|
+
git push
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
## Step 3: Configure GitHub Environments
|
|
867
|
+
|
|
868
|
+
Create environments in your GitHub repository settings. Each environment contains variables and secrets for that deployment target.
|
|
869
|
+
|
|
870
|
+
### Creating an Environment
|
|
871
|
+
|
|
872
|
+
1. Go to your repository on GitHub
|
|
873
|
+
2. Navigate to **Settings → Environments**
|
|
874
|
+
3. Click **New environment**
|
|
875
|
+
4. Name it (e.g., `sandbox`, `development`, `production`)
|
|
876
|
+
5. Click **Configure environment**
|
|
877
|
+
6. Under **Environment variables**, click **Add variable** for each variable
|
|
878
|
+
|
|
879
|
+
### Required Variables (per environment)
|
|
880
|
+
|
|
881
|
+
| Variable | Description | Example |
|
|
882
|
+
|----------|-------------|---------|
|
|
883
|
+
| `AWS_ROLE_ARN` | IAM role ARN for OIDC (deployment fails without this) | `arn:aws:iam::123456789:role/DeployRole` |
|
|
884
|
+
|
|
885
|
+
### Optional Variables (per environment)
|
|
886
|
+
|
|
887
|
+
| Variable | Description | Default |
|
|
888
|
+
|----------|-------------|---------|
|
|
889
|
+
| `AWS_REGION` | AWS region | `us-east-1` |
|
|
890
|
+
| `AWS_HOSTED_ZONE` | Route53 hosted zone | `example.com` |
|
|
891
|
+
| `DATADOG_API_KEY_ARN` | Secrets Manager ARN for Datadog | (none) |
|
|
892
|
+
| `LOG_LEVEL` | Application log level | `debug` |
|
|
893
|
+
| `MODULE_LOG_LEVEL` | Module log level | `warn` |
|
|
894
|
+
| `PROJECT_ENV` | Environment name | `sandbox` |
|
|
895
|
+
| `PROJECT_KEY` | Project identifier | (from package.json name) |
|
|
896
|
+
| `PROJECT_NONCE` | Unique identifier for resources | (random) |
|
|
897
|
+
| `PROJECT_SERVICE` | Service name | (from package.json name) |
|
|
898
|
+
| `PROJECT_SPONSOR` | Organization name | (from repository owner) |
|
|
899
|
+
|
|
900
|
+
### Auto-Generated Variables
|
|
901
|
+
|
|
902
|
+
These variables are set automatically from GitHub context:
|
|
903
|
+
|
|
904
|
+
| Variable | Source | Description |
|
|
905
|
+
|----------|--------|-------------|
|
|
906
|
+
| `CDK_ENV_REPO` | `${{ github.repository }}` | Repository name (owner/repo) |
|
|
907
|
+
| `PROJECT_COMMIT` | `${{ github.sha }}` | Current commit SHA |
|
|
908
|
+
| `PROJECT_VERSION` | `package.json` | Version from package.json |
|
|
909
|
+
|
|
910
|
+
### Environment Secrets
|
|
911
|
+
|
|
912
|
+
Add secrets for sensitive values. Secrets are passed to actions via `${{ secrets.SECRET_NAME }}`.
|
|
913
|
+
|
|
914
|
+
Navigate to: **Settings → Environments → [environment] → Environment secrets**
|
|
915
|
+
|
|
916
|
+
### Deployment Protection Rules (Optional)
|
|
917
|
+
|
|
918
|
+
You can add protection rules to any environment:
|
|
919
|
+
|
|
920
|
+
- **Required reviewers**: Require manual approval before deploying
|
|
921
|
+
- **Wait timer**: Delay deployment by a specified time
|
|
922
|
+
- **Deployment branches**: Limit which branches can deploy
|
|
923
|
+
|
|
924
|
+
### How Variables Are Resolved
|
|
925
|
+
|
|
926
|
+
GitHub Actions composite actions cannot access `vars.*` directly. Variables must be passed as action inputs.
|
|
927
|
+
|
|
928
|
+
The workflow passes variables to `setup-environment`, which applies defaults:
|
|
929
|
+
|
|
930
|
+
```yaml
|
|
931
|
+
jobs:
|
|
932
|
+
deploy:
|
|
933
|
+
environment: sandbox # Variables from this environment
|
|
934
|
+
steps:
|
|
935
|
+
- name: Setup Environment
|
|
936
|
+
id: setup-env
|
|
937
|
+
uses: ./.github/actions/setup-environment
|
|
938
|
+
with:
|
|
939
|
+
aws-region: ${{ vars.AWS_REGION }}
|
|
940
|
+
aws-role-arn: ${{ vars.AWS_ROLE_ARN }}
|
|
941
|
+
# ... other vars
|
|
942
|
+
|
|
943
|
+
# Access resolved values via outputs
|
|
944
|
+
- name: Configure AWS
|
|
945
|
+
uses: ./.github/actions/configure-aws
|
|
946
|
+
with:
|
|
947
|
+
role-arn: ${{ steps.setup-env.outputs.aws-role-arn }}
|
|
948
|
+
aws-region: ${{ steps.setup-env.outputs.aws-region }}
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
The action uses bash parameter expansion to apply defaults:
|
|
952
|
+
|
|
953
|
+
```bash
|
|
954
|
+
AWS_REGION="${{ inputs.aws-region }}"
|
|
955
|
+
AWS_REGION="${AWS_REGION:-us-east-1}" # Default if empty
|
|
956
|
+
echo "AWS_REGION=${AWS_REGION}" >> $GITHUB_ENV
|
|
957
|
+
echo "aws-region=${AWS_REGION}" >> $GITHUB_OUTPUT
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
### Environment Configuration by Target
|
|
961
|
+
|
|
962
|
+
| Environment | `PROJECT_ENV` | `LOG_LEVEL` | Notes |
|
|
963
|
+
|-------------|---------------|-------------|-------|
|
|
964
|
+
| sandbox | `sandbox` | `debug` or `trace` | Shared testing |
|
|
965
|
+
| development | `development` | `debug` | Validates multi-env deployment |
|
|
966
|
+
| production | `production` | `info` | Less verbose logging |
|
|
967
|
+
|
|
968
|
+
## Step 4: Configure AWS OIDC
|
|
969
|
+
|
|
970
|
+
Create an IAM role in AWS that trusts GitHub Actions OIDC provider.
|
|
971
|
+
|
|
972
|
+
### Trust Policy
|
|
973
|
+
|
|
974
|
+
```json
|
|
975
|
+
{
|
|
976
|
+
"Version": "2012-10-17",
|
|
977
|
+
"Statement": [
|
|
978
|
+
{
|
|
979
|
+
"Effect": "Allow",
|
|
980
|
+
"Principal": {
|
|
981
|
+
"Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
|
|
982
|
+
},
|
|
983
|
+
"Action": "sts:AssumeRoleWithWebIdentity",
|
|
984
|
+
"Condition": {
|
|
985
|
+
"StringEquals": {
|
|
986
|
+
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
|
|
987
|
+
},
|
|
988
|
+
"StringLike": {
|
|
989
|
+
"token.actions.githubusercontent.com:sub": "repo:ORG/REPO:*"
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
]
|
|
994
|
+
}
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
### Required Permissions
|
|
998
|
+
|
|
999
|
+
The role needs permissions for:
|
|
1000
|
+
- CDK deployment (CloudFormation, IAM, Lambda, etc.)
|
|
1001
|
+
- Any resources your stack creates
|
|
1002
|
+
|
|
1003
|
+
## Step 5: Verify Setup
|
|
1004
|
+
|
|
1005
|
+
1. Push to a feature branch to trigger sandbox deployment
|
|
1006
|
+
2. Merge to main to trigger development deployment
|
|
1007
|
+
3. Create a version tag to trigger production deployment
|
|
1008
|
+
|
|
1009
|
+
```bash
|
|
1010
|
+
# Test sandbox
|
|
1011
|
+
git checkout -b feat/test-cicd
|
|
1012
|
+
git push origin feat/test-cicd
|
|
1013
|
+
|
|
1014
|
+
# Test development
|
|
1015
|
+
git checkout main
|
|
1016
|
+
git push origin main
|
|
1017
|
+
|
|
1018
|
+
# Test production
|
|
1019
|
+
git tag v0.1.0
|
|
1020
|
+
git push origin v0.1.0
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
## Customization
|
|
1024
|
+
|
|
1025
|
+
### Adding Application Secrets
|
|
1026
|
+
|
|
1027
|
+
Pass secrets to the CDK deploy action by extending `cdk-deploy/action.yml`:
|
|
1028
|
+
|
|
1029
|
+
```yaml
|
|
1030
|
+
inputs:
|
|
1031
|
+
api-key:
|
|
1032
|
+
description: API key for external service
|
|
1033
|
+
required: false
|
|
1034
|
+
|
|
1035
|
+
# In the Deploy step:
|
|
1036
|
+
env:
|
|
1037
|
+
API_KEY: ${{ inputs.api-key }}
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
Then in workflows:
|
|
1041
|
+
|
|
1042
|
+
```yaml
|
|
1043
|
+
- name: Deploy CDK Stack
|
|
1044
|
+
uses: ./.github/actions/cdk-deploy
|
|
1045
|
+
with:
|
|
1046
|
+
stack-name: AppStack
|
|
1047
|
+
api-key: ${{ secrets.API_KEY }}
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
### Adding Framework-Specific Caching
|
|
1051
|
+
|
|
1052
|
+
Extend `setup-node-and-cache/action.yml` for framework builds:
|
|
1053
|
+
|
|
1054
|
+
```yaml
|
|
1055
|
+
- name: Cache Next.js build
|
|
1056
|
+
id: cache-nextjs
|
|
1057
|
+
uses: actions/cache@v4
|
|
1058
|
+
with:
|
|
1059
|
+
path: packages/nextjs/.next
|
|
1060
|
+
key: ${{ runner.os }}-nextjs-${{ hashFiles('packages/nextjs/**/*.ts', 'packages/nextjs/**/*.tsx') }}
|
|
1061
|
+
```
|
|
1062
|
+
|
|
1063
|
+
### Personal Builds
|
|
1064
|
+
|
|
1065
|
+
For personal sandbox builds, create `deploy-personal-build.yml`:
|
|
1066
|
+
|
|
1067
|
+
```yaml
|
|
1068
|
+
name: Personal Build
|
|
1069
|
+
|
|
1070
|
+
on:
|
|
1071
|
+
push:
|
|
1072
|
+
branches-ignore:
|
|
1073
|
+
- main
|
|
1074
|
+
- develop
|
|
1075
|
+
- nobuild-*
|
|
1076
|
+
- nobuild/*
|
|
1077
|
+
- sandbox
|
|
1078
|
+
- sandbox-*
|
|
1079
|
+
- sandbox/*
|
|
1080
|
+
|
|
1081
|
+
concurrency:
|
|
1082
|
+
group: deploy-personal-build-${{ github.actor }}
|
|
1083
|
+
cancel-in-progress: true
|
|
1084
|
+
|
|
1085
|
+
jobs:
|
|
1086
|
+
deploy:
|
|
1087
|
+
environment: sandbox
|
|
1088
|
+
# ... same as sandbox deploy but with PROJECT_ENV set to github.actor
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
## Troubleshooting
|
|
1092
|
+
|
|
1093
|
+
### "The environment 'sandbox' does not exist"
|
|
1094
|
+
|
|
1095
|
+
The environment must be created in GitHub repository settings before the workflow can reference it. See [Creating an Environment](#creating-an-environment).
|
|
1096
|
+
|
|
1097
|
+
### OIDC Authentication Fails / "Unable to assume AWS role"
|
|
1098
|
+
|
|
1099
|
+
- Verify `AWS_ROLE_ARN` variable is set correctly in the environment
|
|
1100
|
+
- Verify the OIDC provider is configured in AWS IAM
|
|
1101
|
+
- Check the trust policy matches your repository
|
|
1102
|
+
- Ensure the workflow has `id-token: write` permission
|
|
1103
|
+
- Verify the role has necessary permissions for CDK deployment
|
|
1104
|
+
|
|
1105
|
+
### Cache Miss on Every Build
|
|
1106
|
+
|
|
1107
|
+
- Verify `package-lock.json` is committed
|
|
1108
|
+
- Check cache key patterns match your file structure
|
|
1109
|
+
|
|
1110
|
+
### CDK Deployment Fails
|
|
1111
|
+
|
|
1112
|
+
- Verify AWS credentials are configured correctly
|
|
1113
|
+
- Check CDK is bootstrapped in the target account/region: `npx cdk bootstrap`
|
|
1114
|
+
- If using Datadog, verify `DATADOG_API_KEY_ARN` points to a valid secret
|
|
1115
|
+
- Verify the AWS role has permissions to access any Secrets Manager secrets
|
|
1116
|
+
- Review CloudFormation events in the AWS Console for specific errors
|
|
1117
|
+
|
|
1118
|
+
### Variables Not Being Applied
|
|
1119
|
+
|
|
1120
|
+
- Composite actions cannot access `vars.*` directly
|
|
1121
|
+
- Verify variables are passed as inputs to `setup-environment`
|
|
1122
|
+
- Check the environment name in the job matches the GitHub environment name
|
|
1123
|
+
- Verify variable names match exactly (case-sensitive)
|