@aws/nx-plugin 1.0.0-rc.89 → 1.0.0-rc.90

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 (148) hide show
  1. package/README.md +22 -22
  2. package/executors.json +5 -0
  3. package/generators.json +2 -1
  4. package/migrations.json +47 -2
  5. package/package.json +1 -1
  6. package/src/infra/app/__snapshots__/generator.spec.ts.snap +24 -12
  7. package/src/infra/app/generator.js +9 -7
  8. package/src/infra/app/generator.js.map +1 -1
  9. package/src/mcp-server/generator-info.d.ts +5 -0
  10. package/src/mcp-server/generator-info.js +6 -2
  11. package/src/mcp-server/generator-info.js.map +1 -1
  12. package/src/migrations/latest/add-assemble-target/metadata.json +3 -0
  13. package/src/migrations/latest/add-assemble-target/migration.d.ts +6 -0
  14. package/src/migrations/latest/add-assemble-target/migration.js +241 -0
  15. package/src/migrations/latest/add-assemble-target/migration.js.map +1 -0
  16. package/src/migrations/latest/declare-cacheable-target-inputs/metadata.json +3 -0
  17. package/src/migrations/latest/declare-cacheable-target-inputs/migration.d.ts +6 -0
  18. package/src/migrations/latest/declare-cacheable-target-inputs/migration.js +94 -0
  19. package/src/migrations/latest/declare-cacheable-target-inputs/migration.js.map +1 -0
  20. package/src/migrations/latest/dev-target-depends-on-pull-image/metadata.json +3 -0
  21. package/src/migrations/latest/dev-target-depends-on-pull-image/migration.d.ts +6 -0
  22. package/src/migrations/latest/dev-target-depends-on-pull-image/migration.js +67 -0
  23. package/src/migrations/latest/dev-target-depends-on-pull-image/migration.js.map +1 -0
  24. package/src/migrations/latest/docker-target-cache-soundness/metadata.json +3 -0
  25. package/src/migrations/latest/docker-target-cache-soundness/migration.d.ts +6 -0
  26. package/src/migrations/latest/docker-target-cache-soundness/migration.js +95 -0
  27. package/src/migrations/latest/docker-target-cache-soundness/migration.js.map +1 -0
  28. package/src/migrations/latest/open-api-codegen-executor/metadata.json +3 -0
  29. package/src/migrations/latest/open-api-codegen-executor/migration.d.ts +6 -0
  30. package/src/migrations/latest/open-api-codegen-executor/migration.js +100 -0
  31. package/src/migrations/latest/open-api-codegen-executor/migration.js.map +1 -0
  32. package/src/migrations/latest/python-test-input-scoping/metadata.json +3 -0
  33. package/src/migrations/latest/python-test-input-scoping/migration.d.ts +6 -0
  34. package/src/migrations/latest/python-test-input-scoping/migration.js +142 -0
  35. package/src/migrations/latest/python-test-input-scoping/migration.js.map +1 -0
  36. package/src/migrations/latest/terraform-fmt-check/metadata.json +3 -0
  37. package/src/migrations/latest/terraform-fmt-check/migration.d.ts +6 -0
  38. package/src/migrations/latest/terraform-fmt-check/migration.js +165 -0
  39. package/src/migrations/latest/terraform-fmt-check/migration.js.map +1 -0
  40. package/src/migrations/latest/terraform-provider-plugin-cache/metadata.json +3 -0
  41. package/src/migrations/latest/terraform-provider-plugin-cache/migration.d.ts +6 -0
  42. package/src/migrations/latest/terraform-provider-plugin-cache/migration.js +164 -0
  43. package/src/migrations/latest/terraform-provider-plugin-cache/migration.js.map +1 -0
  44. package/src/migrations/latest/vitest-coverage-output-dir/metadata.json +3 -0
  45. package/src/migrations/latest/vitest-coverage-output-dir/migration.d.ts +6 -0
  46. package/src/migrations/latest/vitest-coverage-output-dir/migration.js +177 -0
  47. package/src/migrations/latest/vitest-coverage-output-dir/migration.js.map +1 -0
  48. package/src/open-api/codegen/executor-schema.d.js +6 -0
  49. package/src/open-api/codegen/executor-schema.d.js.map +1 -0
  50. package/src/open-api/codegen/executor-schema.d.ts +18 -0
  51. package/src/open-api/codegen/executor-schema.json +33 -0
  52. package/src/open-api/codegen/executor.d.ts +18 -0
  53. package/src/open-api/codegen/executor.js +56 -0
  54. package/src/open-api/codegen/executor.js.map +1 -0
  55. package/src/preset/__snapshots__/generator.spec.ts.snap +2 -1
  56. package/src/py/agent/generator.js +21 -22
  57. package/src/py/agent/generator.js.map +1 -1
  58. package/src/py/agent/react-connection/generator.js +9 -0
  59. package/src/py/agent/react-connection/generator.js.map +1 -1
  60. package/src/py/dynamodb/__snapshots__/generator.spec.ts.snap +0 -30
  61. package/src/py/fast-api/react/open-api.js +19 -7
  62. package/src/py/fast-api/react/open-api.js.map +1 -1
  63. package/src/py/mcp-server/generator.js +4 -4
  64. package/src/py/mcp-server/generator.js.map +1 -1
  65. package/src/py/project/generator.d.ts +9 -0
  66. package/src/py/project/generator.js +51 -1
  67. package/src/py/project/generator.js.map +1 -1
  68. package/src/py/rdb/generator.js +10 -4
  69. package/src/py/rdb/generator.js.map +1 -1
  70. package/src/smithy/project/__snapshots__/generator.spec.ts.snap +15 -0
  71. package/src/smithy/project/generator.js +10 -0
  72. package/src/smithy/project/generator.js.map +1 -1
  73. package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +7 -0
  74. package/src/terraform/project/files/application/scripts/env.ts.template +11 -0
  75. package/src/terraform/project/files/application/scripts/init.ts.template +2 -1
  76. package/src/terraform/project/files/application/src/providers.tf.template +2 -2
  77. package/src/terraform/project/generator.d.ts +11 -1
  78. package/src/terraform/project/generator.js +104 -20
  79. package/src/terraform/project/generator.js.map +1 -1
  80. package/src/trpc/backend/generator.js +2 -2
  81. package/src/trpc/backend/generator.js.map +1 -1
  82. package/src/ts/agent/generator.js +4 -7
  83. package/src/ts/agent/generator.js.map +1 -1
  84. package/src/ts/astro-docs/generator.js +5 -0
  85. package/src/ts/astro-docs/generator.js.map +1 -1
  86. package/src/ts/dynamodb/__snapshots__/generator.spec.ts.snap +0 -30
  87. package/src/ts/dynamodb/generator.js +3 -0
  88. package/src/ts/dynamodb/generator.js.map +1 -1
  89. package/src/ts/lib/__snapshots__/generator.spec.ts.snap +10 -1
  90. package/src/ts/lib/__snapshots__/vitest.spec.ts.snap +1 -1
  91. package/src/ts/lib/generator.d.ts +16 -0
  92. package/src/ts/lib/generator.js +31 -5
  93. package/src/ts/lib/generator.js.map +1 -1
  94. package/src/ts/lib/vitest.d.ts +11 -0
  95. package/src/ts/lib/vitest.js +31 -2
  96. package/src/ts/lib/vitest.js.map +1 -1
  97. package/src/ts/mcp-server/generator.js +4 -7
  98. package/src/ts/mcp-server/generator.js.map +1 -1
  99. package/src/ts/rdb/generator.js +8 -5
  100. package/src/ts/rdb/generator.js.map +1 -1
  101. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +27 -11
  102. package/src/ts/react-website/app/generator.js +9 -1
  103. package/src/ts/react-website/app/generator.js.map +1 -1
  104. package/src/ts/sync/generator.js +19 -2
  105. package/src/ts/sync/generator.js.map +1 -1
  106. package/src/utils/agent-core-constructs/agent-core-constructs.js +3 -3
  107. package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -1
  108. package/src/utils/api-constructs/api-constructs.js +2 -2
  109. package/src/utils/api-constructs/api-constructs.js.map +1 -1
  110. package/src/utils/api-constructs/open-api-metadata.js +15 -36
  111. package/src/utils/api-constructs/open-api-metadata.js.map +1 -1
  112. package/src/utils/api-constructs/trpc-operations-metadata.js +9 -3
  113. package/src/utils/api-constructs/trpc-operations-metadata.js.map +1 -1
  114. package/src/utils/bundle/bundle.js +13 -4
  115. package/src/utils/bundle/bundle.js.map +1 -1
  116. package/src/utils/connection/open-api/react.js +7 -17
  117. package/src/utils/connection/open-api/react.js.map +1 -1
  118. package/src/utils/dcr-proxy-constructs/dcr-proxy-constructs.js +2 -2
  119. package/src/utils/dcr-proxy-constructs/dcr-proxy-constructs.js.map +1 -1
  120. package/src/utils/docker.d.ts +22 -10
  121. package/src/utils/docker.js +27 -17
  122. package/src/utils/docker.js.map +1 -1
  123. package/src/utils/dynamodb-constructs/dynamodb-constructs.js +2 -2
  124. package/src/utils/dynamodb-constructs/dynamodb-constructs.js.map +1 -1
  125. package/src/utils/files/common/scripts/src/dynamodb/start-container.ts.template +0 -15
  126. package/src/utils/files/common/scripts/src/rdb/shared/start-container.ts.template +0 -15
  127. package/src/utils/format.d.ts +5 -0
  128. package/src/utils/format.js +5 -0
  129. package/src/utils/format.js.map +1 -1
  130. package/src/utils/function-constructs/function-constructs.js +2 -2
  131. package/src/utils/function-constructs/function-constructs.js.map +1 -1
  132. package/src/utils/generators.d.ts +33 -0
  133. package/src/utils/generators.js +18 -3
  134. package/src/utils/generators.js.map +1 -1
  135. package/src/utils/init.js +4 -6
  136. package/src/utils/init.js.map +1 -1
  137. package/src/utils/mock-project-graph.js +5 -2
  138. package/src/utils/mock-project-graph.js.map +1 -1
  139. package/src/utils/nx.d.ts +19 -0
  140. package/src/utils/nx.js +23 -0
  141. package/src/utils/nx.js.map +1 -1
  142. package/src/utils/open-api-codegen-target.d.ts +28 -0
  143. package/src/utils/open-api-codegen-target.js +31 -0
  144. package/src/utils/open-api-codegen-target.js.map +1 -0
  145. package/src/utils/rdb-constructs/rdb-constructs.js +2 -2
  146. package/src/utils/rdb-constructs/rdb-constructs.js.map +1 -1
  147. package/src/utils/website-constructs/website-constructs.js +2 -2
  148. package/src/utils/website-constructs/website-constructs.js.map +1 -1
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */ import { getProjects, updateProjectConfiguration } from "@nx/devkit";
5
+ import { TS_DYNAMODB_GENERATOR_INFO } from "../../../ts/dynamodb/generator.js";
6
+ import { TS_RDB_GENERATOR_INFO } from "../../../ts/rdb/generator.js";
7
+ import { formatFilesInSubtree } from "../../../utils/format.js";
8
+ import { addDependencyToTargetIfNotPresent } from "../../../utils/nx.js";
9
+ /**
10
+ * Make the dev target of ts#dynamodb and ts#rdb projects depend on pull-image
11
+ *
12
+ * Without it, `nx dev` starts the container (and, for DynamoDB, creates the
13
+ * local table) while the image is still being fetched, so a first run on a
14
+ * machine without the image races the pull.
15
+ */ const GENERATOR_IDS = [
16
+ TS_DYNAMODB_GENERATOR_INFO.id,
17
+ TS_RDB_GENERATOR_INFO.id
18
+ ];
19
+ const PULL_IMAGE_TARGET = 'pull-image';
20
+ const startsGeneratedContainer = (project)=>{
21
+ const { command, commands } = project.targets?.dev?.options ?? {};
22
+ return [
23
+ ...commands ?? [],
24
+ ...command ? [
25
+ command
26
+ ] : []
27
+ ].some((c)=>typeof c === 'string' && c.includes('start-container.ts'));
28
+ };
29
+ const divergedNextStep = (projectName)=>`${projectName}: its dev target has been customised, so it was left as it is. Add "${PULL_IMAGE_TARGET}" to the target's dependsOn so the container image is fetched before the container starts.`;
30
+ export default async function migration(tree) {
31
+ const nextSteps = [];
32
+ let migrated = false;
33
+ for (const [name, project] of getProjects(tree)){
34
+ if (!GENERATOR_IDS.includes(project.metadata?.generator)) {
35
+ continue;
36
+ }
37
+ const dev = project.targets?.dev;
38
+ if (!dev || !project.targets?.[PULL_IMAGE_TARGET]) {
39
+ continue;
40
+ }
41
+ // Nx requires an array here. Anything else is hand-written and is reported
42
+ // rather than rewritten, so a malformed value is never spread or clobbered.
43
+ if (dev.dependsOn !== undefined && !Array.isArray(dev.dependsOn)) {
44
+ nextSteps.push(divergedNextStep(name));
45
+ continue;
46
+ }
47
+ const alreadyDependsOnPullImage = (dev.dependsOn ?? []).some((dependency)=>dependency === PULL_IMAGE_TARGET || typeof dependency === 'object' && dependency !== null && dependency.target === PULL_IMAGE_TARGET && dependency.projects === undefined);
48
+ if (alreadyDependsOnPullImage) {
49
+ continue;
50
+ }
51
+ if (!startsGeneratedContainer(project)) {
52
+ nextSteps.push(divergedNextStep(name));
53
+ continue;
54
+ }
55
+ addDependencyToTargetIfNotPresent(project, 'dev', PULL_IMAGE_TARGET);
56
+ updateProjectConfiguration(tree, name, project);
57
+ migrated = true;
58
+ }
59
+ if (migrated) {
60
+ await formatFilesInSubtree(tree);
61
+ }
62
+ return {
63
+ nextSteps
64
+ };
65
+ }
66
+
67
+ //# sourceMappingURL=migration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/dev-target-depends-on-pull-image/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n getProjects,\n type MigrationReturnObject,\n type ProjectConfiguration,\n type Tree,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { TS_DYNAMODB_GENERATOR_INFO } from '../../../ts/dynamodb/generator.js';\nimport { TS_RDB_GENERATOR_INFO } from '../../../ts/rdb/generator.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport { addDependencyToTargetIfNotPresent } from '../../../utils/nx.js';\n\n/**\n * Make the dev target of ts#dynamodb and ts#rdb projects depend on pull-image\n *\n * Without it, `nx dev` starts the container (and, for DynamoDB, creates the\n * local table) while the image is still being fetched, so a first run on a\n * machine without the image races the pull.\n */\n\nconst GENERATOR_IDS = [TS_DYNAMODB_GENERATOR_INFO.id, TS_RDB_GENERATOR_INFO.id];\n\nconst PULL_IMAGE_TARGET = 'pull-image';\n\nconst startsGeneratedContainer = (project: ProjectConfiguration): boolean => {\n const { command, commands } = project.targets?.dev?.options ?? {};\n return [...(commands ?? []), ...(command ? [command] : [])].some(\n (c: string) => typeof c === 'string' && c.includes('start-container.ts'),\n );\n};\n\nconst divergedNextStep = (projectName: string): string =>\n `${projectName}: its dev target has been customised, so it was left as it is. Add \"${PULL_IMAGE_TARGET}\" to the target's dependsOn so the container image is fetched before the container starts.`;\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n let migrated = false;\n\n for (const [name, project] of getProjects(tree)) {\n if (!GENERATOR_IDS.includes((project.metadata as any)?.generator)) {\n continue;\n }\n\n const dev = project.targets?.dev;\n if (!dev || !project.targets?.[PULL_IMAGE_TARGET]) {\n continue;\n }\n\n // Nx requires an array here. Anything else is hand-written and is reported\n // rather than rewritten, so a malformed value is never spread or clobbered.\n if (dev.dependsOn !== undefined && !Array.isArray(dev.dependsOn)) {\n nextSteps.push(divergedNextStep(name));\n continue;\n }\n\n const alreadyDependsOnPullImage = (dev.dependsOn ?? []).some(\n (dependency) =>\n dependency === PULL_IMAGE_TARGET ||\n (typeof dependency === 'object' &&\n dependency !== null &&\n dependency.target === PULL_IMAGE_TARGET &&\n dependency.projects === undefined),\n );\n if (alreadyDependsOnPullImage) {\n continue;\n }\n\n if (!startsGeneratedContainer(project)) {\n nextSteps.push(divergedNextStep(name));\n continue;\n }\n\n addDependencyToTargetIfNotPresent(project, 'dev', PULL_IMAGE_TARGET);\n updateProjectConfiguration(tree, name, project);\n migrated = true;\n }\n\n if (migrated) {\n await formatFilesInSubtree(tree);\n }\n\n return { nextSteps };\n}\n"],"names":["getProjects","updateProjectConfiguration","TS_DYNAMODB_GENERATOR_INFO","TS_RDB_GENERATOR_INFO","formatFilesInSubtree","addDependencyToTargetIfNotPresent","GENERATOR_IDS","id","PULL_IMAGE_TARGET","startsGeneratedContainer","project","command","commands","targets","dev","options","some","c","includes","divergedNextStep","projectName","migration","tree","nextSteps","migrated","name","metadata","generator","dependsOn","undefined","Array","isArray","push","alreadyDependsOnPullImage","dependency","target","projects"],"mappings":"AAAA;;;CAGC,GACD,SACEA,WAAW,EAIXC,0BAA0B,QACrB,aAAa;AACpB,SAASC,0BAA0B,QAAQ,oCAAoC;AAC/E,SAASC,qBAAqB,QAAQ,+BAA+B;AACrE,SAASC,oBAAoB,QAAQ,2BAA2B;AAChE,SAASC,iCAAiC,QAAQ,uBAAuB;AAEzE;;;;;;CAMC,GAED,MAAMC,gBAAgB;IAACJ,2BAA2BK,EAAE;IAAEJ,sBAAsBI,EAAE;CAAC;AAE/E,MAAMC,oBAAoB;AAE1B,MAAMC,2BAA2B,CAACC;IAChC,MAAM,EAAEC,OAAO,EAAEC,QAAQ,EAAE,GAAGF,QAAQG,OAAO,EAAEC,KAAKC,WAAW,CAAC;IAChE,OAAO;WAAKH,YAAY,EAAE;WAAOD,UAAU;YAACA;SAAQ,GAAG,EAAE;KAAE,CAACK,IAAI,CAC9D,CAACC,IAAc,OAAOA,MAAM,YAAYA,EAAEC,QAAQ,CAAC;AAEvD;AAEA,MAAMC,mBAAmB,CAACC,cACxB,GAAGA,YAAY,oEAAoE,EAAEZ,kBAAkB,0FAA0F,CAAC;AAEpM,eAAe,eAAea,UAC5BC,IAAU;IAEV,MAAMC,YAAsB,EAAE;IAC9B,IAAIC,WAAW;IAEf,KAAK,MAAM,CAACC,MAAMf,QAAQ,IAAIV,YAAYsB,MAAO;QAC/C,IAAI,CAAChB,cAAcY,QAAQ,CAAER,QAAQgB,QAAQ,EAAUC,YAAY;YACjE;QACF;QAEA,MAAMb,MAAMJ,QAAQG,OAAO,EAAEC;QAC7B,IAAI,CAACA,OAAO,CAACJ,QAAQG,OAAO,EAAE,CAACL,kBAAkB,EAAE;YACjD;QACF;QAEA,2EAA2E;QAC3E,4EAA4E;QAC5E,IAAIM,IAAIc,SAAS,KAAKC,aAAa,CAACC,MAAMC,OAAO,CAACjB,IAAIc,SAAS,GAAG;YAChEL,UAAUS,IAAI,CAACb,iBAAiBM;YAChC;QACF;QAEA,MAAMQ,4BAA4B,AAACnB,CAAAA,IAAIc,SAAS,IAAI,EAAE,AAAD,EAAGZ,IAAI,CAC1D,CAACkB,aACCA,eAAe1B,qBACd,OAAO0B,eAAe,YACrBA,eAAe,QACfA,WAAWC,MAAM,KAAK3B,qBACtB0B,WAAWE,QAAQ,KAAKP;QAE9B,IAAII,2BAA2B;YAC7B;QACF;QAEA,IAAI,CAACxB,yBAAyBC,UAAU;YACtCa,UAAUS,IAAI,CAACb,iBAAiBM;YAChC;QACF;QAEApB,kCAAkCK,SAAS,OAAOF;QAClDP,2BAA2BqB,MAAMG,MAAMf;QACvCc,WAAW;IACb;IAEA,IAAIA,UAAU;QACZ,MAAMpB,qBAAqBkB;IAC7B;IAEA,OAAO;QAAEC;IAAU;AACrB"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "description": "Make container image build and scan targets non-cacheable, so a cache hit can never report success for an image the container engine no longer has"
3
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { type MigrationReturnObject, type Tree } from '@nx/devkit';
6
+ export default function migration(tree: Tree): Promise<MigrationReturnObject>;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */ import { getProjects, updateProjectConfiguration } from "@nx/devkit";
5
+ import { IMAGE_BUILD_CACHE } from "../../../utils/docker.js";
6
+ import { formatFilesInSubtree } from "../../../utils/format.js";
7
+ /**
8
+ * Stop Nx caching the targets that build and scan container images.
9
+ *
10
+ * A built image lives in the container engine's store, not under any `outputs`
11
+ * path, so there was nothing for Nx to restore on a cache hit: `build` reported
12
+ * success for an image the engine no longer had, and the `trivy` target that
13
+ * depends on it reported a cached pass against that missing image — so a stale
14
+ * or absent image could clear both a build and a vulnerability scan.
15
+ *
16
+ * The container engine's layer cache already makes the rebuild cheap and, unlike
17
+ * Nx's, it cannot claim a hit for an image that isn't there.
18
+ *
19
+ * A target named as one of these but no longer running the engine has been
20
+ * reworked by the user, so it is left untouched and reported via `nextSteps`.
21
+ */ /** Names the generators give the targets that build or scan an image. */ const IMAGE_TARGET_NAME = /(^|-)(docker|trivy)$/;
22
+ /** Commands that build an image, or read one back out of the engine to scan it. */ const CONTAINER_ENGINE_COMMAND = /\b(docker|finch)\s+(build|save)\b/;
23
+ /**
24
+ * The `Dockerfile` the TypeScript image targets declared as their output. It is
25
+ * a copy made on the way into the build context, never a product of the build,
26
+ * so it is dropped along with the caching it existed to support.
27
+ */ const VENDED_DOCKERFILE_OUTPUT = /\/bundle\/(agent|mcp)\/[^/]+\/Dockerfile$/;
28
+ /** The directory the scan target stages its tarball and ignore file in. */ const VENDED_SCAN_OUTPUT = /\/trivy\/[^/]+$/;
29
+ /**
30
+ * Outputs the generators declare on these targets. Neither is the artifact the
31
+ * target's success stands for, so dropping their caching loses nothing. An
32
+ * output beyond these belongs to a target the user has repurposed, whose product
33
+ * may genuinely be on disk and soundly cacheable.
34
+ */ const isVendedOutput = (output)=>VENDED_DOCKERFILE_OUTPUT.test(output) || VENDED_SCAN_OUTPUT.test(output);
35
+ /** Every command a target runs, however its options spell them. */ const targetCommands = (target)=>{
36
+ const { command, commands } = target.options ?? {};
37
+ return [
38
+ ...typeof command === 'string' ? [
39
+ command
40
+ ] : [],
41
+ ...Array.isArray(commands) ? commands : []
42
+ ].filter((c)=>typeof c === 'string');
43
+ };
44
+ const divergedMessage = (projectName, targetName)=>`${projectName}:${targetName}: has diverged from the generated shape - left untouched. Set \`"cache": false\` on it by hand if it builds or scans a container image: the image lives in the container engine rather than under the target's \`outputs\`, so a cache hit can report success for an image that is no longer there.`;
45
+ const ownOutputsMessage = (projectName, targetName)=>`${projectName}:${targetName}: declares outputs of its own, so it was left cacheable. If its result depends on a container image rather than only on those outputs, set \`"cache": false\` on it by hand: the image lives in the container engine, so a cache hit can report success for an image that is no longer there.`;
46
+ /** Applies the fix to one target, reporting one it declines to touch. */ const migrateTarget = (projectName, targetName, target, nextSteps)=>{
47
+ // A target with `cache` unset is not cached unless `targetDefaults` opts it
48
+ // in, and one already set to false is sound, so only an explicitly cacheable
49
+ // one needs rewriting. Both leave a re-run a no-op.
50
+ if (target.cache !== true) return false;
51
+ if (!targetCommands(target).some((c)=>CONTAINER_ENGINE_COMMAND.test(c))) {
52
+ nextSteps.push(divergedMessage(projectName, targetName));
53
+ return false;
54
+ }
55
+ // Running the engine does not by itself make a target's result uncacheable:
56
+ // one that also declares outputs of its own may well produce the artifact its
57
+ // success stands for on disk (a `docker save` tarball, a test report), and
58
+ // that is soundly cacheable. Only the shapes the generators vend are rewritten.
59
+ const ownOutputs = target.outputs?.filter((o)=>!isVendedOutput(o)) ?? [];
60
+ if (ownOutputs.length > 0) {
61
+ nextSteps.push(ownOutputsMessage(projectName, targetName));
62
+ return false;
63
+ }
64
+ target.cache = IMAGE_BUILD_CACHE;
65
+ // The scan keeps its staging directory: it is a real directory on disk, and
66
+ // dropping it would leave the tarball behind on a later run.
67
+ const outputs = target.outputs?.filter((o)=>!VENDED_DOCKERFILE_OUTPUT.test(o));
68
+ if (outputs && outputs.length === 0) {
69
+ delete target.outputs;
70
+ } else if (outputs) {
71
+ target.outputs = outputs;
72
+ }
73
+ return true;
74
+ };
75
+ export default async function migration(tree) {
76
+ const nextSteps = [];
77
+ for (const [projectName, project] of getProjects(tree)){
78
+ let changed = false;
79
+ for (const [targetName, target] of Object.entries(project.targets ?? {})){
80
+ // Matched on the name so a target reworked to no longer run the engine is
81
+ // still found and reported, rather than silently left cacheable.
82
+ if (!IMAGE_TARGET_NAME.test(targetName)) continue;
83
+ changed = migrateTarget(projectName, targetName, target, nextSteps) || changed;
84
+ }
85
+ if (changed) {
86
+ updateProjectConfiguration(tree, projectName, project);
87
+ }
88
+ }
89
+ await formatFilesInSubtree(tree);
90
+ return {
91
+ nextSteps
92
+ };
93
+ }
94
+
95
+ //# sourceMappingURL=migration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/docker-target-cache-soundness/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n getProjects,\n type MigrationReturnObject,\n type ProjectConfiguration,\n type TargetConfiguration,\n type Tree,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { IMAGE_BUILD_CACHE } from '../../../utils/docker.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\n\n/**\n * Stop Nx caching the targets that build and scan container images.\n *\n * A built image lives in the container engine's store, not under any `outputs`\n * path, so there was nothing for Nx to restore on a cache hit: `build` reported\n * success for an image the engine no longer had, and the `trivy` target that\n * depends on it reported a cached pass against that missing image — so a stale\n * or absent image could clear both a build and a vulnerability scan.\n *\n * The container engine's layer cache already makes the rebuild cheap and, unlike\n * Nx's, it cannot claim a hit for an image that isn't there.\n *\n * A target named as one of these but no longer running the engine has been\n * reworked by the user, so it is left untouched and reported via `nextSteps`.\n */\n\n/** Names the generators give the targets that build or scan an image. */\nconst IMAGE_TARGET_NAME = /(^|-)(docker|trivy)$/;\n\n/** Commands that build an image, or read one back out of the engine to scan it. */\nconst CONTAINER_ENGINE_COMMAND = /\\b(docker|finch)\\s+(build|save)\\b/;\n\n/**\n * The `Dockerfile` the TypeScript image targets declared as their output. It is\n * a copy made on the way into the build context, never a product of the build,\n * so it is dropped along with the caching it existed to support.\n */\nconst VENDED_DOCKERFILE_OUTPUT = /\\/bundle\\/(agent|mcp)\\/[^/]+\\/Dockerfile$/;\n\n/** The directory the scan target stages its tarball and ignore file in. */\nconst VENDED_SCAN_OUTPUT = /\\/trivy\\/[^/]+$/;\n\n/**\n * Outputs the generators declare on these targets. Neither is the artifact the\n * target's success stands for, so dropping their caching loses nothing. An\n * output beyond these belongs to a target the user has repurposed, whose product\n * may genuinely be on disk and soundly cacheable.\n */\nconst isVendedOutput = (output: string): boolean =>\n VENDED_DOCKERFILE_OUTPUT.test(output) || VENDED_SCAN_OUTPUT.test(output);\n\n/** Every command a target runs, however its options spell them. */\nconst targetCommands = (target: TargetConfiguration): string[] => {\n const { command, commands } = (target.options ?? {}) as {\n command?: unknown;\n commands?: unknown;\n };\n return [\n ...(typeof command === 'string' ? [command] : []),\n ...(Array.isArray(commands) ? commands : []),\n ].filter((c): c is string => typeof c === 'string');\n};\n\nconst divergedMessage = (projectName: string, targetName: string) =>\n `${projectName}:${targetName}: has diverged from the generated shape - left untouched. Set \\`\"cache\": false\\` on it by hand if it builds or scans a container image: the image lives in the container engine rather than under the target's \\`outputs\\`, so a cache hit can report success for an image that is no longer there.`;\n\nconst ownOutputsMessage = (projectName: string, targetName: string) =>\n `${projectName}:${targetName}: declares outputs of its own, so it was left cacheable. If its result depends on a container image rather than only on those outputs, set \\`\"cache\": false\\` on it by hand: the image lives in the container engine, so a cache hit can report success for an image that is no longer there.`;\n\n/** Applies the fix to one target, reporting one it declines to touch. */\nconst migrateTarget = (\n projectName: string,\n targetName: string,\n target: TargetConfiguration,\n nextSteps: string[],\n): boolean => {\n // A target with `cache` unset is not cached unless `targetDefaults` opts it\n // in, and one already set to false is sound, so only an explicitly cacheable\n // one needs rewriting. Both leave a re-run a no-op.\n if (target.cache !== true) return false;\n\n if (!targetCommands(target).some((c) => CONTAINER_ENGINE_COMMAND.test(c))) {\n nextSteps.push(divergedMessage(projectName, targetName));\n return false;\n }\n\n // Running the engine does not by itself make a target's result uncacheable:\n // one that also declares outputs of its own may well produce the artifact its\n // success stands for on disk (a `docker save` tarball, a test report), and\n // that is soundly cacheable. Only the shapes the generators vend are rewritten.\n const ownOutputs = target.outputs?.filter((o) => !isVendedOutput(o)) ?? [];\n if (ownOutputs.length > 0) {\n nextSteps.push(ownOutputsMessage(projectName, targetName));\n return false;\n }\n\n target.cache = IMAGE_BUILD_CACHE;\n\n // The scan keeps its staging directory: it is a real directory on disk, and\n // dropping it would leave the tarball behind on a later run.\n const outputs = target.outputs?.filter(\n (o) => !VENDED_DOCKERFILE_OUTPUT.test(o),\n );\n if (outputs && outputs.length === 0) {\n delete target.outputs;\n } else if (outputs) {\n target.outputs = outputs;\n }\n\n return true;\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const [projectName, project] of getProjects(tree)) {\n let changed = false;\n\n for (const [targetName, target] of Object.entries(project.targets ?? {})) {\n // Matched on the name so a target reworked to no longer run the engine is\n // still found and reported, rather than silently left cacheable.\n if (!IMAGE_TARGET_NAME.test(targetName)) continue;\n\n changed =\n migrateTarget(projectName, targetName, target, nextSteps) || changed;\n }\n\n if (changed) {\n updateProjectConfiguration(\n tree,\n projectName,\n project as ProjectConfiguration,\n );\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["getProjects","updateProjectConfiguration","IMAGE_BUILD_CACHE","formatFilesInSubtree","IMAGE_TARGET_NAME","CONTAINER_ENGINE_COMMAND","VENDED_DOCKERFILE_OUTPUT","VENDED_SCAN_OUTPUT","isVendedOutput","output","test","targetCommands","target","command","commands","options","Array","isArray","filter","c","divergedMessage","projectName","targetName","ownOutputsMessage","migrateTarget","nextSteps","cache","some","push","ownOutputs","outputs","o","length","migration","tree","project","changed","Object","entries","targets"],"mappings":"AAAA;;;CAGC,GACD,SACEA,WAAW,EAKXC,0BAA0B,QACrB,aAAa;AACpB,SAASC,iBAAiB,QAAQ,2BAA2B;AAC7D,SAASC,oBAAoB,QAAQ,2BAA2B;AAEhE;;;;;;;;;;;;;;CAcC,GAED,uEAAuE,GACvE,MAAMC,oBAAoB;AAE1B,iFAAiF,GACjF,MAAMC,2BAA2B;AAEjC;;;;CAIC,GACD,MAAMC,2BAA2B;AAEjC,yEAAyE,GACzE,MAAMC,qBAAqB;AAE3B;;;;;CAKC,GACD,MAAMC,iBAAiB,CAACC,SACtBH,yBAAyBI,IAAI,CAACD,WAAWF,mBAAmBG,IAAI,CAACD;AAEnE,iEAAiE,GACjE,MAAME,iBAAiB,CAACC;IACtB,MAAM,EAAEC,OAAO,EAAEC,QAAQ,EAAE,GAAIF,OAAOG,OAAO,IAAI,CAAC;IAIlD,OAAO;WACD,OAAOF,YAAY,WAAW;YAACA;SAAQ,GAAG,EAAE;WAC5CG,MAAMC,OAAO,CAACH,YAAYA,WAAW,EAAE;KAC5C,CAACI,MAAM,CAAC,CAACC,IAAmB,OAAOA,MAAM;AAC5C;AAEA,MAAMC,kBAAkB,CAACC,aAAqBC,aAC5C,GAAGD,YAAY,CAAC,EAAEC,WAAW,mSAAmS,CAAC;AAEnU,MAAMC,oBAAoB,CAACF,aAAqBC,aAC9C,GAAGD,YAAY,CAAC,EAAEC,WAAW,6RAA6R,CAAC;AAE7T,uEAAuE,GACvE,MAAME,gBAAgB,CACpBH,aACAC,YACAV,QACAa;IAEA,4EAA4E;IAC5E,6EAA6E;IAC7E,oDAAoD;IACpD,IAAIb,OAAOc,KAAK,KAAK,MAAM,OAAO;IAElC,IAAI,CAACf,eAAeC,QAAQe,IAAI,CAAC,CAACR,IAAMd,yBAAyBK,IAAI,CAACS,KAAK;QACzEM,UAAUG,IAAI,CAACR,gBAAgBC,aAAaC;QAC5C,OAAO;IACT;IAEA,4EAA4E;IAC5E,8EAA8E;IAC9E,2EAA2E;IAC3E,gFAAgF;IAChF,MAAMO,aAAajB,OAAOkB,OAAO,EAAEZ,OAAO,CAACa,IAAM,CAACvB,eAAeuB,OAAO,EAAE;IAC1E,IAAIF,WAAWG,MAAM,GAAG,GAAG;QACzBP,UAAUG,IAAI,CAACL,kBAAkBF,aAAaC;QAC9C,OAAO;IACT;IAEAV,OAAOc,KAAK,GAAGxB;IAEf,4EAA4E;IAC5E,6DAA6D;IAC7D,MAAM4B,UAAUlB,OAAOkB,OAAO,EAAEZ,OAC9B,CAACa,IAAM,CAACzB,yBAAyBI,IAAI,CAACqB;IAExC,IAAID,WAAWA,QAAQE,MAAM,KAAK,GAAG;QACnC,OAAOpB,OAAOkB,OAAO;IACvB,OAAO,IAAIA,SAAS;QAClBlB,OAAOkB,OAAO,GAAGA;IACnB;IAEA,OAAO;AACT;AAEA,eAAe,eAAeG,UAC5BC,IAAU;IAEV,MAAMT,YAAsB,EAAE;IAE9B,KAAK,MAAM,CAACJ,aAAac,QAAQ,IAAInC,YAAYkC,MAAO;QACtD,IAAIE,UAAU;QAEd,KAAK,MAAM,CAACd,YAAYV,OAAO,IAAIyB,OAAOC,OAAO,CAACH,QAAQI,OAAO,IAAI,CAAC,GAAI;YACxE,0EAA0E;YAC1E,iEAAiE;YACjE,IAAI,CAACnC,kBAAkBM,IAAI,CAACY,aAAa;YAEzCc,UACEZ,cAAcH,aAAaC,YAAYV,QAAQa,cAAcW;QACjE;QAEA,IAAIA,SAAS;YACXnC,2BACEiC,MACAb,aACAc;QAEJ;IACF;IAEA,MAAMhC,qBAAqB+B;IAE3B,OAAO;QAAET;IAAU;AACrB"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "description": "Switch generated OpenAPI codegen targets to the in-process open-api-codegen executor"
3
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { type MigrationReturnObject, type Tree } from '@nx/devkit';
6
+ export default function migration(tree: Tree): Promise<MigrationReturnObject>;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */ import { getProjects, updateProjectConfiguration } from "@nx/devkit";
5
+ import { formatFilesInSubtree } from "../../../utils/format.js";
6
+ import { normalizeTargetKeyOrder } from "../../../utils/nx.js";
7
+ import { OPEN_API_CODEGEN_EXECUTOR } from "../../../utils/open-api-codegen-target.js";
8
+ /**
9
+ * Codegen targets ran `nx g @aws/nx-plugin:open-api#<generator>` through
10
+ * `nx:run-commands`, paying a second full Nx bootstrap — project graph, plugin
11
+ * resolution and schema validation — for every codegen step. They now use the
12
+ * `open-api-codegen` executor, which runs the same generator in the Nx process
13
+ * that is already running.
14
+ *
15
+ * Only the command shape the generators produced is rewritten. A target that has
16
+ * been customised — an extra flag, a chained command, options beyond `commands` —
17
+ * is the user's, so it is left as it is and reported.
18
+ *
19
+ * How to write a migration:
20
+ * - https://nx.dev/docs/kb/migration-generators
21
+ * - What `nextSteps` means: https://nx.dev/docs/reference/devkit/MigrationReturnObject
22
+ */ /** The generators the executor can run, by the collection name they had. */ const GENERATORS = {
23
+ 'open-api#ts-client': 'ts-client',
24
+ 'open-api#ts-hooks': 'ts-hooks',
25
+ 'open-api#ts-metadata': 'ts-metadata',
26
+ 'open-api#json-metadata': 'json-metadata'
27
+ };
28
+ /**
29
+ * Matches a whole generated codegen command, binding the generator name and its
30
+ * two paths. Anchored, so a command carrying anything else does not match and is
31
+ * reported instead of rewritten.
32
+ */ const CODEGEN_COMMAND = /^nx g @aws\/nx-plugin:(?<generator>[\w#-]+) --openApiSpecPath="(?<specPath>[^"]+)" --outputPath="(?<outputPath>[^"]+)" --no-interactive$/;
33
+ /**
34
+ * The command a target runs, when it is a single-command `nx:run-commands` — the
35
+ * shape the generators produced. A target carrying several commands, or options
36
+ * beyond `commands`, has diverged.
37
+ */ const singleCommand = (target)=>{
38
+ if (target.executor !== 'nx:run-commands') return undefined;
39
+ const options = target.options;
40
+ if (!options || Object.keys(options).length !== 1) return undefined;
41
+ const commands = options.commands;
42
+ if (!Array.isArray(commands) || commands.length !== 1) return undefined;
43
+ return typeof commands[0] === 'string' ? commands[0] : undefined;
44
+ };
45
+ /** Parses a generated codegen command, or undefined if it is not one. */ const parseCodegenCommand = (command)=>{
46
+ const groups = CODEGEN_COMMAND.exec(command)?.groups;
47
+ if (!groups) return undefined;
48
+ const generator = GENERATORS[groups.generator];
49
+ if (!generator) return undefined;
50
+ return {
51
+ generator,
52
+ openApiSpecPath: groups.specPath,
53
+ outputPath: groups.outputPath
54
+ };
55
+ };
56
+ /**
57
+ * Whether a target invokes an OpenAPI generator without being the shape the
58
+ * generators produced, so it must be reported rather than rewritten.
59
+ *
60
+ * Continuous targets are excluded. The vended `watch-generate:<api>-client`
61
+ * wraps `nx watch -- nx run <project>:<codegen target>`, which references the
62
+ * codegen target rather than the generator, so it never matches here anyway. But
63
+ * a watch target rewritten to invoke the generator directly would, and telling
64
+ * its owner to switch to the executor would be wrong advice: `nx watch` runs
65
+ * until interrupted, which a single executor invocation cannot express.
66
+ *
67
+ * `continuous` is the property that makes that true, so it is what is checked —
68
+ * rather than the `watch-` name prefix, which is only a convention.
69
+ */ const hasCustomisedCodegenCommand = (target)=>target.executor === 'nx:run-commands' && !target.continuous && JSON.stringify(target.options ?? {}).includes('@aws/nx-plugin:open-api#');
70
+ export default async function migration(tree) {
71
+ const nextSteps = [];
72
+ for (const [projectName, project] of getProjects(tree)){
73
+ let changed = false;
74
+ for (const [targetName, target] of Object.entries(project.targets ?? {})){
75
+ const command = singleCommand(target);
76
+ const options = command ? parseCodegenCommand(command) : undefined;
77
+ if (!options) {
78
+ if (hasCustomisedCodegenCommand(target)) {
79
+ nextSteps.push(`${projectName}:${targetName} has diverged from the generated shape — left untouched. Switch it to the '${OPEN_API_CODEGEN_EXECUTOR}' executor to avoid a nested Nx invocation.`);
80
+ }
81
+ continue;
82
+ }
83
+ project.targets[targetName] = normalizeTargetKeyOrder({
84
+ ...target,
85
+ executor: OPEN_API_CODEGEN_EXECUTOR,
86
+ options
87
+ });
88
+ changed = true;
89
+ }
90
+ if (changed) {
91
+ updateProjectConfiguration(tree, projectName, project);
92
+ }
93
+ }
94
+ await formatFilesInSubtree(tree);
95
+ return {
96
+ nextSteps
97
+ };
98
+ }
99
+
100
+ //# sourceMappingURL=migration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/open-api-codegen-executor/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n getProjects,\n type MigrationReturnObject,\n type TargetConfiguration,\n type Tree,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport type { OpenApiCodegenTarget } from '../../../open-api/codegen/executor-schema';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport { normalizeTargetKeyOrder } from '../../../utils/nx.js';\nimport { OPEN_API_CODEGEN_EXECUTOR } from '../../../utils/open-api-codegen-target.js';\n\n/**\n * Codegen targets ran `nx g @aws/nx-plugin:open-api#<generator>` through\n * `nx:run-commands`, paying a second full Nx bootstrap — project graph, plugin\n * resolution and schema validation — for every codegen step. They now use the\n * `open-api-codegen` executor, which runs the same generator in the Nx process\n * that is already running.\n *\n * Only the command shape the generators produced is rewritten. A target that has\n * been customised — an extra flag, a chained command, options beyond `commands` —\n * is the user's, so it is left as it is and reported.\n *\n * How to write a migration:\n * - https://nx.dev/docs/kb/migration-generators\n * - What `nextSteps` means: https://nx.dev/docs/reference/devkit/MigrationReturnObject\n */\n\n/** The generators the executor can run, by the collection name they had. */\nconst GENERATORS: Record<string, OpenApiCodegenTarget> = {\n 'open-api#ts-client': 'ts-client',\n 'open-api#ts-hooks': 'ts-hooks',\n 'open-api#ts-metadata': 'ts-metadata',\n 'open-api#json-metadata': 'json-metadata',\n};\n\n/**\n * Matches a whole generated codegen command, binding the generator name and its\n * two paths. Anchored, so a command carrying anything else does not match and is\n * reported instead of rewritten.\n */\nconst CODEGEN_COMMAND =\n /^nx g @aws\\/nx-plugin:(?<generator>[\\w#-]+) --openApiSpecPath=\"(?<specPath>[^\"]+)\" --outputPath=\"(?<outputPath>[^\"]+)\" --no-interactive$/;\n\ninterface CodegenOptions {\n readonly generator: OpenApiCodegenTarget;\n readonly openApiSpecPath: string;\n readonly outputPath: string;\n}\n\n/**\n * The command a target runs, when it is a single-command `nx:run-commands` — the\n * shape the generators produced. A target carrying several commands, or options\n * beyond `commands`, has diverged.\n */\nconst singleCommand = (target: TargetConfiguration): string | undefined => {\n if (target.executor !== 'nx:run-commands') return undefined;\n const options = target.options as Record<string, unknown> | undefined;\n if (!options || Object.keys(options).length !== 1) return undefined;\n const commands = options.commands;\n if (!Array.isArray(commands) || commands.length !== 1) return undefined;\n return typeof commands[0] === 'string' ? commands[0] : undefined;\n};\n\n/** Parses a generated codegen command, or undefined if it is not one. */\nconst parseCodegenCommand = (command: string): CodegenOptions | undefined => {\n const groups = CODEGEN_COMMAND.exec(command)?.groups;\n if (!groups) return undefined;\n const generator = GENERATORS[groups.generator];\n if (!generator) return undefined;\n return {\n generator,\n openApiSpecPath: groups.specPath,\n outputPath: groups.outputPath,\n };\n};\n\n/**\n * Whether a target invokes an OpenAPI generator without being the shape the\n * generators produced, so it must be reported rather than rewritten.\n *\n * Continuous targets are excluded. The vended `watch-generate:<api>-client`\n * wraps `nx watch -- nx run <project>:<codegen target>`, which references the\n * codegen target rather than the generator, so it never matches here anyway. But\n * a watch target rewritten to invoke the generator directly would, and telling\n * its owner to switch to the executor would be wrong advice: `nx watch` runs\n * until interrupted, which a single executor invocation cannot express.\n *\n * `continuous` is the property that makes that true, so it is what is checked —\n * rather than the `watch-` name prefix, which is only a convention.\n */\nconst hasCustomisedCodegenCommand = (target: TargetConfiguration): boolean =>\n target.executor === 'nx:run-commands' &&\n !target.continuous &&\n JSON.stringify(target.options ?? {}).includes('@aws/nx-plugin:open-api#');\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const [projectName, project] of getProjects(tree)) {\n let changed = false;\n\n for (const [targetName, target] of Object.entries(project.targets ?? {})) {\n const command = singleCommand(target);\n const options = command ? parseCodegenCommand(command) : undefined;\n\n if (!options) {\n if (hasCustomisedCodegenCommand(target)) {\n nextSteps.push(\n `${projectName}:${targetName} has diverged from the generated shape — left untouched. Switch it to the '${OPEN_API_CODEGEN_EXECUTOR}' executor to avoid a nested Nx invocation.`,\n );\n }\n continue;\n }\n\n project.targets[targetName] = normalizeTargetKeyOrder({\n ...target,\n executor: OPEN_API_CODEGEN_EXECUTOR,\n options,\n });\n changed = true;\n }\n\n if (changed) {\n updateProjectConfiguration(tree, projectName, project);\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["getProjects","updateProjectConfiguration","formatFilesInSubtree","normalizeTargetKeyOrder","OPEN_API_CODEGEN_EXECUTOR","GENERATORS","CODEGEN_COMMAND","singleCommand","target","executor","undefined","options","Object","keys","length","commands","Array","isArray","parseCodegenCommand","command","groups","exec","generator","openApiSpecPath","specPath","outputPath","hasCustomisedCodegenCommand","continuous","JSON","stringify","includes","migration","tree","nextSteps","projectName","project","changed","targetName","entries","targets","push"],"mappings":"AAAA;;;CAGC,GACD,SACEA,WAAW,EAIXC,0BAA0B,QACrB,aAAa;AAEpB,SAASC,oBAAoB,QAAQ,2BAA2B;AAChE,SAASC,uBAAuB,QAAQ,uBAAuB;AAC/D,SAASC,yBAAyB,QAAQ,4CAA4C;AAEtF;;;;;;;;;;;;;;CAcC,GAED,0EAA0E,GAC1E,MAAMC,aAAmD;IACvD,sBAAsB;IACtB,qBAAqB;IACrB,wBAAwB;IACxB,0BAA0B;AAC5B;AAEA;;;;CAIC,GACD,MAAMC,kBACJ;AAQF;;;;CAIC,GACD,MAAMC,gBAAgB,CAACC;IACrB,IAAIA,OAAOC,QAAQ,KAAK,mBAAmB,OAAOC;IAClD,MAAMC,UAAUH,OAAOG,OAAO;IAC9B,IAAI,CAACA,WAAWC,OAAOC,IAAI,CAACF,SAASG,MAAM,KAAK,GAAG,OAAOJ;IAC1D,MAAMK,WAAWJ,QAAQI,QAAQ;IACjC,IAAI,CAACC,MAAMC,OAAO,CAACF,aAAaA,SAASD,MAAM,KAAK,GAAG,OAAOJ;IAC9D,OAAO,OAAOK,QAAQ,CAAC,EAAE,KAAK,WAAWA,QAAQ,CAAC,EAAE,GAAGL;AACzD;AAEA,uEAAuE,GACvE,MAAMQ,sBAAsB,CAACC;IAC3B,MAAMC,SAASd,gBAAgBe,IAAI,CAACF,UAAUC;IAC9C,IAAI,CAACA,QAAQ,OAAOV;IACpB,MAAMY,YAAYjB,UAAU,CAACe,OAAOE,SAAS,CAAC;IAC9C,IAAI,CAACA,WAAW,OAAOZ;IACvB,OAAO;QACLY;QACAC,iBAAiBH,OAAOI,QAAQ;QAChCC,YAAYL,OAAOK,UAAU;IAC/B;AACF;AAEA;;;;;;;;;;;;;CAaC,GACD,MAAMC,8BAA8B,CAAClB,SACnCA,OAAOC,QAAQ,KAAK,qBACpB,CAACD,OAAOmB,UAAU,IAClBC,KAAKC,SAAS,CAACrB,OAAOG,OAAO,IAAI,CAAC,GAAGmB,QAAQ,CAAC;AAEhD,eAAe,eAAeC,UAC5BC,IAAU;IAEV,MAAMC,YAAsB,EAAE;IAE9B,KAAK,MAAM,CAACC,aAAaC,QAAQ,IAAInC,YAAYgC,MAAO;QACtD,IAAII,UAAU;QAEd,KAAK,MAAM,CAACC,YAAY7B,OAAO,IAAII,OAAO0B,OAAO,CAACH,QAAQI,OAAO,IAAI,CAAC,GAAI;YACxE,MAAMpB,UAAUZ,cAAcC;YAC9B,MAAMG,UAAUQ,UAAUD,oBAAoBC,WAAWT;YAEzD,IAAI,CAACC,SAAS;gBACZ,IAAIe,4BAA4BlB,SAAS;oBACvCyB,UAAUO,IAAI,CACZ,GAAGN,YAAY,CAAC,EAAEG,WAAW,2EAA2E,EAAEjC,0BAA0B,2CAA2C,CAAC;gBAEpL;gBACA;YACF;YAEA+B,QAAQI,OAAO,CAACF,WAAW,GAAGlC,wBAAwB;gBACpD,GAAGK,MAAM;gBACTC,UAAUL;gBACVO;YACF;YACAyB,UAAU;QACZ;QAEA,IAAIA,SAAS;YACXnC,2BAA2B+B,MAAME,aAAaC;QAChD;IACF;IAEA,MAAMjC,qBAAqB8B;IAE3B,OAAO;QAAEC;IAAU;AACrB"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "description": "Exclude Python test files from the production named input and scope dependent task outputs to dist so Python test edits do not invalidate build tasks"
3
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { type MigrationReturnObject, type Tree } from '@nx/devkit';
6
+ export default function migration(tree: Tree): Promise<MigrationReturnObject>;
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */ import { getProjects, readNxJson, updateNxJson, updateProjectConfiguration } from "@nx/devkit";
5
+ import { PYTHON_TEST_FILE_EXCLUSIONS } from "../../../py/project/generator.js";
6
+ import { DEPENDENT_TASKS_OUTPUT_FILES_INPUT } from "../../../ts/lib/generator.js";
7
+ import { formatFilesInSubtree } from "../../../utils/format.js";
8
+ /**
9
+ * Editing a Python test file used to invalidate the whole build chain for its
10
+ * project, and cascade into every downstream project. Two independent causes:
11
+ *
12
+ * 1. The `production` named input excluded TypeScript specs but nothing Python,
13
+ * and the Python build targets read `default` (every file in the project), so
14
+ * a test file was a build input by construction.
15
+ * 2. `default` consumed *every* output of a dependency task. The Python `test`
16
+ * target writes JUnit and coverage reports that pytest stamps wall-clock time
17
+ * into, so each test run minted a new hash that propagated downstream.
18
+ *
19
+ * Targets whose output genuinely cannot contain a test file move to
20
+ * `production`; `typecheck` deliberately stays on `default`, because `ty` checks
21
+ * the tests directory and must re-run when a test changes.
22
+ */ /** The Python targets whose declared output cannot contain a test file. */ const PRODUCTION_SCOPED_TARGETS = [
23
+ 'compile',
24
+ 'bundle-x86',
25
+ 'bundle-arm'
26
+ ];
27
+ /** The inputs those targets read once scoped to production sources. */ const PRODUCTION_INPUTS = [
28
+ 'production',
29
+ '^production'
30
+ ];
31
+ /** The inputs those targets previously read. */ const PREVIOUS_INPUTS = [
32
+ 'default',
33
+ '^production'
34
+ ];
35
+ /**
36
+ * Dependent-task-output globs this plugin has vended. Any of them is rewritten
37
+ * to the current one; anything else is the user's own scoping decision.
38
+ */ const PREVIOUSLY_VENDED_GLOBS = [
39
+ '**/*',
40
+ 'dist/**'
41
+ ];
42
+ const isPythonProject = (project)=>Object.values(project.targets ?? {}).some((target)=>String(target?.executor ?? '').startsWith('@nxlv/python'));
43
+ /**
44
+ * An OpenAPI spec generation target as the generators vend it: named `openapi`
45
+ * or `<agent>-openapi`, running a command, and writing its spec under
46
+ * `dist/.../openapi`. The extra checks keep a user-authored target that merely
47
+ * shares the suffix from being rescoped, since these targets are identified by
48
+ * declaring no `inputs` at all.
49
+ */ const isOpenApiTarget = (name, target)=>{
50
+ if (name !== 'openapi' && !name.endsWith('-openapi')) return false;
51
+ if (!target) return true;
52
+ return target.executor === 'nx:run-commands' && (target.outputs ?? []).some((output)=>output.includes('/openapi'));
53
+ };
54
+ const arraysEqual = (a, b)=>a.length === b.length && a.every((entry, index)=>entry === b[index]);
55
+ /**
56
+ * Excludes Python test files from the `production` named input, and narrows the
57
+ * dependent-task-output input so test reports no longer propagate downstream.
58
+ */ const migrateNamedInputs = (tree, nextSteps)=>{
59
+ const nxJson = readNxJson(tree);
60
+ if (!nxJson) return;
61
+ // Seeded as `['default']` when absent, exactly as the generator does. The
62
+ // targets below are rewritten to read `production`, and Nx fails hard on a
63
+ // named input that is not defined, so the two must not diverge.
64
+ const production = nxJson.namedInputs?.production ?? [
65
+ 'default'
66
+ ];
67
+ const nextProduction = [
68
+ ...production,
69
+ ...PYTHON_TEST_FILE_EXCLUSIONS.filter((exclusion)=>!production.includes(exclusion))
70
+ ];
71
+ const defaultInput = nxJson.namedInputs?.default;
72
+ let nextDefault = defaultInput;
73
+ if (defaultInput) {
74
+ const glob = DEPENDENT_TASKS_OUTPUT_FILES_INPUT.dependentTasksOutputFiles;
75
+ // Only an entry this plugin vended is rewritten; a different glob is the
76
+ // user's own scoping decision.
77
+ nextDefault = defaultInput.map((input)=>typeof input === 'object' && input !== null && 'dependentTasksOutputFiles' in input && PREVIOUSLY_VENDED_GLOBS.includes(input.dependentTasksOutputFiles) ? {
78
+ ...input,
79
+ dependentTasksOutputFiles: glob
80
+ } : input);
81
+ const hadVendedEntry = nextDefault.some((input)=>typeof input === 'object' && input !== null && 'dependentTasksOutputFiles' in input && input.dependentTasksOutputFiles === glob);
82
+ const hasCustomEntry = defaultInput.some((input)=>typeof input === 'object' && input !== null && 'dependentTasksOutputFiles' in input && !PREVIOUSLY_VENDED_GLOBS.includes(input.dependentTasksOutputFiles) && input.dependentTasksOutputFiles !== glob);
83
+ if (!hadVendedEntry && hasCustomEntry) {
84
+ nextSteps.push(`nx.json's 'default' named input scopes 'dependentTasksOutputFiles' to a custom glob — left untouched. Narrow it to '${glob}' so timestamped test reports do not invalidate downstream tasks.`);
85
+ }
86
+ }
87
+ updateNxJson(tree, {
88
+ ...nxJson,
89
+ namedInputs: {
90
+ ...nxJson.namedInputs,
91
+ production: nextProduction,
92
+ ...nextDefault ? {
93
+ default: nextDefault
94
+ } : {}
95
+ }
96
+ });
97
+ };
98
+ /**
99
+ * Scopes a Python project's build-chain targets to production sources. Only
100
+ * targets still carrying the inputs the generator vended are rewritten, so a
101
+ * project whose inputs the user has tuned is reported rather than clobbered.
102
+ */ const migrateProjectTargets = (tree, projectName, project, nextSteps)=>{
103
+ let changed = false;
104
+ for (const [targetName, target] of Object.entries(project.targets ?? {})){
105
+ if (!PRODUCTION_SCOPED_TARGETS.includes(targetName) && !isOpenApiTarget(targetName, target)) {
106
+ continue;
107
+ }
108
+ const inputs = target.inputs;
109
+ // Already migrated, so a re-run is a no-op.
110
+ if (inputs && arraysEqual(inputs, PRODUCTION_INPUTS)) {
111
+ continue;
112
+ }
113
+ // The OpenAPI targets declared no inputs at all, so they fell back to
114
+ // `default`. Everything else carried the vended pair.
115
+ const isVendedShape = inputs ? arraysEqual(inputs, PREVIOUS_INPUTS) : isOpenApiTarget(targetName, target);
116
+ if (!isVendedShape) {
117
+ nextSteps.push(`${projectName}:${targetName}: 'inputs' has diverged from the generated shape — left untouched. Scope it to ['production', '^production'] so Python test edits do not invalidate it.`);
118
+ continue;
119
+ }
120
+ target.inputs = [
121
+ ...PRODUCTION_INPUTS
122
+ ];
123
+ changed = true;
124
+ }
125
+ if (changed) {
126
+ updateProjectConfiguration(tree, projectName, project);
127
+ }
128
+ };
129
+ export default async function migration(tree) {
130
+ const nextSteps = [];
131
+ migrateNamedInputs(tree, nextSteps);
132
+ for (const [projectName, project] of getProjects(tree)){
133
+ if (!isPythonProject(project)) continue;
134
+ migrateProjectTargets(tree, projectName, project, nextSteps);
135
+ }
136
+ await formatFilesInSubtree(tree);
137
+ return {
138
+ nextSteps
139
+ };
140
+ }
141
+
142
+ //# sourceMappingURL=migration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/python-test-input-scoping/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n getProjects,\n type MigrationReturnObject,\n type ProjectConfiguration,\n readNxJson,\n type TargetConfiguration,\n type Tree,\n updateNxJson,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { PYTHON_TEST_FILE_EXCLUSIONS } from '../../../py/project/generator.js';\nimport { DEPENDENT_TASKS_OUTPUT_FILES_INPUT } from '../../../ts/lib/generator.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\n\n/**\n * Editing a Python test file used to invalidate the whole build chain for its\n * project, and cascade into every downstream project. Two independent causes:\n *\n * 1. The `production` named input excluded TypeScript specs but nothing Python,\n * and the Python build targets read `default` (every file in the project), so\n * a test file was a build input by construction.\n * 2. `default` consumed *every* output of a dependency task. The Python `test`\n * target writes JUnit and coverage reports that pytest stamps wall-clock time\n * into, so each test run minted a new hash that propagated downstream.\n *\n * Targets whose output genuinely cannot contain a test file move to\n * `production`; `typecheck` deliberately stays on `default`, because `ty` checks\n * the tests directory and must re-run when a test changes.\n */\n\n/** The Python targets whose declared output cannot contain a test file. */\nconst PRODUCTION_SCOPED_TARGETS = ['compile', 'bundle-x86', 'bundle-arm'];\n\n/** The inputs those targets read once scoped to production sources. */\nconst PRODUCTION_INPUTS = ['production', '^production'];\n\n/** The inputs those targets previously read. */\nconst PREVIOUS_INPUTS = ['default', '^production'];\n\n/**\n * Dependent-task-output globs this plugin has vended. Any of them is rewritten\n * to the current one; anything else is the user's own scoping decision.\n */\nconst PREVIOUSLY_VENDED_GLOBS = ['**/*', 'dist/**'];\n\nconst isPythonProject = (project: ProjectConfiguration): boolean =>\n Object.values(project.targets ?? {}).some((target) =>\n String(target?.executor ?? '').startsWith('@nxlv/python'),\n );\n\n/**\n * An OpenAPI spec generation target as the generators vend it: named `openapi`\n * or `<agent>-openapi`, running a command, and writing its spec under\n * `dist/.../openapi`. The extra checks keep a user-authored target that merely\n * shares the suffix from being rescoped, since these targets are identified by\n * declaring no `inputs` at all.\n */\nconst isOpenApiTarget = (\n name: string,\n target?: TargetConfiguration,\n): boolean => {\n if (name !== 'openapi' && !name.endsWith('-openapi')) return false;\n if (!target) return true;\n return (\n target.executor === 'nx:run-commands' &&\n (target.outputs ?? []).some((output) => output.includes('/openapi'))\n );\n};\n\nconst arraysEqual = (a: readonly unknown[], b: readonly unknown[]): boolean =>\n a.length === b.length && a.every((entry, index) => entry === b[index]);\n\n/**\n * Excludes Python test files from the `production` named input, and narrows the\n * dependent-task-output input so test reports no longer propagate downstream.\n */\nconst migrateNamedInputs = (tree: Tree, nextSteps: string[]): void => {\n const nxJson = readNxJson(tree);\n if (!nxJson) return;\n\n // Seeded as `['default']` when absent, exactly as the generator does. The\n // targets below are rewritten to read `production`, and Nx fails hard on a\n // named input that is not defined, so the two must not diverge.\n const production = nxJson.namedInputs?.production ?? ['default'];\n const nextProduction = [\n ...production,\n ...PYTHON_TEST_FILE_EXCLUSIONS.filter(\n (exclusion) => !production.includes(exclusion),\n ),\n ];\n\n const defaultInput = nxJson.namedInputs?.default;\n let nextDefault = defaultInput;\n if (defaultInput) {\n const glob = DEPENDENT_TASKS_OUTPUT_FILES_INPUT.dependentTasksOutputFiles;\n // Only an entry this plugin vended is rewritten; a different glob is the\n // user's own scoping decision.\n nextDefault = defaultInput.map((input) =>\n typeof input === 'object' &&\n input !== null &&\n 'dependentTasksOutputFiles' in input &&\n PREVIOUSLY_VENDED_GLOBS.includes(\n input.dependentTasksOutputFiles as string,\n )\n ? { ...input, dependentTasksOutputFiles: glob }\n : input,\n );\n const hadVendedEntry = nextDefault.some(\n (input) =>\n typeof input === 'object' &&\n input !== null &&\n 'dependentTasksOutputFiles' in input &&\n input.dependentTasksOutputFiles === glob,\n );\n const hasCustomEntry = defaultInput.some(\n (input) =>\n typeof input === 'object' &&\n input !== null &&\n 'dependentTasksOutputFiles' in input &&\n !PREVIOUSLY_VENDED_GLOBS.includes(\n input.dependentTasksOutputFiles as string,\n ) &&\n input.dependentTasksOutputFiles !== glob,\n );\n if (!hadVendedEntry && hasCustomEntry) {\n nextSteps.push(\n `nx.json's 'default' named input scopes 'dependentTasksOutputFiles' to a custom glob — left untouched. Narrow it to '${glob}' so timestamped test reports do not invalidate downstream tasks.`,\n );\n }\n }\n\n updateNxJson(tree, {\n ...nxJson,\n namedInputs: {\n ...nxJson.namedInputs,\n production: nextProduction,\n ...(nextDefault ? { default: nextDefault } : {}),\n },\n });\n};\n\n/**\n * Scopes a Python project's build-chain targets to production sources. Only\n * targets still carrying the inputs the generator vended are rewritten, so a\n * project whose inputs the user has tuned is reported rather than clobbered.\n */\nconst migrateProjectTargets = (\n tree: Tree,\n projectName: string,\n project: ProjectConfiguration,\n nextSteps: string[],\n): void => {\n let changed = false;\n\n for (const [targetName, target] of Object.entries(project.targets ?? {})) {\n if (\n !PRODUCTION_SCOPED_TARGETS.includes(targetName) &&\n !isOpenApiTarget(targetName, target)\n ) {\n continue;\n }\n\n const inputs = target.inputs;\n\n // Already migrated, so a re-run is a no-op.\n if (inputs && arraysEqual(inputs as unknown[], PRODUCTION_INPUTS)) {\n continue;\n }\n\n // The OpenAPI targets declared no inputs at all, so they fell back to\n // `default`. Everything else carried the vended pair.\n const isVendedShape = inputs\n ? arraysEqual(inputs as unknown[], PREVIOUS_INPUTS)\n : isOpenApiTarget(targetName, target);\n\n if (!isVendedShape) {\n nextSteps.push(\n `${projectName}:${targetName}: 'inputs' has diverged from the generated shape — left untouched. Scope it to ['production', '^production'] so Python test edits do not invalidate it.`,\n );\n continue;\n }\n\n target.inputs = [...PRODUCTION_INPUTS];\n changed = true;\n }\n\n if (changed) {\n updateProjectConfiguration(tree, projectName, project);\n }\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n migrateNamedInputs(tree, nextSteps);\n\n for (const [projectName, project] of getProjects(tree)) {\n if (!isPythonProject(project)) continue;\n migrateProjectTargets(tree, projectName, project, nextSteps);\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["getProjects","readNxJson","updateNxJson","updateProjectConfiguration","PYTHON_TEST_FILE_EXCLUSIONS","DEPENDENT_TASKS_OUTPUT_FILES_INPUT","formatFilesInSubtree","PRODUCTION_SCOPED_TARGETS","PRODUCTION_INPUTS","PREVIOUS_INPUTS","PREVIOUSLY_VENDED_GLOBS","isPythonProject","project","Object","values","targets","some","target","String","executor","startsWith","isOpenApiTarget","name","endsWith","outputs","output","includes","arraysEqual","a","b","length","every","entry","index","migrateNamedInputs","tree","nextSteps","nxJson","production","namedInputs","nextProduction","filter","exclusion","defaultInput","default","nextDefault","glob","dependentTasksOutputFiles","map","input","hadVendedEntry","hasCustomEntry","push","migrateProjectTargets","projectName","changed","targetName","entries","inputs","isVendedShape","migration"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "description": "Make the terraform fmt target check formatting rather than rewrite its own inputs, moving the write to a fix configuration, and add a lint target which orchestrates it"
3
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { type MigrationReturnObject, type Tree } from '@nx/devkit';
6
+ export default function migration(tree: Tree): Promise<MigrationReturnObject>;