@empire-builder-kit/nx 1.0.0-rc.30 → 1.0.0-rc.32

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 (310) hide show
  1. package/executors.json +8 -3
  2. package/package.json +14 -7
  3. package/src/aws/child-environment.d.ts +10 -0
  4. package/src/aws/child-environment.js +77 -0
  5. package/src/aws/child-environment.js.map +1 -0
  6. package/src/aws/identity.d.ts +13 -0
  7. package/src/aws/identity.js +41 -0
  8. package/src/aws/identity.js.map +1 -0
  9. package/src/aws-authority/index.d.ts +1 -0
  10. package/src/aws-authority/index.js +5 -0
  11. package/src/aws-authority/index.js.map +1 -0
  12. package/src/aws-authority/stage-authority.d.ts +240 -0
  13. package/src/aws-authority/stage-authority.js +2968 -0
  14. package/src/aws-authority/stage-authority.js.map +1 -0
  15. package/src/cli/main.d.ts +2 -0
  16. package/src/cli/main.js +11 -0
  17. package/src/cli/main.js.map +1 -1
  18. package/src/developer-agent/generator-planning.js +4 -4
  19. package/src/developer-agent/generator-planning.js.map +1 -1
  20. package/src/developer-agent/operation-registry.js +14 -6
  21. package/src/developer-agent/operation-registry.js.map +1 -1
  22. package/src/developer-agent/workspace-inspection.js +11 -2
  23. package/src/developer-agent/workspace-inspection.js.map +1 -1
  24. package/src/executors/deployment-plan/deployment-plan.js +1 -1
  25. package/src/executors/deployment-plan/deployment-plan.js.map +1 -1
  26. package/src/executors/foundation-preflight/foundation-preflight.d.ts +0 -1
  27. package/src/executors/foundation-preflight/foundation-preflight.js.map +1 -1
  28. package/src/executors/foundation-preflight/schema.json +0 -5
  29. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.d.ts +12 -3
  30. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js +95 -32
  31. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js.map +1 -1
  32. package/src/executors/foundation-profile-preflight/production-database-safeguard.d.ts +64 -0
  33. package/src/executors/foundation-profile-preflight/production-database-safeguard.js +221 -0
  34. package/src/executors/foundation-profile-preflight/production-database-safeguard.js.map +1 -0
  35. package/src/executors/foundation-profile-preflight/schema.json +1 -6
  36. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.d.ts +11 -1
  37. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js +44 -21
  38. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js.map +1 -1
  39. package/src/executors/foundation-remove-preflight/schema.json +6 -1
  40. package/src/executors/github-bootstrap/github-bootstrap.d.ts +1 -0
  41. package/src/executors/github-bootstrap/github-bootstrap.js +1 -0
  42. package/src/executors/github-bootstrap/github-bootstrap.js.map +1 -1
  43. package/src/executors/github-bootstrap/schema.json +7 -1
  44. package/src/executors/infrastructure-plan/infrastructure-plan.d.ts +27 -369
  45. package/src/executors/infrastructure-plan/infrastructure-plan.js +237 -5470
  46. package/src/executors/infrastructure-plan/infrastructure-plan.js.map +1 -1
  47. package/src/executors/infrastructure-plan/schema.json +8 -64
  48. package/src/executors/local-deploy/federated-credential-transport.d.ts +48 -0
  49. package/src/executors/local-deploy/federated-credential-transport.js +252 -0
  50. package/src/executors/local-deploy/federated-credential-transport.js.map +1 -0
  51. package/src/executors/local-deploy/local-approval.d.ts +4 -9
  52. package/src/executors/local-deploy/local-approval.js +3 -7
  53. package/src/executors/local-deploy/local-approval.js.map +1 -1
  54. package/src/executors/local-deploy/local-deploy.d.ts +60 -43
  55. package/src/executors/local-deploy/local-deploy.js +955 -804
  56. package/src/executors/local-deploy/local-deploy.js.map +1 -1
  57. package/src/executors/local-deploy/schema.json +3 -20
  58. package/src/executors/refresh/refresh.js +2 -2
  59. package/src/executors/refresh/refresh.js.map +1 -1
  60. package/src/executors/remove/remove.js +2 -2
  61. package/src/executors/remove/remove.js.map +1 -1
  62. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.d.ts +27 -1
  63. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js +907 -22
  64. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js.map +1 -1
  65. package/src/executors/router-waf-rehearsal/schema.json +17 -2
  66. package/src/executors/secret-initialize/schema.json +42 -0
  67. package/src/executors/secret-initialize/secret-initialize.d.ts +32 -0
  68. package/src/executors/secret-initialize/secret-initialize.js +243 -0
  69. package/src/executors/secret-initialize/secret-initialize.js.map +1 -0
  70. package/src/executors/secret-metadata/secret-metadata.d.ts +4 -1
  71. package/src/executors/secret-metadata/secret-metadata.js +76 -29
  72. package/src/executors/secret-metadata/secret-metadata.js.map +1 -1
  73. package/src/executors/sst-lifecycle/generated-secret-retirement.d.ts +53 -0
  74. package/src/executors/sst-lifecycle/generated-secret-retirement.js +192 -0
  75. package/src/executors/sst-lifecycle/generated-secret-retirement.js.map +1 -0
  76. package/src/executors/sst-lifecycle/governed-native-operation.d.ts +46 -0
  77. package/src/executors/sst-lifecycle/governed-native-operation.js +153 -0
  78. package/src/executors/sst-lifecycle/governed-native-operation.js.map +1 -0
  79. package/src/executors/sst-lifecycle/run-sst-command.d.ts +27 -7
  80. package/src/executors/sst-lifecycle/run-sst-command.js +474 -89
  81. package/src/executors/sst-lifecycle/run-sst-command.js.map +1 -1
  82. package/src/executors/sst-lifecycle/schema.json +0 -5
  83. package/src/executors/sst-lifecycle/secret-preflight.d.ts +2 -0
  84. package/src/executors/sst-lifecycle/secret-preflight.js +13 -7
  85. package/src/executors/sst-lifecycle/secret-preflight.js.map +1 -1
  86. package/src/executors/unlock/unlock.js +2 -2
  87. package/src/executors/unlock/unlock.js.map +1 -1
  88. package/src/foundation/provision-logical-database.js +2 -0
  89. package/src/foundation/provision-logical-database.js.map +1 -1
  90. package/src/foundation/router-waf-rate-rehearsal.d.ts +97 -0
  91. package/src/foundation/router-waf-rate-rehearsal.js +210 -0
  92. package/src/foundation/router-waf-rate-rehearsal.js.map +1 -0
  93. package/src/foundation/router-waf.d.ts +59 -9
  94. package/src/foundation/router-waf.js +427 -32
  95. package/src/foundation/router-waf.js.map +1 -1
  96. package/src/foundation/stages.d.ts +1 -0
  97. package/src/foundation/stages.js +11 -0
  98. package/src/foundation/stages.js.map +1 -1
  99. package/src/generators/formatting.d.ts +8 -0
  100. package/src/generators/formatting.js +118 -0
  101. package/src/generators/formatting.js.map +1 -0
  102. package/src/generators/function/function.js +37 -17
  103. package/src/generators/function/function.js.map +1 -1
  104. package/src/generators/post-generation-install.js +2 -2
  105. package/src/generators/post-generation-install.js.map +1 -1
  106. package/src/generators/preset/generator.js +28 -8
  107. package/src/generators/preset/generator.js.map +1 -1
  108. package/src/generators/preset/schema.d.ts +3 -0
  109. package/src/generators/preset/schema.json +5 -5
  110. package/src/generators/preset/workspace-files-developer-agent.js +15 -7
  111. package/src/generators/preset/workspace-files-developer-agent.js.map +1 -1
  112. package/src/generators/preset/workspace-files-ecr-inspector.d.ts +18 -0
  113. package/src/generators/preset/workspace-files-ecr-inspector.js +298 -0
  114. package/src/generators/preset/workspace-files-ecr-inspector.js.map +1 -0
  115. package/src/generators/preset/workspace-files-performance-cost.d.ts +2 -2
  116. package/src/generators/preset/workspace-files-performance-cost.js +8 -8
  117. package/src/generators/preset/workspace-files-performance-cost.js.map +1 -1
  118. package/src/generators/preset/workspace-files-performance-load.d.ts +1 -1
  119. package/src/generators/preset/workspace-files-performance-load.js +29 -24
  120. package/src/generators/preset/workspace-files-performance-load.js.map +1 -1
  121. package/src/generators/preset/workspace-files-stage-authority.d.ts +6 -0
  122. package/src/generators/preset/workspace-files-stage-authority.js +479 -0
  123. package/src/generators/preset/workspace-files-stage-authority.js.map +1 -0
  124. package/src/generators/preset/workspace-files-thin-deploy.d.ts +40 -0
  125. package/src/generators/preset/workspace-files-thin-deploy.js +1500 -0
  126. package/src/generators/preset/workspace-files-thin-deploy.js.map +1 -0
  127. package/src/generators/preset/workspace-files-thin-docs.d.ts +5 -0
  128. package/src/generators/preset/workspace-files-thin-docs.js +240 -0
  129. package/src/generators/preset/workspace-files-thin-docs.js.map +1 -0
  130. package/src/generators/preset/workspace-files-thin-release.d.ts +14 -0
  131. package/src/generators/preset/workspace-files-thin-release.js +26 -0
  132. package/src/generators/preset/workspace-files-thin-release.js.map +1 -0
  133. package/src/generators/preset/workspace-files.d.ts +2 -1
  134. package/src/generators/preset/workspace-files.js +448 -680
  135. package/src/generators/preset/workspace-files.js.map +1 -1
  136. package/src/generators/repository-policy/repository-policy.js +149 -19
  137. package/src/generators/repository-policy/repository-policy.js.map +1 -1
  138. package/src/generators/repository-policy/schema.d.ts +2 -0
  139. package/src/generators/repository-policy/schema.json +11 -1
  140. package/src/generators/repository-policy/tree-policy.d.ts +3 -0
  141. package/src/generators/repository-policy/tree-policy.js +25 -0
  142. package/src/generators/repository-policy/tree-policy.js.map +1 -0
  143. package/src/generators/slice/built-in-plan-api-files.js +1 -0
  144. package/src/generators/slice/built-in-plan-api-files.js.map +1 -1
  145. package/src/generators/slice/built-in-plan-async-work-files.js +41 -0
  146. package/src/generators/slice/built-in-plan-async-work-files.js.map +1 -1
  147. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +6 -0
  148. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +1 -1
  149. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js +5 -31
  150. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js.map +1 -1
  151. package/src/generators/slice/built-in-plan-data-files.d.ts +1 -1
  152. package/src/generators/slice/built-in-plan-data-files.js +89 -30
  153. package/src/generators/slice/built-in-plan-data-files.js.map +1 -1
  154. package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js +19 -0
  155. package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js.map +1 -1
  156. package/src/generators/slice/built-in-plan-data-retention-launcher-files.js +21 -3
  157. package/src/generators/slice/built-in-plan-data-retention-launcher-files.js.map +1 -1
  158. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js +20 -1
  159. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js.map +1 -1
  160. package/src/generators/slice/built-in-plan-foundation-files.js +3 -5
  161. package/src/generators/slice/built-in-plan-foundation-files.js.map +1 -1
  162. package/src/generators/slice/built-in-plan-frontend-files.js +5 -1
  163. package/src/generators/slice/built-in-plan-frontend-files.js.map +1 -1
  164. package/src/generators/slice/built-in-plan-infrastructure-files.d.ts +1 -1
  165. package/src/generators/slice/built-in-plan-infrastructure-files.js +199 -188
  166. package/src/generators/slice/built-in-plan-infrastructure-files.js.map +1 -1
  167. package/src/generators/slice/built-in-plan-metadata-files.js +13 -0
  168. package/src/generators/slice/built-in-plan-metadata-files.js.map +1 -1
  169. package/src/generators/slice/built-in-plan-native-runtime-authority-files.d.ts +2 -0
  170. package/src/generators/slice/built-in-plan-native-runtime-authority-files.js +307 -0
  171. package/src/generators/slice/built-in-plan-native-runtime-authority-files.js.map +1 -0
  172. package/src/generators/slice/built-in-plan-operations-files.js +65 -51
  173. package/src/generators/slice/built-in-plan-operations-files.js.map +1 -1
  174. package/src/generators/slice/built-in-plan-performance-files.js +97 -19
  175. package/src/generators/slice/built-in-plan-performance-files.js.map +1 -1
  176. package/src/generators/slice/built-in-plan-project-files.js +9 -68
  177. package/src/generators/slice/built-in-plan-project-files.js.map +1 -1
  178. package/src/generators/slice/built-in-plan-secret-runtime-files.d.ts +1 -1
  179. package/src/generators/slice/built-in-plan-secret-runtime-files.js +87 -76
  180. package/src/generators/slice/built-in-plan-secret-runtime-files.js.map +1 -1
  181. package/src/generators/slice/built-in-plan-testing-files.js +94 -47
  182. package/src/generators/slice/built-in-plan-testing-files.js.map +1 -1
  183. package/src/generators/slice/built-in-plan-thin-release-files.d.ts +11 -0
  184. package/src/generators/slice/built-in-plan-thin-release-files.js +129 -0
  185. package/src/generators/slice/built-in-plan-thin-release-files.js.map +1 -0
  186. package/src/generators/slice/built-in-plan-verification-files.js +6 -2
  187. package/src/generators/slice/built-in-plan-verification-files.js.map +1 -1
  188. package/src/generators/slice/built-in-plan-web-files.js +4 -2
  189. package/src/generators/slice/built-in-plan-web-files.js.map +1 -1
  190. package/src/generators/slice/built-in-plan-workload-role-files.js +26 -4
  191. package/src/generators/slice/built-in-plan-workload-role-files.js.map +1 -1
  192. package/src/generators/slice/built-in-plan.d.ts +1 -0
  193. package/src/generators/slice/built-in-plan.js +28 -11
  194. package/src/generators/slice/built-in-plan.js.map +1 -1
  195. package/src/generators/slice/slice.js +30 -32
  196. package/src/generators/slice/slice.js.map +1 -1
  197. package/src/generators/upgrade/upgrade.d.ts +1 -1
  198. package/src/generators/upgrade/upgrade.js +200 -48
  199. package/src/generators/upgrade/upgrade.js.map +1 -1
  200. package/src/generators/workload/performance-registration.d.ts +1 -0
  201. package/src/generators/workload/performance-registration.js +9 -11
  202. package/src/generators/workload/performance-registration.js.map +1 -1
  203. package/src/generators/workload/service-performance-files.d.ts +1 -0
  204. package/src/generators/workload/service-performance-files.js +27 -7
  205. package/src/generators/workload/service-performance-files.js.map +1 -1
  206. package/src/generators/workload/workload.d.ts +1 -1
  207. package/src/generators/workload/workload.js +55 -28
  208. package/src/generators/workload/workload.js.map +1 -1
  209. package/src/git-policy/github-bootstrap.d.ts +28 -7
  210. package/src/git-policy/github-bootstrap.js +205 -163
  211. package/src/git-policy/github-bootstrap.js.map +1 -1
  212. package/src/index.d.ts +1 -0
  213. package/src/index.js +1 -0
  214. package/src/index.js.map +1 -1
  215. package/src/local-dev/private-state.d.ts +3 -0
  216. package/src/local-dev/private-state.js +17 -5
  217. package/src/local-dev/private-state.js.map +1 -1
  218. package/src/local-dev/tls.js +5 -1
  219. package/src/local-dev/tls.js.map +1 -1
  220. package/src/performance/cost-collection.d.ts +1 -1
  221. package/src/performance/cost-collection.js +4 -2
  222. package/src/performance/cost-collection.js.map +1 -1
  223. package/src/performance/cost-controls.d.ts +3 -1
  224. package/src/performance/cost-controls.js +9 -4
  225. package/src/performance/cost-controls.js.map +1 -1
  226. package/src/performance/evidence.d.ts +1 -1
  227. package/src/performance/evidence.js +5 -3
  228. package/src/performance/evidence.js.map +1 -1
  229. package/src/performance/load-safety.js +5 -3
  230. package/src/performance/load-safety.js.map +1 -1
  231. package/src/performance/model.d.ts +2 -2
  232. package/src/performance/model.js +4 -3
  233. package/src/performance/model.js.map +1 -1
  234. package/src/release/index.d.ts +0 -2
  235. package/src/release/index.js +0 -2
  236. package/src/release/index.js.map +1 -1
  237. package/src/release/source-authority.d.ts +4 -5
  238. package/src/release/source-authority.js +13 -123
  239. package/src/release/source-authority.js.map +1 -1
  240. package/src/repository-policy/aurora-preflight.d.ts +1 -1
  241. package/src/repository-policy/aurora-preflight.js +4 -1
  242. package/src/repository-policy/aurora-preflight.js.map +1 -1
  243. package/src/repository-policy/policy.d.ts +21 -2
  244. package/src/repository-policy/policy.js +290 -25
  245. package/src/repository-policy/policy.js.map +1 -1
  246. package/src/schemas/performance-policy-v1.json +3 -1
  247. package/src/schemas/repository-policy-v1.json +46 -2
  248. package/src/secrets/adapters.d.ts +7 -2
  249. package/src/secrets/adapters.js +34 -3
  250. package/src/secrets/adapters.js.map +1 -1
  251. package/src/secrets/aws-control-plane.d.ts +10 -1
  252. package/src/secrets/aws-control-plane.js +67 -0
  253. package/src/secrets/aws-control-plane.js.map +1 -1
  254. package/src/secrets/generated-aws-control-plane.d.ts +23 -0
  255. package/src/secrets/generated-aws-control-plane.js +259 -0
  256. package/src/secrets/generated-aws-control-plane.js.map +1 -0
  257. package/src/secrets/metadata.js +38 -10
  258. package/src/secrets/metadata.js.map +1 -1
  259. package/src/secrets/model.d.ts +8 -1
  260. package/src/secrets/model.js +24 -3
  261. package/src/secrets/model.js.map +1 -1
  262. package/src/secrets/operator.d.ts +89 -3
  263. package/src/secrets/operator.js +422 -9
  264. package/src/secrets/operator.js.map +1 -1
  265. package/src/secrets/registry.d.ts +2 -1
  266. package/src/secrets/registry.js +18 -1
  267. package/src/secrets/registry.js.map +1 -1
  268. package/src/secrets/workload-access.js +10 -2
  269. package/src/secrets/workload-access.js.map +1 -1
  270. package/src/supply-chain/action-pins.js +61 -7
  271. package/src/supply-chain/action-pins.js.map +1 -1
  272. package/src/supply-chain/build-output-bounds.d.ts +2 -0
  273. package/src/supply-chain/build-output-bounds.js +12 -0
  274. package/src/supply-chain/build-output-bounds.js.map +1 -0
  275. package/src/supply-chain/build-output.js +2 -4
  276. package/src/supply-chain/build-output.js.map +1 -1
  277. package/src/supply-chain/ecr-scanning.d.ts +5 -3
  278. package/src/supply-chain/ecr-scanning.js +11 -7
  279. package/src/supply-chain/ecr-scanning.js.map +1 -1
  280. package/src/supply-chain/ecs-deployed-image.d.ts +5 -3
  281. package/src/supply-chain/ecs-deployed-image.js +6 -4
  282. package/src/supply-chain/ecs-deployed-image.js.map +1 -1
  283. package/src/supply-chain/generated-container-release-command.js +849 -44
  284. package/src/supply-chain/generated-container-release-command.js.map +1 -1
  285. package/src/supply-chain/generated-ecr-collector-command.d.ts +0 -1
  286. package/src/supply-chain/generated-ecr-collector-command.js +188 -91
  287. package/src/supply-chain/generated-ecr-collector-command.js.map +1 -1
  288. package/src/supply-chain/generated-files.js +4 -3
  289. package/src/supply-chain/generated-files.js.map +1 -1
  290. package/src/supply-chain/index.d.ts +1 -1
  291. package/src/supply-chain/index.js +2 -1
  292. package/src/supply-chain/index.js.map +1 -1
  293. package/src/workload-roles/index.d.ts +10 -17
  294. package/src/workload-roles/index.js +5 -18
  295. package/src/workload-roles/index.js.map +1 -1
  296. package/src/workspace-deployment/identity.d.ts +3 -0
  297. package/src/workspace-deployment/identity.js +22 -0
  298. package/src/workspace-deployment/identity.js.map +1 -0
  299. package/src/workspace-deployment/index.d.ts +3 -0
  300. package/src/workspace-deployment/index.js +7 -0
  301. package/src/workspace-deployment/index.js.map +1 -0
  302. package/src/workspace-deployment/migration-evidence.d.ts +29 -0
  303. package/src/workspace-deployment/migration-evidence.js +83 -0
  304. package/src/workspace-deployment/migration-evidence.js.map +1 -0
  305. package/src/workspace-deployment/migration.d.ts +27 -0
  306. package/src/workspace-deployment/migration.js +109 -0
  307. package/src/workspace-deployment/migration.js.map +1 -0
  308. package/src/workspace-deployment/ownership.d.ts +81 -0
  309. package/src/workspace-deployment/ownership.js +381 -0
  310. package/src/workspace-deployment/ownership.js.map +1 -0
