@aws/nx-plugin 0.75.0 → 0.77.0

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.
Files changed (41) hide show
  1. package/LICENSE-THIRD-PARTY +60 -2
  2. package/README.md +5 -0
  3. package/package.json +9 -9
  4. package/src/infra/app/__snapshots__/generator.spec.ts.snap +378 -7
  5. package/src/infra/app/files/app/src/main.ts.template +17 -2
  6. package/src/infra/app/generator.js +34 -9
  7. package/src/infra/app/generator.js.map +1 -1
  8. package/src/infra/app/schema.d.ts +1 -0
  9. package/src/infra/app/schema.json +6 -0
  10. package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +21 -4
  11. package/src/py/mcp-server/files/app/http.py.template +18 -2
  12. package/src/py/mcp-server/files/deploy/Dockerfile.template +1 -1
  13. package/src/py/mcp-server/generator.js +4 -2
  14. package/src/py/mcp-server/generator.js.map +1 -1
  15. package/src/py/strands-agent/__snapshots__/generator.spec.ts.snap +6 -6
  16. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +1 -1
  17. package/src/ts/nx-plugin/__snapshots__/generator.spec.ts.snap +1 -1
  18. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +13 -13
  19. package/src/utils/files/common/infra-config/src/index.ts.template +3 -0
  20. package/src/utils/files/common/infra-config/src/resolve-stage.ts.template +23 -0
  21. package/src/utils/files/common/infra-config/src/stages.config.ts.template +48 -0
  22. package/src/utils/files/common/infra-config/src/stages.types.ts.template +66 -0
  23. package/src/utils/files/common/scripts/src/index.ts.template +1 -0
  24. package/src/utils/files/common/scripts/src/infra-deploy.ts.template +2 -0
  25. package/src/utils/files/common/scripts/src/infra-destroy.ts.template +2 -0
  26. package/src/utils/files/common/scripts/src/stage-credentials/cdk-command.ts.template +18 -0
  27. package/src/utils/files/common/scripts/src/stage-credentials/credentials.ts.template +100 -0
  28. package/src/utils/files/common/scripts/src/stage-credentials/run.ts.template +52 -0
  29. package/src/utils/files/common/scripts/src/stage-credentials/stage-parser.ts.template +15 -0
  30. package/src/utils/shared-constructs-constants.d.ts +4 -0
  31. package/src/utils/shared-constructs-constants.js +5 -1
  32. package/src/utils/shared-constructs-constants.js.map +1 -1
  33. package/src/utils/shared-infra-config.d.ts +11 -0
  34. package/src/utils/shared-infra-config.js +47 -0
  35. package/src/utils/shared-infra-config.js.map +1 -0
  36. package/src/utils/shared-scripts.d.ts +12 -0
  37. package/src/utils/shared-scripts.js +49 -0
  38. package/src/utils/shared-scripts.js.map +1 -0
  39. package/src/utils/versions.d.ts +29 -28
  40. package/src/utils/versions.js +28 -27
  41. package/src/utils/versions.js.map +1 -1
@@ -3903,7 +3903,7 @@ THE SOFTWARE.
3903
3903
 
3904
3904
  ---
3905
3905
 
3906
- The following software may be included in this product: @modelcontextprotocol/sdk (1.26.0)
3906
+ The following software may be included in this product: @modelcontextprotocol/sdk (1.27.0)
3907
3907
  This software contains the following license and notice below:
3908
3908
 
3909
3909
  MIT License
@@ -8493,6 +8493,35 @@ SOFTWARE.
8493
8493
 
8494
8494
  ---
8495
8495
 
