@jaypie/mcp 0.2.5 → 0.2.7

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,1139 @@
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
+ ### Error: "Cannot retrieve value from context provider hosted-zone"
1119
+
1120
+ Stacks using context providers must extend `JaypieAppStack`, not `cdk.Stack`:
1121
+
1122
+ ```typescript
1123
+ // Wrong
1124
+ import * as cdk from "aws-cdk-lib";
1125
+ export class AppStack extends cdk.Stack { ... }
1126
+
1127
+ // Correct
1128
+ import { JaypieAppStack } from "@jaypie/constructs";
1129
+ export class AppStack extends JaypieAppStack { ... }
1130
+ ```
1131
+
1132
+ `JaypieAppStack` automatically sets `env` with `CDK_DEFAULT_ACCOUNT` and `CDK_DEFAULT_REGION`.
1133
+
1134
+ ### Variables Not Being Applied
1135
+
1136
+ - Composite actions cannot access `vars.*` directly
1137
+ - Verify variables are passed as inputs to `setup-environment`
1138
+ - Check the environment name in the job matches the GitHub environment name
1139
+ - Verify variable names match exactly (case-sensitive)