@intentius/chant 0.124.28 → 0.124.29

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 (395) hide show
  1. package/dist/build-params.d.ts +34 -1
  2. package/dist/build-params.d.ts.map +1 -1
  3. package/dist/build-params.js +31 -1
  4. package/dist/change-set-graph.d.ts +8 -1
  5. package/dist/change-set-graph.d.ts.map +1 -1
  6. package/dist/change-set-graph.js +4 -2
  7. package/dist/change-set.d.ts +10 -0
  8. package/dist/change-set.d.ts.map +1 -1
  9. package/dist/change-set.js +4 -3
  10. package/dist/cli/build-params-cli.d.ts +7 -1
  11. package/dist/cli/build-params-cli.d.ts.map +1 -1
  12. package/dist/cli/build-params-cli.js +17 -8
  13. package/dist/cli/commands/build.d.ts.map +1 -1
  14. package/dist/cli/commands/build.js +6 -3
  15. package/dist/cli/handlers/approve.d.ts.map +1 -1
  16. package/dist/cli/handlers/approve.js +45 -0
  17. package/dist/cli/handlers/build.d.ts.map +1 -1
  18. package/dist/cli/handlers/build.js +15 -4
  19. package/dist/cli/handlers/fan-out.d.ts +9 -0
  20. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  21. package/dist/cli/handlers/fan-out.js +40 -3
  22. package/dist/cli/handlers/lifecycle-gc.d.ts +19 -0
  23. package/dist/cli/handlers/lifecycle-gc.d.ts.map +1 -0
  24. package/dist/cli/handlers/lifecycle-gc.js +143 -0
  25. package/dist/cli/handlers/pr.d.ts +11 -3
  26. package/dist/cli/handlers/pr.d.ts.map +1 -1
  27. package/dist/cli/handlers/pr.js +134 -26
  28. package/dist/cli/handlers/promote.d.ts +7 -0
  29. package/dist/cli/handlers/promote.d.ts.map +1 -1
  30. package/dist/cli/handlers/promote.js +210 -33
  31. package/dist/cli/handlers/release.d.ts +62 -5
  32. package/dist/cli/handlers/release.d.ts.map +1 -1
  33. package/dist/cli/handlers/release.js +414 -28
  34. package/dist/cli/handlers/run.d.ts +11 -1
  35. package/dist/cli/handlers/run.d.ts.map +1 -1
  36. package/dist/cli/handlers/run.js +67 -6
  37. package/dist/cli/main.d.ts.map +1 -1
  38. package/dist/cli/main.js +61 -10
  39. package/dist/cli/parse-args.d.ts.map +1 -1
  40. package/dist/cli/parse-args.js +24 -2
  41. package/dist/cli/registry.d.ts +15 -3
  42. package/dist/cli/registry.d.ts.map +1 -1
  43. package/dist/cli/run-events.d.ts +3 -2
  44. package/dist/cli/run-events.d.ts.map +1 -1
  45. package/dist/cli/run-events.js +5 -3
  46. package/dist/components/auto-release.d.ts +2 -0
  47. package/dist/components/auto-release.d.ts.map +1 -1
  48. package/dist/components/auto-release.js +1 -0
  49. package/dist/components/capability.d.ts +9 -0
  50. package/dist/components/capability.d.ts.map +1 -1
  51. package/dist/components/driver.d.ts +8 -0
  52. package/dist/components/driver.d.ts.map +1 -1
  53. package/dist/components/driver.js +35 -0
  54. package/dist/components/fan-out-inputs.d.ts +69 -0
  55. package/dist/components/fan-out-inputs.d.ts.map +1 -0
  56. package/dist/components/fan-out-inputs.js +169 -0
  57. package/dist/components/fan-out-record.d.ts +13 -0
  58. package/dist/components/fan-out-record.d.ts.map +1 -1
  59. package/dist/components/fan-out-record.js +16 -0
  60. package/dist/components/fan-out-run.d.ts.map +1 -1
  61. package/dist/components/fan-out-run.js +50 -0
  62. package/dist/components/fan-out.d.ts +4 -0
  63. package/dist/components/fan-out.d.ts.map +1 -1
  64. package/dist/components/pr-pipeline.d.ts +25 -0
  65. package/dist/components/pr-pipeline.d.ts.map +1 -1
  66. package/dist/components/pr-pipeline.js +22 -4
  67. package/dist/components/promote.d.ts +23 -0
  68. package/dist/components/promote.d.ts.map +1 -1
  69. package/dist/components/promote.js +60 -2
  70. package/dist/components/release-compile.d.ts +9 -0
  71. package/dist/components/release-compile.d.ts.map +1 -1
  72. package/dist/components/release-compile.js +9 -3
  73. package/dist/components/release-gate.d.ts +11 -1
  74. package/dist/components/release-gate.d.ts.map +1 -1
  75. package/dist/components/release-gate.js +10 -4
  76. package/dist/components/wave-plan.d.ts +5 -0
  77. package/dist/components/wave-plan.d.ts.map +1 -1
  78. package/dist/components/wave-plan.js +10 -2
  79. package/dist/config.d.ts +38 -0
  80. package/dist/config.d.ts.map +1 -1
  81. package/dist/config.js +2 -0
  82. package/dist/flag-params.d.ts +54 -0
  83. package/dist/flag-params.d.ts.map +1 -0
  84. package/dist/flag-params.js +130 -0
  85. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
  86. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
  87. package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
  88. package/dist/lifecycle/change-set-store.d.ts +2 -1
  89. package/dist/lifecycle/change-set-store.d.ts.map +1 -1
  90. package/dist/lifecycle/change-set-store.js +2 -1
  91. package/dist/lifecycle/delivery-metrics.d.ts +139 -0
  92. package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
  93. package/dist/lifecycle/delivery-metrics.js +145 -0
  94. package/dist/lifecycle/deploy-outcome.d.ts +81 -0
  95. package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
  96. package/dist/lifecycle/deploy-outcome.js +101 -0
  97. package/dist/lifecycle/gate-change-set.d.ts +10 -1
  98. package/dist/lifecycle/gate-change-set.d.ts.map +1 -1
  99. package/dist/lifecycle/gate-change-set.js +35 -3
  100. package/dist/lifecycle/gate-ledger.d.ts +2 -3
  101. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  102. package/dist/lifecycle/git.d.ts +30 -1
  103. package/dist/lifecycle/git.d.ts.map +1 -1
  104. package/dist/lifecycle/git.js +31 -5
  105. package/dist/lifecycle/index.d.ts +4 -0
  106. package/dist/lifecycle/index.d.ts.map +1 -1
  107. package/dist/lifecycle/index.js +4 -0
  108. package/dist/lifecycle/mixed-environment.d.ts +95 -0
  109. package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
  110. package/dist/lifecycle/mixed-environment.js +154 -0
  111. package/dist/lifecycle/publish-attachments.d.ts +79 -0
  112. package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
  113. package/dist/lifecycle/publish-attachments.js +179 -0
  114. package/dist/lifecycle/publish-credentials.d.ts +37 -0
  115. package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
  116. package/dist/lifecycle/publish-credentials.js +69 -0
  117. package/dist/lifecycle/publish-declaration.d.ts +97 -0
  118. package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
  119. package/dist/lifecycle/publish-declaration.js +83 -0
  120. package/dist/lifecycle/publish-forge.d.ts +53 -0
  121. package/dist/lifecycle/publish-forge.d.ts.map +1 -0
  122. package/dist/lifecycle/publish-forge.js +298 -0
  123. package/dist/lifecycle/publish-npm.d.ts +61 -0
  124. package/dist/lifecycle/publish-npm.d.ts.map +1 -0
  125. package/dist/lifecycle/publish-npm.js +242 -0
  126. package/dist/lifecycle/publish-oci.d.ts +64 -0
  127. package/dist/lifecycle/publish-oci.d.ts.map +1 -0
  128. package/dist/lifecycle/publish-oci.js +383 -0
  129. package/dist/lifecycle/publish-record.d.ts +66 -0
  130. package/dist/lifecycle/publish-record.d.ts.map +1 -0
  131. package/dist/lifecycle/publish-record.js +84 -0
  132. package/dist/lifecycle/publish-targets.d.ts +45 -0
  133. package/dist/lifecycle/publish-targets.d.ts.map +1 -0
  134. package/dist/lifecycle/publish-targets.js +85 -0
  135. package/dist/lifecycle/release-cut.d.ts +48 -1
  136. package/dist/lifecycle/release-cut.d.ts.map +1 -1
  137. package/dist/lifecycle/release-cut.js +116 -23
  138. package/dist/lifecycle/release-join.d.ts +95 -0
  139. package/dist/lifecycle/release-join.d.ts.map +1 -0
  140. package/dist/lifecycle/release-join.js +139 -0
  141. package/dist/lifecycle/release-publish.d.ts +312 -0
  142. package/dist/lifecycle/release-publish.d.ts.map +1 -0
  143. package/dist/lifecycle/release-publish.js +335 -0
  144. package/dist/lifecycle/release-revocation.d.ts +80 -0
  145. package/dist/lifecycle/release-revocation.d.ts.map +1 -0
  146. package/dist/lifecycle/release-revocation.js +123 -0
  147. package/dist/lifecycle/release-store.d.ts +12 -2
  148. package/dist/lifecycle/release-store.d.ts.map +1 -1
  149. package/dist/lifecycle/release-store.js +4 -2
  150. package/dist/lifecycle/release-verify.d.ts +61 -0
  151. package/dist/lifecycle/release-verify.d.ts.map +1 -0
  152. package/dist/lifecycle/release-verify.js +92 -0
  153. package/dist/lifecycle/release-workstation.d.ts +91 -0
  154. package/dist/lifecycle/release-workstation.d.ts.map +1 -0
  155. package/dist/lifecycle/release-workstation.js +180 -0
  156. package/dist/lifecycle/retention-policy.d.ts +45 -0
  157. package/dist/lifecycle/retention-policy.d.ts.map +1 -0
  158. package/dist/lifecycle/retention-policy.js +43 -0
  159. package/dist/lifecycle/retention.d.ts +185 -0
  160. package/dist/lifecycle/retention.d.ts.map +1 -0
  161. package/dist/lifecycle/retention.js +400 -0
  162. package/dist/plan-summary.d.ts.map +1 -1
  163. package/dist/plan-summary.js +8 -2
  164. package/dist/pr-loop.d.ts +7 -0
  165. package/dist/pr-loop.d.ts.map +1 -1
  166. package/dist/pr-loop.js +23 -4
  167. package/dist/pr-report-url.d.ts +82 -0
  168. package/dist/pr-report-url.d.ts.map +1 -0
  169. package/dist/pr-report-url.js +174 -0
  170. package/dist/provenance.d.ts +2 -2
  171. package/dist/provenance.d.ts.map +1 -1
  172. package/dist/run-event-store.d.ts +29 -2
  173. package/dist/run-event-store.d.ts.map +1 -1
  174. package/dist/run-event-store.js +50 -3
  175. package/dist/run-event-stream.d.ts +2 -1
  176. package/dist/run-event-stream.d.ts.map +1 -1
  177. package/dist/run-event-stream.js +5 -1
  178. package/dist/run-event.d.ts +19 -1
  179. package/dist/run-event.d.ts.map +1 -1
  180. package/dist/run-event.js +2 -0
  181. package/dist/wave-runtime.mjs +260 -229
  182. package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
  183. package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
  184. package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
  185. package/dist/workspace/change-set.schema.json +5 -1
  186. package/dist/workspace/check.schema.json +4 -2
  187. package/dist/workspace/checks/records.d.ts +9 -1
  188. package/dist/workspace/checks/records.d.ts.map +1 -1
  189. package/dist/workspace/checks/records.js +35 -1
  190. package/dist/workspace/checks.d.ts +1 -0
  191. package/dist/workspace/checks.d.ts.map +1 -1
  192. package/dist/workspace/checks.js +2 -1
  193. package/dist/workspace/ci-green.d.ts +8 -0
  194. package/dist/workspace/ci-green.d.ts.map +1 -1
  195. package/dist/workspace/ci-green.js +10 -3
  196. package/dist/workspace/declaration.d.ts +16 -0
  197. package/dist/workspace/declaration.d.ts.map +1 -1
  198. package/dist/workspace/declaration.js +26 -0
  199. package/dist/workspace/declaration.schema.json +64 -0
  200. package/dist/workspace/declared-kinds.d.ts +16 -0
  201. package/dist/workspace/declared-kinds.d.ts.map +1 -1
  202. package/dist/workspace/declared-kinds.js +25 -0
  203. package/dist/workspace/delivery-metrics.d.ts +91 -0
  204. package/dist/workspace/delivery-metrics.d.ts.map +1 -0
  205. package/dist/workspace/delivery-metrics.js +217 -0
  206. package/dist/workspace/delivery-metrics.schema.json +519 -0
  207. package/dist/workspace/flags-read.d.ts +37 -0
  208. package/dist/workspace/flags-read.d.ts.map +1 -0
  209. package/dist/workspace/flags-read.js +103 -0
  210. package/dist/workspace/flags.d.ts +100 -0
  211. package/dist/workspace/flags.d.ts.map +1 -0
  212. package/dist/workspace/flags.js +98 -0
  213. package/dist/workspace/graph-cli.js +1 -1
  214. package/dist/workspace/intent-cli.d.ts +11 -2
  215. package/dist/workspace/intent-cli.d.ts.map +1 -1
  216. package/dist/workspace/intent-cli.js +54 -5
  217. package/dist/workspace/intent-trace.d.ts +219 -0
  218. package/dist/workspace/intent-trace.d.ts.map +1 -0
  219. package/dist/workspace/intent-trace.js +314 -0
  220. package/dist/workspace/intent.d.ts +30 -6
  221. package/dist/workspace/intent.d.ts.map +1 -1
  222. package/dist/workspace/intent.js +141 -21
  223. package/dist/workspace/intent.schema.json +702 -6
  224. package/dist/workspace/overlap.d.ts +146 -0
  225. package/dist/workspace/overlap.d.ts.map +1 -0
  226. package/dist/workspace/overlap.js +276 -0
  227. package/dist/workspace/reason-codes.d.ts +2 -0
  228. package/dist/workspace/reason-codes.d.ts.map +1 -1
  229. package/dist/workspace/reason-codes.js +3 -0
  230. package/dist/workspace/records.d.ts +15 -2
  231. package/dist/workspace/records.d.ts.map +1 -1
  232. package/dist/workspace/records.js +32 -0
  233. package/dist/workspace/records.schema.json +21 -2
  234. package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  235. package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
  236. package/dist/workspace/releases.d.ts +9 -0
  237. package/dist/workspace/releases.d.ts.map +1 -1
  238. package/dist/workspace/releases.js +9 -0
  239. package/dist/workspace/releases.schema.json +123 -1
  240. package/dist/workspace/run-event.schema.json +10 -3
  241. package/dist/workspace/status-gates.d.ts +8 -0
  242. package/dist/workspace/status-gates.d.ts.map +1 -1
  243. package/dist/workspace/status.d.ts +78 -0
  244. package/dist/workspace/status.d.ts.map +1 -1
  245. package/dist/workspace/status.js +170 -3
  246. package/dist/workspace/status.schema.json +198 -1
  247. package/dist/workspace/wip.d.ts.map +1 -1
  248. package/dist/workspace/wip.js +11 -3
  249. package/package.json +1 -1
  250. package/src/build-params.test.ts +46 -0
  251. package/src/build-params.ts +55 -1
  252. package/src/change-set-graph.ts +10 -2
  253. package/src/change-set.ts +14 -3
  254. package/src/cli/build-params-cli.ts +22 -9
  255. package/src/cli/commands/build.ts +6 -3
  256. package/src/cli/handlers/approve-release.test.ts +5 -1
  257. package/src/cli/handlers/approve-revoked.test.ts +155 -0
  258. package/src/cli/handlers/approve.ts +47 -0
  259. package/src/cli/handlers/build.test.ts +15 -0
  260. package/src/cli/handlers/build.ts +17 -4
  261. package/src/cli/handlers/fan-out.test.ts +180 -1
  262. package/src/cli/handlers/fan-out.ts +38 -3
  263. package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
  264. package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
  265. package/src/cli/handlers/lifecycle-gc.ts +146 -0
  266. package/src/cli/handlers/pr.test.ts +148 -1
  267. package/src/cli/handlers/pr.ts +139 -28
  268. package/src/cli/handlers/promote.test.ts +280 -2
  269. package/src/cli/handlers/promote.ts +210 -34
  270. package/src/cli/handlers/release-implicit.e2e.test.ts +12 -4
  271. package/src/cli/handlers/release-join.e2e.test.ts +185 -0
  272. package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
  273. package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
  274. package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
  275. package/src/cli/handlers/release.e2e.test.ts +146 -32
  276. package/src/cli/handlers/release.ts +415 -28
  277. package/src/cli/handlers/run-auto-release.test.ts +9 -0
  278. package/src/cli/handlers/run-release.test.ts +55 -0
  279. package/src/cli/handlers/run.ts +69 -5
  280. package/src/cli/main.test.ts +1 -0
  281. package/src/cli/main.ts +61 -10
  282. package/src/cli/parse-args.ts +19 -2
  283. package/src/cli/registry.ts +15 -3
  284. package/src/cli/run-events.ts +5 -2
  285. package/src/components/auto-release.test.ts +9 -0
  286. package/src/components/auto-release.ts +3 -0
  287. package/src/components/capability.ts +9 -0
  288. package/src/components/driver.test.ts +24 -0
  289. package/src/components/driver.ts +36 -0
  290. package/src/components/fan-out-inputs.test.ts +85 -0
  291. package/src/components/fan-out-inputs.ts +192 -0
  292. package/src/components/fan-out-record.test.ts +10 -0
  293. package/src/components/fan-out-record.ts +19 -0
  294. package/src/components/fan-out-run.ts +48 -0
  295. package/src/components/fan-out.ts +4 -0
  296. package/src/components/pr-pipeline.ts +35 -4
  297. package/src/components/promote.ts +68 -2
  298. package/src/components/release-compile.ts +17 -3
  299. package/src/components/release-gate.ts +18 -4
  300. package/src/components/wave-plan.ts +15 -2
  301. package/src/config.ts +8 -0
  302. package/src/flag-params.test.ts +144 -0
  303. package/src/flag-params.ts +134 -0
  304. package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
  305. package/src/lifecycle/change-set-store.ts +2 -1
  306. package/src/lifecycle/delivery-metrics.ts +263 -0
  307. package/src/lifecycle/deploy-outcome.ts +147 -0
  308. package/src/lifecycle/gate-change-set.ts +37 -6
  309. package/src/lifecycle/gate-ledger.ts +2 -2
  310. package/src/lifecycle/git.ts +61 -8
  311. package/src/lifecycle/index.ts +4 -0
  312. package/src/lifecycle/mixed-environment.test.ts +97 -0
  313. package/src/lifecycle/mixed-environment.ts +204 -0
  314. package/src/lifecycle/publish-attachments.ts +196 -0
  315. package/src/lifecycle/publish-credentials.ts +84 -0
  316. package/src/lifecycle/publish-declaration.ts +95 -0
  317. package/src/lifecycle/publish-forge.ts +325 -0
  318. package/src/lifecycle/publish-npm.ts +259 -0
  319. package/src/lifecycle/publish-oci.ts +396 -0
  320. package/src/lifecycle/publish-record.ts +113 -0
  321. package/src/lifecycle/publish-resume.test.ts +168 -0
  322. package/src/lifecycle/publish-targets.test.ts +494 -0
  323. package/src/lifecycle/publish-targets.ts +100 -0
  324. package/src/lifecycle/release-cut.test.ts +74 -2
  325. package/src/lifecycle/release-cut.ts +158 -22
  326. package/src/lifecycle/release-flag.test.ts +112 -0
  327. package/src/lifecycle/release-join.test.ts +68 -0
  328. package/src/lifecycle/release-join.ts +182 -0
  329. package/src/lifecycle/release-publish.test.ts +225 -0
  330. package/src/lifecycle/release-publish.ts +568 -0
  331. package/src/lifecycle/release-revocation.ts +147 -0
  332. package/src/lifecycle/release-store.ts +15 -4
  333. package/src/lifecycle/release-verify.test.ts +110 -0
  334. package/src/lifecycle/release-verify.ts +120 -0
  335. package/src/lifecycle/release-workstation.test.ts +204 -0
  336. package/src/lifecycle/release-workstation.ts +216 -0
  337. package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
  338. package/src/lifecycle/retention-policy.ts +63 -0
  339. package/src/lifecycle/retention.test.ts +363 -0
  340. package/src/lifecycle/retention.ts +508 -0
  341. package/src/plan-summary.test.ts +20 -0
  342. package/src/plan-summary.ts +8 -1
  343. package/src/pr-loop.ts +25 -4
  344. package/src/pr-report-url.test.ts +131 -0
  345. package/src/pr-report-url.ts +209 -0
  346. package/src/provenance.ts +2 -2
  347. package/src/run-event-store.ts +63 -3
  348. package/src/run-event-stream.ts +4 -1
  349. package/src/run-event.ts +21 -1
  350. package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
  351. package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
  352. package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
  353. package/src/workspace/change-set.schema.json +5 -1
  354. package/src/workspace/check.schema.json +4 -2
  355. package/src/workspace/checks/records.ts +35 -1
  356. package/src/workspace/checks.ts +2 -1
  357. package/src/workspace/ci-green.ts +14 -3
  358. package/src/workspace/declaration.schema.json +64 -0
  359. package/src/workspace/declaration.test.ts +28 -0
  360. package/src/workspace/declaration.ts +38 -0
  361. package/src/workspace/declared-kinds.ts +40 -1
  362. package/src/workspace/delivery-metrics.schema.json +519 -0
  363. package/src/workspace/delivery-metrics.test.ts +236 -0
  364. package/src/workspace/delivery-metrics.ts +286 -0
  365. package/src/workspace/flags-read.ts +121 -0
  366. package/src/workspace/flags.test.ts +141 -0
  367. package/src/workspace/flags.ts +157 -0
  368. package/src/workspace/graph-cli.ts +1 -1
  369. package/src/workspace/intent-cli.ts +46 -5
  370. package/src/workspace/intent-trace.test.ts +173 -0
  371. package/src/workspace/intent-trace.ts +484 -0
  372. package/src/workspace/intent.schema.json +702 -6
  373. package/src/workspace/intent.ts +148 -25
  374. package/src/workspace/older-records.test.ts +412 -0
  375. package/src/workspace/overlap.test.ts +165 -0
  376. package/src/workspace/overlap.ts +340 -0
  377. package/src/workspace/read-contract.e2e.test.ts +2 -1
  378. package/src/workspace/reason-codes.test.ts +5 -0
  379. package/src/workspace/reason-codes.ts +3 -0
  380. package/src/workspace/record-assets.test.ts +2 -1
  381. package/src/workspace/records-contract.e2e.test.ts +2 -1
  382. package/src/workspace/records.schema.json +21 -2
  383. package/src/workspace/records.ts +36 -2
  384. package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  385. package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
  386. package/src/workspace/releases.schema.json +123 -1
  387. package/src/workspace/releases.ts +11 -0
  388. package/src/workspace/run-event.schema.json +10 -3
  389. package/src/workspace/status-contract.test.ts +29 -0
  390. package/src/workspace/status-gates.ts +8 -0
  391. package/src/workspace/status-mixed.test.ts +152 -0
  392. package/src/workspace/status-revoked.test.ts +153 -0
  393. package/src/workspace/status.schema.json +198 -1
  394. package/src/workspace/status.ts +254 -3
  395. package/src/workspace/wip.ts +11 -2
