@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
@@ -1,10 +1,22 @@
1
1
  /**
2
2
  * `chant release` (#4026, ws-104): the release is what graduates.
3
3
  *
4
- * release cut [<label>] [--build <command>] [--param k=v]... [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>] [--actor <name>] [--sign [<key file>]] [--json]
5
- * cut a release of this member from the checkout's HEAD, which must be green;
4
+ * release cut [<label>] [--yes] [--build <command>] [--param k=v]... [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>] [--actor <name>] [--sign [<key file>]] [--json]
5
+ * a dry run of a cut of this member from the checkout's HEAD, which must be
6
+ * clean, on trunk and pushed, and green (#4027); `--yes` cuts, records,
7
+ * and tags the commit `release/<label>` locally;
6
8
  * --sign signs the cut with the actor's ssh key (#4031); `--src`, `-o` and
7
9
  * `--lexicon` shape the build of a project with no components (#4046)
10
+ * release publish <label|digest> [--yes] [--allow-unsigned] [--require-provenance] [--json]
11
+ * publish a cut release (#4027) to the targets release.publish declares
12
+ * (#4047): a dry run unless --yes; refuses a revoked one, one with no
13
+ * signature that verifies against .chant/allowed_signers unless
14
+ * --allow-unsigned (#4049), and with a target a checkout that is not
15
+ * the release's commit; warns with no OIDC provenance, and refuses
16
+ * under --require-provenance; attaches SBOMs and provenance (#4049)
17
+ * release verify <label|digest> [--json]
18
+ * check the release's signature offline against .chant/allowed_signers
19
+ * at the release's commit (#4049)
8
20
  * release list [--json]
9
21
  * every release of this member with its targets, its cuts and where it runs;
10
22
  * `--json` prints the read contract's `releases` document
@@ -13,14 +25,17 @@
13
25
  * release compile --env <env> [--build <command>] [--artifacts --component <a,b>] --json
14
26
  * internal: what the cut runs in a child process per environment
15
27
  *
16
- * The workstation UX for `cut` grows in #4027. A declaration whose
17
- * `identity.gates` names a `release` gate requires a signed cut by an actor
18
- * in its class (#4031).
28
+ * The workstation checks, the actor and the tag are in
29
+ * ../../lifecycle/release-workstation.ts, and the publish contract in
30
+ * ../../lifecycle/release-publish.ts. A declaration whose `identity.gates`
31
+ * names a `release` gate requires a signed cut by an actor in its class (#4031).
19
32
  */
33
+ import { execFileSync } from "node:child_process";
34
+ import { existsSync } from "node:fs";
20
35
  import { resolve } from "node:path";
21
36
  import { formatBold, formatError, formatSuccess, formatWarning } from "../format.js";
22
37
  import { parseParamFlags } from "../build-params-cli.js";
23
- const USAGE = "chant release cut [<label>] [--build <command>] [--param <k=v>] [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>] [--actor <name>] [--sign [<key file>]] [--json] | release list [--json] | release export <label|digest> --env <env> [-o <dir>] [--json]";
38
+ const USAGE = "chant release cut [<label>] [--yes] [--build <command>] [--param <k=v>] [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>] [--actor <name>] [--sign [<key file>]] [--json] | release publish <label|digest> [--yes] [--allow-unsigned] [--require-provenance] [--json] | release verify <label|digest> [--json] | release list [--json] | release export <label|digest> --env <env> [-o <dir>] [--json]";
24
39
  /** The build inputs `release cut` and `release compile` share. */
25
40
  function compileInputs(ctx, params) {
26
41
  const { args } = ctx;
@@ -34,9 +49,45 @@ function compileInputs(ctx, params) {
34
49
  ...(args.lexicon ? { lexicon: args.lexicon } : {}),
35
50
  };
36
51
  }