8496
+ The following software may be included in this product: balanced-match (4.0.4)
8497
+ This software contains the following license and notice below:
8498
+
8499
+ (MIT)
8500
+
8501
+ Original code Copyright Julian Gruber <julian@juliangruber.com>
8502
+
8503
+ Port to TypeScript Copyright Isaac Z. Schlueter <i@izs.me>
8504
+
8505
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
8506
+ this software and associated documentation files (the "Software"), to deal in
8507
+ the Software without restriction, including without limitation the rights to
8508
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8509
+ of the Software, and to permit persons to whom the Software is furnished to do
8510
+ so, subject to the following conditions:
8511
+
8512
+ The above copyright notice and this permission notice shall be included in all
8513
+ copies or substantial portions of the Software.
8514
+
8515
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8516
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
8517
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
8518
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8519
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8520
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
8521
+ SOFTWARE.
8522
+
8523
+ ---
8524
+
8496
8525
  The following software may be included in this product: base64-js (1.5.1)
8497
8526
  This software contains the following license and notice below:
8498
8527
 
@@ -8737,6 +8766,35 @@ SOFTWARE.
8737
8766
 
8738
8767
  ---
8739
8768
 
8769
+ The following software may be included in this product: brace-expansion (5.0.3)
8770
+ This software contains the following license and notice below:
8771
+
8772
+ MIT License
8773
+
8774
+ Copyright Julian Gruber <julian@juliangruber.com>
8775
+
8776
+ TypeScript port Copyright Isaac Z. Schlueter <i@izs.me>
8777
+
8778
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8779
+ of this software and associated documentation files (the "Software"), to deal
8780
+ in the Software without restriction, including without limitation the rights
8781
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8782
+ copies of the Software, and to permit persons to whom the Software is
8783
+ furnished to do so, subject to the following conditions:
8784
+
8785
+ The above copyright notice and this permission notice shall be included in all
8786
+ copies or substantial portions of the Software.
8787
+
8788
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8789
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
8790
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
8791
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8792
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8793
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
8794
+ SOFTWARE.
8795
+
8796
+ ---
8797
+
8740
8798
  The following software may be included in this product: braces (3.0.3)
8741
8799
  This software contains the following license and notice below:
8742
8800
 
@@ -39041,7 +39099,7 @@ software or this license, under any kind of legal claim._**
39041
39099
 
39042
39100
  ---
39043
39101
 
39044
- The following software may be included in this product: minimatch (10.1.2)
39102
+ The following software may be included in this product: minimatch (10.2.2)
39045
39103
  This software contains the following license and notice below:
39046
39104
 
39047
39105
  # Blue Oak Model License
package/README.md CHANGED
@@ -33,6 +33,7 @@
33
33
  <figure>
34
34
  <img src="docs/src/content/docs/assets/website-generator.gif" alt="Demo" />
35
35
  </figure>
36
+ <br />
36
37
  👉 See full documentation on <a href="https://awslabs.github.io/nx-plugin-for-aws">https://awslabs.github.io/nx-plugin-for-aws</a> 👈
37
38
  </div>
38
39
 