@@ -28,7 +28,7 @@ import { spawn, spawnSync } from "node:child_process";
28
28
  import { existsSync, readFileSync, statSync } from "node:fs";
29
29
  import { isAbsolute, relative, resolve } from "node:path";
30
30
  import { loadChantConfig, type ChantConfig } from "../config";
31
- import { resolveCliBuildParams } from "../cli/build-params-cli";
31
+ import { resolveFlaggedBuildParams } from "../flag-params";
32
32
  import { discoverComponents } from "./discover";
33
33
  import { withoutDisabled } from "./cli-support";
34
34
  import { applyConfigDefaults } from "./config-defaults";
@@ -72,6 +72,13 @@ export interface CompiledTarget {
72
72
  components: string[];
73
73
  /** Each component's build-time steps, as declared for this environment, in composition order. */
74
74
  buildSteps: Record<string, unknown[]>;
75
+ /**
76
+ * Each component's publish steps (`PUBLISH_STEP_KINDS`), as declared for
77
+ * this environment, in composition order (#4027). The cut's dry run shows
78
+ * them as what each environment's deploy publishes. Absent from a compiler
79
+ * that does not report them.
80
+ */
81
+ publishSteps?: Record<string, unknown[]>;
75
82
  /** For each component, the files its build steps read, relative to the member's directory. Used to tell whether an artifact's source changed. */
76
83
  buildInputs: Record<string, string[]>;
77
84
  /** For each component, the file that declares it, relative to the member's directory. */
@@ -132,6 +139,11 @@ export function buildStepsOf(component: DriverComponent): DriverStep[] {
132
139
  return stepsOf(component.deploy).map((s) => s.step).filter((s) => BUILD_STEP_KINDS.includes(s.kind));
133
140
  }
134
141
 
142
+ /** A component's publish steps ({@link PUBLISH_STEP_KINDS}), each as declared, in composition order. */
143
+ export function publishStepsOf(component: DriverComponent): DriverStep[] {
144
+ return stepsOf(component.deploy).map((s) => s.step).filter((s) => PUBLISH_STEP_KINDS.includes(s.kind));
145
+ }
146
+
135
147
  /** The literal paths a component's archive steps read: `docker-build`'s `context` and `dockerfile`, `zip-package`'s `source`, `jvm-build`'s `path`. */
136
148
  export function buildInputPaths(component: DriverComponent): string[] {
137
149
  const out: string[] = [];
@@ -201,10 +213,11 @@ async function loadComponents(
201
213
  env: string,
202
214
  ): Promise<{ config: ChantConfig; params: ReleaseParam[]; declared: number; components: Array<{ component: DriverComponent; file: string }> }> {
203
215
  const { config } = await loadChantConfig(inputs.projectDir).catch(() => ({ config: {} as ChantConfig }));
204
- const resolved = resolveCliBuildParams(config.buildParams, {
216
+ // #4024: a workspace member's flag records back their parameters, so a flag's value is a param with source flag:<name>.
217
+ const resolved = await resolveFlaggedBuildParams(inputs.projectDir, config.buildParams, {
205
218
  ...(inputs.params ? { cli: inputs.params } : {}),
206
219
  ...(inputs.paramsFile ? { paramsFile: inputs.paramsFile.replaceAll("{env}", env) } : {}),
207
- });
220
+ }, env);
208
221
  if (!resolved.success) throw new ReleaseCompileError(`build parameters for "${env}" did not resolve:\n${resolved.errors.join("\n")}`);
209
222
  const discovered = await discoverComponents(inputs.projectDir, { buildParams: resolved.provenance });
210
223
  if (discovered.errors.length > 0) throw new ReleaseCompileError(discovered.errors.map((e) => e.message).join("\n"));
@@ -267,6 +280,7 @@ export async function compileEnvironment(inputs: CompileInputs, env: string): Pr
267
280
  params,
268
281
  components: components.map((c) => c.component.name),
269
282
  buildSteps: Object.fromEntries(components.map(({ component }) => [component.name, buildStepsOf(component)])),
283
+ publishSteps: Object.fromEntries(components.map(({ component }) => [component.name, publishStepsOf(component)])),
270
284
  buildInputs: Object.fromEntries(components.map(({ component }) => [component.name, buildInputPaths(component)])),
271
285
  componentFiles: Object.fromEntries(components.map(({ component, file }) => [component.name, rel(file)])),
272
286
  templates,
@@ -47,14 +47,16 @@
47
47
  * environment has no deploy of the component). When they are all equal the
48
48
  * member has no entries. When the environment's deploy names no release, or
49
49
  * the release it names can't be read, the member has a hole: nothing says
50
- * what the environment runs, so the change can't be shown empty.
50
+ * what the environment runs, so the change can't be shown empty. So does a
51
+ * component a promote, rollback or redeploy left failed or gated with nothing
52
+ * recorded since (#4035): its record names what ran before that run.
51
53
  */
52
54
 
53
55
  import type { CapabilityRegistry } from "./capability";
54
56
  import { deployContext, resolveComponentGraph, resolveStepInput, type DriverComponent, type DriverGate, type DriverPhase, type DriverStep } from "./driver";
55
57
  import { componentPlanDigest } from "./gate-plan";
56
58
  import { gateName, RELEASE_GATE_OP } from "../op/gate-name";
57
- import { capabilitySteps, readComponentOutputs } from "./wave-plan";
59
+ import { capabilitySteps, planContext, readComponentOutputs } from "./wave-plan";
58
60
  import {
59
61
  composeChangeSet,
60
62
  isEmptyChangeSet,
@@ -111,6 +113,12 @@ export interface ReleaseChangeSetInput {
111
113
  registry: CapabilityRegistry;
112
114
  /** Per component, what `env` runs now. A component missing here has never been deployed to it. */
113
115
  current: ReadonlyMap<string, CurrentDeploy>;
116
+ /**
117
+ * Components a promote, rollback or redeploy left failed or gated in `env`
118
+ * with nothing recorded since (#4035), each with why. What they run is not
119
+ * known, so each is a hole whatever its ledger record says.
120
+ */
121
+ unsettled?: ReadonlyMap<string, string>;
114
122
  /** Outputs supplied by the caller (`--seed-outputs`). */
115
123
  seededOutputs?: Record<string, Record<string, unknown>>;
116
124
  /** The chant version to record in the document. */
@@ -151,6 +159,8 @@ export function chantComponentPart(input: {
151
159
  env: string;
152
160
  digest: string;
153
161
  current: CurrentDeploy | undefined;
162
+ /** Why the component is unsettled in `env` (#4035), when it is. */
163
+ unsettled?: string;
154
164
  }): ChangeSetPart {
155
165
  const { component, release, env, digest, current } = input;
156
166
  const name = component.name;
@@ -184,6 +194,10 @@ export function chantComponentPart(input: {
184
194
  },
185
195
  ],
186
196
  });
197
+ if (input.unsettled !== undefined) {
198
+ member.holes.push({ address: name, type: RELEASE_COMPONENT_ENTRY_TYPE, reason: input.unsettled });
199
+ return { member, entries: [] };
200
+ }
187
201
  if (!current) {
188
202
  return entry("create", [
189
203
  { path: "artifact", after: digest },
@@ -238,7 +252,7 @@ export async function planReleaseChangeSet(input: ReleaseChangeSetInput): Promis
238
252
  const digest = input.digests.get(name) ?? "";
239
253
  const plannable = capabilitySteps(component.deploy).filter((s) => registry.has(s.kind) && registry.resolve(s.kind).plan !== undefined);
240
254
  if (plannable.length === 0) {
241
- parts.push(chantComponentPart({ component, release, env, digest, current: input.current.get(name) }));
255
+ parts.push(chantComponentPart({ component, release, env, digest, current: input.current.get(name), unsettled: input.unsettled?.get(name) }));
242
256
  continue;
243
257
  }
244
258
  const ctx = deployContext({ env }, name);
@@ -251,7 +265,7 @@ export async function planReleaseChangeSet(input: ReleaseChangeSetInput): Promis
251
265
  }
252
266
  for (const step of plannable) {
253
267
  const { kind, ...rest } = step;
254
- const planned = await registry.resolve(kind).plan!(ctx, resolveStepInput(rest, {}, outputs) as never);
268
+ const planned = await registry.resolve(kind).plan!(planContext(ctx, rest, outputs), resolveStepInput(rest, {}, outputs) as never);
255
269
  parts.push(
256
270
  planned.changeSet ?? {
257
271
  member: {
@@ -19,7 +19,7 @@
19
19
  * `@<component>.publish.*`) resolves.
20
20
  */
21
21
 
22
- import { resolveStepInput, type DriverComponent, type DriverPhase, type DriverStep } from "./driver";
22
+ import { resolveStepInput, unresolvedWiringSources, type DriverComponent, type DriverPhase, type DriverStep } from "./driver";
23
23
  import { componentPlanDigest } from "./gate-plan";
24
24
  import type { CapabilityRegistry, DeployContext } from "./capability";
25
25
  import type { WaveMember } from "../gated-waves";
@@ -60,6 +60,19 @@ export function capabilitySteps(phases: readonly DriverPhase[]): DriverStep[] {
60
60
  return out;
61
61
  }
62
62
 
63
+ /**
64
+ * The context a step is planned with: `ctx`, plus the components whose
65
+ * outputs the step reads and could not read yet (`unresolvedInputs`, #4084).
66
+ */
67
+ export function planContext(
68
+ ctx: DeployContext,
69
+ stepInput: Record<string, unknown>,
70
+ componentOutputs: Record<string, Record<string, unknown>>,
71
+ ): DeployContext {
72
+ const unresolved = unresolvedWiringSources(stepInput, componentOutputs);
73
+ return unresolved.length > 0 ? { ...ctx, unresolvedInputs: unresolved } : ctx;
74
+ }
75
+
63
76
  /** Plan one component against the outputs earlier waves produced. Never throws: a failure comes back as `error`. */
64
77
  export async function planWaveComponent(
65
78
  component: DriverComponent,
@@ -76,7 +89,7 @@ export async function planWaveComponent(
76
89
  if (!capability.plan) continue;
77
90
  const { kind: _kind, ...rest } = step;
78
91
  const input = resolveStepInput(rest, {}, componentOutputs);
79
- const planned = await capability.plan(ctx, input as never);
92
+ const planned = await capability.plan(planContext(ctx, rest, componentOutputs), input as never);
80
93
  const member = { member: planned.member, planDigest: planned.planDigest };
81
94
  members.push(member);
82
95
  parts.push(planned.changeSet ?? emptyPart(member));
package/src/config.ts CHANGED
@@ -12,6 +12,7 @@ import { findProjectConfig } from "./project-root";
12
12
  import { evaluateProjectConfig } from "./config-sandbox";
13
13
  import { lexiconNames, pathLexiconRoot, registerLexiconDeclarations, type LexiconDeclaration } from "./lexicon-module";
14
14
  import { decideConfigSchema, type DecideConfig } from "./op/decide-config";
15
+ import { PublishDeclarationSchema, type PublishDeclaration } from "./lifecycle/publish-declaration";
15
16
 
16
17
  /**
17
18
  * One project-declared environment (chant #1166). Historically always a bare
@@ -169,6 +170,7 @@ export const ChantConfigSchema = z.object({
169
170
  ).optional(),
170
171
  release: z.object({
171
172
  autoRecord: z.boolean().optional(),
173
+ publish: z.array(PublishDeclarationSchema).optional(),
172
174
  }).optional(),
173
175
  // Strict: a misspelt key here would leave Ops on the local runtime with
174
176
  // nothing said (#2523).
@@ -382,6 +384,12 @@ export interface ChantConfig {
382
384
  release?: {
383
385
  /** Set false to disable auto-emitting a release record after a successful component deploy project-wide. The `--no-release-record` CLI flag overrides this per-invocation. */
384
386
  autoRecord?: boolean;
387
+ /**
388
+ * Where `chant release publish` publishes a cut release (#4047): npm
389
+ * packages, an image in an OCI registry, release assets on a forge. Run in
390
+ * the order given. See ./lifecycle/publish-declaration.ts.
391
+ */
392
+ publish?: PublishDeclaration[];
385
393
  };
386
394
 
387
395
  /** Defaults for `chant run` and its subcommands (#2523). */
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Build-time parameters from workspace flags (#4024): what a build for an
3
+ * environment resolves in a member with flags, and the precedence rule that a
4
+ * flag-backed parameter can't be overridden where the environment has a gate.
5
+ * On the fixture in `workspace/__fixtures__/flag-workspace.ts`, where member
6
+ * `shop`'s component declares a gate and member `blog`'s does not.
7
+ */
8
+
9
+ import { writeFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { afterAll, beforeAll, describe, expect, test, vi } from "vitest";
12
+ import { buildCommand } from "./cli/commands/build";
13
+ import type { Serializer } from "./serializer";
14
+ import type { BuildParamsConfig } from "./build-params";
15
+ import { loadBuildParamFlags, resolveFlaggedBuildParams } from "./flag-params";
16
+ import { cleanScratch, scratchDir } from "./workspace/__fixtures__/contract-repo";
17
+ import { flagWorkspace } from "./workspace/__fixtures__/flag-workspace";
18
+
19
+ const SHOP: BuildParamsConfig = { newCheckout: { type: "boolean", default: false }, tier: { type: "string", default: "small" } };
20
+ const BLOG: BuildParamsConfig = { banner: { type: "boolean", default: false } };
21
+
22
+ let root: string;
23
+ beforeAll(() => {
24
+ root = flagWorkspace();
25
+ });
26
+ afterAll(cleanScratch);
27
+
28
+ const quiet = { echo: false } as const;
29
+
30
+ describe("loadBuildParamFlags", () => {
31
+ test("outside a workspace, and without an environment, there are no flags", async () => {
32
+ const plain = scratchDir("chant-flags-plain-");
33
+ expect(await loadBuildParamFlags(plain, "prod")).toEqual({ flags: undefined, errors: [] });
34
+ expect(await loadBuildParamFlags(join(root, "shop"), undefined)).toEqual({ flags: undefined, errors: [] });
35
+ });
36
+
37
+ test("in a member, each flag-backed parameter with the flag's value for the environment", async () => {
38
+ expect(await loadBuildParamFlags(join(root, "shop"), "dev")).toEqual({
39
+ flags: { env: "dev", byParam: { newCheckout: { flag: "new-checkout", value: true } } },
40
+ errors: [],
41
+ });
42
+ });
43
+
44
+ test("a flag kind that can't be read stops the build rather than building without its flags", async () => {
45
+ const dir = flagWorkspace({ "flags/flag.schema.json": "{ not json" });
46
+ const loaded = await loadBuildParamFlags(join(dir, "shop"), "prod");
47
+ expect(loaded.flags).toBeUndefined();
48
+ expect(loaded.errors[0]).toMatch(/the workspace's flag records can't be read/);
49
+ });
50
+ });
51
+
52
+ describe("resolveFlaggedBuildParams", () => {
53
+ test("records the flag as the parameter's source, per environment", async () => {
54
+ const prod = await resolveFlaggedBuildParams(join(root, "shop"), SHOP, quiet, "prod");
55
+ expect(prod).toEqual({
56
+ success: true,
57
+ errors: [],
58
+ provenance: [
59
+ { name: "newCheckout", value: false, source: "flag:new-checkout" },
60
+ { name: "tier", value: "small", source: "default" },
61
+ ],
62
+ });
63
+ const dev = await resolveFlaggedBuildParams(join(root, "shop"), SHOP, quiet, "dev");
64
+ expect(dev.provenance[0]).toEqual({ name: "newCheckout", value: true, source: "flag:new-checkout" });
65
+ // staging: the record gives no value, so the declared default applies.
66
+ expect((await resolveFlaggedBuildParams(join(root, "shop"), SHOP, quiet, "staging")).provenance[0]).toEqual({ name: "newCheckout", value: false, source: "default" });
67
+ });
68
+
69
+ test("--param can't override a flag in an environment with a gate: the build refuses", async () => {
70
+ const refused = await resolveFlaggedBuildParams(join(root, "shop"), SHOP, { ...quiet, cli: { newCheckout: "true" } }, "prod");
71
+ expect(refused.success).toBe(false);
72
+ expect(refused.provenance).toEqual([]);
73
+ expect(refused.errors).toHaveLength(1);
74
+ expect(refused.errors[0]).toMatch(/build parameter "newCheckout" is backed by the flag "new-checkout", and "prod" has a gate, so --param can't set it/);
75
+ expect(refused.errors[0]).toMatch(/gates: prod-release/);
76
+ });
77
+
78
+ test("--params-file can't either, and a parameter no flag backs still can", async () => {
79
+ const file = join(scratchDir("chant-flags-file-"), "params.json");
80
+ writeFileSync(file, JSON.stringify({ newCheckout: true }));
81
+ const refused = await resolveFlaggedBuildParams(join(root, "shop"), SHOP, { ...quiet, paramsFile: file }, "prod");
82
+ expect(refused.success).toBe(false);
83
+ expect(refused.errors[0]).toMatch(/so --params-file can't set it/);
84
+ const tier = await resolveFlaggedBuildParams(join(root, "shop"), SHOP, { ...quiet, cli: { tier: "large" } }, "prod");
85
+ expect(tier.success).toBe(true);
86
+ expect(tier.provenance).toContainEqual({ name: "tier", value: "large", source: "cli" });
87
+ });
88
+
89
+ test("where no component declares a gate, --param still overrides the flag", async () => {
90
+ const r = await resolveFlaggedBuildParams(join(root, "blog"), BLOG, { ...quiet, cli: { banner: "false" } }, "prod");
91
+ expect(r.success).toBe(true);
92
+ expect(r.provenance).toEqual([{ name: "banner", value: false, source: "cli" }]);
93
+ });
94
+
95
+ test("components that can't be read refuse an override, since the gate can't be ruled out", async () => {
96
+ const dir = flagWorkspace({ "shop/broken.component.ts": "export const = ;\n" });
97
+ const r = await resolveFlaggedBuildParams(join(dir, "shop"), SHOP, { ...quiet, cli: { newCheckout: "true" } }, "prod");
98
+ expect(r.success).toBe(false);
99
+ expect(r.errors[0]).toMatch(/whether "prod" has a gate can't be told/);
100
+ });
101
+ });
102
+
103
+ describe("chant build --env in a member with flags", () => {
104
+ const runtime = join(import.meta.dirname, "runtime");
105
+ const paramsModule = join(import.meta.dirname, "params");
106
+ const serializer: Serializer = {
107
+ name: "aws",
108
+ rulePrefix: "TEST",
109
+ serialize: (entities) => JSON.stringify([...entities.values()].map((e) => (e as unknown as { props: unknown }).props)),
110
+ };
111
+ const shopFiles = {
112
+ "shop/chant.config.ts": `export default { environments: ["dev", "staging", "prod"], buildParams: { newCheckout: { type: "boolean", default: false } } };\n`,
113
+ "shop/main.ts":
114
+ `import { createResource } from ${JSON.stringify(runtime)};\n` +
115
+ `import { params } from ${JSON.stringify(paramsModule)};\n` +
116
+ `const Bucket = createResource("Test::Bucket", "aws", { arn: "Arn" });\n` +
117
+ `export const bucket = new Bucket({ checkout: params.newCheckout ? "new" : "old" });\n`,
118
+ };
119
+
120
+ test("records the flag on BuildResult.buildParams and builds with its value", async () => {
121
+ const dir = flagWorkspace(shopFiles);
122
+ const spy = vi.spyOn(console, "error").mockImplementation(() => {});
123
+ try {
124
+ const result = await buildCommand({ path: join(dir, "shop"), format: "json", serializers: [serializer], env: "dev" });
125
+ expect(result.errors).toEqual([]);
126
+ expect(result.buildParams).toEqual([{ name: "newCheckout", value: true, source: "flag:new-checkout" }]);
127
+ expect(spy.mock.calls.map((c) => String(c[0])).some((l) => l.includes("1 build parameter resolved (1 from flags)"))).toBe(true);
128
+ } finally {
129
+ spy.mockRestore();
130
+ }
131
+ });
132
+
133
+ test("refuses --param for a flag-backed parameter in an environment with a gate", async () => {
134
+ const dir = flagWorkspace(shopFiles);
135
+ const spy = vi.spyOn(console, "error").mockImplementation(() => {});
136
+ try {
137
+ const result = await buildCommand({ path: join(dir, "shop"), format: "json", serializers: [serializer], env: "prod", params: { newCheckout: "true" } });
138
+ expect(result.success).toBe(false);
139
+ expect(result.errors.join("\n")).toMatch(/"newCheckout" is backed by the flag "new-checkout", and "prod" has a gate, so --param can't set it/);
140
+ } finally {
141
+ spy.mockRestore();
142
+ }
143
+ });
144
+ });
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Build-time parameters from workspace flags (#4024, decided in #4016).
3
+ *
4
+ * A member of a workspace may have flag records: records of a kind with a
5
+ * `flag` block, each naming the member, one of its declared build
6
+ * parameters and a value per environment (`workspace/flags.ts`). A build
7
+ * for an environment (`chant build --env <env>`, `chant run --components
8
+ * --env <env>`, and each environment a release cut compiles) reads them, and
9
+ * a flag-backed parameter takes the flag's value for that environment,
10
+ * recorded as `source: "flag:<name>"` on `BuildResult.buildParams` and on the
11
+ * release target's `params`.
12
+ *
13
+ * The precedence rule: where the environment has a gate, `--param`,
14
+ * `--params-file` and the parameter's env var can't set a flag-backed
15
+ * parameter, and the build refuses. An environment has a gate when a
16
+ * component enabled in it declares a gate step, the same test `components
17
+ * promote --release` uses to bind a release gate. The test runs only when one
18
+ * of those sources did set a flag-backed parameter, since it imports the
19
+ * member's components; a project with no components has no gate.
20
+ *
21
+ * Level 0 pays one upward `existsSync` walk here and nothing more. Workspace
22
+ * code is imported only once a declaration is found (#2525 rule 5, pinned by
23
+ * #2526's goldens).
24
+ */
25
+
26
+ import { findWorkspaceRoot } from "./project-root";
27
+ import { flagGateRefusal, type BuildParamFlags, type BuildParamsConfig } from "./build-params";
28
+ import type { BuildParamProvenance } from "./provenance";
29
+ import { echoBuildParams, resolveCliBuildParams, type CliBuildParamsArgs, type CliBuildParamsResolution } from "./cli/build-params-cli";
30
+ import { formatError } from "./cli/format";
31
+
32
+ /** The flags backing parameters of the member at `projectDir` in `env`, or why they can't be read. `flags` is undefined outside a workspace, without an environment, or when no flag names the member. */
33
+ export async function loadBuildParamFlags(projectDir: string, env: string | undefined): Promise<{ flags: BuildParamFlags | undefined; errors: string[] }> {
34
+ if (!env) return { flags: undefined, errors: [] };
35
+ const found = findWorkspaceRoot(projectDir);
36
+ if (!found) return { flags: undefined, errors: [] };
37
+ const { memberFlagBindings } = await import("./workspace/flags-read");
38
+ try {
39
+ const bindings = await memberFlagBindings(found.dir, projectDir, env);
40
+ if (!bindings) return { flags: undefined, errors: [] };
41
+ if (bindings.errors.length > 0) return { flags: undefined, errors: bindings.errors };
42
+ if (Object.keys(bindings.byParam).length === 0) return { flags: undefined, errors: [] };
43
+ return { flags: { env, byParam: bindings.byParam }, errors: [] };
44
+ } catch (err) {
45
+ const { WorkspaceReadError } = await import("./workspace/declaration");
46
+ const { RecordReadError } = await import("./workspace/records");
47
+ if (err instanceof WorkspaceReadError || err instanceof RecordReadError) {
48
+ return { flags: undefined, errors: [`the workspace's flag records can't be read, so this build can't tell which parameters they set: ${err.message}`] };
49
+ }
50
+ throw err;
51
+ }
52
+ }
53
+
54
+ /** The parameters a flag backs that `--param`, `--params-file` or an env var set instead. */
55
+ export function overriddenFlagParams(provenance: readonly BuildParamProvenance[], flags: BuildParamFlags | undefined): BuildParamProvenance[] {
56
+ if (!flags) return [];
57
+ return provenance.filter((p) => flags.byParam[p.name] !== undefined && (p.source === "cli" || p.source === "params-file" || p.source === "env"));
58
+ }
59
+
60
+ /**
61
+ * The gates the member's components declare in the environment the process
62
+ * builds for (`CHANT_ENV`), with the parameters as resolved, or why the
63
+ * components can't be read. Only components enabled under those parameters
64
+ * count.
65
+ */
66
+ export async function environmentGates(projectDir: string, provenance: BuildParamProvenance[]): Promise<{ gates: string[] } | { error: string }> {
67
+ const { discoverComponents } = await import("./components/discover");
68
+ const { withoutDisabled } = await import("./components/cli-support");
69
+ const { gateNamesOf } = await import("./components/release-gate");
70
+ try {
71
+ const discovered = await discoverComponents(projectDir, { buildParams: provenance });
72
+ if (discovered.errors.length > 0) return { error: discovered.errors.map((e) => e.message).join("; ") };
73
+ const all = [...discovered.components.values()].map((d) => d.component);
74
+ const plain = all.map((c) => ({ name: c.name, dependsOn: c.dependsOn, deploy: c.deploy as never }));
75
+ return { gates: gateNamesOf(withoutDisabled(all, plain)) };
76
+ } catch (err) {
77
+ return { error: err instanceof Error ? err.message : String(err) };
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Resolve the build parameters of the project at `projectDir` for `env`,
83
+ * with the workspace's flags, as `resolveCliBuildParams` does without them.
84
+ * Refuses when a flag record can't be read, and when `--param`,
85
+ * `--params-file` or an env var sets a flag-backed parameter in an
86
+ * environment with a gate. Prints the summary line only on success.
87
+ */
88
+ export async function resolveFlaggedBuildParams(
89
+ projectDir: string,
90
+ buildParamsConfig: BuildParamsConfig | undefined,
91
+ args: CliBuildParamsArgs,
92
+ env: string | undefined,
93
+ ): Promise<CliBuildParamsResolution> {
94
+ const loaded = await loadBuildParamFlags(projectDir, env);
95
+ if (loaded.errors.length > 0) {
96
+ return { success: false, provenance: [], errors: loaded.errors.map((message) => formatError({ message })) };
97
+ }
98
+ const { flags } = loaded;
99
+ const resolution = resolveCliBuildParams(buildParamsConfig, { ...args, ...(flags ? { flags } : {}), echo: false });
100
+ if (!resolution.success || !flags) {
101
+ if (resolution.success && args.echo !== false) echoBuildParams(resolution.provenance, args.verbose);
102
+ return resolution;
103
+ }
104
+ const overridden = overriddenFlagParams(resolution.provenance, flags);
105
+ if (overridden.length > 0) {
106
+ const found = await environmentGates(projectDir, resolution.provenance);
107
+ if ("error" in found) {
108
+ return {
109
+ success: false,
110
+ provenance: [],
111
+ errors: [
112
+ formatError({
113
+ message:
114
+ `${overridden.map((p) => `"${p.name}"`).join(", ")} ${overridden.length === 1 ? "is" : "are"} backed by a flag and set by ${[...new Set(overridden.map((p) => p.source))].join(", ")}, ` +
115
+ `and whether "${flags.env}" has a gate can't be told, because its components can't be read: ${found.error}`,
116
+ }),
117
+ ],
118
+ };
119
+ }
120
+ if (found.gates.length > 0) {
121
+ const around = (p: BuildParamProvenance): string =>
122
+ p.source === "cli" ? "--param" : p.source === "params-file" ? "--params-file" : `the env var ${buildParamsConfig?.[p.name]?.env ?? "(unknown)"}`;
123
+ return {
124
+ success: false,
125
+ provenance: [],
126
+ errors: overridden.map((p) =>
127
+ formatError({ message: `${flagGateRefusal(p.name, flags.byParam[p.name].flag, flags.env, around(p))} (gates: ${found.gates.join(", ")})` }),
128
+ ),
129
+ };
130
+ }
131
+ }
132
+ if (args.echo !== false) echoBuildParams(resolution.provenance, args.verbose);
133
+ return resolution;
134
+ }