37
- function resolveActor(ctx) {
38
- return ctx.args.actor ?? process.env.GITHUB_ACTOR ?? process.env.GITLAB_USER_LOGIN ?? process.env.USER;
52
+ /** What to run after a cut (#4027): publish from CI, or push the tag so CI publishes, or deploy the first environment. */
53
+ export function nextCommands(req) {
54
+ const publish = `chant release publish ${req.ref}`;
55
+ const deploy = req.firstEnv === null
56
+ ? null
57
+ : req.noComponents
58
+ ? `chant release export ${req.ref} --env ${req.firstEnv}`
59
+ : `chant components promote --release ${req.ref} --to ${req.firstEnv}`;
60
+ if (req.inCi)
61
+ return [publish, ...(deploy ? [deploy] : [])];
62
+ return [...(req.tag ? [`git push origin ${req.tag}`] : []), publish, ...(deploy ? [deploy] : [])];
39
63
  }
64
+ /** The command line that ran, without `--json`, with `--yes` added: what a dry run says to run next. */
65
+ export function sameWithYes(argv) {
66
+ const quote = (a) => (/^[\w@%+=:,./-]+$/.test(a) ? a : `'${a.replace(/'/g, `'\\''`)}'`);
67
+ return ["chant", ...argv.filter((a) => a !== "--json" && a !== "--yes"), "--yes"].map(quote).join(" ");
68
+ }
69
+ function describeActorSource(source) {
70
+ switch (source) {
71
+ case "flag":
72
+ return "--actor";
73
+ case "signer":
74
+ return "the signers file at base, for the signing key";
75
+ case "ci":
76
+ return "the CI's actor";
77
+ case "git":
78
+ return "git user.email";
79
+ case "user":
80
+ return "$USER";
81
+ }
82
+ }
83
+ /**
84
+ * `release cut [<label>]` (#4026, #4027). A dry run unless `--yes`: it
85
+ * refuses a dirty tree, a commit that is not on trunk and pushed, and one
86
+ * that is not green, compiles every environment, and prints what the cut
87
+ * would record, build and tag, and what publishes where. With `--yes` it
88
+ * cuts, signs with `--sign`, records the cut with its actor and run id,
89
+ * tags the commit locally and prints the next command.
90
+ */
40
91
  export async function runReleaseCut(ctx) {
41
92
  const { args } = ctx;
42
93
  const positional = args.extraPositional;
@@ -45,23 +96,169 @@ export async function runReleaseCut(ctx) {
45
96
  return 1;
46
97
  }
47
98
  const label = positional ?? args.label ?? null;
48
- const actor = resolveActor(ctx);
49
- if (!actor) {
50
- console.error(formatError({ message: "Could not resolve --actor from the environment", hint: "Pass --actor explicitly, or set GITHUB_ACTOR / GITLAB_USER_LOGIN / USER." }));
51
- return 1;
52
- }
99
+ const dryRun = args.yes !== true;
53
100
  const cwd = resolve(".");
54
101
  const { spawnTargetCompiler, ReleaseCompileError } = await import("../../components/release-compile.js");
55
- const { cutRelease, ReleaseCutError } = await import("../../lifecycle/release-cut.js");
102
+ const { cutRelease, planRelease, ReleaseCutError } = await import("../../lifecycle/release-cut.js");
56
103
  const { ReleaseLabelError } = await import("../../lifecycle/release-store.js");
57
- const { pushLifecycleReport } = await import("../../lifecycle/git.js");
58
- const params = parseParamFlags(args.param);
59
- const compiler = spawnTargetCompiler(compileInputs(ctx, params));
104
+ const { pushLifecycleReport, getHeadCommit } = await import("../../lifecycle/git.js");
105
+ const { resolveCiRun } = await import("../../lifecycle/release-ledger.js");
106
+ const ws = await import("../../lifecycle/release-workstation.js");
107
+ const { detectProvenance, releasePublishTargets } = await import("../../lifecycle/release-publish.js");
108
+ const fail = (code, message, fix) => {
109
+ console.error(formatError({ message: `${message} [${code}]`, ...(fix ? { hint: fix } : {}) }));
110
+ return 1;
111
+ };
60
112
  try {
61
- const result = await cutRelease({ cwd, compiler, label, actor, ...(args.sign !== undefined ? { sign: args.sign } : {}) });
113
+ // The workstation checks come first, in the order #4027 lists them; the
114
+ // green check and the rest are the cut's own.
115
+ const top = (() => {
116
+ try {
117
+ return execFileSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
118
+ }
119
+ catch {
120
+ return null;
121
+ }
122
+ })();
123
+ let commit = null;
124
+ let trunk = null;
125
+ let signers = [];
126
+ let member = null;
127
+ if (top) {
128
+ commit = await getHeadCommit({ cwd });
129
+ ws.checkCleanTree(top, commit);
130
+ const { scopeSource } = await import("../../workspace/write-scope.js");
131
+ const { releaseRuleFrom } = await import("../../workspace/identity.js");
132
+ const source = scopeSource(cwd);
133
+ trunk = ws.resolveTrunk(top, source.declaration?.ci?.green?.branch ?? null);
134
+ ws.checkOnTrunk(top, commit, trunk);
135
+ signers = releaseRuleFrom(source).rule.signers.signers;
136
+ const { resolveMemberLedger } = await import("../../lifecycle/member-ledger.js");
137
+ const ledger = await resolveMemberLedger(cwd);
138
+ member = ledger.members.length > 0 ? ledger.members.join("/") : null;
139
+ }
140
+ // The signing key is resolved now, so a dry run shows a key that can't be read.
141
+ let publicKey = null;
142
+ if (args.sign !== undefined) {
143
+ const { resolveSigningKey, SealError } = await import("../../workspace/trust/seal.js");
144
+ try {
145
+ const key = resolveSigningKey(args.sign, cwd);
146
+ try {
147
+ if (!existsSync(key.file))
148
+ return fail("release-signer-refused", `no release was cut: the signing key ${key.file} does not exist`);
149
+ publicKey = ws.publicKeyOf(key.file);
150
+ }
151
+ finally {
152
+ key.cleanup();
153
+ }
154
+ }
155
+ catch (err) {
156
+ if (!(err instanceof SealError))
157
+ throw err;
158
+ return fail("release-signer-refused", `no release was cut: ${err.message}`);
159
+ }
160
+ }
161
+ const who = ws.resolveCutActor({ cwd, ...(args.actor ? { explicit: args.actor } : {}), publicKey, signers });
162
+ if (!who) {
163
+ console.error(formatError({ message: "Could not resolve --actor", hint: "Pass --actor explicitly, or set git's user.email, GITHUB_ACTOR, GITLAB_USER_LOGIN or USER." }));
164
+ return 1;
165
+ }
166
+ const actor = who.actor;
167
+ const tag = label !== null && commit !== null && top ? { name: ws.releaseTagName(member, label), state: ws.checkReleaseTag(top, ws.releaseTagName(member, label), commit) } : null;
168
+ const ci = resolveCiRun(process.env);
169
+ const params = parseParamFlags(args.param);
170
+ const compiler = spawnTargetCompiler(compileInputs(ctx, params));
171
+ const request = { cwd, compiler, label, actor, ...(args.sign !== undefined ? { sign: args.sign } : {}) };
172
+ if (dryRun) {
173
+ const plan = await planRelease(request);
174
+ // Under the release gate, a key the signers file at base lists for someone else is refused now, not after the build.
175
+ if (plan.signatureRequired && publicKey !== null && !signers.some((s) => s.key === publicKey && s.principal.trim().toLowerCase() === actor.trim().toLowerCase())) {
176
+ return fail("release-signer-refused", `no release would be cut: the signing key is not listed for ${actor} in the signers file at base, and identity.gates.release counts only a cut its signer signs`, "Pass --actor <principal> naming the key's signer, or sign with that principal's key.");
177
+ }
178
+ const ref = plan.label ?? plan.alreadyCut ?? plan.releaseDigest;
179
+ const provenance = detectProvenance();
180
+ // The targets release publish would run, as the config here declares them; their files are read at publish.
181
+ const publishTargets = await releasePublishTargets(cwd).then((ts) => ts.map((t) => `${t.name} to ${t.destination}`), (err) => (err instanceof Error ? err : new Error(String(err))));
182
+ const deployRef = plan.label ?? "<label|digest>";
183
+ const doc = {
184
+ dryRun: true,
185
+ ...plan,
186
+ actor,
187
+ actorSource: who.source,
188
+ run: ci ? { runId: ci.runId, runOrigin: ci.runOrigin } : { runId: null, runOrigin: { forge: "local" } },
189
+ trunk,
190
+ sign: args.sign !== undefined ? { key: publicKey !== null ? publicKey.split(" ")[0] : null, readable: publicKey !== null } : null,
191
+ tag: tag ? { name: tag.name, exists: tag.state === "present" } : null,
192
+ publish: { targets: Array.isArray(publishTargets) ? publishTargets : [], provenance },
193
+ next: [sameWithYes(process.argv.slice(2))],
194
+ };
195
+ if (args.json) {
196
+ console.log(JSON.stringify(doc, null, 2));
197
+ return 0;
198
+ }
199
+ const name = plan.label ? formatBold(plan.label) : "an unlabelled release";
200
+ console.log(`Dry run: cut ${name} from commit ${plan.commit.slice(0, 12)}${trunk ? ` (on ${trunk.remote}/${trunk.branch}, green)` : ""}`);
201
+ if (plan.alreadyCut)
202
+ console.log(` already cut as ${plan.alreadyCut}: the cut would record nothing new`);
203
+ console.log(` actor ${actor} (from ${describeActorSource(who.source)})`);
204
+ console.log(` run ${ci ? `${ci.runId} (${ci.runOrigin.forge})` : "minted on this machine at the cut (local)"}`);
205
+ console.log(` ${args.sign !== undefined ? `signed with ${publicKey ? publicKey.split(" ")[0] : "a key whose public half can't be read until it signs"}` : "not signed"}` +
206
+ (plan.signatureRequired ? "; identity.gates.release requires a signed cut" : ""));
207
+ console.log(` tag ${tag ? `${tag.name}${tag.state === "present" ? " (exists)" : ""}, made here and not pushed` : "none, since the release has no label"}`);
208
+ for (const t of plan.targets) {
209
+ console.log(` ${t.env}: ${t.templates.length} template(s)${t.params.length > 0 ? `, ${t.params.map((p) => `${p.name}=${p.value}`).join(" ")}` : ""}`);
210
+ }
211
+ for (const b of plan.build)
212
+ console.log(` ${b.component}: artifacts built in ${b.env}`);
213
+ for (const r of plan.reused)
214
+ console.log(` ${r.component}: artifacts unchanged, carried from ${r.from}`);
215
+ console.log(` digest ${plan.releaseDigest ?? "decided when the artifacts are built"}`);
216
+ console.log("What will be published where:");
217
+ if (plan.publishSteps.length === 0)
218
+ console.log(" no component declares a publish step");
219
+ for (const p of plan.publishSteps) {
220
+ console.log(` ${p.component}: ${p.kind}${p.to ? ` to ${p.to}` : ""} in ${p.envs.join(", ")}, by that environment's deploy (chant components promote --release ${deployRef})`);
221
+ }
222
+ const publishCmd = `chant release publish ${ref ?? "<label|digest>"}`;
223
+ if (!Array.isArray(publishTargets))
224
+ console.log(` ${publishCmd}: the publish targets can't be read: ${publishTargets.message}`);
225
+ else if (publishTargets.length === 0)
226
+ console.log(` ${publishCmd}: nothing, since this member declares no publish target (release.publish in chant.config.ts)`);
227
+ else
228
+ for (const t of publishTargets)
229
+ console.log(` ${publishCmd}: ${t}`);
230
+ if (!provenance.oidc)
231
+ console.log(` a publish from here would carry no OIDC provenance: ${provenance.reason}`);
232
+ console.error(`Dry run: nothing was cut, signed or tagged. Run again with --yes to cut: ${doc.next[0]}`);
233
+ return 0;
234
+ }
235
+ const result = await cutRelease(request);
62
236
  const push = await pushLifecycleReport({ cwd }).catch(() => ({ pushed: false, pushWarning: undefined }));
237
+ // The tag names the release's label, which a re-cut with no label reads from the earlier cut.
238
+ let tagged = null;
239
+ let tagWarning;
240
+ if (result.label !== null && top) {
241
+ const name = ws.releaseTagName(member, result.label);
242
+ try {
243
+ const state = ws.checkReleaseTag(top, name, result.release.commit);
244
+ if (state === "absent")
245
+ ws.createReleaseTag(top, name, result.release.commit, { releaseDigest: result.release.releaseDigest, label: result.label, member });
246
+ tagged = { name, created: state === "absent" };
247
+ }
248
+ catch (err) {
249
+ tagWarning = `the release was cut, and the commit was not tagged: ${err instanceof Error ? err.message : String(err)}`;
250
+ }
251
+ }
252
+ const firstEnv = result.release.targets[0]?.env ?? null;
253
+ const next = nextCommands({
254
+ ref: result.label ?? result.release.releaseDigest,
255
+ tag: tagged?.name ?? null,
256
+ inCi: ci !== undefined,
257
+ firstEnv,
258
+ noComponents: result.release.targets.every((t) => t.templates.every((tpl) => tpl.component === null)) && result.release.artifactDigests.length === 0,
259
+ });
63
260
  if (args.json) {
64
- console.log(JSON.stringify({ release: result.release, label: result.label, cut: result.cut, stored: result.stored, reused: result.reused }, null, 2));
261
+ console.log(JSON.stringify({ dryRun: false, release: result.release, label: result.label, cut: result.cut, stored: result.stored, reused: result.reused, actor, actorSource: who.source, tag: tagged, next }, null, 2));
65
262
  }
66
263
  else {
67
264
  const name = result.label ? formatBold(result.label) : "unlabelled release";
@@ -71,25 +268,29 @@ export async function runReleaseCut(ctx) {
71
268
  console.error(formatSuccess(`Already cut${result.label ? ` as ${formatBold(result.label)}` : ""}: ${result.release.releaseDigest}`));
72
269
  console.error(` commit ${result.release.commit}`);
73
270
  if (result.cut)
74
- console.error(result.cut.signature ? ` signed by ${result.cut.actor}` : " not signed");
271
+ console.error(` ${result.cut.signature ? `signed by ${result.cut.actor}` : `not signed; actor ${result.cut.actor}`}, run ${result.cut.runId}`);
75
272
  console.error(` artifacts ${result.release.artifactDigests.length}, targets ${result.release.targets.map((t) => `${t.env} (${t.templates.length} template${t.templates.length === 1 ? "" : "s"})`).join(", ")}`);
76
273
  for (const r of result.reused)
77
274
  console.error(` ${r.component}: artifacts unchanged, carried from ${r.from}`);
275
+ if (tagged)
276
+ console.error(` tag ${tagged.name}${tagged.created ? "" : " (already there)"}, not pushed`);
277
+ console.error(`Next: ${next[0]}`);
278
+ for (const n of next.slice(1))
279
+ console.error(` or: ${n}`);
78
280
  console.log(result.release.releaseDigest);
79
281
  }
80
282
  if (push.pushWarning)
81
283
  console.error(formatWarning({ message: push.pushWarning }));
284
+ if (tagWarning)
285
+ console.error(formatWarning({ message: tagWarning }));
82
286
  return 0;
83
287
  }
84
288
  catch (err) {
85
- if (err instanceof ReleaseCutError) {
86
- console.error(formatError({ message: `${err.message} [${err.code}]`, ...(err.fix ? { hint: err.fix } : {}) }));
87
- return 1;
88
- }
89
- if (err instanceof ReleaseLabelError) {
90
- console.error(formatError({ message: `${err.message} [${err.code}]` }));
91
- return 1;
289
+ if (err instanceof ws.WorkstationCheckError || err instanceof ReleaseCutError) {
290
+ return fail(err.code, err.message, err.fix);
92
291
  }
292
+ if (err instanceof ReleaseLabelError)
293
+ return fail(err.code, err.message);
93
294
  if (err instanceof ReleaseCompileError) {
94
295
  console.error(formatError({ message: `no release was cut: ${err.message}` }));
95
296
  return 1;
@@ -97,6 +298,186 @@ export async function runReleaseCut(ctx) {
97
298
  throw err;
98
299
  }
99
300
  }
301
+ /**
302
+ * `release publish <label|digest> [--yes]` (#4027, #4047): publish a cut
303
+ * release to the targets the member declares under `release.publish` in
304
+ * its `chant.config.ts`. It refuses a release no cut names, one cut from a
305
+ * revoked commit, one with no signature that verifies against the signers
306
+ * file at base unless `--allow-unsigned` (#4049; never under
307
+ * identity.gates.release), and, when there is a target, a checkout that is
308
+ * not the release's commit with a clean tree. It warns before publishing
309
+ * when the run has no OIDC token, so what it publishes carries no
310
+ * provenance, and refuses under `--require-provenance`. Each target attaches
311
+ * an SBOM and the release's provenance statement (#4049). Every
312
+ * target is planned (its files read and checked, its credentials found)
313
+ * before any publishes. Without `--yes` it is a dry run that prints the plan
314
+ * and uploads nothing. With `--yes` it stages every target, then reveals them
315
+ * (#4048, `runPublish`), recording each step in `_releases/publishes.jsonl`,
316
+ * so running it again after a failure resumes. Every line is redacted.
317
+ */
318
+ export async function runReleasePublish(ctx) {
319
+ const { args } = ctx;
320
+ const ref = args.extraPositional;
321
+ if (!ref) {
322
+ console.error(formatError({ message: "release publish needs a release", hint: USAGE }));
323
+ return 1;
324
+ }
325
+ const dryRun = args.yes !== true;
326
+ const cwd = resolve(".");
327
+ const fail = (code, message, fix) => {
328
+ console.error(formatError({ message: `${message} [${code}]`, ...(fix ? { hint: fix } : {}) }));
329
+ return 1;
330
+ };
331
+ const { fetchLifecycle } = await import("../../lifecycle/git.js");
332
+ const { preparePublish, publishContext, runPublish, PublishTargetError, PublishRunError } = await import("../../lifecycle/release-publish.js");
333
+ const { publishProgress, progressOf, readPublishRecords } = await import("../../lifecycle/publish-record.js");
334
+ await fetchLifecycle({ cwd }).catch(() => false);
335
+ const prepared = await preparePublish({ cwd, ref, allowUnsigned: args.allowUnsigned === true, requireProvenance: args.requireProvenance === true });
336
+ if ("refusal" in prepared)
337
+ return fail(prepared.refusal.code, prepared.refusal.message, prepared.refusal.fix);
338
+ const { release, label, signature, provenance, warning, signatureWarning, targets, redactor } = prepared;
339
+ const name = label ?? release.releaseDigest;
340
+ const deploySteps = release.targets.map((t) => t.env);
341
+ const say = (line) => console.error(redactor.redact(line));
342
+ const failed = (code, message, fix) => fail(code, redactor.redact(message), fix);
343
+ const errorOf = (err) => err instanceof PublishTargetError ? { code: err.code, message: err.message, ...(err.fix ? { fix: err.fix } : {}) } : { code: "publish-failed", message: err instanceof Error ? err.message : String(err) };
344
+ if (!args.json) {
345
+ say(`${dryRun ? "Dry run: publish" : "Publishing"} ${formatBold(name)} (${release.releaseDigest}) from commit ${release.commit.slice(0, 12)}`);
346
+ say(` ${signature.status === "verified" ? `signed by ${signature.actor}` : signature.status === "unsigned" ? "not signed" : `signature unverified: ${signature.reason}`}`);
347
+ }
348
+ // The warnings go to stderr with --json too, before any target runs.
349
+ if (signatureWarning)
350
+ console.error(formatWarning({ message: signatureWarning }));
351
+ if (warning)
352
+ console.error(formatWarning({ message: warning }));
353
+ const pctx = publishContext(prepared, { cwd, log: (line) => (args.json ? undefined : console.error(` ${line}`)) });
354
+ // Every target is planned before any publishes, so a missing file or credential stops the publish with nothing sent.
355
+ const progress = publishProgress(await readPublishRecords(release.releaseDigest, { cwd }).catch(() => []));
356
+ const planned = [];
357
+ for (const target of targets) {
358
+ try {
359
+ planned.push({ kind: target.kind, name: target.name, destination: target.destination, progress: progressOf(progress, target), items: await target.plan(pctx) });
360
+ }
361
+ catch (err) {
362
+ const e = errorOf(err);
363
+ return failed(e.code, `nothing was published: ${target.name}: ${e.message}`, e.fix);
364
+ }
365
+ }
366
+ const promoteHint = () => {
367
+ if (deploySteps.length === 0)
368
+ return null;
369
+ const noComponents = release.artifactDigests.length === 0 && release.targets.every((t) => t.templates.every((tpl) => tpl.component === null));
370
+ return noComponents
371
+ ? ` Each environment deploys the files the release recorded: chant release export ${name} --env ${deploySteps[0]}`
372
+ : ` A component's publish steps run in each environment's deploy of the release: chant components promote --release ${name} --to ${deploySteps[0]}`;
373
+ };
374
+ const base = { releaseDigest: release.releaseDigest, label, commit: release.commit, signature, provenance, warnings: [signatureWarning, warning].filter((w) => w !== null) };
375
+ if (dryRun) {
376
+ const next = [sameWithYes(process.argv.slice(2))];
377
+ if (args.json) {
378
+ console.log(redactor.redact(JSON.stringify({ dryRun: true, ...base, targets: planned, published: [], environments: deploySteps, next }, null, 2)));
379
+ return 0;
380
+ }
381
+ const out = (line) => console.log(redactor.redact(line));
382
+ out("What would be published where:");
383
+ if (planned.length === 0)
384
+ out(" nothing: this member declares no publish target (release.publish in chant.config.ts)");
385
+ for (const p of planned) {
386
+ const earlier = p.progress === "revealed" ? " (published by an earlier run; skipped)" : p.progress === "staged" ? " (staged by an earlier run; only revealed)" : "";
387
+ out(` ${p.name}${provenance.oidc && p.kind === "npm" ? ", with --provenance" : ""}${earlier}:`);
388
+ for (const i of p.items)
389
+ out(` ${describeItem(i)}`);
390
+ }
391
+ say(`Dry run: nothing was published. Run again with --yes to publish: ${next[0]}`);
392
+ const hint = planned.length === 0 ? promoteHint() : null;
393
+ if (hint)
394
+ say(hint);
395
+ return 0;
396
+ }
397
+ if (targets.length > 0 && !args.json)
398
+ say(" every target stages first, and none is visible until all have staged");
399
+ let run;
400
+ try {
401
+ run = await runPublish({ cwd, release, label, targets, ctx: pctx, say: (line) => (args.json ? undefined : say(line)) });
402
+ }
403
+ catch (err) {
404
+ if (err instanceof PublishRunError) {
405
+ const again = sameWithYes(process.argv.slice(2));
406
+ const where = err.step === "stage"
407
+ ? `nothing is visible yet${err.staged.length > 0 ? `; staged: ${err.staged.join(", ")}` : ""}`
408
+ : `revealed: ${err.revealed.length > 0 ? err.revealed.join(", ") : "none"}; staged and not yet revealed: ${err.staged.filter((t) => !err.revealed.includes(t)).join(", ")}`;
409
+ return failed(err.code, `${err.message}; ${where}`, `${err.fix ? `${err.fix} ` : ""}Run the same command again to resume; what went out is not published again: ${again}`);
410
+ }
411
+ const e = errorOf(err);
412
+ return failed(e.code, e.message, e.fix);
413
+ }
414
+ const { published } = run;
415
+ if (run.recordWarning && targets.length > 0)
416
+ console.error(formatWarning({ message: redactor.redact(`${run.recordWarning}. A resume asks the registries what is already there.`) }));
417
+ if (args.json) {
418
+ console.log(redactor.redact(JSON.stringify({ dryRun: false, ...base, targets: planned, published, resumed: run.resumed, environments: deploySteps }, null, 2)));
419
+ return 0;
420
+ }
421
+ if (targets.length === 0) {
422
+ say("Nothing was published: this member declares no publish target (release.publish in chant.config.ts).");
423
+ const hint = promoteHint();
424
+ if (hint)
425
+ say(hint);
426
+ }
427
+ else {
428
+ for (const p of published)
429
+ for (const i of p.items)
430
+ say(` ${i.present ? "already there" : "published"}: ${describeItem(i)}`);
431
+ console.error(formatSuccess(`Published ${name} to ${published.length} target(s)`));
432
+ }
433
+ return 0;
434
+ }
435
+ /** One item a target publishes, for a line: what, where, and the SBOM or attachment it is (#4049). */
436
+ function describeItem(i) {
437
+ const what = i.attachment === "sbom" ? `${i.name} (SBOM)` : i.attachment === "provenance" ? `${i.name} (provenance)` : i.name;
438
+ return `${what} to ${i.destination}${i.sbom ? `, carrying ${i.sbom}` : ""}`;
439
+ }
440
+ /**
441
+ * `release verify <label|digest> [--json]` (#4049): check, offline, that the
442
+ * release was signed by a key `.chant/allowed_signers` lists at the release's
443
+ * commit, and that its file is the digest of what it holds. Exits 0 when it
444
+ * verifies and 1 when it does not or can't be checked.
445
+ */
446
+ export async function runReleaseVerify(ctx) {
447
+ const { args } = ctx;
448
+ const ref = args.extraPositional;
449
+ if (!ref) {
450
+ console.error(formatError({ message: "release verify needs a release", hint: USAGE }));
451
+ return 1;
452
+ }
453
+ const { verifyRelease, verificationCode } = await import("../../lifecycle/release-verify.js");
454
+ const result = await verifyRelease({ cwd: resolve("."), ref });
455
+ if ("error" in result) {
456
+ if (args.json)
457
+ console.log(JSON.stringify({ ref, error: result.error }, null, 2));
458
+ else
459
+ console.error(formatError({ message: `${result.error.message} [${result.error.code}]`, ...(result.error.fix ? { hint: result.error.fix } : {}) }));
460
+ return 1;
461
+ }
462
+ const code = verificationCode(result);
463
+ if (args.json) {
464
+ console.log(JSON.stringify({ ...result, ...(code ? { error: { code, message: result.reason } } : {}) }, null, 2));
465
+ return code ? 1 : 0;
466
+ }
467
+ const name = result.label ? `${formatBold(result.label)} (${result.releaseDigest})` : result.releaseDigest;
468
+ for (const c of result.cuts) {
469
+ console.log(` cut by ${c.actor} at ${c.timestamp}: ${c.signatureCheck.status}${c.signatureCheck.key ? ` (${c.signatureCheck.key})` : ""}`);
470
+ }
471
+ if (code) {
472
+ console.error(formatError({
473
+ message: `${name} does not verify: ${result.reason} [${code}]`,
474
+ hint: code === "release-digest-mismatch" ? "Do not publish or deploy it; cut the release again." : `Cut it again signed with a key ${result.signers.path} lists: chant release cut ${result.label ?? "<label>"} --sign --yes`,
475
+ }));
476
+ return 1;
477
+ }
478
+ console.error(formatSuccess(`Verified ${name}: ${result.reason}`));
479
+ return 0;
480
+ }
100
481
  export async function runReleaseList(ctx) {
101
482
  const { releasesDocument } = await import("../../workspace/releases.js");
102
483
  const doc = await releasesDocument({ cwd: resolve(".") });
@@ -119,9 +500,14 @@ export async function runReleaseList(ctx) {
119
500
  // #4031: the newest cut's signature, as the policy at base judges it.
120
501
  const newest = r.cuts.at(-1);
121
502
  const signature = newest ? (newest.signatureCheck.status === "verified" ? `signed by ${newest.actor}` : newest.signatureCheck.status) : "-";
122
- console.log(`${(r.label ?? "-").padEnd(16)} ${r.releaseDigest} ${r.commit.slice(0, 12)} ${signature} ${where}`);
503
+ // #4033: a release cut from a revoked commit says so at the end of its line, and why on stderr.
504
+ console.log(`${(r.label ?? "-").padEnd(16)} ${r.releaseDigest} ${r.commit.slice(0, 12)} ${signature} ${where}${r.revoked ? " revoked" : ""}`);
123
505
  if (newest?.signatureCheck.status === "unverified")
124
506
  console.error(` ${r.label ?? r.releaseDigest}: ${newest.signatureCheck.reason}`);
507
+ if (r.revoked) {
508
+ const { describeRevocation } = await import("../../lifecycle/release-revocation.js");
509
+ console.error(` ${r.label ?? r.releaseDigest}: revoked by ${r.revoked.tag}, ${describeRevocation(r.revoked)}`);
510
+ }
125
511
  }
126
512
  return 0;
127
513
  }
@@ -109,7 +109,7 @@ export declare function autoReleaseRun(localId: string): {
109
109
  *
110
110
  * Returns the records it wrote, for `--digest-file` (#2602).
111
111
  */
112
- export declare function recordAutoReleasesForRun(results: DriverComponentResult[], env: string, runId: string, disabled: boolean, runOrigin?: RunOrigin): Promise<ReleaseRecord[]>;
112
+ export declare function recordAutoReleasesForRun(results: DriverComponentResult[], env: string, runId: string, disabled: boolean, runOrigin?: RunOrigin, releaseDigest?: string): Promise<ReleaseRecord[]>;
113
113
  /**
114
114
  * `chant run --components <name|all|a,b,c> [--env <env>]` (#585) — the
115
115
  * interpret driver's CLI entrypoint, and since #2116 the only component run
@@ -146,6 +146,16 @@ export declare function recordAutoReleasesForRun(results: DriverComponentResult[
146
146
  * rather than loaded a second time.
147
147
  */
148
148
  export declare function runOpComponents(ctx: CommandContext): Promise<number>;
149
+ /**
150
+ * Resolve the release `chant run --components --release` names (#4043), or the
151
+ * reason it can't be used. The run builds from the checked-out commit, so the
152
+ * release's commit must be HEAD, and the release needs a target for `env`.
153
+ */
154
+ export declare function resolveRunRelease(ref: string, env: string, cwd: string): Promise<string | {
155
+ releaseDigest: string;
156
+ label: string | null;
157
+ release: import("../../lifecycle/release-store.js").Release;
158
+ }>;
149
159
  /**
150
160
  * `chant run <name>` on the resolved runtime (#2121) — the built-in `local`
151
161
  * provider by default, a lexicon's `opRuntime` under `--on <name>`.
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/run.ts"],"names":[],"mappings":"AAMA,OAAO,EAAmB,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEhE,OAAO,EAAkD,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAU1G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAWlD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIhD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAErE,OAAO,EAAgB,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAIlG;;;;;GAKG;AACH,eAAO,MAAM,eAAe,IAAI,CAAC;AA+BjC;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,GAAG,OAAO,CAMjF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAChC,eAAe,EAAE,OAAO,GAAG,SAAS,EACpC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,GACzC,MAAM,CAQR;AAuLD,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAGpE;AAuED,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAetE;AA8BD,wBAAsB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAenE;AA2CD,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAKrD;AAOD;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA+CtE;AAOD;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA6CvE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAQvE;AAID,iGAAiG;AACjG,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBtE;AAsMD,wBAAsB,KAAK,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BhE;AAID;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,SAAS,CAAA;CAAE,CAEvF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,qBAAqB,EAAE,EAChC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,OAAO,EACjB,SAAS,GAAE,SAA8B,GACxC,OAAO,CAAC,aAAa,EAAE,CAAC,CA6C1B;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAEpE;AAkND;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnE;AAyPD,wBAAgB,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAMjE"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/run.ts"],"names":[],"mappings":"AAMA,OAAO,EAAmB,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEhE,OAAO,EAAkD,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAU1G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAWlD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIhD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAErE,OAAO,EAAgB,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAIlG;;;;;GAKG;AACH,eAAO,MAAM,eAAe,IAAI,CAAC;AA+BjC;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,GAAG,OAAO,CAMjF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAChC,eAAe,EAAE,OAAO,GAAG,SAAS,EACpC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,GACzC,MAAM,CAQR;AAuLD,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAGpE;AAuED,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAetE;AA8BD,wBAAsB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAenE;AA2CD,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAWrD;AAOD;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA+CtE;AAOD;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA6CvE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAQvE;AAID,iGAAiG;AACjG,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBtE;AAsMD,wBAAsB,KAAK,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BhE;AAID;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,SAAS,CAAA;CAAE,CAEvF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,qBAAqB,EAAE,EAChC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,OAAO,EACjB,SAAS,GAAE,SAA8B,EACzC,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,aAAa,EAAE,CAAC,CA+C1B;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAEpE;AAuCD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;;;;GAe5E;AA8MD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnE;AAyPD,wBAAgB,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAMjE"}