@@ -106,6 +107,10 @@ If you have issues such as `ENOENT npx`, replace the command with `/full/path/to
106
107
 
107
108
  For more details, [take a look at the guide here](https://awslabs.github.io/nx-plugin-for-aws/en/get_started/building-with-ai/)
108
109
 
110
+ ## Community
111
+
112
+ Join us on Slack in the [#nx-plugin-for-aws](https://cdk-dev.slack.com/archives/C0AG11EUHM4) channel to ask questions, share feedback, and connect with other users and contributors.
113
+
109
114
  ## Contributing
110
115
 
111
116
  The main purpose of this repository is to continue evolving @aws/nx-plugin, making it faster and easier to use. Development happens in the open on GitHub, and we are grateful to the community for contributing bugfixes and improvements.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aws/nx-plugin",
3
- "version": "0.75.0",
3
+ "version": "0.77.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/awslabs/nx-plugin-for-aws.git",
@@ -19,19 +19,19 @@
19
19
  },
20
20
  "generators": "./generators.json",
21
21
  "peerDependencies": {
22
- "nx": "~22.5.0",
22
+ "nx": "~22.5.1",
23
23
  "prettier": "^3.8.1"
24
24
  },
25
25
  "dependencies": {
26
26
  "@apidevtools/swagger-parser": "^10.1.1",
27
27
  "@hey-api/openapi-ts": "0.64.13",
28
28
  "@iarna/toml": "^2.2.5",
29
- "@modelcontextprotocol/sdk": "~1.26.0",
30
- "@nx/devkit": "~22.5.0",
31
- "@nx/eslint": "~22.5.0",
32
- "@nx/js": "~22.5.0",
33
- "@nx/react": "~22.5.0",
34
- "@nx/vite": "~22.5.0",
29
+ "@modelcontextprotocol/sdk": "~1.27.0",
30
+ "@nx/devkit": "~22.5.1",
31
+ "@nx/eslint": "~22.5.1",
32
+ "@nx/js": "~22.5.1",
33
+ "@nx/react": "~22.5.1",
34
+ "@nx/vite": "~22.5.1",
35
35
  "@nxlv/python": "~22.1.0",
36
36
  "@phenomnomnominal/tsquery": "6.1.4",
37
37
  "enquirer": "^2.4.1",
@@ -42,7 +42,7 @@
42
42
  "lodash.orderby": "^4.6.0",
43
43
  "lodash.trim": "^4.5.1",
44
44
  "lodash.uniqby": "^4.7.0",
45
- "minimatch": "^10.1.2",
45
+ "minimatch": "^10.2.1",
46
46
  "openapi-types": "^12.1.3",
47
47
  "pip-requirements-js": "^0.2.1",
48
48
  "typescript": "~5.9.3",
@@ -2,9 +2,9 @@
2
2
 
3
3
  exports[`infra generator > should add required dependencies to package.json > dependencies 1`] = `
4
4
  {
5
- "aws-cdk": "2.1105.0",
5
+ "aws-cdk": "2.1106.0",
6
6
  "aws-cdk-lib": "2.238.0",
7
- "constructs": "10.4.5",
7
+ "constructs": "10.5.0",
8
8
  "esbuild": "0.27.3",
9
9
  "source-map-support": "0.5.21",
10
10
  }