@@ -1,911 +1,1062 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.prepareLocalSstBackend = prepareLocalSstBackend;
4
- exports.localPlanSummaryDigest = localPlanSummaryDigest;
3
+ exports.localWorkingTreeDigest = localWorkingTreeDigest;
4
+ exports.localDeploymentProjectOrder = localDeploymentProjectOrder;
5
5
  exports.runLocalDeploy = runLocalDeploy;
6
6
  const node_crypto_1 = require("node:crypto");
7
7
  const node_child_process_1 = require("node:child_process");
8
8
  const node_fs_1 = require("node:fs");
9
- const node_os_1 = require("node:os");
10
9
  const node_path_1 = require("node:path");
11
- const client_sts_1 = require("@aws-sdk/client-sts");
12
- const client_ssm_1 = require("@aws-sdk/client-ssm");
13
- const credential_providers_1 = require("@aws-sdk/credential-providers");
14
10
  const devkit_1 = require("@nx/devkit");
15
- const index_js_1 = require("../../infrastructure-change/index.js");
16
- const local_capability_js_1 = require("../../infrastructure-change/local-capability.js");
11
+ const client_sts_1 = require("@aws-sdk/client-sts");
12
+ const index_js_1 = require("../../aws-authority/index.js");
13
+ const child_environment_js_1 = require("../../aws/child-environment.js");
14
+ const identity_js_1 = require("../../aws/identity.js");
17
15
  const index_js_2 = require("../../repository-policy/index.js");
18
- const source_authority_js_1 = require("../../release/source-authority.js");
16
+ const production_database_safeguard_js_1 = require("../foundation-profile-preflight/production-database-safeguard.js");
19
17
  const private_state_js_1 = require("../../local-dev/private-state.js");
20
- const infrastructure_plan_js_1 = require("../infrastructure-plan/infrastructure-plan.js");
21
- const sst_environment_source_js_1 = require("../sst-lifecycle/sst-environment-source.js");
18
+ const federated_credential_transport_js_1 = require("./federated-credential-transport.js");
22
19
  const local_approval_js_1 = require("./local-approval.js");
23
- const SHA = /^[a-f0-9]{40}$/;
24
- const SHA256 = /^[a-f0-9]{64}$/;
25
- const MAX_PLAN_BYTES = 2 * 1024 * 1024;
26
- const ROLE = /^arn:(aws|aws-us-gov|aws-cn):iam::\d{12}:role\/[A-Za-z0-9+=,.@_/-]{1,512}$/;
27
- const AMBIENT_CREDENTIAL_KEYS = [
28
- 'AWS_ACCESS_KEY_ID',
29
- 'AWS_SECRET_ACCESS_KEY',
30
- 'AWS_SESSION_TOKEN',
31
- 'AWS_SECURITY_TOKEN',
32
- 'AWS_ROLE_ARN',
33
- 'AWS_ROLE_SESSION_NAME',
34
- 'AWS_WEB_IDENTITY_TOKEN_FILE',
35
- 'AWS_CONTAINER_AUTHORIZATION_TOKEN',
36
- 'AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE',
37
- 'AWS_CONTAINER_CREDENTIALS_FULL_URI',
38
- 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI',
39
- ];
40
- const PROTECTED_PLAN_KEYS = [
41
- 'EBK_LOCAL_MUTATION_CAPABILITY_NONCE',
42
- 'EBK_LOCAL_MUTATION_CAPABILITY_PATH',
43
- 'EBK_LOCAL_PLAN_APPROVER_ARN',
44
- 'EBK_LOCAL_PLAN_MATERIAL_DIGEST',
45
- 'EBK_LOCAL_PLAN_SUMMARY_DIGEST',
46
- 'AWS_PLAN_GENERATED_OBJECT_ARNS_JSON',
47
- 'AWS_PLAN_GENERATED_PARAMETER_ARNS_JSON',
48
- 'AWS_PLAN_GENERATED_SECRET_ARNS_JSON',
49
- 'AWS_PLAN_PROVIDER_REFRESH_POLICY_JSON',
50
- 'AWS_PLAN_STATE_KMS_KEY_ARN',
51
- 'EBK_RELEASE_SOURCE_COMMIT',
52
- ];
53
- function projectEvidencePath(context, project, name) {
54
- const configured = context.projectsConfigurations?.projects[project]?.root;
55
- if (!configured) {
56
- throw new Error(`Local deployment references unknown project ${project}.`);
57
- }
58
- return (0, node_path_1.join)((0, node_path_1.isAbsolute)(configured) ? configured : (0, node_path_1.join)(context.root, configured), '.ebk', 'evidence', name);
59
- }
60
- function writeLocalMutationCapability(input) {
61
- const nonce = (0, node_crypto_1.randomBytes)(32).toString('hex');
62
- const directory = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'ebk-local-mutation-'));
63
- (0, private_state_js_1.ensurePrivateDirectory)(directory);
64
- const path = (0, node_path_1.join)(directory, 'capability.json');
65
- const planContents = (0, node_fs_1.readFileSync)(projectEvidencePath(input.context, input.project, 'infrastructure-plan.json'), 'utf8');
66
- const authorizationContents = (0, node_fs_1.readFileSync)(projectEvidencePath(input.context, input.project, 'infrastructure-plan-authorization.json'), 'utf8');
67
- const capability = (0, local_capability_js_1.createLocalMutationCapability)({
68
- actorArn: input.actorArn,
69
- authorizationContents,
70
- deploymentProfile: input.deploymentProfile,
71
- deploymentSourceCommit: input.deploymentSourceCommit,
72
- deploymentSourceTree: input.deploymentSourceTree,
73
- expectedRoleArn: input.expectedRoleArn,
74
- issuedAt: input.issuedAt,
75
- nonce,
76
- plan: input.plan,
77
- planContents,
78
- project: input.project,
79
- releaseSourceTree: input.releaseSourceTree,
80
- });
81
- (0, private_state_js_1.atomicWritePrivateFile)(path, `${JSON.stringify(capability, null, 2)}\n`);
82
- return { directory, nonce, path };
83
- }
84
- function digest(value) {
85
- return (0, node_crypto_1.createHash)('sha256').update(value, 'utf8').digest('hex');
86
- }
87
- function ssmParameterAuthority(value, expectedName) {
88
- const parameter = record(value, expectedName);
89
- if (parameter['Name'] !== expectedName ||
90
- parameter['Type'] !== 'SecureString' ||
91
- typeof parameter['Version'] !== 'number' ||
92
- !Number.isSafeInteger(parameter['Version']) ||
93
- parameter['Version'] < 1) {
94
- throw new Error(`Local SST backend parameter ${expectedName} is missing or malformed.`);
95
- }
96
- return {
97
- name: expectedName,
98
- type: parameter['Type'],
99
- version: parameter['Version'],
100
- };
101
- }
102
- /** @internal */
103
- async function prepareLocalSstBackend(input, dependencies = {}) {
104
- if (input.applications.length === 0 ||
105
- new Set(input.applications).size !== input.applications.length ||
106
- input.applications.some((application) => !/^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/.test(application))) {
107
- throw new Error('Local SST backend requires a unique exact application inventory.');
108
- }
109
- const client = new client_ssm_1.SSMClient({
110
- region: input.region,
111
- ...(input.profile
112
- ? { credentials: (0, credential_providers_1.fromIni)({ profile: input.profile }) }
113
- : {}),
114
- });
115
- const send = dependencies.send ??
116
- (async (command) => client.send(command));
117
- const bootstrapResponse = await send(new client_ssm_1.GetParameterCommand({ Name: '/sst/bootstrap' }));
118
- const bootstrapParameter = bootstrapResponse.Parameter;
119
- let bootstrap;
120
- try {
121
- bootstrap = record(JSON.parse(bootstrapParameter?.Value ?? ''), 'Local SST bootstrap value');
122
- }
123
- catch {
124
- throw new Error('Local deployment requires an existing exact SST v5 account bootstrap.');
125
- }
126
- const exactBootstrapKeys = [
127
- 'appsyncHttp',
128
- 'appsyncRealtime',
129
- 'asset',
130
- 'assetEcrRegistryId',
131
- 'assetEcrUrl',
132
- 'state',
133
- 'version',
134
- ];
135
- const state = bootstrap['state'];
136
- if (bootstrapParameter?.Name !== '/sst/bootstrap' ||
137
- bootstrapParameter.Type !== 'String' ||
138
- typeof bootstrapParameter.Version !== 'number' ||
139
- !Number.isSafeInteger(bootstrapParameter.Version) ||
140
- bootstrapParameter.Version < 1 ||
141
- Object.keys(bootstrap).sort().join(',') !== exactBootstrapKeys.join(',') ||
142
- bootstrap['version'] !== 5 ||
143
- typeof state !== 'string' ||
144
- !/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(state) ||
145
- typeof bootstrap['asset'] !== 'string' ||
146
- !/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(bootstrap['asset']) ||
147
- bootstrap['assetEcrRegistryId'] !== input.account ||
148
- bootstrap['assetEcrUrl'] !==
149
- `${input.account}.dkr.ecr.${input.region}.amazonaws.com/sst-asset` ||
150
- typeof bootstrap['appsyncHttp'] !== 'string' ||
151
- typeof bootstrap['appsyncRealtime'] !== 'string') {
152
- throw new Error('Local deployment requires an existing exact SST v5 account bootstrap.');
153
- }
154
- const parameterAuthority = new Map();
155
- const missingApplications = [];
156
- const createdApplications = [];
157
- for (const application of input.applications) {
158
- const name = `/sst/passphrase/${application}/${input.stage}`;
159
- try {
160
- const observed = await send(new client_ssm_1.GetParameterCommand({ Name: name }));
161
- parameterAuthority.set(application, ssmParameterAuthority(observed.Parameter, name));
162
- }
163
- catch (error) {
164
- if (!(error instanceof Error) || error.name !== 'ParameterNotFound') {
165
- throw error;
166
- }
167
- missingApplications.push(application);
168
- }
169
- }
170
- for (const application of missingApplications) {
171
- const name = `/sst/passphrase/${application}/${input.stage}`;
172
- await send(new client_ssm_1.PutParameterCommand({
173
- Description: `SST state passphrase for ${application}/${input.stage}`,
174
- Name: name,
175
- Overwrite: false,
176
- Type: 'SecureString',
177
- Value: dependencies.passphrase?.() ?? (0, node_crypto_1.randomBytes)(32).toString('base64'),
178
- }));
179
- createdApplications.push(application);
180
- const observed = await send(new client_ssm_1.GetParameterCommand({ Name: name }));
181
- parameterAuthority.set(application, ssmParameterAuthority(observed.Parameter, name));
182
- }
183
- const parameters = input.applications.map((application) => {
184
- const authority = parameterAuthority.get(application);
185
- if (!authority) {
186
- throw new Error(`Local SST backend parameter authority is absent for ${application}.`);
187
- }
188
- return authority;
189
- });
190
- return {
191
- authorityDigest: digest(JSON.stringify({
192
- applications: [...input.applications],
193
- bootstrap: {
194
- name: bootstrapParameter.Name,
195
- type: bootstrapParameter.Type,
196
- version: bootstrapParameter.Version,
197
- },
198
- parameters,
199
- region: input.region,
200
- stage: input.stage,
201
- state,
202
- })),
203
- createdApplications,
204
- stateBucketArn: `arn:aws:s3:::${state}`,
205
- };
20
+ const container_release_js_1 = require("../../supply-chain/container-release.js");
21
+ const run_sst_command_js_1 = require("../sst-lifecycle/run-sst-command.js");
22
+ const index_js_3 = require("../../workspace-deployment/index.js");
23
+ const SHA = /^[a-f0-9]{40,64}$/u;
24
+ const ROLE = /^arn:(aws|aws-us-gov|aws-cn):iam::\d{12}:role\/[A-Za-z0-9+=,.@_/-]{1,512}$/u;
25
+ const MAX_LOCAL_RELEASE_CANDIDATE_FILE_BYTES = 64 * 1024;
26
+ const MAX_LOCAL_RELEASE_ARTIFACTS_BYTES = 64 * 1024 * 1024;
27
+ const MAX_LOCAL_RELEASE_CANDIDATE_FILES = 32;
28
+ const MAX_LOCAL_RELEASE_CANDIDATE_BYTES = MAX_LOCAL_RELEASE_ARTIFACTS_BYTES +
29
+ (MAX_LOCAL_RELEASE_CANDIDATE_FILES - 1) *
30
+ MAX_LOCAL_RELEASE_CANDIDATE_FILE_BYTES;
31
+ function partitionForRegion(region) {
32
+ if (region.startsWith('cn-'))
33
+ return 'aws-cn';
34
+ if (region.startsWith('us-gov-'))
35
+ return 'aws-us-gov';
36
+ return 'aws';
206
37
  }
207
38
  function git(root, args, run) {
208
39
  const result = run('git', args, {
209
40
  cwd: root,
210
41
  encoding: 'utf8',
211
42
  env: process.env,
212
- maxBuffer: 1024 * 1024,
43
+ maxBuffer: 32 * 1024 * 1024,
213
44
  shell: false,
214
45
  });
215
46
  if (result.status !== 0) {
216
47
  throw new Error(`Local deployment Git preflight failed: ${String(result.stderr).trim()}`);
217
48
  }
218
- return String(result.stdout).trim();
49
+ return String(result.stdout);
219
50
  }
220
- function gitRaw(root, args, run) {
221
- const result = run('git', args, {
222
- cwd: root,
223
- encoding: 'utf8',
224
- env: process.env,
225
- maxBuffer: 1024 * 1024,
226
- shell: false,
227
- });
228
- if (result.status !== 0 || String(result.stderr) !== '') {
229
- throw new Error(`Local deployment Git reconciliation failed: ${String(result.stderr).trim()}`);
51
+ function confinedWorkspacePath(root, candidate) {
52
+ if (!candidate || (0, node_path_1.isAbsolute)(candidate) || candidate.includes('\0')) {
53
+ throw new Error('Working-tree inventory contains an invalid path.');
230
54
  }
231
- return String(result.stdout);
55
+ const absolute = (0, node_path_1.join)(root, candidate);
56
+ const confined = (0, node_path_1.relative)(root, absolute);
57
+ if (confined === '..' || confined.startsWith('../') || (0, node_path_1.isAbsolute)(confined)) {
58
+ throw new Error('Working-tree inventory escapes the workspace.');
59
+ }
60
+ return absolute;
232
61
  }
233
- function toolOwnedSourceSnapshots(context, projects) {
234
- const workspaceRoot = (0, node_fs_1.realpathSync)(context.root);
235
- return projects.map((project) => {
236
- const configured = context.projectsConfigurations?.projects[project]?.root;
237
- if (!configured) {
238
- throw new Error(`Local deployment tool-owned source references unknown project ${project}.`);
62
+ /** Bind a development deployment to the exact tracked diff and untracked
63
+ * source bytes without requiring a commit. Ignored caches and build output are
64
+ * intentionally outside this identity. */
65
+ function localWorkingTreeDigest(root, run = node_child_process_1.spawnSync) {
66
+ const commit = git(root, ['rev-parse', 'HEAD'], run).trim();
67
+ if (!SHA.test(commit)) {
68
+ throw new Error('Local deployment could not resolve one exact Git HEAD.');
69
+ }
70
+ const status = git(root, ['status', '--porcelain=v1', '--untracked-files=all'], run);
71
+ const trackedDiff = git(root, ['diff', '--binary', 'HEAD'], run);
72
+ const untracked = git(root, ['ls-files', '--others', '--exclude-standard', '-z'], run)
73
+ .split('\0')
74
+ .filter(Boolean)
75
+ .sort();
76
+ const hash = (0, node_crypto_1.createHash)('sha256');
77
+ hash.update('ebk-working-tree-v1\0');
78
+ hash.update(commit);
79
+ hash.update('\0');
80
+ hash.update(status);
81
+ hash.update('\0');
82
+ hash.update(trackedDiff);
83
+ hash.update('\0');
84
+ for (const path of untracked) {
85
+ const absolute = confinedWorkspacePath(root, path);
86
+ const stat = (0, node_fs_1.lstatSync)(absolute);
87
+ hash.update(path);
88
+ hash.update('\0');
89
+ if (stat.isSymbolicLink()) {
90
+ hash.update('symlink\0');
91
+ hash.update((0, node_fs_1.readlinkSync)(absolute));
239
92
  }
240
- return (0, sst_environment_source_js_1.captureSstEnvironmentSourceTree)({
241
- projectRoot: (0, node_path_1.isAbsolute)(configured)
242
- ? configured
243
- : (0, node_path_1.join)(workspaceRoot, configured),
244
- workspaceRoot,
245
- });
246
- });
247
- }
248
- function nulSeparatedPaths(value) {
249
- return value.split('\0').filter((entry) => entry !== '');
250
- }
251
- function reconcileToolOwnedSource(root, snapshots, run) {
252
- for (const snapshot of snapshots) {
253
- (0, sst_environment_source_js_1.reconcileSstEnvironmentSourceTree)(snapshot);
93
+ else if (stat.isFile()) {
94
+ hash.update('file\0');
95
+ hash.update((0, node_fs_1.readFileSync)(absolute));
96
+ }
97
+ else {
98
+ throw new Error(`Untracked deployment source ${path} is not a regular file or symbolic link.`);
99
+ }
100
+ hash.update('\0');
254
101
  }
255
- const remaining = [
256
- ...nulSeparatedPaths(gitRaw(root, ['diff', 'HEAD', '--name-only', '--no-renames', '-z'], run)),
257
- ...nulSeparatedPaths(gitRaw(root, ['ls-files', '--others', '--exclude-standard', '-z'], run)),
258
- ];
259
- if (remaining.length > 0) {
260
- throw new Error(`Local deployment child changed source outside exact tool-owned files: ${remaining.sort().join(', ')}.`);
102
+ return { clean: status === '', commit, digest: hash.digest('hex') };
103
+ }
104
+ function assertDeploymentSourceUnchanged(root, expected, run) {
105
+ const current = localWorkingTreeDigest(root, run);
106
+ if (current.clean !== expected.clean ||
107
+ current.commit !== expected.commit ||
108
+ current.digest !== expected.workingTreeDigest) {
109
+ throw new Error('Deployment source changed after lifecycle authority was established; downstream execution is blocked.');
261
110
  }
262
111
  }
263
- function projectInventory(context, stage, phase, action) {
264
- const projects = Object.entries(context.projectsConfigurations?.projects ?? {}).map(([name, configuration]) => ({
112
+ function projectDescriptors(context) {
113
+ return Object.entries(context.projectsConfigurations?.projects ?? {}).map(([name, configuration]) => ({
265
114
  name,
266
- tags: configuration.tags,
267
- targets: configuration.targets,
115
+ root: configuration.root,
116
+ tags: configuration.tags ?? [],
117
+ targets: configuration.targets ?? {},
268
118
  }));
269
- const policy = (0, index_js_2.readRepositoryOperatingPolicy)(context.root);
270
- const foundations = projects
271
- .filter(({ tags }) => tags?.includes('scope:foundation'))
272
- .map(({ name }) => name);
273
- if (foundations.length !== 1) {
274
- throw new Error('Local deployment requires exactly one Foundation project.');
119
+ }
120
+ function supportsTarget(project, target) {
121
+ return Object.prototype.hasOwnProperty.call(project.targets, target);
122
+ }
123
+ const LOCAL_CONTAINER_TARGETS = Object.freeze([
124
+ 'release-candidate',
125
+ 'container-release-amd64',
126
+ 'container-release-arm64',
127
+ 'container-release-assemble',
128
+ ]);
129
+ function requiresLocalContainerPreparation(profile, stage) {
130
+ return ((profile === 'starter' && stage === 'production') ||
131
+ (profile === 'production-isolated' && stage === 'staging'));
132
+ }
133
+ function confinedEvidenceSegments(root, candidate) {
134
+ const confined = (0, node_path_1.relative)(root, candidate);
135
+ if (confined === '' ||
136
+ confined === '..' ||
137
+ confined.startsWith(`..${node_path_1.sep}`) ||
138
+ (0, node_path_1.isAbsolute)(confined)) {
139
+ throw new Error('Local container evidence path escapes the workspace.');
275
140
  }
276
- if (phase === 'foundation-bootstrap') {
277
- return foundations;
141
+ const segments = confined.split(node_path_1.sep);
142
+ if (segments.some((segment) => segment === '' ||
143
+ segment === '.' ||
144
+ segment === '..' ||
145
+ segment.includes('\0'))) {
146
+ throw new Error('Local container evidence path is invalid.');
278
147
  }
279
- if (stage !== 'development') {
280
- return (0, index_js_2.createProtectedDeploymentPlan)(policy, {
281
- action,
282
- projects,
283
- stage,
284
- }).orderedProjects;
148
+ return segments;
149
+ }
150
+ function assertSafeEvidenceTree(path) {
151
+ let entries = 0;
152
+ const visit = (directory) => {
153
+ for (const entry of (0, node_fs_1.readdirSync)(directory, { withFileTypes: true })) {
154
+ entries += 1;
155
+ if (entries > 4096) {
156
+ throw new Error('Local container evidence tree is unexpectedly large.');
157
+ }
158
+ const child = (0, node_path_1.join)(directory, entry.name);
159
+ const identity = (0, node_fs_1.lstatSync)(child);
160
+ if (identity.isSymbolicLink()) {
161
+ throw new Error('Local container evidence cleanup refuses symbolic links.');
162
+ }
163
+ if (identity.isDirectory()) {
164
+ visit(child);
165
+ }
166
+ else if (!identity.isFile()) {
167
+ throw new Error('Local container evidence cleanup accepts only regular files and directories.');
168
+ }
169
+ }
170
+ };
171
+ visit(path);
172
+ }
173
+ function removeExactLocalEvidenceDirectory(root, path) {
174
+ const segments = confinedEvidenceSegments(root, path);
175
+ let current = root;
176
+ const rootIdentity = (0, node_fs_1.lstatSync)(current);
177
+ if (!rootIdentity.isDirectory() || rootIdentity.isSymbolicLink()) {
178
+ throw new Error('Local deployment workspace root is absent or unsafe.');
285
179
  }
286
- const slices = projects
287
- .filter(({ tags }) => tags?.includes('type:slice'))
288
- .map(({ name }) => name)
289
- .sort();
290
- return [foundations[0], ...slices];
180
+ for (const segment of segments) {
181
+ current = (0, node_path_1.join)(current, segment);
182
+ if (!(0, node_fs_1.existsSync)(current))
183
+ return;
184
+ const identity = (0, node_fs_1.lstatSync)(current);
185
+ if (!identity.isDirectory() || identity.isSymbolicLink()) {
186
+ throw new Error('Local container evidence cleanup encountered an unsafe path.');
187
+ }
188
+ }
189
+ assertSafeEvidenceTree(current);
190
+ (0, node_fs_1.rmSync)(current, { recursive: true });
291
191
  }
292
- function runNx(context, project, target, forwarded, environment, run) {
293
- const result = run(process.execPath, [
294
- (0, node_path_1.join)(context.root, 'node_modules', 'nx', 'dist', 'bin', 'nx.js'),
295
- 'run',
296
- `${project}:${target}`,
297
- '--',
298
- ...forwarded,
299
- ], {
300
- cwd: context.root,
301
- encoding: 'utf8',
302
- env: environment,
303
- maxBuffer: 16 * 1024 * 1024,
304
- shell: false,
305
- stdio: 'inherit',
306
- });
307
- if (result.status !== 0) {
308
- throw new Error(`Local ${target} failed for ${project}.`);
192
+ function ensurePrivateEvidenceDirectory(root, path) {
193
+ const segments = confinedEvidenceSegments(root, path);
194
+ let current = root;
195
+ for (const segment of segments) {
196
+ current = (0, node_path_1.join)(current, segment);
197
+ if ((0, node_fs_1.existsSync)(current)) {
198
+ const identity = (0, node_fs_1.lstatSync)(current);
199
+ if (!identity.isDirectory() || identity.isSymbolicLink()) {
200
+ throw new Error('Local container evidence directory encountered an unsafe path.');
201
+ }
202
+ }
203
+ (0, private_state_js_1.ensurePrivateDirectory)(current);
309
204
  }
310
205
  }
311
- function runNxWithToolOwnedSourceReconciliation(context, project, target, forwarded, environment, snapshots, run, acceptExecutionFailure) {
312
- let executionError;
313
- try {
314
- runNx(context, project, target, forwarded, environment, run);
206
+ function resetLocalContainerWorkingEvidence(input) {
207
+ const projectRoot = confinedWorkspacePath(input.root, input.project.root);
208
+ for (const path of [
209
+ (0, node_path_1.join)(projectRoot, '.ebk', 'evidence', 'release-candidate'),
210
+ (0, node_path_1.join)(input.root, '.ebk', 'evidence', 'release-candidate', 'packages', input.project.name),
211
+ (0, node_path_1.join)(input.root, '.ebk', 'evidence', 'container-release', 'partials', input.stage, input.project.name),
212
+ (0, node_path_1.join)(input.root, '.ebk', 'evidence', 'container-release', input.stage, 'packages', input.project.name),
213
+ ]) {
214
+ removeExactLocalEvidenceDirectory(input.root, path);
315
215
  }
316
- catch (error) {
317
- executionError = error;
216
+ }
217
+ /** Materialize the same bounded workspace-level candidate layout restored by
218
+ * protected GitHub deployment. The generated container helper and SST image
219
+ * resolver both consume this exact path. */
220
+ function materializeLocalReleaseCandidate(input) {
221
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(input.project.name)) {
222
+ throw new Error(`Local container release project ${input.project.name} has an unsafe evidence identity.`);
318
223
  }
319
- let reconciliationError;
224
+ const projectRoot = confinedWorkspacePath(input.root, input.project.root);
225
+ const source = (0, node_path_1.join)(projectRoot, '.ebk', 'evidence', 'release-candidate');
226
+ const sourceIdentity = (0, node_fs_1.lstatSync)(source);
227
+ if (!sourceIdentity.isDirectory() || sourceIdentity.isSymbolicLink()) {
228
+ throw new Error(`Local release candidate for ${input.project.name} is absent or unsafe.`);
229
+ }
230
+ const entries = (0, node_fs_1.readdirSync)(source, { withFileTypes: true }).sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
231
+ if (entries.length === 0 ||
232
+ entries.length > MAX_LOCAL_RELEASE_CANDIDATE_FILES ||
233
+ entries.some((entry) => !entry.isFile() ||
234
+ entry.isSymbolicLink() ||
235
+ !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}\.json$/u.test(entry.name))) {
236
+ throw new Error(`Local release candidate for ${input.project.name} has an invalid file inventory.`);
237
+ }
238
+ let totalBytes = 0;
239
+ const files = entries.map(({ name }) => {
240
+ const path = (0, node_path_1.join)(source, name);
241
+ const identity = (0, node_fs_1.lstatSync)(path);
242
+ const maximumBytes = name === 'release-artifacts.json'
243
+ ? MAX_LOCAL_RELEASE_ARTIFACTS_BYTES
244
+ : MAX_LOCAL_RELEASE_CANDIDATE_FILE_BYTES;
245
+ if (!identity.isFile() ||
246
+ identity.isSymbolicLink() ||
247
+ identity.size <= 0 ||
248
+ identity.size > maximumBytes) {
249
+ throw new Error(`Local release candidate file ${input.project.name}/${name} is absent or unsafe.`);
250
+ }
251
+ totalBytes += identity.size;
252
+ return { name, path };
253
+ });
254
+ if (totalBytes > MAX_LOCAL_RELEASE_CANDIDATE_BYTES) {
255
+ throw new Error(`Local release candidate for ${input.project.name} exceeds the bounded inventory size.`);
256
+ }
257
+ const candidateFiles = files.map(({ name, path }) => ({
258
+ contents: (0, node_fs_1.readFileSync)(path),
259
+ name,
260
+ }));
261
+ const candidateFile = candidateFiles.find(({ name }) => name === 'candidate.json');
262
+ let candidate;
320
263
  try {
321
- reconcileToolOwnedSource(context.root, snapshots, run);
264
+ candidate = JSON.parse(candidateFile?.contents.toString('utf8') ?? '');
322
265
  }
323
- catch (error) {
324
- reconciliationError = error;
266
+ catch {
267
+ throw new Error(`Local release candidate for ${input.project.name} is invalid JSON.`);
325
268
  }
326
- if (executionError !== undefined && reconciliationError !== undefined) {
327
- throw new Error(`Local ${target} and tool-owned source reconciliation both failed for ${project}. Execution: ${executionError instanceof Error ? executionError.message : String(executionError)} Reconciliation: ${reconciliationError instanceof Error ? reconciliationError.message : String(reconciliationError)}`);
269
+ if (candidate.kind !== 'ebk-release-candidate' ||
270
+ candidate.schemaVersion !== 1 ||
271
+ candidate.slice !== input.project.name ||
272
+ candidate.sourceCommit !== input.sourceCommit) {
273
+ throw new Error(`Local release candidate for ${input.project.name} differs from the exact deployment source.`);
328
274
  }
329
- if (reconciliationError !== undefined)
330
- throw reconciliationError;
331
- if (executionError !== undefined) {
332
- let accepted = false;
333
- try {
334
- accepted = acceptExecutionFailure?.() === true;
275
+ const destination = (0, node_path_1.join)(input.root, '.ebk', 'evidence', 'release-candidate', 'packages', input.project.name);
276
+ removeExactLocalEvidenceDirectory(input.root, destination);
277
+ ensurePrivateEvidenceDirectory(input.root, destination);
278
+ for (const { contents, name } of candidateFiles) {
279
+ if (name === 'release-artifacts.json') {
280
+ (0, private_state_js_1.atomicWritePrivateFileWithMaximumBytes)((0, node_path_1.join)(destination, name), contents, MAX_LOCAL_RELEASE_ARTIFACTS_BYTES);
335
281
  }
336
- catch {
337
- accepted = false;
282
+ else {
283
+ (0, private_state_js_1.atomicWritePrivateFile)((0, node_path_1.join)(destination, name), contents);
338
284
  }
339
- if (!accepted)
340
- throw executionError;
341
285
  }
342
286
  }
343
- function record(value, label) {
344
- if (value === null || typeof value !== 'object' || Array.isArray(value)) {
345
- throw new Error(`${label} must be a JSON object.`);
287
+ function attestLocalContainerBinding(input) {
288
+ const path = (0, node_path_1.join)(input.root, '.ebk', 'evidence', 'container-release', input.stage, 'packages', input.project.name, 'binding.json');
289
+ const identity = (0, node_fs_1.lstatSync)(path);
290
+ if (!identity.isFile() ||
291
+ identity.isSymbolicLink() ||
292
+ identity.size <= 0 ||
293
+ identity.size > 64 * 1024 * 1024) {
294
+ throw new Error(`Local protected container binding for ${input.project.name} is absent or unsafe.`);
295
+ }
296
+ const binding = (0, container_release_js_1.validateContainerReleaseBinding)(JSON.parse((0, node_fs_1.readFileSync)(path, 'utf8')));
297
+ if (binding.accountId !== input.account ||
298
+ binding.region !== input.region ||
299
+ binding.slice !== input.project.name ||
300
+ binding.sourceCommit !== input.sourceCommit ||
301
+ binding.stage !== input.stage) {
302
+ throw new Error(`Local protected container binding for ${input.project.name} differs from the exact deployment authority.`);
346
303
  }
347
- return value;
348
304
  }
349
- function retainedPlan(context, project, deploymentSourceCommit, account, region, stage, action) {
350
- const configured = context.projectsConfigurations?.projects[project]?.root;
351
- if (!configured) {
352
- throw new Error(`Local deployment references unknown project ${project}.`);
353
- }
354
- const workspaceRoot = (0, node_fs_1.realpathSync)(context.root);
355
- const projectRoot = (0, node_fs_1.realpathSync)((0, node_path_1.isAbsolute)(configured) ? configured : (0, node_path_1.join)(workspaceRoot, configured));
356
- const projectRelative = (0, node_path_1.relative)(workspaceRoot, projectRoot);
357
- if (projectRelative === '..' ||
358
- projectRelative.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`) ||
359
- (0, node_path_1.isAbsolute)(projectRelative)) {
360
- throw new Error(`Local deployment project ${project} escapes the workspace.`);
361
- }
362
- const path = (0, node_path_1.join)(projectRoot, '.ebk', 'evidence', 'infrastructure-plan.json');
363
- const before = (0, node_fs_1.lstatSync)(path, { bigint: true });
364
- if (!before.isFile() ||
365
- before.nlink !== 1n ||
366
- before.size > BigInt(MAX_PLAN_BYTES)) {
367
- throw new Error(`Local deployment plan for ${project} is not one bounded regular file.`);
368
- }
369
- const bytes = (0, node_fs_1.readFileSync)(path);
370
- const after = (0, node_fs_1.lstatSync)(path, { bigint: true });
371
- if (before.dev !== after.dev ||
372
- before.ino !== after.ino ||
373
- before.mode !== after.mode ||
374
- before.nlink !== after.nlink ||
375
- before.size !== after.size ||
376
- before.mtimeNs !== after.mtimeNs ||
377
- before.ctimeNs !== after.ctimeNs ||
378
- BigInt(bytes.byteLength) !== after.size) {
379
- throw new Error(`Local deployment plan for ${project} changed while it was read.`);
380
- }
381
- let parsed;
305
+ /** Read only the bounded public outputs SST writes after a successful native
306
+ * deploy. This is post-operation smoke input, never preview or mutation
307
+ * authority. Stage and project identity prevent a stale output file from
308
+ * selecting another deployment. */
309
+ function readDeployedSmokeTarget(workspaceRoot, project, stage) {
310
+ const projectRoot = confinedWorkspacePath(workspaceRoot, project.root);
311
+ const outputPath = (0, node_path_1.join)(projectRoot, '.sst', 'outputs.json');
312
+ const identity = (0, node_fs_1.lstatSync)(outputPath);
313
+ if (!identity.isFile() ||
314
+ identity.isSymbolicLink() ||
315
+ identity.size < 2 ||
316
+ identity.size > 64 * 1024) {
317
+ throw new Error(`Native SST outputs for ${project.name} are absent or unsafe.`);
318
+ }
319
+ const value = JSON.parse((0, node_fs_1.readFileSync)(outputPath, 'utf8'));
320
+ let api;
321
+ let web;
382
322
  try {
383
- parsed = record(JSON.parse(bytes.toString('utf8')), `${project} plan`);
323
+ api = new URL(String(value.apiUrl ?? ''));
324
+ web = new URL(String(value.webUrl ?? ''));
384
325
  }
385
326
  catch {
386
- throw new Error(`Local deployment plan for ${project} is invalid JSON.`);
387
- }
388
- const plan = parsed;
389
- if (plan.kind !== index_js_1.INFRASTRUCTURE_PLAN_KIND ||
390
- plan.schemaVersion !== index_js_1.INFRASTRUCTURE_CHANGE_SCHEMA_VERSION ||
391
- plan.action !== action ||
392
- !SHA.test(plan.sourceCommit) ||
393
- plan.target?.accountFingerprint !== digest(account) ||
394
- plan.target?.region !== region ||
395
- plan.target?.stage !== stage ||
396
- !Array.isArray(plan.projects) ||
397
- plan.projects.length !== 1 ||
398
- plan.projects[0] !== project ||
399
- !SHA256.test(plan.materialDigest) ||
400
- (0, index_js_1.computeMaterialPlanDigest)(plan) !== plan.materialDigest) {
401
- throw new Error(`Local deployment plan for ${project} is not the exact current deployment authority.`);
402
- }
403
- if (plan.sourceCommit !== deploymentSourceCommit) {
404
- try {
405
- (0, source_authority_js_1.validateReviewedReleaseCheckout)({
406
- deploymentSourceCommit,
407
- releaseSourceCommit: plan.sourceCommit,
408
- repositoryRoot: context.root,
409
- reviewedSourceCommit: deploymentSourceCommit,
410
- });
411
- }
412
- catch {
413
- throw new Error(`Local deployment plan for ${project} is not bound through one exact approval-only source transition.`);
414
- }
327
+ throw new Error(`Native SST outputs for ${project.name} contain invalid smoke URLs.`);
415
328
  }
416
- return plan;
329
+ const credentialFreeHttps = (url) => url.protocol === 'https:' &&
330
+ !url.username &&
331
+ !url.password &&
332
+ !url.search &&
333
+ !url.hash;
334
+ if (value.name !== project.name ||
335
+ value.stage !== stage ||
336
+ !credentialFreeHttps(api) ||
337
+ !credentialFreeHttps(web) ||
338
+ api.pathname !== '/' ||
339
+ web.pathname.replace(/\/$/u, '') !== `/${project.name}` ||
340
+ !/^[a-z0-9]{1,128}$/u.test(String(value.authClientId ?? '')) ||
341
+ !/^[A-Za-z0-9_-]{1,128}_[A-Za-z0-9]{1,128}$/u.test(String(value.authUserPoolId ?? ''))) {
342
+ throw new Error(`Native SST outputs for ${project.name} do not match the deployed stage and smoke target.`);
343
+ }
344
+ return {
345
+ apiUrl: api.href,
346
+ clientId: String(value.authClientId),
347
+ userPoolId: String(value.authUserPoolId),
348
+ webUrl: web.href,
349
+ };
417
350
  }
418
- function hasExactPendingLocalAuthorizationRefusal(context, project, plan) {
419
- const path = projectEvidencePath(context, project, 'infrastructure-plan-authorization.json');
420
- try {
421
- const before = (0, node_fs_1.lstatSync)(path, { bigint: true });
422
- if (!before.isFile() ||
423
- before.nlink !== 1n ||
424
- before.size > BigInt(MAX_PLAN_BYTES)) {
425
- return false;
426
- }
427
- const bytes = (0, node_fs_1.readFileSync)(path);
428
- const after = (0, node_fs_1.lstatSync)(path, { bigint: true });
429
- if (before.dev !== after.dev ||
430
- before.ino !== after.ino ||
431
- before.mode !== after.mode ||
432
- before.nlink !== after.nlink ||
433
- before.size !== after.size ||
434
- before.mtimeNs !== after.mtimeNs ||
435
- before.ctimeNs !== after.ctimeNs ||
436
- BigInt(bytes.byteLength) !== after.size) {
437
- return false;
438
- }
439
- const authorization = record(JSON.parse(bytes.toString('utf8')), `${project} authorization`);
440
- return (authorization.kind === 'ebk-infrastructure-authorization' &&
441
- authorization.schemaVersion === index_js_1.INFRASTRUCTURE_CHANGE_SCHEMA_VERSION &&
442
- authorization.action === 'deploy' &&
443
- authorization.approval === undefined &&
444
- authorization.localGovernance === undefined &&
445
- authorization.governanceEvidenceDigests === undefined &&
446
- authorization.plannedMaterialDigest === plan.materialDigest &&
447
- authorization.recomputedMaterialDigest === plan.materialDigest &&
448
- Array.isArray(authorization.sourceChangesSincePlan) &&
449
- authorization.sourceChangesSincePlan.length === 0 &&
450
- authorization.validation.allowed === false &&
451
- authorization.validation.outcome === 'plan-risk-blocked' &&
452
- authorization.validation.findings.length === 1 &&
453
- authorization.validation.findings[0]?.code === 'approval-missing');
351
+ /** Determine the one native deployment order. Foundation is always first and
352
+ * production Slice admission remains a committed policy decision. */
353
+ function localDeploymentProjectOrder(policy, context, stage) {
354
+ if (!(0, index_js_2.isActiveDeploymentStage)(policy.deploymentProfile, stage)) {
355
+ throw new Error(`Deployment stage ${stage} is inactive for the ${policy.deploymentProfile} deployment profile.`);
454
356
  }
455
- catch {
456
- return false;
357
+ const projects = projectDescriptors(context);
358
+ const foundations = projects.filter((project) => project.tags.includes('scope:foundation') &&
359
+ supportsTarget(project, 'deploy'));
360
+ if (foundations.length !== 1) {
361
+ throw new Error(`Local deployment requires exactly one deployable Foundation; found ${foundations.length}.`);
457
362
  }
458
- }
459
- /** @internal */
460
- function localPlanSummaryDigest(input) {
461
- const action = input.action ?? 'deploy';
462
- if ((action !== 'deploy' && action !== 'refresh') ||
463
- !/^\d{12}$/.test(input.account) ||
464
- !SHA.test(input.sourceCommit) ||
465
- !/^[a-z]{2}(?:-gov)?-[a-z]+-\d$/.test(input.region) ||
466
- input.orderedProjects.length === 0 ||
467
- new Set(input.orderedProjects).size !== input.orderedProjects.length ||
468
- Object.keys(input.plans).length !== input.orderedProjects.length) {
469
- throw new Error('Local deployment plan summary input is invalid.');
470
- }
471
- const plans = input.orderedProjects.map((project) => {
472
- const plan = input.plans[project];
473
- if (!plan ||
474
- plan.action !== action ||
475
- plan.projects.length !== 1 ||
476
- plan.projects[0] !== project ||
477
- !SHA256.test(plan.materialDigest)) {
478
- throw new Error(`Local deployment plan summary omitted exact project ${project}.`);
363
+ const slices = projects
364
+ .filter((project) => project.tags.includes('type:slice') &&
365
+ supportsTarget(project, 'deploy'))
366
+ .sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
367
+ if (stage === 'production') {
368
+ const missing = slices
369
+ .filter(({ name }) => policy.slices[name] === undefined)
370
+ .map(({ name }) => name);
371
+ if (missing.length > 0) {
372
+ throw new Error(`Registered Product Slices lack an explicit production policy: ${missing.join(', ')}.`);
479
373
  }
480
- return { materialDigest: plan.materialDigest, project };
374
+ }
375
+ const selectedSlices = stage === 'production'
376
+ ? slices.filter(({ name }) => {
377
+ const status = policy.slices[name]?.productionStatus;
378
+ return status === 'pilot' || status === 'ready';
379
+ })
380
+ : slices;
381
+ const selected = [
382
+ { kind: 'foundation', name: foundations[0]?.name ?? '' },
383
+ ...selectedSlices.map(({ name }) => ({ kind: 'slice', name })),
384
+ ];
385
+ const decision = (0, index_js_2.evaluateRepositoryPolicyRequest)(policy, {
386
+ action: 'deploy',
387
+ projects: selected,
388
+ stage,
481
389
  });
482
- return digest(JSON.stringify({
483
- accountFingerprint: digest(input.account),
484
- action,
485
- orderedProjects: [...input.orderedProjects],
486
- phase: input.phase,
487
- plans,
488
- region: input.region,
489
- sourceCommit: input.sourceCommit,
490
- stage: input.stage,
491
- }));
492
- }
493
- function retainEvidence(root, evidence) {
494
- const directory = (0, node_path_1.join)(root, '.ebk', 'evidence', 'local-deployment');
495
- (0, node_fs_1.mkdirSync)(directory, { recursive: true, mode: 0o700 });
496
- (0, node_fs_1.chmodSync)(directory, 0o700);
497
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(directory, 'latest.json'), `${JSON.stringify(evidence, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
390
+ if (!decision.allowed) {
391
+ throw new Error(`Repository Operating Policy rejected local deployment:\n${decision.reasons.map((reason) => `- ${reason}`).join('\n')}`);
392
+ }
393
+ return selected.map(({ name }) => name);
498
394
  }
499
- const REFRESH_FRAMEWORK_TAG_OBSERVATION_PATHS = new Map([
500
- ['/inputs/tagsAll/ebk:framework-version', true],
501
- ['/outputs/tags/ebk:framework-version', false],
502
- ['/outputs/tagsAll/ebk:framework-version', false],
503
- ]);
504
- function isFrameworkTagProviderObservation(resource) {
505
- if (resource.operation !== 'update' ||
506
- resource.replacement ||
507
- resource.resourceType !== 'aws:ssm/parameter:Parameter' ||
508
- resource.changedProperties.length !==
509
- REFRESH_FRAMEWORK_TAG_OBSERVATION_PATHS.size) {
510
- return false;
511
- }
512
- const observed = new Set();
513
- for (const property of resource.changedProperties) {
514
- const expectedInputDiff = REFRESH_FRAMEWORK_TAG_OBSERVATION_PATHS.get(property.path);
515
- if (expectedInputDiff === undefined ||
516
- property.inputDiff !== expectedInputDiff ||
517
- property.kind !== 'update' ||
518
- property.sensitive ||
519
- observed.has(property.path)) {
520
- return false;
395
+ function frameworkVersion(root) {
396
+ const candidates = [
397
+ (0, node_path_1.join)(root, 'node_modules', '@empire-builder-kit', 'nx', 'package.json'),
398
+ (0, node_path_1.join)(root, 'package.json'),
399
+ ];
400
+ for (const path of candidates) {
401
+ try {
402
+ const value = JSON.parse((0, node_fs_1.readFileSync)(path, 'utf8'));
403
+ if (value.name === '@empire-builder-kit/nx' && value.version) {
404
+ return value.version;
405
+ }
406
+ const dependency = value.devDependencies?.['@empire-builder-kit/nx'] ??
407
+ value.dependencies?.['@empire-builder-kit/nx'];
408
+ if (dependency)
409
+ return dependency;
410
+ }
411
+ catch {
412
+ // Try the next deterministic package identity.
521
413
  }
522
- observed.add(property.path);
523
414
  }
524
- return observed.size === REFRESH_FRAMEWORK_TAG_OBSERVATION_PATHS.size;
415
+ return 'unknown';
525
416
  }
526
- function isSemanticallyConvergedRefresh(plan) {
527
- if (plan.action !== 'refresh' ||
528
- plan.state.locked ||
529
- !plan.diff.complete ||
530
- plan.diff.failures.length > 0) {
531
- return false;
532
- }
533
- if (plan.diff.resources.length === 0) {
534
- return plan.driftStatus === 'no-drift';
535
- }
536
- if (plan.driftStatus !== 'drift-detected' ||
537
- !plan.diff.resources.every(isFrameworkTagProviderObservation) ||
538
- plan.requiredApprovals.some((approval) => ![
539
- 'deployment-review',
540
- 'drift-decision-and-state-recovery',
541
- 'point-of-no-return',
542
- ].includes(approval))) {
543
- return false;
544
- }
545
- const observedResourceIds = new Set(plan.diff.resources.map(({ logicalId }) => logicalId));
546
- return plan.resourceFindings.every((finding) => (finding.resourceId === undefined &&
547
- finding.class === 'stateful-or-destructive' &&
548
- finding.code === 'protected-state-refresh' &&
549
- finding.disposition === 'block') ||
550
- (finding.resourceId !== undefined &&
551
- observedResourceIds.has(finding.resourceId) &&
552
- finding.class === 'in-place-reviewed' &&
553
- finding.code === 'in-place-update' &&
554
- finding.disposition === 'review'));
417
+ function prepareReceiptDestination(root) {
418
+ const directory = (0, node_path_1.join)(root, '.ebk', 'evidence', 'deployment');
419
+ ensurePrivateEvidenceDirectory(root, directory);
420
+ const path = (0, node_path_1.join)(directory, 'latest.json');
421
+ if ((0, node_fs_1.existsSync)(path))
422
+ (0, private_state_js_1.assertPrivatePath)(path, 'file');
423
+ return path;
555
424
  }
556
- function isSemanticallyConvergedDeploy(plan) {
557
- if (plan.action !== 'deploy' ||
558
- plan.state.locked !== false ||
559
- !plan.diff.complete ||
560
- plan.diff.failures.length > 0) {
561
- return false;
562
- }
563
- if (plan.diff.resources.length === 0) {
564
- return (plan.aggregateRisk === 'no-change' &&
565
- plan.driftStatus === 'no-drift' &&
566
- plan.requiredApprovals.length === 0 &&
567
- plan.resourceFindings.length === 0);
568
- }
569
- if (plan.aggregateRisk !== 'in-place-reviewed' ||
570
- (plan.driftStatus !== 'no-drift' &&
571
- plan.driftStatus !== 'drift-detected') ||
572
- plan.requiredApprovals.length !== 1 ||
573
- plan.requiredApprovals[0] !== 'deployment-review') {
574
- return false;
575
- }
576
- const providerReads = new Map();
577
- for (const resource of plan.diff.resources) {
578
- const retainedResourceType = providerReads.get(resource.logicalId);
579
- if (resource.operation !== 'read' ||
580
- resource.providerOperation !== 'read' ||
581
- resource.replacement ||
582
- resource.changedProperties.length > 0 ||
583
- (retainedResourceType !== undefined &&
584
- retainedResourceType !== resource.resourceType)) {
585
- return false;
586
- }
587
- providerReads.set(resource.logicalId, resource.resourceType);
588
- }
589
- if (plan.resourceFindings.length !== providerReads.size) {
590
- return false;
591
- }
592
- const providerReadFindings = new Set();
593
- for (const finding of plan.resourceFindings) {
594
- if (finding.resourceId === undefined ||
595
- !providerReads.has(finding.resourceId) ||
596
- finding.class !== 'in-place-reviewed' ||
597
- finding.code !== 'provider-read' ||
598
- finding.disposition !== 'review' ||
599
- providerReadFindings.has(finding.resourceId)) {
600
- return false;
601
- }
602
- providerReadFindings.add(finding.resourceId);
425
+ function writeReceipt(root, receipt) {
426
+ (0, private_state_js_1.atomicWritePrivateFile)(prepareReceiptDestination(root), `${JSON.stringify(receipt, null, 2)}\n`);
427
+ }
428
+ function invalidatePriorReceipt(root) {
429
+ const path = prepareReceiptDestination(root);
430
+ if (!(0, node_fs_1.existsSync)(path))
431
+ return;
432
+ (0, private_state_js_1.assertPrivatePath)(path, 'file');
433
+ (0, node_fs_1.rmSync)(path, { force: true });
434
+ if ((0, node_fs_1.existsSync)(path)) {
435
+ throw new Error('Prior deployment receipt invalidation failed.');
603
436
  }
604
- return providerReadFindings.size === providerReads.size;
605
437
  }
606
- function isFreshInvocationPlan(plan, invocationStartedAt, observedAt) {
607
- const started = Date.parse(invocationStartedAt);
608
- const generated = Date.parse(plan.generatedAt);
609
- const observed = observedAt.getTime();
610
- const expires = Date.parse(plan.expiresAt);
611
- return (Number.isFinite(started) &&
612
- Number.isFinite(generated) &&
613
- Number.isFinite(observed) &&
614
- Number.isFinite(expires) &&
615
- generated >= started &&
616
- generated <= observed &&
617
- expires > observed);
438
+ async function runNxTarget(input) {
439
+ const forwarded = [
440
+ `--stage=${input.stage}`,
441
+ `--expectedRoleArn=${input.expectedRoleArn}`,
442
+ `--region=${input.region}`,
443
+ ...(input.profile ? [`--profile=${input.profile}`] : []),
444
+ ];
445
+ const args = [
446
+ (0, node_path_1.join)(input.context.root, 'node_modules', 'nx', 'dist', 'bin', 'nx.js'),
447
+ 'run',
448
+ `${input.project}:${input.target}`,
449
+ '--',
450
+ ...forwarded,
451
+ ];
452
+ const result = input.runSync
453
+ ? input.runSync(process.execPath, args, {
454
+ cwd: input.context.root,
455
+ env: input.environment,
456
+ shell: false,
457
+ stdio: 'inherit',
458
+ })
459
+ : await (0, run_sst_command_js_1.runGovernedChildProcess)({
460
+ args,
461
+ cwd: input.context.root,
462
+ environment: input.environment,
463
+ executable: process.execPath,
464
+ ...(input.spawnChild ? { spawnChild: input.spawnChild } : {}),
465
+ });
466
+ if (result.error || result.signal || result.status !== 0) {
467
+ throw new Error(result.error
468
+ ? `${input.target} failed for ${input.project}: ${result.error.message}`
469
+ : result.signal
470
+ ? `${input.target} failed for ${input.project} with signal ${result.signal}.`
471
+ : `${input.target} failed for ${input.project} with status ${result.status ?? 'unknown'}.`);
472
+ }
473
+ }
474
+ function productionConfirmation(account, region, commit) {
475
+ return `deploy:production:${account}:${region}:${commit}`;
476
+ }
477
+ async function defaultIdentifyGithubCaller(region) {
478
+ return new client_sts_1.STSClient({
479
+ endpoint: (0, identity_js_1.canonicalStsEndpoint)(region),
480
+ region,
481
+ }).send(new client_sts_1.GetCallerIdentityCommand({}));
482
+ }
483
+ function assertGithubDeploymentAuthority(input) {
484
+ const repository = process.env.GITHUB_REPOSITORY?.trim() ?? '';
485
+ const ref = process.env.GITHUB_REF?.trim() ?? '';
486
+ const eventName = process.env.GITHUB_EVENT_NAME?.trim() ?? '';
487
+ const callerWorkflow = process.env.GITHUB_WORKFLOW_REF?.trim() ?? '';
488
+ const calledWorkflow = process.env.EBK_GITHUB_JOB_WORKFLOW_REF?.trim() ?? '';
489
+ const expectedBranch = input.stage === 'production' ? 'main' : 'develop';
490
+ const expectedRef = `refs/heads/${expectedBranch}`;
491
+ const expectedCalledWorkflow = `${repository}/.github/workflows/deploy.yml@${expectedRef}`;
492
+ const expectedCallerWorkflow = eventName === 'push'
493
+ ? `${repository}/.github/workflows/ci.yml@${expectedRef}`
494
+ : eventName === 'workflow_dispatch'
495
+ ? expectedCalledWorkflow
496
+ : undefined;
497
+ const allowedEvent = eventName === 'workflow_dispatch' ||
498
+ (eventName === 'push' && input.policy.deploymentProfile !== 'starter');
499
+ if (process.env.GITHUB_ACTIONS !== 'true' ||
500
+ !repository ||
501
+ !allowedEvent ||
502
+ process.env.GITHUB_SHA !== input.commit ||
503
+ ref !== expectedRef ||
504
+ callerWorkflow !== expectedCallerWorkflow ||
505
+ calledWorkflow !== expectedCalledWorkflow) {
506
+ throw new Error('Protected deployment requires the exact generated GitHub caller and reusable deploy workflows, branch, and immutable source commit.');
507
+ }
508
+ if (input.policy.deploymentProfile === 'starter' &&
509
+ input.stage === 'staging') {
510
+ throw new Error('The starter deployment profile has no staging stage.');
511
+ }
512
+ const clean = git(input.root, ['status', '--porcelain=v1', '--untracked-files=all'], input.run);
513
+ if (clean !== '') {
514
+ throw new Error('Protected GitHub deployment requires a clean checkout.');
515
+ }
618
516
  }
619
517
  async function runLocalDeploy(options, context, dependencies = {}) {
620
518
  const run = dependencies.run ?? node_child_process_1.spawnSync;
621
- if (process.env.GITHUB_ACTIONS === 'true') {
622
- throw new Error('Local deployment is not a GitHub Actions authority mode.');
623
- }
624
- const policy = (0, index_js_2.readRepositoryOperatingPolicy)(context.root);
625
- const action = options.action ?? 'deploy';
626
- if (action !== 'deploy' && action !== 'refresh') {
627
- throw new Error('Local infrastructure action is invalid.');
628
- }
629
- const phase = options.phase ?? 'full-stage';
630
- if (phase !== 'foundation-bootstrap' && phase !== 'full-stage') {
631
- throw new Error('Local deployment phase is invalid.');
632
- }
633
- if ((0, index_js_2.requiresGithubDeployment)(policy.deploymentProfile, options.stage)) {
634
- throw new Error(`The ${policy.deploymentProfile} deployment profile requires GitHub deployment authority for ${options.stage}.`);
635
- }
636
- const accounts = (0, index_js_2.configuredDeploymentAccountTopology)(policy.deploymentProfile, policy.deploymentAccounts);
637
- const region = (0, index_js_2.configuredDeploymentRegion)(policy.deploymentRegion);
638
- const expectedAccount = accounts[options.stage];
639
- if (!ROLE.test(options.expectedRoleArn)) {
640
- throw new Error('Local deployment requires one exact expected IAM role ARN.');
641
- }
642
- const status = git(context.root, ['status', '--porcelain=v1', '--untracked-files=all'], run);
643
- if (status !== '') {
644
- throw new Error('Local deployment requires a completely clean source tree.');
645
- }
646
- const sourceCommit = git(context.root, ['rev-parse', 'HEAD'], run);
647
- const sourceTree = git(context.root, ['rev-parse', 'HEAD^{tree}'], run);
648
- if (!SHA.test(sourceCommit) || !SHA.test(sourceTree)) {
649
- throw new Error('Local deployment could not bind one immutable Git source.');
650
- }
651
- const expectedConfirmation = (0, infrastructure_plan_js_1.localProductionConfirmation)({
652
- action,
653
- accountId: expectedAccount,
654
- region,
655
- sourceCommit,
656
- });
657
- if (options.stage === 'production' &&
658
- options.confirmation !== expectedConfirmation) {
659
- throw new Error(`Local production ${action} requires --confirmation=${expectedConfirmation}.`);
660
- }
661
- const caller = await (dependencies.caller ??
662
- (async (targetRegion) => new client_sts_1.STSClient({
663
- region: targetRegion,
664
- ...(options.profile
665
- ? { credentials: (0, credential_providers_1.fromIni)({ profile: options.profile }) }
666
- : {}),
667
- }).send(new client_sts_1.GetCallerIdentityCommand({}))))(region);
668
- const account = caller.Account?.trim() ?? '';
669
- const arn = caller.Arn?.trim() ?? '';
670
- if (account !== expectedAccount ||
671
- !(0, infrastructure_plan_js_1.matchesExpectedAssumedRoleArn)(expectedAccount, arn, options.expectedRoleArn)) {
672
- throw new Error('Local AWS caller does not match the committed account and exact temporary assumed role.');
673
- }
674
- const orderedProjects = projectInventory(context, options.stage, phase, action);
675
- const sourceSnapshots = toolOwnedSourceSnapshots(context, orderedProjects);
676
- const startedAt = (dependencies.now ?? (() => new Date()))().toISOString();
677
- let backend;
678
- let result = 'failed';
679
- let planDigests = {};
680
- let planSummaryDigest = null;
681
- let deployNoOpProjects = [];
682
- const mutatedProjects = [];
683
- let operatorAcknowledgementDigest = null;
684
- let replanRequiredProject = null;
685
- let refreshNoOpProjects = [];
686
- try {
687
- backend = await (dependencies.backend ?? prepareLocalSstBackend)({
688
- account: expectedAccount,
689
- applications: orderedProjects,
690
- ...(options.profile ? { profile: options.profile } : {}),
519
+ const now = dependencies.now ?? (() => new Date());
520
+ const startedAt = now().toISOString();
521
+ const children = [];
522
+ const migrations = {};
523
+ const smoke = {};
524
+ let account = '';
525
+ let actor = '';
526
+ let policy;
527
+ let region = '';
528
+ let source = { clean: false, commit: '', workingTreeDigest: '' };
529
+ let transport;
530
+ let productionDatabaseBefore;
531
+ let productionDatabaseAfterFoundation;
532
+ let outcome = 'failed';
533
+ let receiptDestinationPrepared = false;
534
+ const githubAuthority = process.env.GITHUB_ACTIONS === 'true';
535
+ const retain = () => {
536
+ if (!policy || !receiptDestinationPrepared)
537
+ return;
538
+ writeReceipt(context.root, {
539
+ account,
540
+ actor,
541
+ authority: githubAuthority ? 'github' : 'local',
542
+ children,
543
+ completedAt: now().toISOString(),
544
+ frameworkVersion: frameworkVersion(context.root),
545
+ kind: 'ebk-deployment-receipt',
546
+ migrations,
547
+ outcome,
548
+ profile: policy.deploymentProfile,
691
549
  region,
550
+ schemaVersion: 1,
551
+ smoke,
552
+ source,
692
553
  stage: options.stage,
554
+ startedAt,
693
555
  });
694
- const environment = {
695
- ...process.env,
696
- AWS_EXPECTED_ACCOUNT_ID: expectedAccount,
697
- AWS_PLAN_ROLE_ARN: options.expectedRoleArn,
698
- AWS_PLAN_SST_APPS_JSON: JSON.stringify(orderedProjects),
699
- AWS_PLAN_STATE_BUCKET_ARN: backend.stateBucketArn,
700
- ...(options.profile ? { AWS_PROFILE: options.profile } : {}),
701
- AWS_REGION: region,
702
- // Local deployment's reviewed clean Git commit is the one stage-wide
703
- // workload-role candidate shared by Foundation and every Slice. Never
704
- // accept an ambient candidate identity from the caller.
705
- EBK_ACTION: action,
706
- EBK_CANDIDATE_IDENTITY: sourceCommit,
707
- EBK_LOCAL_DEPLOYMENT_MODE: 'federated',
708
- EBK_LOCAL_SOURCE_TREE: sourceTree,
709
- EBK_SOURCE_COMMIT: sourceCommit,
710
- EBK_STAGE: options.stage,
711
- ORDERED_PROJECTS: orderedProjects.join(','),
712
- };
713
- for (const key of PROTECTED_PLAN_KEYS)
714
- delete environment[key];
715
- if (options.profile) {
716
- for (const key of AMBIENT_CREDENTIAL_KEYS)
717
- delete environment[key];
556
+ };
557
+ try {
558
+ invalidatePriorReceipt(context.root);
559
+ receiptDestinationPrepared = true;
560
+ (0, run_sst_command_js_1.assertGovernedAwsLifecyclePlatform)(dependencies.platform);
561
+ policy = (0, index_js_2.readRepositoryOperatingPolicy)(context.root);
562
+ if (!(0, index_js_2.isActiveDeploymentStage)(policy.deploymentProfile, options.stage)) {
563
+ throw new Error(`Deployment stage ${options.stage} is inactive for the ${policy.deploymentProfile} deployment profile.`);
718
564
  }
719
- const common = [
720
- `--stage=${options.stage}`,
721
- `--expectedRoleArn=${options.expectedRoleArn}`,
722
- `--region=${region}`,
723
- ...(options.profile ? [`--profile=${options.profile}`] : []),
724
- ];
725
- for (const project of orderedProjects) {
726
- runNxWithToolOwnedSourceReconciliation(context, project, 'infrastructure-plan', [`--action=${action}`, ...common], environment, sourceSnapshots, run, action === 'deploy'
727
- ? () => {
728
- const plan = retainedPlan(context, project, sourceCommit, expectedAccount, region, options.stage, action);
729
- const accepted = isFreshInvocationPlan(plan, startedAt, (dependencies.now ?? (() => new Date()))()) &&
730
- isSemanticallyConvergedDeploy(plan) &&
731
- hasExactPendingLocalAuthorizationRefusal(context, project, plan);
732
- if (accepted) {
733
- devkit_1.logger.info(`Local infrastructure-plan for ${project} returned a conservative non-success after emitting one fresh provider-read-only semantic convergence record.`);
734
- }
735
- return accepted;
736
- }
737
- : undefined);
565
+ if (!githubAuthority &&
566
+ (0, index_js_2.requiresGithubDeployment)(policy.deploymentProfile, options.stage)) {
567
+ throw new Error(`The ${policy.deploymentProfile} profile requires GitHub deployment authority for ${options.stage}.`);
568
+ }
569
+ if (!ROLE.test(options.expectedRoleArn)) {
570
+ throw new Error('Local deployment requires one exact IAM role ARN.');
738
571
  }
739
- const plans = Object.fromEntries(orderedProjects.map((project) => [
740
- project,
741
- retainedPlan(context, project, sourceCommit, expectedAccount, region, options.stage, action),
742
- ]));
743
- planDigests = Object.fromEntries(orderedProjects.map((project) => [
744
- project,
745
- plans[project].materialDigest,
746
- ]));
747
- planSummaryDigest = localPlanSummaryDigest({
748
- account: expectedAccount,
749
- action,
750
- orderedProjects,
751
- phase,
752
- plans,
572
+ region = (0, index_js_2.configuredDeploymentRegion)(policy.deploymentRegion);
573
+ account = (0, index_js_2.configuredDeploymentAccountForStage)(policy.deploymentProfile, policy.deploymentAccounts, options.stage);
574
+ const expectedRoleArn = (0, index_js_1.createStageAuthorityIdentity)({
575
+ accountId: account,
576
+ partition: partitionForRegion(region),
753
577
  region,
754
- sourceCommit,
755
578
  stage: options.stage,
756
- });
757
- const approvalEnvironment = {
758
- ...environment,
759
- EBK_LOCAL_PLAN_APPROVER_ARN: arn,
760
- EBK_LOCAL_PLAN_SUMMARY_DIGEST: planSummaryDigest,
579
+ }).deploymentRoleArn;
580
+ if (options.expectedRoleArn !== expectedRoleArn) {
581
+ throw new Error(`Deployment role must equal the committed ${options.stage} stage authority role ${expectedRoleArn}.`);
582
+ }
583
+ const identity = localWorkingTreeDigest(context.root, run);
584
+ source = {
585
+ clean: identity.clean,
586
+ commit: identity.commit,
587
+ workingTreeDigest: identity.digest,
761
588
  };
762
- const planEnvironment = (project) => plans[project].requiredApprovals.length > 0
763
- ? {
764
- ...approvalEnvironment,
765
- EBK_LOCAL_PLAN_MATERIAL_DIGEST: plans[project].materialDigest,
589
+ if ((githubAuthority || options.stage !== 'development') && !source.clean) {
590
+ throw new Error(`Local ${options.stage} deployment requires a completely clean source tree.`);
591
+ }
592
+ if (githubAuthority) {
593
+ if (options.profile) {
594
+ throw new Error('Protected GitHub deployment must use ambient OIDC credentials, not a local AWS profile.');
766
595
  }
767
- : environment;
768
- deployNoOpProjects =
769
- action === 'deploy'
770
- ? orderedProjects.filter((project) => isSemanticallyConvergedDeploy(plans[project]))
771
- : [];
772
- refreshNoOpProjects =
773
- action === 'refresh'
774
- ? orderedProjects.filter((project) => isSemanticallyConvergedRefresh(plans[project]))
775
- : [];
776
- const mutationNoOpProjectSet = new Set([
777
- ...deployNoOpProjects,
778
- ...refreshNoOpProjects,
779
- ]);
780
- const materialProjectIndex = orderedProjects.findIndex((project) => !mutationNoOpProjectSet.has(project));
781
- if (materialProjectIndex >= 0) {
782
- const project = orderedProjects[materialProjectIndex];
783
- const plan = plans[project];
784
- const challenge = `approve:${action}:${options.stage}:${project}:${plan.materialDigest}`;
785
- devkit_1.logger.info(`[ebk-local-deployment-review]\n${JSON.stringify({
786
- action,
787
- challenge,
788
- firstMaterialProject: project,
789
- orderedProjects,
790
- plan: {
791
- aggregateRisk: plan.aggregateRisk,
792
- diff: plan.diff,
793
- materialDigest: plan.materialDigest,
794
- requiredApprovals: plan.requiredApprovals,
795
- resourceFindings: plan.resourceFindings,
796
- stateDigest: plan.state.digest,
797
- },
798
- planSummaryDigest,
799
- sourceCommit,
596
+ assertGithubDeploymentAuthority({
597
+ commit: source.commit,
598
+ policy,
599
+ root: context.root,
600
+ run,
800
601
  stage: options.stage,
801
- }, null, 2)}`);
602
+ });
603
+ const caller = await (dependencies.identifyGithubCaller ?? defaultIdentifyGithubCaller)(region);
604
+ if (caller.Account?.trim() !== account ||
605
+ !(0, identity_js_1.matchesExpectedAssumedRoleArn)(account, caller.Arn?.trim() ?? '', expectedRoleArn)) {
606
+ throw new Error('Protected GitHub deployment caller does not match the committed account and exact assumed role.');
607
+ }
608
+ actor = process.env.GITHUB_ACTOR?.trim() ?? '';
609
+ if (!actor) {
610
+ throw new Error('Protected GitHub deployment actor is absent.');
611
+ }
612
+ }
613
+ else if (options.stage === 'staging') {
614
+ const challenge = `approve:deploy:staging:${source.commit}`;
802
615
  let acknowledged = false;
803
616
  try {
804
617
  acknowledged = await (dependencies.approve ?? (0, local_approval_js_1.createTtyLocalMutationApprover)())({
805
- action,
618
+ action: 'deploy',
806
619
  challenge,
807
- planDigest: plan.materialDigest,
808
- project,
620
+ sourceCommit: source.commit,
809
621
  stage: options.stage,
810
622
  });
811
623
  }
812
624
  catch (error) {
813
625
  if (error instanceof local_approval_js_1.LocalMutationApprovalUnavailableError) {
814
- result = 'review-required';
626
+ outcome = 'review-required';
815
627
  }
816
628
  throw error;
817
629
  }
818
630
  if (!acknowledged) {
819
- result = 'review-required';
820
- throw new Error(`Local ${action} acknowledgement was declined for ${project}.`);
631
+ outcome = 'review-required';
632
+ throw new Error('Local staging deployment acknowledgement was declined.');
821
633
  }
822
- operatorAcknowledgementDigest = digest(challenge);
823
- const issuedAt = (dependencies.now ?? (() => new Date()))();
824
- (0, infrastructure_plan_js_1.authorizeRetainedLocalInfrastructurePlan)({
825
- context: { ...context, projectName: project },
826
- environment: planEnvironment(project),
827
- now: issuedAt,
828
- plan,
829
- });
830
- if (retainedPlan(context, project, sourceCommit, expectedAccount, region, options.stage, action).materialDigest !== planDigests[project]) {
831
- throw new Error(`Local deployment plan for ${project} changed before invocation-scoped authority was established.`);
634
+ }
635
+ if (!githubAuthority && options.stage === 'production') {
636
+ const expected = productionConfirmation(account, region, source.commit);
637
+ let acknowledged = false;
638
+ try {
639
+ acknowledged = await (dependencies.approve ?? (0, local_approval_js_1.createTtyLocalMutationApprover)())({
640
+ action: 'deploy',
641
+ challenge: expected,
642
+ sourceCommit: source.commit,
643
+ stage: options.stage,
644
+ });
832
645
  }
833
- const capability = writeLocalMutationCapability({
834
- actorArn: arn,
835
- context,
836
- deploymentProfile: policy.deploymentProfile,
837
- deploymentSourceCommit: sourceCommit,
838
- deploymentSourceTree: sourceTree,
839
- expectedRoleArn: options.expectedRoleArn,
840
- issuedAt,
841
- plan,
646
+ catch (error) {
647
+ if (error instanceof local_approval_js_1.LocalMutationApprovalUnavailableError) {
648
+ outcome = 'review-required';
649
+ }
650
+ throw error;
651
+ }
652
+ if (!acknowledged) {
653
+ outcome = 'review-required';
654
+ throw new Error('Local production deployment acknowledgement was declined.');
655
+ }
656
+ }
657
+ const orderedProjects = localDeploymentProjectOrder(policy, context, options.stage);
658
+ const descriptors = projectDescriptors(context);
659
+ const localProtectedContainerStage = !githubAuthority &&
660
+ requiresLocalContainerPreparation(policy.deploymentProfile, options.stage)
661
+ ? options.stage
662
+ : undefined;
663
+ const localProtectedContainerProjects = localProtectedContainerStage
664
+ ? orderedProjects
665
+ .map((project) => descriptors.find((candidate) => candidate.name === project))
666
+ .filter((project) => project?.tags.includes('type:slice') === true)
667
+ : [];
668
+ for (const project of localProtectedContainerProjects) {
669
+ const missingTargets = LOCAL_CONTAINER_TARGETS.filter((target) => !supportsTarget(project, target));
670
+ if (missingTargets.length > 0) {
671
+ throw new Error(`Local ${policy.deploymentProfile} ${options.stage} requires the complete immutable OCI target set for ${project.name}: ${missingTargets.join(', ')}.`);
672
+ }
673
+ }
674
+ if (!githubAuthority) {
675
+ transport = await (dependencies.createTransport ?? federated_credential_transport_js_1.createLocalFederatedCredentialTransport)({
676
+ account,
677
+ expectedRoleArn,
678
+ ...(options.profile ? { profile: options.profile } : {}),
679
+ region,
680
+ });
681
+ const firstIdentity = await transport.refresh();
682
+ actor = firstIdentity.actorArn;
683
+ }
684
+ const ownershipEnvironment = transport
685
+ ? transport.childEnvironment((0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(process.env))
686
+ : (0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(process.env, {
687
+ preserveVerifiedGithubCredentials: true,
688
+ });
689
+ await (dependencies.ownership ?? index_js_3.assertWorkspaceStageOwnershipForWorkspace)(context.root, options.stage, {
690
+ ...(ownershipEnvironment.AWS_CONFIG_FILE
691
+ ? { configFilepath: ownershipEnvironment.AWS_CONFIG_FILE }
692
+ : {}),
693
+ ...(ownershipEnvironment.AWS_SHARED_CREDENTIALS_FILE
694
+ ? {
695
+ credentialsFilepath: ownershipEnvironment.AWS_SHARED_CREDENTIALS_FILE,
696
+ }
697
+ : {}),
698
+ ...(transport ? { profile: transport.profile } : {}),
699
+ });
700
+ if (options.stage === 'production') {
701
+ if (transport)
702
+ await transport.refresh();
703
+ const boundaryEnvironment = transport
704
+ ? transport.childEnvironment((0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(process.env))
705
+ : (0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(process.env, {
706
+ preserveVerifiedGithubCredentials: true,
707
+ });
708
+ productionDatabaseBefore = await (dependencies.attestProductionDatabase ??
709
+ production_database_safeguard_js_1.attestProductionDatabaseLifecycle)({
710
+ ...(boundaryEnvironment.AWS_CONFIG_FILE
711
+ ? { configFilepath: boundaryEnvironment.AWS_CONFIG_FILE }
712
+ : {}),
713
+ ...(boundaryEnvironment.AWS_SHARED_CREDENTIALS_FILE
714
+ ? {
715
+ credentialsFilepath: boundaryEnvironment.AWS_SHARED_CREDENTIALS_FILE,
716
+ }
717
+ : {}),
718
+ expectedAccountId: account,
719
+ expectedRegion: region,
720
+ expectedRoleArn,
721
+ now: now(),
722
+ ...(transport ? { profile: transport.profile } : {}),
723
+ requireExisting: false,
724
+ });
725
+ }
726
+ const localContainerRunId = localProtectedContainerProjects.length === 0
727
+ ? ''
728
+ : ((0, node_crypto_1.randomBytes)(8).readBigUInt64BE() + 1n).toString();
729
+ for (const project of localProtectedContainerProjects) {
730
+ resetLocalContainerWorkingEvidence({
842
731
  project,
843
- releaseSourceTree: git(context.root, ['rev-parse', `${plan.sourceCommit}^{tree}`], run),
732
+ root: context.root,
733
+ stage: localProtectedContainerStage,
844
734
  });
735
+ for (const target of LOCAL_CONTAINER_TARGETS) {
736
+ if (transport)
737
+ await transport.refresh();
738
+ const environment = {
739
+ ...(transport
740
+ ? transport.childEnvironment((0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(process.env))
741
+ : (0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(process.env)),
742
+ AWS_EXPECTED_ACCOUNT_ID: account,
743
+ AWS_REGION: region,
744
+ EBK_LOCAL_CONTAINER_RUN_ID: localContainerRunId,
745
+ EBK_LOCAL_DEPLOYMENT_MODE: 'federated',
746
+ EBK_DEPLOYMENT_PROFILE: policy.deploymentProfile,
747
+ EBK_NX_STAGE_LIFECYCLE: 'local',
748
+ EBK_RELEASE_SOURCE_COMMIT: source.commit,
749
+ EBK_SOURCE_COMMIT: source.commit,
750
+ EBK_STAGE: options.stage,
751
+ EBK_WORKING_TREE_DIRTY: 'false',
752
+ };
753
+ assertDeploymentSourceUnchanged(context.root, source, run);
754
+ const childStartedAt = now().toISOString();
755
+ try {
756
+ await runNxTarget({
757
+ context,
758
+ environment,
759
+ expectedRoleArn,
760
+ ...(transport ? { profile: transport.profile } : {}),
761
+ project: project.name,
762
+ region,
763
+ ...(dependencies.run ? { runSync: dependencies.run } : {}),
764
+ ...(dependencies.spawnChild
765
+ ? { spawnChild: dependencies.spawnChild }
766
+ : {}),
767
+ stage: options.stage,
768
+ target,
769
+ });
770
+ if (target === 'release-candidate') {
771
+ materializeLocalReleaseCandidate({
772
+ project,
773
+ root: context.root,
774
+ sourceCommit: source.commit,
775
+ });
776
+ }
777
+ if (target === 'container-release-assemble') {
778
+ (dependencies.attestContainerBinding ?? attestLocalContainerBinding)({
779
+ account,
780
+ project,
781
+ region,
782
+ root: context.root,
783
+ sourceCommit: source.commit,
784
+ stage: options.stage,
785
+ });
786
+ }
787
+ children.push({
788
+ completedAt: now().toISOString(),
789
+ outcome: 'succeeded',
790
+ project: project.name,
791
+ startedAt: childStartedAt,
792
+ target,
793
+ });
794
+ }
795
+ catch (error) {
796
+ children.push({
797
+ completedAt: now().toISOString(),
798
+ outcome: 'failed',
799
+ project: project.name,
800
+ startedAt: childStartedAt,
801
+ target,
802
+ });
803
+ throw error;
804
+ }
805
+ }
806
+ }
807
+ for (const project of orderedProjects) {
808
+ const descriptor = descriptors.find((candidate) => candidate.name === project);
809
+ if (!descriptor) {
810
+ throw new Error(`Local deployment project ${project} disappeared.`);
811
+ }
812
+ if (transport)
813
+ await transport.refresh();
814
+ const environment = {
815
+ ...(transport
816
+ ? transport.childEnvironment((0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(process.env))
817
+ : (0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(process.env, {
818
+ preserveVerifiedGithubCredentials: true,
819
+ })),
820
+ AWS_EXPECTED_ACCOUNT_ID: account,
821
+ AWS_REGION: region,
822
+ EBK_LOCAL_SOURCE_TREE: source.workingTreeDigest,
823
+ EBK_NX_STAGE_LIFECYCLE: githubAuthority ? 'github' : 'local',
824
+ EBK_SOURCE_COMMIT: source.commit,
825
+ EBK_WORKING_TREE_DIRTY: source.clean ? 'false' : 'true',
826
+ };
827
+ assertDeploymentSourceUnchanged(context.root, source, run);
828
+ const childStartedAt = now().toISOString();
845
829
  try {
846
- runNxWithToolOwnedSourceReconciliation(context, project, action, [
847
- ...common,
848
- ...(options.stage === 'production'
849
- ? [`--localProductionConfirmation=${expectedConfirmation}`]
850
- : []),
851
- ], {
852
- ...environment,
853
- EBK_LOCAL_MUTATION_CAPABILITY_NONCE: capability.nonce,
854
- EBK_LOCAL_MUTATION_CAPABILITY_PATH: capability.path,
855
- }, sourceSnapshots, run);
830
+ await runNxTarget({
831
+ context,
832
+ environment,
833
+ expectedRoleArn,
834
+ ...(transport ? { profile: transport.profile } : {}),
835
+ project,
836
+ region,
837
+ ...(dependencies.run ? { runSync: dependencies.run } : {}),
838
+ ...(dependencies.spawnChild
839
+ ? { spawnChild: dependencies.spawnChild }
840
+ : {}),
841
+ stage: options.stage,
842
+ target: 'deploy',
843
+ });
844
+ children.push({
845
+ completedAt: now().toISOString(),
846
+ outcome: 'succeeded',
847
+ project,
848
+ startedAt: childStartedAt,
849
+ target: 'deploy',
850
+ });
851
+ }
852
+ catch (error) {
853
+ children.push({
854
+ completedAt: now().toISOString(),
855
+ outcome: 'failed',
856
+ project,
857
+ startedAt: childStartedAt,
858
+ target: 'deploy',
859
+ });
860
+ throw error;
856
861
  }
857
- finally {
858
- (0, node_fs_1.rmSync)(capability.directory, { force: true, recursive: true });
862
+ if (options.stage === 'production' &&
863
+ descriptor.tags.includes('scope:foundation')) {
864
+ if (transport)
865
+ await transport.refresh();
866
+ const boundaryEnvironment = transport
867
+ ? transport.childEnvironment((0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(process.env))
868
+ : (0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(process.env, {
869
+ preserveVerifiedGithubCredentials: true,
870
+ });
871
+ const postflightStartedAt = now().toISOString();
872
+ const profile = (0, index_js_2.productionFoundationProfileContract)(policy.productionFoundation.profile).contract;
873
+ try {
874
+ productionDatabaseAfterFoundation = await (dependencies.attestProductionDatabase ??
875
+ production_database_safeguard_js_1.attestProductionDatabaseLifecycle)({
876
+ ...(boundaryEnvironment.AWS_CONFIG_FILE
877
+ ? { configFilepath: boundaryEnvironment.AWS_CONFIG_FILE }
878
+ : {}),
879
+ ...(boundaryEnvironment.AWS_SHARED_CREDENTIALS_FILE
880
+ ? {
881
+ credentialsFilepath: boundaryEnvironment.AWS_SHARED_CREDENTIALS_FILE,
882
+ }
883
+ : {}),
884
+ expectedAccountId: account,
885
+ expectedCapacity: {
886
+ max: Number.parseFloat(profile.aurora.max),
887
+ min: Number.parseFloat(profile.aurora.min),
888
+ },
889
+ expectedRegion: region,
890
+ expectedRoleArn,
891
+ now: now(),
892
+ priorDiscovery: productionDatabaseBefore,
893
+ ...(transport ? { profile: transport.profile } : {}),
894
+ requireExisting: true,
895
+ });
896
+ children.push({
897
+ completedAt: now().toISOString(),
898
+ outcome: 'succeeded',
899
+ project,
900
+ startedAt: postflightStartedAt,
901
+ target: 'production-database-postflight',
902
+ });
903
+ }
904
+ catch (error) {
905
+ children.push({
906
+ completedAt: now().toISOString(),
907
+ outcome: 'failed',
908
+ project,
909
+ startedAt: postflightStartedAt,
910
+ target: 'production-database-postflight',
911
+ });
912
+ throw error;
913
+ }
914
+ }
915
+ migrations[project] = supportsTarget(descriptor, 'db-migrate')
916
+ ? 'completed-by-deploy-target'
917
+ : 'not-applicable';
918
+ if (supportsTarget(descriptor, 'smoke-deployed')) {
919
+ if (transport)
920
+ await transport.refresh();
921
+ const smokeTarget = readDeployedSmokeTarget(context.root, descriptor, options.stage);
922
+ assertDeploymentSourceUnchanged(context.root, source, run);
923
+ const smokeStartedAt = now().toISOString();
924
+ try {
925
+ await runNxTarget({
926
+ context,
927
+ environment: {
928
+ ...(transport
929
+ ? transport.childEnvironment((0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(process.env))
930
+ : (0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(process.env, {
931
+ preserveVerifiedGithubCredentials: true,
932
+ })),
933
+ AWS_EXPECTED_ACCOUNT_ID: account,
934
+ AWS_REGION: region,
935
+ EBK_NX_STAGE_LIFECYCLE: githubAuthority ? 'github' : 'local',
936
+ EBK_SOURCE_COMMIT: source.commit,
937
+ EBK_SMOKE_API_URL: smokeTarget.apiUrl,
938
+ EBK_SMOKE_AUTHORITY_VERSION: 'cognito-admin-cloudwatch-observe-v1',
939
+ EBK_SMOKE_CLIENT_ID: smokeTarget.clientId,
940
+ EBK_SMOKE_URL: smokeTarget.webUrl,
941
+ EBK_SMOKE_USER_POOL_ID: smokeTarget.userPoolId,
942
+ EBK_STAGE: options.stage,
943
+ },
944
+ expectedRoleArn,
945
+ ...(transport ? { profile: transport.profile } : {}),
946
+ project,
947
+ region,
948
+ ...(dependencies.run ? { runSync: dependencies.run } : {}),
949
+ ...(dependencies.spawnChild
950
+ ? { spawnChild: dependencies.spawnChild }
951
+ : {}),
952
+ stage: options.stage,
953
+ target: 'smoke-deployed',
954
+ });
955
+ children.push({
956
+ completedAt: now().toISOString(),
957
+ outcome: 'succeeded',
958
+ project,
959
+ startedAt: smokeStartedAt,
960
+ target: 'smoke-deployed',
961
+ });
962
+ smoke[project] = 'passed';
963
+ }
964
+ catch (error) {
965
+ children.push({
966
+ completedAt: now().toISOString(),
967
+ outcome: 'failed',
968
+ project,
969
+ startedAt: smokeStartedAt,
970
+ target: 'smoke-deployed',
971
+ });
972
+ throw error;
973
+ }
859
974
  }
860
- mutatedProjects.push(project);
861
- result = 'replan-required';
862
- replanRequiredProject = orderedProjects[materialProjectIndex + 1] ?? null;
863
- throw new Error(replanRequiredProject
864
- ? `Local deployment applied ${project} and requires a new complete-stage plan before ${replanRequiredProject}. Rerun and acknowledge the new current plan before any further mutation.`
865
- : `Local deployment applied ${project} and requires a new complete-stage plan before accepting stage convergence. Rerun and require every current project to be a semantic no-op.`);
975
+ else {
976
+ smoke[project] = 'not-applicable';
977
+ }
978
+ }
979
+ if (options.stage === 'production') {
980
+ if (!productionDatabaseAfterFoundation) {
981
+ throw new Error('Production Foundation database postflight was not completed before final deployment acceptance.');
982
+ }
983
+ if (transport)
984
+ await transport.refresh();
985
+ const boundaryEnvironment = transport
986
+ ? transport.childEnvironment((0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(process.env))
987
+ : (0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(process.env, {
988
+ preserveVerifiedGithubCredentials: true,
989
+ });
990
+ const finalPostflightStartedAt = now().toISOString();
991
+ const foundationProject = orderedProjects[0] ?? 'foundation';
992
+ const profile = (0, index_js_2.productionFoundationProfileContract)(policy.productionFoundation.profile).contract;
993
+ try {
994
+ await (dependencies.attestProductionDatabase ??
995
+ production_database_safeguard_js_1.attestProductionDatabaseLifecycle)({
996
+ ...(boundaryEnvironment.AWS_CONFIG_FILE
997
+ ? { configFilepath: boundaryEnvironment.AWS_CONFIG_FILE }
998
+ : {}),
999
+ ...(boundaryEnvironment.AWS_SHARED_CREDENTIALS_FILE
1000
+ ? {
1001
+ credentialsFilepath: boundaryEnvironment.AWS_SHARED_CREDENTIALS_FILE,
1002
+ }
1003
+ : {}),
1004
+ expectedAccountId: account,
1005
+ expectedCapacity: {
1006
+ max: Number.parseFloat(profile.aurora.max),
1007
+ min: Number.parseFloat(profile.aurora.min),
1008
+ },
1009
+ expectedRegion: region,
1010
+ expectedRoleArn,
1011
+ now: now(),
1012
+ priorDiscovery: productionDatabaseAfterFoundation,
1013
+ ...(transport ? { profile: transport.profile } : {}),
1014
+ requireExisting: true,
1015
+ });
1016
+ children.push({
1017
+ completedAt: now().toISOString(),
1018
+ outcome: 'succeeded',
1019
+ project: foundationProject,
1020
+ startedAt: finalPostflightStartedAt,
1021
+ target: 'production-database-final-postflight',
1022
+ });
1023
+ }
1024
+ catch (error) {
1025
+ children.push({
1026
+ completedAt: now().toISOString(),
1027
+ outcome: 'failed',
1028
+ project: foundationProject,
1029
+ startedAt: finalPostflightStartedAt,
1030
+ target: 'production-database-final-postflight',
1031
+ });
1032
+ throw error;
1033
+ }
1034
+ }
1035
+ assertDeploymentSourceUnchanged(context.root, source, run);
1036
+ if (transport) {
1037
+ transport.cleanup();
1038
+ transport = undefined;
866
1039
  }
867
- result = 'succeeded';
1040
+ outcome = 'succeeded';
1041
+ retain();
1042
+ devkit_1.logger.info(`${githubAuthority ? 'Protected GitHub' : 'Local'} ${options.stage} deployment completed through native SST for ${children.filter(({ target }) => target === 'deploy').length} project(s).`);
868
1043
  return { success: true };
869
1044
  }
870
- finally {
871
- retainEvidence(context.root, {
872
- account: expectedAccount,
873
- action,
874
- actorArnDigest: digest(arn),
875
- backendAuthorityDigest: backend?.authorityDigest ?? null,
876
- backendCreatedApplications: backend
877
- ? [...backend.createdApplications]
878
- : [],
879
- completedAt: (dependencies.now ?? (() => new Date()))().toISOString(),
880
- deployNoOpProjects,
881
- kind: 'ebk-local-deployment-evidence',
882
- mutatedProjects,
883
- orderedProjects,
884
- operatorAcknowledgementDigest,
885
- planDigests,
886
- planSummaryDigest,
887
- phase,
888
- profile: policy.deploymentProfile,
889
- replanRequiredProject,
890
- refreshNoOpProjects,
891
- region,
892
- result,
893
- schemaVersion: 1,
894
- sourceCommit,
895
- sourceDigest: digest(`${sourceCommit}:${sourceTree}`),
896
- stage: options.stage,
897
- startedAt,
898
- });
899
- }
900
- }
901
- const executor = async (options, context) => {
902
- try {
903
- return await runLocalDeploy(options, context);
904
- }
905
1045
  catch (error) {
906
1046
  devkit_1.logger.error(error instanceof Error ? error.message : String(error));
1047
+ if (transport) {
1048
+ try {
1049
+ transport.cleanup();
1050
+ transport = undefined;
1051
+ }
1052
+ catch {
1053
+ devkit_1.logger.error('Local federated credential transport cleanup failed; deployment acceptance is blocked.');
1054
+ }
1055
+ }
1056
+ retain();
907
1057
  return { success: false };
908
1058
  }
909
- };
1059
+ }
1060
+ const executor = (options, context) => runLocalDeploy(options, context);
910
1061
  exports.default = executor;
911
1062
  //# sourceMappingURL=local-deploy.js.map