@@ -39,9 +39,9 @@ exports[`infra generator > should add required dependencies to package.json > de
39
39
  exports[`infra generator > should add required dependencies to package.json > package-json 1`] = `
40
40
  {
41
41
  "dependencies": {
42
- "aws-cdk": "2.1105.0",
42
+ "aws-cdk": "2.1106.0",
43
43
  "aws-cdk-lib": "2.238.0",
44
- "constructs": "10.4.5",
44
+ "constructs": "10.5.0",
45
45
  "esbuild": "0.27.3",
46
46
  "source-map-support": "0.5.21",
47
47
  },
@@ -84,7 +84,7 @@ exports[`infra generator > should configure Checkov target correctly > checkov-t
84
84
  "{workspaceRoot}/dist/{projectRoot}/cdk.out",
85
85
  ],
86
86
  "options": {
87
- "command": "uvx checkov==3.2.501 --config-file {projectRoot}/checkov.yml --directory dist/{projectRoot}/cdk.out --framework cloudformation",
87
+ "command": "uvx checkov==3.2.505 --config-file {projectRoot}/checkov.yml --directory dist/{projectRoot}/cdk.out --framework cloudformation",
88
88
  },
89
89
  "outputs": [
90
90
  "{workspaceRoot}/dist/{projectRoot}/checkov",
@@ -177,7 +177,7 @@ exports[`infra generator > should configure project.json with correct targets >
177
177
  "{workspaceRoot}/dist/{projectRoot}/cdk.out",
178
178
  ],
179
179
  "options": {
180
- "command": "uvx checkov==3.2.501 --config-file {projectRoot}/checkov.yml --directory dist/{projectRoot}/cdk.out --framework cloudformation",
180
+ "command": "uvx checkov==3.2.505 --config-file {projectRoot}/checkov.yml --directory dist/{projectRoot}/cdk.out --framework cloudformation",
181
181
  },
182
182
  "outputs": [
183
183
  "{workspaceRoot}/dist/{projectRoot}/checkov",
@@ -727,7 +727,7 @@ exports[`infra generator > should handle custom project names correctly > custom
727
727
  "{workspaceRoot}/dist/{projectRoot}/cdk.out",
728
728
  ],
729
729
  "options": {
730
- "command": "uvx checkov==3.2.501 --config-file {projectRoot}/checkov.yml --directory dist/{projectRoot}/cdk.out --framework cloudformation",
730
+ "command": "uvx checkov==3.2.505 --config-file {projectRoot}/checkov.yml --directory dist/{projectRoot}/cdk.out --framework cloudformation",
731
731
  },
732
732
  "outputs": [
733
733
  "{workspaceRoot}/dist/{projectRoot}/checkov",
@@ -812,3 +812,374 @@ exports[`infra generator > should handle custom project names correctly > custom
812
812
  },
813
813
  }
814
814
  `;
815
+
816
+ exports[`infra generator > with enableStageConfig > should snapshot generated infra-config src directory > packages/common/infra-config/src/index.ts 1`] = `
817
+ "export * from './stages.types.js';
818
+ export { default } from './stages.config.js';
819
+ export { resolveStage } from './resolve-stage.js';
820
+ "
821
+ `;
822
+
823
+ exports[`infra generator > with enableStageConfig > should snapshot generated infra-config src directory > packages/common/infra-config/src/resolve-stage.ts 1`] = `
824
+ "import type { StageConfig, StagesConfig } from './stages.types.js';
825
+ import stagesConfig from './stages.config.js';
826
+
827
+ // Widen the narrow \`as const\` type to StagesConfig for dynamic key access
828
+ const config: StagesConfig = stagesConfig;
829
+
830
+ /**
831
+ * Resolves stage config for a given project and stage name.
832
+ * Project-specific fields take priority over shared ones.
833
+ *
834
+ * @param projectPath - Project path relative to workspace root (e.g., 'packages/infra')
835
+ * @param stageName - CDK stage name (e.g., 'my-app-dev')
836
+ * @returns Merged StageConfig or undefined if no config exists for this stage
837
+ */
838
+ export function resolveStage(
839
+ projectPath: string,
840
+ stageName: string,
841
+ ): StageConfig | undefined {
842
+ const shared = config.shared?.stages?.[stageName];
843
+ const project = config.projects?.[projectPath]?.stages?.[stageName];
844
+ if (!shared && !project) return undefined;
845
+ return { ...shared, ...project } as StageConfig;
846
+ }
847
+ "
848
+ `;
849
+
850
+ exports[`infra generator > with enableStageConfig > should snapshot generated infra-config src directory > packages/common/infra-config/src/stages.config.ts 1`] = `
851
+ "/**
852
+ * Stage configuration for CDK deployments.
853
+ *
854
+ * This file maps CDK stage names to their deployment settings. When you run
855
+ * \`npx nx run <project>:deploy <stage-name>/*\`, the infra-deploy script
856
+ * automatically resolves and applies the correct credentials.
857
+ *
858
+ * Project keys are the project path relative to the workspace root
859
+ * (e.g., 'packages/infra').
860
+ *
861
+ * Stage names must match the CDK stage identifiers defined in your main.ts —
862
+ * the first argument to \`new ApplicationStage(app, '<stage-name>', ...)\`.
863
+ * For example, if main.ts has \`new ApplicationStage(app, 'my-app-dev', ...)\`
864
+ * then the stage name here is 'my-app-dev'.
865
+ *
866
+ * We recommend committing this file so the team shares a single source of truth.
867
+ * If it contains personal profile names, you can add it to .gitignore instead.
868
+ */
869
+ import type { StagesConfig } from './stages.types.js';
870
+
871
+ export default {
872
+ projects: {
873
+ // Example: map stages for a specific infra project
874
+ // 'packages/infra': {
875
+ // stages: {
876
+ // 'my-app-dev': {
877
+ // credentials: { type: 'profile', profile: 'dev-account' },
878
+ // region: 'us-east-1',
879
+ // // account is optional — if omitted, CDK infers from the profile
880
+ // },
881
+ // 'my-app-prod': {
882
+ // credentials: { type: 'assumeRole', assumeRole: 'arn:aws:iam::123456789012:role/DeployRole' },
883
+ // region: 'us-west-2',
884
+ // account: '123456789012',
885
+ // },
886
+ // },
887
+ // },
888
+ },
889
+ shared: {
890
+ stages: {
891
+ // Example: shared sandbox stage available to all projects
892
+ // 'sandbox': {
893
+ // credentials: { type: 'profile', profile: 'sandbox-profile' },
894
+ // region: 'us-east-1',
895
+ // },
896
+ },
897
+ },
898
+ } as const satisfies StagesConfig;
899
+ "
900
+ `;
901
+
902
+ exports[`infra generator > with enableStageConfig > should snapshot generated infra-config src directory > packages/common/infra-config/src/stages.types.ts 1`] = `
903
+ "/**
904
+ * Type definitions for stage and project configuration.
905
+ *
906
+ * These types are used by both stages.config.ts and the infra-deploy/
907
+ * infra-destroy scripts. They live in a shared package so any
908
+ * project in the workspace can import them.
909
+ */
910
+
911
+ /** Use an AWS CLI profile from ~/.aws/config */
912
+ export type ProfileCredentials = {
913
+ type: 'profile';
914
+ /** AWS CLI profile name */
915
+ profile: string;
916
+ };
917
+
918
+ /** Assume an IAM role via STS, optionally using a profile as the source credentials */
919
+ export type AssumeRoleCredentials = {
920
+ type: 'assumeRole';
921
+ /** IAM Role ARN to assume */
922
+ assumeRole: string;
923
+ /** Optional: AWS CLI profile to use as source credentials for the AssumeRole call */
924
+ profile?: string;
925
+ /** Optional: External ID required by the role's trust policy */
926
+ externalId?: string;
927
+ /** Optional: Session duration in seconds (default: 3600). Increase for long deployments. */
928
+ sessionDuration?: number;
929
+ };
930
+
931
+ /**
932
+ * Credentials for deploying to a specific CDK stage.
933
+ * The \`type\` field determines which credential strategy is used.
934
+ */
935
+ export type StageCredentials = ProfileCredentials | AssumeRoleCredentials;
936
+
937
+ /**
938
+ * Configuration for a single CDK stage.
939
+ * Includes credentials, region, and optionally account.
940
+ */
941
+ export type StageConfig = {
942
+ /** How to authenticate when deploying this stage */
943
+ credentials: StageCredentials;
944
+ /** AWS region for this stage (e.g., 'us-east-1') */
945
+ region: string;
946
+ /** AWS account ID. If omitted, CDK infers it from the active credentials. */
947
+ account?: string;
948
+ };
949
+
950
+ /**
951
+ * Configuration for a single infrastructure project.
952
+ * The key in the parent map is the project path relative to workspace root
953
+ * (e.g., 'packages/infra').
954
+ */
955
+ export type ProjectConfig = {
956
+ /** Map of CDK stage names to their configuration */
957
+ stages: { [stageName: string]: StageConfig };
958
+ };
959
+
960
+ /** Top-level configuration mapping projects and stages to their settings. */
961
+ export type StagesConfig = {
962
+ /** Project-specific config. Key is the project path relative to workspace root. */
963
+ projects?: { [projectPath: string]: ProjectConfig };
964
+ /** Shared stage config available to all projects. */
965
+ shared?: {
966
+ stages: { [stageName: string]: StageConfig };
967
+ };
968
+ };
969
+ "
970
+ `;
971
+
972
+ exports[`infra generator > with enableStageConfig > should snapshot generated scripts src directory > packages/common/scripts/src/index.ts 1`] = `
973
+ "// Scripts (infra-deploy, infra-destroy) are the public interface of this package.
974
+ "
975
+ `;
976
+
977
+ exports[`infra generator > with enableStageConfig > should snapshot generated scripts src directory > packages/common/scripts/src/infra-deploy.ts 1`] = `
978
+ "import { run } from './stage-credentials/run.js';
979
+ run('deploy');
980
+ "
981
+ `;
982
+
983
+ exports[`infra generator > with enableStageConfig > should snapshot generated scripts src directory > packages/common/scripts/src/infra-destroy.ts 1`] = `
984
+ "import { run } from './stage-credentials/run.js';
985
+ run('destroy');
986
+ "
987
+ `;
988
+
989
+ exports[`infra generator > with enableStageConfig > should snapshot generated scripts src directory > packages/common/scripts/src/stage-credentials/cdk-command.ts 1`] = `
990
+ "/**
991
+ * Builds the CDK command as an array of arguments for spawnSync.
992
+ *
993
+ * Defaults to --require-approval=never (standard for local dev deploys).
994
+ * If the user explicitly passes --require-approval with any value, we
995
+ * respect their choice and don't add the default.
996
+ */
997
+ export function buildCdkCommand(
998
+ action: string,
999
+ remainingArgs: string[],
1000
+ ): string[] {
1001
+ const hasRequireApproval = remainingArgs.some(
1002
+ (a) => a === '--require-approval' || a.startsWith('--require-approval='),
1003
+ );
1004
+ return hasRequireApproval
1005
+ ? ['cdk', action, ...remainingArgs]
1006
+ : ['cdk', action, '--require-approval=never', ...remainingArgs];
1007
+ }
1008
+ "
1009
+ `;
1010
+
1011
+ exports[`infra generator > with enableStageConfig > should snapshot generated scripts src directory > packages/common/scripts/src/stage-credentials/credentials.ts 1`] = `
1012
+ "import type { StageCredentials, StagesConfig } from ':proj/common-infra-config';
1013
+
1014
+ /**
1015
+ * Looks up credentials for a given project + stage combination.
1016
+ *
1017
+ * Lookup order:
1018
+ * 1. Project-specific: config.projects[projectPath].stages[stageName].credentials
1019
+ * 2. Shared: config.shared.stages[stageName].credentials
1020
+ * 3. No match: returns undefined — caller falls back to env vars
1021
+ */
1022
+ export function lookupCredentials(
1023
+ config: StagesConfig | undefined,
1024
+ projectPath: string,
1025
+ stageName: string,
1026
+ ): { credentials: StageCredentials | undefined; source: string } {
1027
+ const projectCreds =
1028
+ config?.projects?.[projectPath]?.stages?.[stageName]?.credentials;
1029
+ if (projectCreds) {
1030
+ return { credentials: projectCreds, source: 'project-specific' };
1031
+ }
1032
+
1033
+ const sharedCreds = config?.shared?.stages?.[stageName]?.credentials;
1034
+ if (sharedCreds) {
1035
+ return { credentials: sharedCreds, source: 'shared' };
1036
+ }
1037
+
1038
+ return { credentials: undefined, source: 'environment fallback' };
1039
+ }
1040
+
1041
+ /**
1042
+ * Builds a child process environment with the resolved credentials overlaid.
1043
+ * Never modifies process.env — returns a new object.
1044
+ */
1045
+ export async function buildChildEnv(
1046
+ credentials: StageCredentials,
1047
+ projectPath: string,
1048
+ ): Promise<Record<string, string | undefined>> {
1049
+ const env = { ...process.env };
1050
+
1051
+ switch (credentials.type) {
1052
+ case 'profile': {
1053
+ env.AWS_PROFILE = credentials.profile;
1054
+ break;
1055
+ }
1056
+ case 'assumeRole': {
1057
+ let STSClient: typeof import('@aws-sdk/client-sts').STSClient;
1058
+ let AssumeRoleCommand: typeof import('@aws-sdk/client-sts').AssumeRoleCommand;
1059
+ try {
1060
+ ({ STSClient, AssumeRoleCommand } =
1061
+ await import('@aws-sdk/client-sts'));
1062
+ } catch {
1063
+ console.error(
1064
+ '[infra-deploy] Error: @aws-sdk/client-sts is required for assumeRole credentials but is not installed.',
1065
+ );
1066
+ console.error('[infra-deploy] Please install @aws-sdk/client-sts');
1067
+ process.exit(1);
1068
+ }
1069
+
1070
+ // If a source profile is specified, configure the STS client to use it
1071
+ const stsClientOptions: Record<string, unknown> = {};
1072
+ if (credentials.profile) {
1073
+ const { fromIni } = await import('@aws-sdk/credential-providers');
1074
+ stsClientOptions.credentials = fromIni({
1075
+ profile: credentials.profile,
1076
+ });
1077
+ }
1078
+
1079
+ const response = await new STSClient(stsClientOptions).send(
1080
+ new AssumeRoleCommand({
1081
+ RoleArn: credentials.assumeRole,
1082
+ RoleSessionName: \`infra-deploy-\${projectPath.replace(/\\//g, '-')}\`,
1083
+ ...(credentials.externalId
1084
+ ? { ExternalId: credentials.externalId }
1085
+ : {}),
1086
+ ...(credentials.sessionDuration
1087
+ ? { DurationSeconds: credentials.sessionDuration }
1088
+ : {}),
1089
+ }),
1090
+ );
1091
+
1092
+ env.AWS_ACCESS_KEY_ID = response.Credentials?.AccessKeyId;
1093
+ env.AWS_SECRET_ACCESS_KEY = response.Credentials?.SecretAccessKey;
1094
+ env.AWS_SESSION_TOKEN = response.Credentials?.SessionToken;
1095
+ delete env.AWS_PROFILE;
1096
+ break;
1097
+ }
1098
+ }
1099
+
1100
+ return env;
1101
+ }
1102
+
1103
+ /** Human-readable description of credentials for logging */
1104
+ export function describeCredentials(creds: StageCredentials): string {
1105
+ return creds.type === 'profile'
1106
+ ? \`profile '\${creds.profile}'\`
1107
+ : \`role '\${creds.assumeRole}'\`;
1108
+ }
1109
+ "
1110
+ `;
1111
+
1112
+ exports[`infra generator > with enableStageConfig > should snapshot generated scripts src directory > packages/common/scripts/src/stage-credentials/run.ts 1`] = `
1113
+ "import { spawnSync } from 'child_process';
1114
+ import stagesConfig from ':proj/common-infra-config';
1115
+ import { parseStageName } from './stage-parser.js';
1116
+ import {
1117
+ lookupCredentials,
1118
+ buildChildEnv,
1119
+ describeCredentials,
1120
+ } from './credentials.js';
1121
+ import { buildCdkCommand } from './cdk-command.js';
1122
+
1123
+ const log = (msg: string) => console.error(\`[infra-deploy] \${msg}\`);
1124
+
1125
+ export async function run(action: 'deploy' | 'destroy'): Promise<void> {
1126
+ const [projectPath, ...remainingArgs] = process.argv.slice(2);
1127
+
1128
+ if (!projectPath) {
1129
+ log(\`Usage: infra-\${action} <project-path> [stage/*] [cdk-args...]\`);
1130
+ process.exit(1);
1131
+ }
1132
+
1133
+ const stageName = parseStageName(remainingArgs[0]);
1134
+ let childEnv: Record<string, string | undefined> = { ...process.env };
1135
+
1136
+ if (stageName) {
1137
+ const { credentials, source } = lookupCredentials(
1138
+ stagesConfig,
1139
+ projectPath,
1140
+ stageName,
1141
+ );
1142
+
1143
+ if (credentials) {
1144
+ log(
1145
+ \`Using \${describeCredentials(credentials)} for '\${stageName}' (\${source})\`,
1146
+ );
1147
+ childEnv = await buildChildEnv(credentials, projectPath);
1148
+ } else {
1149
+ log(\`No credentials for '\${stageName}' — using environment\`);
1150
+ }
1151
+ } else {
1152
+ log('No stage specified — using environment credentials');
1153
+ }
1154
+
1155
+ // Run CDK from the project directory so it finds cdk.json
1156
+ const cmd = buildCdkCommand(action, remainingArgs);
1157
+ const { status } = spawnSync(cmd[0], cmd.slice(1), {
1158
+ stdio: 'inherit',
1159
+ env: childEnv,
1160
+ cwd: projectPath,
1161
+ });
1162
+
1163
+ process.exit(status ?? 1);
1164
+ }
1165
+ "
1166
+ `;
1167
+
1168
+ exports[`infra generator > with enableStageConfig > should snapshot generated scripts src directory > packages/common/scripts/src/stage-credentials/stage-parser.ts 1`] = `
1169
+ "/**
1170
+ * Extracts the CDK stage name from the first positional argument.
1171
+ *
1172
+ * Examples:
1173
+ * parseStageName('my-app-dev/*') → 'my-app-dev'
1174
+ * parseStageName('my-app-dev') → 'my-app-dev'
1175
+ * parseStageName('--verbose') → undefined (flag, not a stage)
1176
+ * parseStageName(undefined) → undefined
1177
+ */
1178
+ export function parseStageName(
1179
+ firstArg: string | undefined,
1180
+ ): string | undefined {
1181
+ if (!firstArg || firstArg.startsWith('-')) return undefined;
1182
+ return firstArg.includes('/') ? firstArg.split('/')[0] : firstArg;
1183
+ }
1184
+ "
1185
+ `;
@@ -1,8 +1,23 @@
1
1
  import { ApplicationStage } from './stages/application-stage.js';
2
2
  import { App } from '<%= scopeAlias %>common-constructs';
3
-
3
+ <% if (enableStageConfig) { %>import { resolveStage } from '<%= scopeAlias %>common-infra-config';
4
+ <% } %>
4
5
  const app = new App();
6
+ <% if (enableStageConfig) { %>
7
+ // Stage configuration is defined in packages/common/infra-config/src/stages.config.ts
8
+ // The project path '<%= dir %>' is used as the key in the config.
9
+ // Project-specific settings override shared settings for the same stage name.
5
10
 
11
+ // Sandbox stage — uses your CLI credentials by default.
12
+ // Add an entry in stages.config.ts to configure specific credentials.
13
+ const sandboxConfig = resolveStage('<%= dir %>', '<%= namespace %>-sandbox');
14
+ new ApplicationStage(app, '<%= namespace %>-sandbox', {
15
+ env: {
16
+ account: sandboxConfig?.account ?? process.env.CDK_DEFAULT_ACCOUNT,
17
+ region: sandboxConfig?.region ?? process.env.CDK_DEFAULT_REGION,
18
+ },
19
+ });
20
+ <% } else { %>
6
21
  // Use this to deploy your own sandbox environment (assumes your CLI credentials)
7
22
  new ApplicationStage(app, '<%= namespace %>-sandbox', {
8
23
  env: {
@@ -10,5 +25,5 @@ new ApplicationStage(app, '<%= namespace %>-sandbox', {
10
25
  region: process.env.CDK_DEFAULT_REGION,
11
26
  },
12
27
  });
13
-
28
+ <% } %>
14
29
  app.synth();