@intentius/chant 0.124.27 → 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 (681) hide show
  1. package/README.md +1 -1
  2. package/dist/__golden__/wave-loop/fan-out-gated.json +8 -0
  3. package/dist/__golden__/wave-loop/fan-out-no-members.json +4 -0
  4. package/dist/__golden__/wave-loop/fan-out-only.json +6 -0
  5. package/dist/__golden__/wave-loop/op-by-apply-hold.json +2 -0
  6. package/dist/__golden__/wave-loop/op-cost-member.json +3 -0
  7. package/dist/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
  8. package/dist/__golden__/wave-loop/op-held-on.json +2 -0
  9. package/dist/__golden__/wave-loop/op-hooks-held.json +2 -0
  10. package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
  11. package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
  12. package/dist/__golden__/wave-loop/op-hooks-share.json +3 -0
  13. package/dist/__golden__/wave-loop/op-per-run.json +7 -0
  14. package/dist/__golden__/wave-loop/op-pr-review.json +7 -0
  15. package/dist/__golden__/wave-loop/op-sealed.json +3 -0
  16. package/dist/__golden__/wave-loop/op-spend.json +3 -0
  17. package/dist/__golden__/wave-loop/op-wave-commands.json +7 -0
  18. package/dist/__golden__/wave-loop/op-wide-waiting.json +4 -0
  19. package/dist/build-params.d.ts +34 -1
  20. package/dist/build-params.d.ts.map +1 -1
  21. package/dist/build-params.js +31 -1
  22. package/dist/change-set-graph.d.ts +63 -0
  23. package/dist/change-set-graph.d.ts.map +1 -0
  24. package/dist/change-set-graph.js +130 -0
  25. package/dist/change-set.d.ts +62 -3
  26. package/dist/change-set.d.ts.map +1 -1
  27. package/dist/change-set.js +36 -4
  28. package/dist/chant-version.d.ts +9 -0
  29. package/dist/chant-version.d.ts.map +1 -0
  30. package/dist/chant-version.js +16 -0
  31. package/dist/cli/build-params-cli.d.ts +7 -1
  32. package/dist/cli/build-params-cli.d.ts.map +1 -1
  33. package/dist/cli/build-params-cli.js +17 -8
  34. package/dist/cli/commands/build.d.ts +8 -0
  35. package/dist/cli/commands/build.d.ts.map +1 -1
  36. package/dist/cli/commands/build.js +15 -11
  37. package/dist/cli/commands/check-lexicon-docs.d.ts.map +1 -1
  38. package/dist/cli/commands/check-lexicon-docs.js +30 -2
  39. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  40. package/dist/cli/commands/check-lexicon.js +13 -2
  41. package/dist/cli/commands/init-lexicon/templates/docs.d.ts +4 -1
  42. package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -1
  43. package/dist/cli/commands/init-lexicon/templates/docs.js +6 -6
  44. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  45. package/dist/cli/commands/init-lexicon.js +17 -8
  46. package/dist/cli/handlers/approve.d.ts.map +1 -1
  47. package/dist/cli/handlers/approve.js +64 -4
  48. package/dist/cli/handlers/build.d.ts.map +1 -1
  49. package/dist/cli/handlers/build.js +43 -4
  50. package/dist/cli/handlers/change-set.d.ts +10 -0
  51. package/dist/cli/handlers/change-set.d.ts.map +1 -1
  52. package/dist/cli/handlers/change-set.js +51 -1
  53. package/dist/cli/handlers/fan-out.d.ts +10 -1
  54. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  55. package/dist/cli/handlers/fan-out.js +54 -6
  56. package/dist/cli/handlers/lifecycle-gc.d.ts +19 -0
  57. package/dist/cli/handlers/lifecycle-gc.d.ts.map +1 -0
  58. package/dist/cli/handlers/lifecycle-gc.js +143 -0
  59. package/dist/cli/handlers/pr.d.ts +16 -2
  60. package/dist/cli/handlers/pr.d.ts.map +1 -1
  61. package/dist/cli/handlers/pr.js +269 -37
  62. package/dist/cli/handlers/promote.d.ts +7 -0
  63. package/dist/cli/handlers/promote.d.ts.map +1 -1
  64. package/dist/cli/handlers/promote.js +440 -28
  65. package/dist/cli/handlers/release.d.ts +91 -0
  66. package/dist/cli/handlers/release.d.ts.map +1 -0
  67. package/dist/cli/handlers/release.js +589 -0
  68. package/dist/cli/handlers/run.d.ts +38 -2
  69. package/dist/cli/handlers/run.d.ts.map +1 -1
  70. package/dist/cli/handlers/run.js +234 -16
  71. package/dist/cli/main.d.ts.map +1 -1
  72. package/dist/cli/main.js +143 -17
  73. package/dist/cli/mcp/workspace-tools.js +1 -1
  74. package/dist/cli/parse-args.d.ts.map +1 -1
  75. package/dist/cli/parse-args.js +73 -5
  76. package/dist/cli/registry.d.ts +43 -5
  77. package/dist/cli/registry.d.ts.map +1 -1
  78. package/dist/cli/run-events.d.ts +30 -0
  79. package/dist/cli/run-events.d.ts.map +1 -0
  80. package/dist/cli/run-events.js +49 -0
  81. package/dist/cli/version.d.ts +3 -5
  82. package/dist/cli/version.d.ts.map +1 -1
  83. package/dist/cli/version.js +3 -13
  84. package/dist/codegen/docs-in-site.d.ts +71 -0
  85. package/dist/codegen/docs-in-site.d.ts.map +1 -0
  86. package/dist/codegen/docs-in-site.js +177 -0
  87. package/dist/codegen/docs-types.d.ts +14 -1
  88. package/dist/codegen/docs-types.d.ts.map +1 -1
  89. package/dist/codegen/docs.d.ts +18 -1
  90. package/dist/codegen/docs.d.ts.map +1 -1
  91. package/dist/codegen/docs.js +40 -7
  92. package/dist/codegen/rehype-base-url.mjs +15 -9
  93. package/dist/components/auto-release.d.ts +6 -1
  94. package/dist/components/auto-release.d.ts.map +1 -1
  95. package/dist/components/auto-release.js +3 -0
  96. package/dist/components/capability.d.ts +9 -0
  97. package/dist/components/capability.d.ts.map +1 -1
  98. package/dist/components/cli-support.d.ts +6 -0
  99. package/dist/components/cli-support.d.ts.map +1 -1
  100. package/dist/components/cli-support.js +10 -9
  101. package/dist/components/driver.d.ts +50 -9
  102. package/dist/components/driver.d.ts.map +1 -1
  103. package/dist/components/driver.js +116 -54
  104. package/dist/components/fan-out-inputs.d.ts +69 -0
  105. package/dist/components/fan-out-inputs.d.ts.map +1 -0
  106. package/dist/components/fan-out-inputs.js +169 -0
  107. package/dist/components/fan-out-record.d.ts +13 -0
  108. package/dist/components/fan-out-record.d.ts.map +1 -1
  109. package/dist/components/fan-out-record.js +16 -0
  110. package/dist/components/fan-out-run.d.ts +7 -0
  111. package/dist/components/fan-out-run.d.ts.map +1 -1
  112. package/dist/components/fan-out-run.js +85 -26
  113. package/dist/components/fan-out.d.ts +4 -0
  114. package/dist/components/fan-out.d.ts.map +1 -1
  115. package/dist/components/pr-pipeline.d.ts +25 -0
  116. package/dist/components/pr-pipeline.d.ts.map +1 -1
  117. package/dist/components/pr-pipeline.js +22 -4
  118. package/dist/components/pr-run.d.ts +11 -0
  119. package/dist/components/pr-run.d.ts.map +1 -1
  120. package/dist/components/pr-run.js +20 -1
  121. package/dist/components/promote-order.d.ts +38 -0
  122. package/dist/components/promote-order.d.ts.map +1 -0
  123. package/dist/components/promote-order.js +62 -0
  124. package/dist/components/promote.d.ts +38 -2
  125. package/dist/components/promote.d.ts.map +1 -1
  126. package/dist/components/promote.js +77 -2
  127. package/dist/components/release-compile.d.ts +142 -0
  128. package/dist/components/release-compile.d.ts.map +1 -0
  129. package/dist/components/release-compile.js +382 -0
  130. package/dist/components/release-gate.d.ts +135 -0
  131. package/dist/components/release-gate.d.ts.map +1 -0
  132. package/dist/components/release-gate.js +288 -0
  133. package/dist/components/release-promote.d.ts +96 -0
  134. package/dist/components/release-promote.d.ts.map +1 -0
  135. package/dist/components/release-promote.js +204 -0
  136. package/dist/components/run-progress.d.ts +7 -0
  137. package/dist/components/run-progress.d.ts.map +1 -1
  138. package/dist/components/wave-plan.d.ts +8 -1
  139. package/dist/components/wave-plan.d.ts.map +1 -1
  140. package/dist/components/wave-plan.js +11 -3
  141. package/dist/config.d.ts +45 -0
  142. package/dist/config.d.ts.map +1 -1
  143. package/dist/config.js +3 -0
  144. package/dist/flag-params.d.ts +54 -0
  145. package/dist/flag-params.d.ts.map +1 -0
  146. package/dist/flag-params.js +130 -0
  147. package/dist/index.d.ts +1 -0
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +2 -0
  150. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
  151. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
  152. package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
  153. package/dist/lifecycle/change-set-store.d.ts +68 -0
  154. package/dist/lifecycle/change-set-store.d.ts.map +1 -0
  155. package/dist/lifecycle/change-set-store.js +97 -0
  156. package/dist/lifecycle/commit-sha.d.ts +13 -0
  157. package/dist/lifecycle/commit-sha.d.ts.map +1 -0
  158. package/dist/lifecycle/commit-sha.js +21 -0
  159. package/dist/lifecycle/delivery-metrics.d.ts +139 -0
  160. package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
  161. package/dist/lifecycle/delivery-metrics.js +145 -0
  162. package/dist/lifecycle/deploy-outcome.d.ts +81 -0
  163. package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
  164. package/dist/lifecycle/deploy-outcome.js +101 -0
  165. package/dist/lifecycle/gate-change-set.d.ts +78 -0
  166. package/dist/lifecycle/gate-change-set.d.ts.map +1 -0
  167. package/dist/lifecycle/gate-change-set.js +138 -0
  168. package/dist/lifecycle/gate-ledger.d.ts +51 -4
  169. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  170. package/dist/lifecycle/gate-ledger.js +18 -0
  171. package/dist/lifecycle/git.d.ts +30 -1
  172. package/dist/lifecycle/git.d.ts.map +1 -1
  173. package/dist/lifecycle/git.js +31 -5
  174. package/dist/lifecycle/index.d.ts +4 -0
  175. package/dist/lifecycle/index.d.ts.map +1 -1
  176. package/dist/lifecycle/index.js +4 -0
  177. package/dist/lifecycle/mixed-environment.d.ts +95 -0
  178. package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
  179. package/dist/lifecycle/mixed-environment.js +154 -0
  180. package/dist/lifecycle/publish-attachments.d.ts +79 -0
  181. package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
  182. package/dist/lifecycle/publish-attachments.js +179 -0
  183. package/dist/lifecycle/publish-credentials.d.ts +37 -0
  184. package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
  185. package/dist/lifecycle/publish-credentials.js +69 -0
  186. package/dist/lifecycle/publish-declaration.d.ts +97 -0
  187. package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
  188. package/dist/lifecycle/publish-declaration.js +83 -0
  189. package/dist/lifecycle/publish-forge.d.ts +53 -0
  190. package/dist/lifecycle/publish-forge.d.ts.map +1 -0
  191. package/dist/lifecycle/publish-forge.js +298 -0
  192. package/dist/lifecycle/publish-npm.d.ts +61 -0
  193. package/dist/lifecycle/publish-npm.d.ts.map +1 -0
  194. package/dist/lifecycle/publish-npm.js +242 -0
  195. package/dist/lifecycle/publish-oci.d.ts +64 -0
  196. package/dist/lifecycle/publish-oci.d.ts.map +1 -0
  197. package/dist/lifecycle/publish-oci.js +383 -0
  198. package/dist/lifecycle/publish-record.d.ts +66 -0
  199. package/dist/lifecycle/publish-record.d.ts.map +1 -0
  200. package/dist/lifecycle/publish-record.js +84 -0
  201. package/dist/lifecycle/publish-targets.d.ts +45 -0
  202. package/dist/lifecycle/publish-targets.d.ts.map +1 -0
  203. package/dist/lifecycle/publish-targets.js +85 -0
  204. package/dist/lifecycle/release-cut.d.ts +154 -0
  205. package/dist/lifecycle/release-cut.d.ts.map +1 -0
  206. package/dist/lifecycle/release-cut.js +366 -0
  207. package/dist/lifecycle/release-join.d.ts +95 -0
  208. package/dist/lifecycle/release-join.d.ts.map +1 -0
  209. package/dist/lifecycle/release-join.js +139 -0
  210. package/dist/lifecycle/release-ledger.d.ts +54 -0
  211. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  212. package/dist/lifecycle/release-ledger.js +53 -20
  213. package/dist/lifecycle/release-publish.d.ts +312 -0
  214. package/dist/lifecycle/release-publish.d.ts.map +1 -0
  215. package/dist/lifecycle/release-publish.js +335 -0
  216. package/dist/lifecycle/release-revocation.d.ts +80 -0
  217. package/dist/lifecycle/release-revocation.d.ts.map +1 -0
  218. package/dist/lifecycle/release-revocation.js +123 -0
  219. package/dist/lifecycle/release-store.d.ts +200 -0
  220. package/dist/lifecycle/release-store.d.ts.map +1 -0
  221. package/dist/lifecycle/release-store.js +320 -0
  222. package/dist/lifecycle/release-verify.d.ts +61 -0
  223. package/dist/lifecycle/release-verify.d.ts.map +1 -0
  224. package/dist/lifecycle/release-verify.js +92 -0
  225. package/dist/lifecycle/release-workstation.d.ts +91 -0
  226. package/dist/lifecycle/release-workstation.d.ts.map +1 -0
  227. package/dist/lifecycle/release-workstation.js +180 -0
  228. package/dist/lifecycle/retention-policy.d.ts +45 -0
  229. package/dist/lifecycle/retention-policy.d.ts.map +1 -0
  230. package/dist/lifecycle/retention-policy.js +43 -0
  231. package/dist/lifecycle/retention.d.ts +185 -0
  232. package/dist/lifecycle/retention.d.ts.map +1 -0
  233. package/dist/lifecycle/retention.js +400 -0
  234. package/dist/lifecycle/run-ledger.d.ts +4 -0
  235. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  236. package/dist/lifecycle/run-ledger.js +2 -0
  237. package/dist/op/activities/change-coverage.d.ts.map +1 -1
  238. package/dist/op/activities/change-coverage.js +4 -2
  239. package/dist/op/activities/source-release.d.ts.map +1 -1
  240. package/dist/op/activities/source-release.js +7 -3
  241. package/dist/op/activities/source-rollback.d.ts.map +1 -1
  242. package/dist/op/activities/source-rollback.js +7 -3
  243. package/dist/op/gate-name.d.ts +11 -2
  244. package/dist/op/gate-name.d.ts.map +1 -1
  245. package/dist/op/gate-name.js +12 -3
  246. package/dist/op/gate-run-locator.d.ts +22 -0
  247. package/dist/op/gate-run-locator.d.ts.map +1 -1
  248. package/dist/op/gate-run-locator.js +31 -0
  249. package/dist/op/gate.d.ts +27 -3
  250. package/dist/op/gate.d.ts.map +1 -1
  251. package/dist/op/gate.js +57 -3
  252. package/dist/op/local-executor.d.ts +36 -1
  253. package/dist/op/local-executor.d.ts.map +1 -1
  254. package/dist/op/local-executor.js +158 -52
  255. package/dist/op/op-waves-run.d.ts.map +1 -1
  256. package/dist/op/op-waves-run.js +4 -0
  257. package/dist/op/run-live.d.ts +2 -0
  258. package/dist/op/run-live.d.ts.map +1 -1
  259. package/dist/op/run-live.js +1 -1
  260. package/dist/op/runtime.d.ts +46 -0
  261. package/dist/op/runtime.d.ts.map +1 -1
  262. package/dist/op/runtimes/local.d.ts.map +1 -1
  263. package/dist/op/runtimes/local.js +8 -0
  264. package/dist/plan-summary.d.ts.map +1 -1
  265. package/dist/plan-summary.js +8 -2
  266. package/dist/point-decision.d.ts +2 -0
  267. package/dist/point-decision.d.ts.map +1 -1
  268. package/dist/point-decision.js +9 -2
  269. package/dist/pr-loop.d.ts +7 -0
  270. package/dist/pr-loop.d.ts.map +1 -1
  271. package/dist/pr-loop.js +23 -4
  272. package/dist/pr-report-url.d.ts +82 -0
  273. package/dist/pr-report-url.d.ts.map +1 -0
  274. package/dist/pr-report-url.js +174 -0
  275. package/dist/provenance.d.ts +2 -2
  276. package/dist/provenance.d.ts.map +1 -1
  277. package/dist/run-event-store.d.ts +104 -0
  278. package/dist/run-event-store.d.ts.map +1 -0
  279. package/dist/run-event-store.js +251 -0
  280. package/dist/run-event-stream.d.ts +133 -0
  281. package/dist/run-event-stream.d.ts.map +1 -0
  282. package/dist/run-event-stream.js +242 -0
  283. package/dist/run-event.d.ts +285 -0
  284. package/dist/run-event.d.ts.map +1 -0
  285. package/dist/run-event.js +209 -0
  286. package/dist/wave-runtime.mjs +262 -226
  287. package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
  288. package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
  289. package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
  290. package/dist/workspace/__fixtures__/run-event-check.d.ts +19 -0
  291. package/dist/workspace/__fixtures__/run-event-check.d.ts.map +1 -0
  292. package/dist/workspace/__fixtures__/run-event-check.js +59 -0
  293. package/dist/workspace/change-set-show.d.ts +56 -0
  294. package/dist/workspace/change-set-show.d.ts.map +1 -0
  295. package/dist/workspace/change-set-show.js +54 -0
  296. package/dist/workspace/change-set-show.schema.json +102 -0
  297. package/dist/workspace/change-set.schema.json +32 -1
  298. package/dist/workspace/changes-cli.d.ts.map +1 -1
  299. package/dist/workspace/changes-cli.js +10 -1
  300. package/dist/workspace/changes.d.ts +37 -0
  301. package/dist/workspace/changes.d.ts.map +1 -1
  302. package/dist/workspace/changes.js +49 -2
  303. package/dist/workspace/changes.schema.json +135 -0
  304. package/dist/workspace/check.schema.json +4 -2
  305. package/dist/workspace/checks/records.d.ts +9 -1
  306. package/dist/workspace/checks/records.d.ts.map +1 -1
  307. package/dist/workspace/checks/records.js +35 -1
  308. package/dist/workspace/checks.d.ts +1 -0
  309. package/dist/workspace/checks.d.ts.map +1 -1
  310. package/dist/workspace/checks.js +2 -1
  311. package/dist/workspace/ci-green-cli.d.ts +19 -4
  312. package/dist/workspace/ci-green-cli.d.ts.map +1 -1
  313. package/dist/workspace/ci-green-cli.js +75 -12
  314. package/dist/workspace/ci-green-forge.d.ts +20 -3
  315. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  316. package/dist/workspace/ci-green-forge.js +6 -4
  317. package/dist/workspace/ci-green-workflow.d.ts +11 -0
  318. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  319. package/dist/workspace/ci-green-workflow.js +6 -0
  320. package/dist/workspace/ci-green.d.ts +281 -13
  321. package/dist/workspace/ci-green.d.ts.map +1 -1
  322. package/dist/workspace/ci-green.js +456 -38
  323. package/dist/workspace/ci-last-green.schema.json +32 -2
  324. package/dist/workspace/ci-list.schema.json +525 -0
  325. package/dist/workspace/commit-links.d.ts +85 -0
  326. package/dist/workspace/commit-links.d.ts.map +1 -0
  327. package/dist/workspace/commit-links.js +163 -0
  328. package/dist/workspace/declaration.d.ts +35 -0
  329. package/dist/workspace/declaration.d.ts.map +1 -1
  330. package/dist/workspace/declaration.js +27 -1
  331. package/dist/workspace/declaration.schema.json +76 -0
  332. package/dist/workspace/declared-kinds.d.ts +16 -0
  333. package/dist/workspace/declared-kinds.d.ts.map +1 -1
  334. package/dist/workspace/declared-kinds.js +25 -0
  335. package/dist/workspace/delivery-metrics.d.ts +91 -0
  336. package/dist/workspace/delivery-metrics.d.ts.map +1 -0
  337. package/dist/workspace/delivery-metrics.js +217 -0
  338. package/dist/workspace/delivery-metrics.schema.json +519 -0
  339. package/dist/workspace/flags-read.d.ts +37 -0
  340. package/dist/workspace/flags-read.d.ts.map +1 -0
  341. package/dist/workspace/flags-read.js +103 -0
  342. package/dist/workspace/flags.d.ts +100 -0
  343. package/dist/workspace/flags.d.ts.map +1 -0
  344. package/dist/workspace/flags.js +98 -0
  345. package/dist/workspace/graph-cli.js +1 -1
  346. package/dist/workspace/identity-gate-rule.d.ts +37 -0
  347. package/dist/workspace/identity-gate-rule.d.ts.map +1 -1
  348. package/dist/workspace/identity-gate-rule.js +72 -7
  349. package/dist/workspace/identity.d.ts +12 -0
  350. package/dist/workspace/identity.d.ts.map +1 -1
  351. package/dist/workspace/identity.js +13 -1
  352. package/dist/workspace/intent-cli.d.ts +11 -2
  353. package/dist/workspace/intent-cli.d.ts.map +1 -1
  354. package/dist/workspace/intent-cli.js +54 -5
  355. package/dist/workspace/intent-trace.d.ts +219 -0
  356. package/dist/workspace/intent-trace.d.ts.map +1 -0
  357. package/dist/workspace/intent-trace.js +314 -0
  358. package/dist/workspace/intent.d.ts +36 -7
  359. package/dist/workspace/intent.d.ts.map +1 -1
  360. package/dist/workspace/intent.js +192 -22
  361. package/dist/workspace/intent.schema.json +702 -6
  362. package/dist/workspace/ls.d.ts +4 -0
  363. package/dist/workspace/ls.d.ts.map +1 -1
  364. package/dist/workspace/ls.js +2 -0
  365. package/dist/workspace/ls.schema.json +3 -1
  366. package/dist/workspace/member-pipeline.d.ts +6 -0
  367. package/dist/workspace/member-pipeline.d.ts.map +1 -1
  368. package/dist/workspace/member-pipeline.js +6 -0
  369. package/dist/workspace/overlap.d.ts +146 -0
  370. package/dist/workspace/overlap.d.ts.map +1 -0
  371. package/dist/workspace/overlap.js +276 -0
  372. package/dist/workspace/promotion-order.d.ts +41 -0
  373. package/dist/workspace/promotion-order.d.ts.map +1 -0
  374. package/dist/workspace/promotion-order.js +95 -0
  375. package/dist/workspace/reason-codes.d.ts +4 -0
  376. package/dist/workspace/reason-codes.d.ts.map +1 -1
  377. package/dist/workspace/reason-codes.js +7 -1
  378. package/dist/workspace/records.d.ts +15 -2
  379. package/dist/workspace/records.d.ts.map +1 -1
  380. package/dist/workspace/records.js +32 -0
  381. package/dist/workspace/records.schema.json +21 -2
  382. package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  383. package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
  384. package/dist/workspace/releases.d.ts +81 -0
  385. package/dist/workspace/releases.d.ts.map +1 -0
  386. package/dist/workspace/releases.js +103 -0
  387. package/dist/workspace/releases.schema.json +513 -0
  388. package/dist/workspace/run-event.schema.json +295 -0
  389. package/dist/workspace/status-gates.d.ts +34 -1
  390. package/dist/workspace/status-gates.d.ts.map +1 -1
  391. package/dist/workspace/status-gates.js +31 -2
  392. package/dist/workspace/status-stewards.d.ts +4 -0
  393. package/dist/workspace/status-stewards.d.ts.map +1 -1
  394. package/dist/workspace/status-stewards.js +2 -0
  395. package/dist/workspace/status.d.ts +117 -0
  396. package/dist/workspace/status.d.ts.map +1 -1
  397. package/dist/workspace/status.js +218 -4
  398. package/dist/workspace/status.schema.json +268 -2
  399. package/dist/workspace/trust/ci-tag-statement.d.ts +102 -0
  400. package/dist/workspace/trust/ci-tag-statement.d.ts.map +1 -0
  401. package/dist/workspace/trust/ci-tag-statement.js +123 -0
  402. package/dist/workspace/trust/op-run-statement.d.ts +91 -0
  403. package/dist/workspace/trust/op-run-statement.d.ts.map +1 -0
  404. package/dist/workspace/trust/op-run-statement.js +143 -0
  405. package/dist/workspace/trust/rotation.d.ts +18 -2
  406. package/dist/workspace/trust/rotation.d.ts.map +1 -1
  407. package/dist/workspace/trust/rotation.js +65 -16
  408. package/dist/workspace/trust/seal.d.ts +11 -0
  409. package/dist/workspace/trust/seal.d.ts.map +1 -1
  410. package/dist/workspace/trust/seal.js +9 -0
  411. package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
  412. package/dist/workspace/trust/ssh-commit.js +10 -10
  413. package/dist/workspace/wip.d.ts.map +1 -1
  414. package/dist/workspace/wip.js +11 -3
  415. package/package.json +1 -1
  416. package/src/__golden__/wave-loop/fan-out-gated.json +8 -0
  417. package/src/__golden__/wave-loop/fan-out-no-members.json +4 -0
  418. package/src/__golden__/wave-loop/fan-out-only.json +6 -0
  419. package/src/__golden__/wave-loop/op-by-apply-hold.json +2 -0
  420. package/src/__golden__/wave-loop/op-cost-member.json +3 -0
  421. package/src/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
  422. package/src/__golden__/wave-loop/op-held-on.json +2 -0
  423. package/src/__golden__/wave-loop/op-hooks-held.json +2 -0
  424. package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
  425. package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
  426. package/src/__golden__/wave-loop/op-hooks-share.json +3 -0
  427. package/src/__golden__/wave-loop/op-per-run.json +7 -0
  428. package/src/__golden__/wave-loop/op-pr-review.json +7 -0
  429. package/src/__golden__/wave-loop/op-sealed.json +3 -0
  430. package/src/__golden__/wave-loop/op-spend.json +3 -0
  431. package/src/__golden__/wave-loop/op-wave-commands.json +7 -0
  432. package/src/__golden__/wave-loop/op-wide-waiting.json +4 -0
  433. package/src/build-params.test.ts +46 -0
  434. package/src/build-params.ts +55 -1
  435. package/src/change-set-graph.test.ts +72 -0
  436. package/src/change-set-graph.ts +141 -0
  437. package/src/change-set.ts +87 -6
  438. package/src/chant-version.ts +16 -0
  439. package/src/cli/build-params-cli.ts +22 -9
  440. package/src/cli/commands/build.ts +25 -11
  441. package/src/cli/commands/check-lexicon-docs.test.ts +51 -0
  442. package/src/cli/commands/check-lexicon-docs.ts +39 -2
  443. package/src/cli/commands/check-lexicon.ts +14 -2
  444. package/src/cli/commands/init-lexicon/templates/docs.ts +7 -6
  445. package/src/cli/commands/init-lexicon.test.ts +25 -0
  446. package/src/cli/commands/init-lexicon.ts +18 -8
  447. package/src/cli/handlers/approve-release.test.ts +84 -0
  448. package/src/cli/handlers/approve-revoked.test.ts +155 -0
  449. package/src/cli/handlers/approve.ts +65 -2
  450. package/src/cli/handlers/build.test.ts +67 -0
  451. package/src/cli/handlers/build.ts +46 -4
  452. package/src/cli/handlers/change-set.ts +49 -1
  453. package/src/cli/handlers/fan-out.test.ts +228 -1
  454. package/src/cli/handlers/fan-out.ts +53 -6
  455. package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
  456. package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
  457. package/src/cli/handlers/lifecycle-gc.ts +146 -0
  458. package/src/cli/handlers/operator.test.ts +37 -0
  459. package/src/cli/handlers/pr.test.ts +341 -0
  460. package/src/cli/handlers/pr.ts +292 -47
  461. package/src/cli/handlers/promote.test.ts +669 -7
  462. package/src/cli/handlers/promote.ts +464 -29
  463. package/src/cli/handlers/release-implicit.e2e.test.ts +161 -0
  464. package/src/cli/handlers/release-join.e2e.test.ts +185 -0
  465. package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
  466. package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
  467. package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
  468. package/src/cli/handlers/release.e2e.test.ts +199 -0
  469. package/src/cli/handlers/release.ts +593 -0
  470. package/src/cli/handlers/run-auto-release.test.ts +80 -0
  471. package/src/cli/handlers/run-events-cmd.test.ts +92 -0
  472. package/src/cli/handlers/run-release.test.ts +55 -0
  473. package/src/cli/handlers/run.test.ts +30 -0
  474. package/src/cli/handlers/run.ts +243 -18
  475. package/src/cli/main.test.ts +8 -0
  476. package/src/cli/main.ts +143 -17
  477. package/src/cli/mcp/never-over-mcp.test.ts +2 -0
  478. package/src/cli/mcp/workspace-tools.ts +1 -1
  479. package/src/cli/parse-args.ts +56 -5
  480. package/src/cli/registry.ts +43 -5
  481. package/src/cli/run-events.ts +57 -0
  482. package/src/cli/version.ts +3 -13
  483. package/src/codegen/docs-in-site.test.ts +166 -0
  484. package/src/codegen/docs-in-site.ts +182 -0
  485. package/src/codegen/docs-site/AgentPrompt.astro +26 -1
  486. package/src/codegen/docs-types.ts +14 -1
  487. package/src/codegen/docs.ts +60 -7
  488. package/src/codegen/rehype-base-url.d.mts +3 -1
  489. package/src/codegen/rehype-base-url.mjs +15 -9
  490. package/src/codegen/rehype-base-url.test.ts +34 -0
  491. package/src/components/auto-release.test.ts +30 -0
  492. package/src/components/auto-release.ts +9 -1
  493. package/src/components/capability.ts +9 -0
  494. package/src/components/cli-support.test.ts +82 -0
  495. package/src/components/cli-support.ts +16 -9
  496. package/src/components/driver.test.ts +24 -0
  497. package/src/components/driver.ts +165 -56
  498. package/src/components/fan-out-inputs.test.ts +85 -0
  499. package/src/components/fan-out-inputs.ts +192 -0
  500. package/src/components/fan-out-record.test.ts +10 -0
  501. package/src/components/fan-out-record.ts +19 -0
  502. package/src/components/fan-out-run.ts +104 -35
  503. package/src/components/fan-out.ts +4 -0
  504. package/src/components/pr-pipeline.ts +35 -4
  505. package/src/components/pr-run.ts +30 -1
  506. package/src/components/promote-order.test.ts +90 -0
  507. package/src/components/promote-order.ts +75 -0
  508. package/src/components/promote.test.ts +10 -1
  509. package/src/components/promote.ts +94 -3
  510. package/src/components/release-compile.test.ts +147 -0
  511. package/src/components/release-compile.ts +459 -0
  512. package/src/components/release-gate.test.ts +215 -0
  513. package/src/components/release-gate.ts +355 -0
  514. package/src/components/release-promote.test.ts +159 -0
  515. package/src/components/release-promote.ts +232 -0
  516. package/src/components/run-progress.ts +7 -0
  517. package/src/components/wave-plan.ts +16 -3
  518. package/src/config.ts +15 -1
  519. package/src/flag-params.test.ts +144 -0
  520. package/src/flag-params.ts +134 -0
  521. package/src/index.ts +2 -0
  522. package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
  523. package/src/lifecycle/change-set-store.test.ts +217 -0
  524. package/src/lifecycle/change-set-store.ts +113 -0
  525. package/src/lifecycle/commit-sha.ts +23 -0
  526. package/src/lifecycle/delivery-metrics.ts +263 -0
  527. package/src/lifecycle/deploy-outcome.ts +147 -0
  528. package/src/lifecycle/gate-change-set.e2e.test.ts +122 -0
  529. package/src/lifecycle/gate-change-set.ts +164 -0
  530. package/src/lifecycle/gate-ledger.test.ts +33 -0
  531. package/src/lifecycle/gate-ledger.ts +68 -3
  532. package/src/lifecycle/git.ts +61 -8
  533. package/src/lifecycle/index.ts +4 -0
  534. package/src/lifecycle/mixed-environment.test.ts +97 -0
  535. package/src/lifecycle/mixed-environment.ts +204 -0
  536. package/src/lifecycle/publish-attachments.ts +196 -0
  537. package/src/lifecycle/publish-credentials.ts +84 -0
  538. package/src/lifecycle/publish-declaration.ts +95 -0
  539. package/src/lifecycle/publish-forge.ts +325 -0
  540. package/src/lifecycle/publish-npm.ts +259 -0
  541. package/src/lifecycle/publish-oci.ts +396 -0
  542. package/src/lifecycle/publish-record.ts +113 -0
  543. package/src/lifecycle/publish-resume.test.ts +168 -0
  544. package/src/lifecycle/publish-targets.test.ts +494 -0
  545. package/src/lifecycle/publish-targets.ts +100 -0
  546. package/src/lifecycle/release-cut-signed.test.ts +180 -0
  547. package/src/lifecycle/release-cut.test.ts +483 -0
  548. package/src/lifecycle/release-cut.ts +503 -0
  549. package/src/lifecycle/release-flag.test.ts +112 -0
  550. package/src/lifecycle/release-join.test.ts +68 -0
  551. package/src/lifecycle/release-join.ts +182 -0
  552. package/src/lifecycle/release-ledger.test.ts +14 -0
  553. package/src/lifecycle/release-ledger.ts +85 -17
  554. package/src/lifecycle/release-publish.test.ts +225 -0
  555. package/src/lifecycle/release-publish.ts +568 -0
  556. package/src/lifecycle/release-revocation.ts +147 -0
  557. package/src/lifecycle/release-store.ts +434 -0
  558. package/src/lifecycle/release-verify.test.ts +110 -0
  559. package/src/lifecycle/release-verify.ts +120 -0
  560. package/src/lifecycle/release-workstation.test.ts +204 -0
  561. package/src/lifecycle/release-workstation.ts +216 -0
  562. package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
  563. package/src/lifecycle/retention-policy.ts +63 -0
  564. package/src/lifecycle/retention.test.ts +363 -0
  565. package/src/lifecycle/retention.ts +508 -0
  566. package/src/lifecycle/run-ledger.test.ts +10 -0
  567. package/src/lifecycle/run-ledger.ts +6 -0
  568. package/src/op/activities/change-coverage.ts +4 -1
  569. package/src/op/activities/source-release.ts +8 -3
  570. package/src/op/activities/source-rollback.ts +8 -4
  571. package/src/op/commit-run-gate-deploy-join.test.ts +117 -0
  572. package/src/op/gate-name.ts +13 -3
  573. package/src/op/gate-release.test.ts +126 -0
  574. package/src/op/gate-run-locator.test.ts +110 -0
  575. package/src/op/gate-run-locator.ts +35 -0
  576. package/src/op/gate.ts +88 -6
  577. package/src/op/local-executor.ts +207 -52
  578. package/src/op/op-waves-run.ts +4 -0
  579. package/src/op/run-events.test.ts +166 -0
  580. package/src/op/run-live.ts +1 -1
  581. package/src/op/runtime.ts +39 -0
  582. package/src/op/runtimes/local.test.ts +16 -0
  583. package/src/op/runtimes/local.ts +9 -0
  584. package/src/plan-summary.test.ts +20 -0
  585. package/src/plan-summary.ts +8 -1
  586. package/src/point-decision.ts +12 -2
  587. package/src/pr-loop.ts +25 -4
  588. package/src/pr-report-url.test.ts +131 -0
  589. package/src/pr-report-url.ts +209 -0
  590. package/src/provenance.ts +2 -2
  591. package/src/run-event-store.test.ts +144 -0
  592. package/src/run-event-store.ts +273 -0
  593. package/src/run-event-stream.test.ts +175 -0
  594. package/src/run-event-stream.ts +309 -0
  595. package/src/run-event.test.ts +277 -0
  596. package/src/run-event.ts +471 -0
  597. package/src/wave-loop-golden.test.ts +9 -0
  598. package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
  599. package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
  600. package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
  601. package/src/workspace/__fixtures__/run-event-check.ts +58 -0
  602. package/src/workspace/ancestry-index.test.ts +93 -0
  603. package/src/workspace/change-set-show.schema.json +102 -0
  604. package/src/workspace/change-set-show.ts +72 -0
  605. package/src/workspace/change-set.schema.json +32 -1
  606. package/src/workspace/changes-cli.ts +10 -1
  607. package/src/workspace/changes.schema.json +135 -0
  608. package/src/workspace/changes.test.ts +1 -1
  609. package/src/workspace/changes.ts +84 -2
  610. package/src/workspace/check.schema.json +4 -2
  611. package/src/workspace/checks/records.ts +35 -1
  612. package/src/workspace/checks.ts +2 -1
  613. package/src/workspace/ci-green-cli.ts +75 -13
  614. package/src/workspace/ci-green-forge.ts +24 -4
  615. package/src/workspace/ci-green-runs.test.ts +242 -0
  616. package/src/workspace/ci-green-signed.test.ts +302 -0
  617. package/src/workspace/ci-green-workflow.test.ts +16 -0
  618. package/src/workspace/ci-green-workflow.ts +12 -0
  619. package/src/workspace/ci-green.test.ts +131 -2
  620. package/src/workspace/ci-green.ts +635 -42
  621. package/src/workspace/ci-last-green.schema.json +32 -2
  622. package/src/workspace/ci-list.schema.json +525 -0
  623. package/src/workspace/commit-links.test.ts +279 -0
  624. package/src/workspace/commit-links.ts +183 -0
  625. package/src/workspace/declaration.schema.json +76 -0
  626. package/src/workspace/declaration.test.ts +28 -0
  627. package/src/workspace/declaration.ts +60 -2
  628. package/src/workspace/declared-kinds.ts +40 -1
  629. package/src/workspace/delivery-metrics.schema.json +519 -0
  630. package/src/workspace/delivery-metrics.test.ts +236 -0
  631. package/src/workspace/delivery-metrics.ts +286 -0
  632. package/src/workspace/flags-read.ts +121 -0
  633. package/src/workspace/flags.test.ts +141 -0
  634. package/src/workspace/flags.ts +157 -0
  635. package/src/workspace/graph-cli.ts +1 -1
  636. package/src/workspace/identity-gate-rule.ts +91 -7
  637. package/src/workspace/identity.ts +14 -1
  638. package/src/workspace/intent-cli.ts +46 -5
  639. package/src/workspace/intent-trace.test.ts +173 -0
  640. package/src/workspace/intent-trace.ts +484 -0
  641. package/src/workspace/intent.schema.json +702 -6
  642. package/src/workspace/intent.ts +196 -26
  643. package/src/workspace/ls.schema.json +3 -1
  644. package/src/workspace/ls.ts +6 -0
  645. package/src/workspace/member-pipeline.test.ts +17 -0
  646. package/src/workspace/member-pipeline.ts +9 -0
  647. package/src/workspace/older-records.test.ts +412 -0
  648. package/src/workspace/overlap.test.ts +165 -0
  649. package/src/workspace/overlap.ts +340 -0
  650. package/src/workspace/promotion-order.ts +97 -0
  651. package/src/workspace/read-contract.e2e.test.ts +5 -1
  652. package/src/workspace/reason-codes.test.ts +7 -1
  653. package/src/workspace/reason-codes.ts +7 -1
  654. package/src/workspace/record-assets.test.ts +2 -1
  655. package/src/workspace/records-contract.e2e.test.ts +2 -1
  656. package/src/workspace/records.schema.json +21 -2
  657. package/src/workspace/records.ts +36 -2
  658. package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  659. package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
  660. package/src/workspace/releases.schema.json +513 -0
  661. package/src/workspace/releases.test.ts +108 -0
  662. package/src/workspace/releases.ts +133 -0
  663. package/src/workspace/run-event.schema.json +295 -0
  664. package/src/workspace/status-contract.test.ts +81 -1
  665. package/src/workspace/status-gates.ts +64 -1
  666. package/src/workspace/status-mixed.test.ts +152 -0
  667. package/src/workspace/status-revoked.test.ts +153 -0
  668. package/src/workspace/status-stewards.ts +6 -0
  669. package/src/workspace/status.schema.json +268 -2
  670. package/src/workspace/status.ts +334 -4
  671. package/src/workspace/trust/ci-tag-statement.ts +151 -0
  672. package/src/workspace/trust/op-run-statement.ts +148 -0
  673. package/src/workspace/trust/rotation.ts +61 -14
  674. package/src/workspace/trust/seal.ts +10 -0
  675. package/src/workspace/trust/ssh-commit.ts +10 -9
  676. package/src/workspace/wip.ts +11 -2
  677. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +0 -14
  678. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +0 -16
  679. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +0 -8
  680. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +0 -15
  681. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +0 -10
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Whether an environment runs a mix of releases, and the lines that settle
3
+ * it (#4035). Derived when read, from the environment's release ledger and
4
+ * its deploy outcomes (./deploy-outcome.ts); nothing here is written.
5
+ *
6
+ * Two things make an environment mixed:
7
+ *
8
+ * - a component is unsettled: the newest promote, rollback or redeploy that
9
+ * named it left it `failed` or `gated`, and the release ledger has no
10
+ * record of it newer than that run, so what it runs is not known;
11
+ * - the latest records of its components name more than one release, a
12
+ * record of no release counting as one value, and at least one names a
13
+ * release.
14
+ *
15
+ * The ledger already says per component what deployed, and with which
16
+ * release, so the second needs no record of its own. Only the first needs
17
+ * the outcome: a failed deploy writes no release record, by design, so the
18
+ * ledger alone can't tell a component that failed partway from one that was
19
+ * never touched.
20
+ */
21
+
22
+ import type { ReleaseRecord } from "./release-ledger";
23
+ import type { DeployOutcome } from "./deploy-outcome";
24
+
25
+ /** One component of a mixed environment. */
26
+ export interface MixedComponent {
27
+ component: string;
28
+ /** The digest the release ledger records the component running, or null when it has no record in the environment. */
29
+ digest: string | null;
30
+ /** The release that record names, or null. */
31
+ releaseDigest: string | null;
32
+ releaseLabel: string | null;
33
+ /** `settled` when the ledger's record is what it runs; `failed` or `gated` when the newest run that named it left it so. */
34
+ state: "settled" | "failed" | "gated";
35
+ /** For an unsettled component, the run that left it so and what that run was deploying. Null when settled. */
36
+ attempt: {
37
+ verb: DeployOutcome["verb"];
38
+ runId: string;
39
+ timestamp: string;
40
+ digest: string;
41
+ releaseDigest: string | null;
42
+ releaseLabel: string | null;
43
+ } | null;
44
+ }
45
+
46
+ export interface MixedEnvironment {
47
+ /** Every component the ledger records or a run left unsettled, sorted by name. */
48
+ components: MixedComponent[];
49
+ /** `chant components redeploy <env> --component <c>` for each unsettled component the ledger records, which puts its recorded release back. */
50
+ redeploy: string[];
51
+ /** `chant components rollback <env> --release <label|digest>`, naming the release to go back to, or null when there is none to name. */
52
+ rollback: string | null;
53
+ }
54
+
55
+ /** What a reader knows about a release a rollback could go back to. */
56
+ export interface ReleaseCheck {
57
+ /** Its release file can still be read, so its templates can be deployed. */
58
+ kept: boolean;
59
+ /** Its commit is revoked (#4033), so it is refused. */
60
+ revoked: boolean;
61
+ }
62
+
63
+ function latest(records: readonly ReleaseRecord[]): Map<string, ReleaseRecord> {
64
+ const out = new Map<string, ReleaseRecord>();
65
+ for (const r of records) {
66
+ const current = out.get(r.component);
67
+ if (!current || r.timestamp >= current.timestamp) out.set(r.component, r);
68
+ }
69
+ return out;
70
+ }
71
+
72
+ /**
73
+ * The components a run left unsettled: for each, the newest outcome naming it
74
+ * `failed` or `gated` that no release record of it is newer than.
75
+ */
76
+ export function unsettledComponents(
77
+ records: readonly ReleaseRecord[],
78
+ outcomes: readonly DeployOutcome[],
79
+ ): Map<string, { outcome: DeployOutcome; state: "failed" | "gated"; digest: string; releaseDigest: string | null }> {
80
+ const newest = new Map<string, { outcome: DeployOutcome; state: "failed" | "gated"; digest: string; releaseDigest: string | null }>();
81
+ for (const outcome of outcomes) {
82
+ for (const c of outcome.components) {
83
+ if (c.outcome !== "failed" && c.outcome !== "gated") continue;
84
+ const seen = newest.get(c.component);
85
+ if (!seen || outcome.timestamp >= seen.outcome.timestamp) {
86
+ newest.set(c.component, { outcome, state: c.outcome, digest: c.digest, releaseDigest: c.releaseDigest });
87
+ }
88
+ }
89
+ }
90
+ const recorded = latest(records);
91
+ for (const [component, u] of newest) {
92
+ const r = recorded.get(component);
93
+ if (r && r.timestamp > u.outcome.timestamp) newest.delete(component);
94
+ }
95
+ return newest;
96
+ }
97
+
98
+ /**
99
+ * The newest release the environment's ledger names that a rollback can go
100
+ * back to: not in `exclude`, still kept, and not revoked. With `before`, only
101
+ * records older than that time count. Null when there is none.
102
+ */
103
+ export async function rollbackTarget(input: {
104
+ records: readonly ReleaseRecord[];
105
+ exclude: ReadonlySet<string>;
106
+ before?: string;
107
+ check: (releaseDigest: string) => Promise<ReleaseCheck>;
108
+ }): Promise<string | null> {
109
+ const ordered = input.records
110
+ .map((r, i) => ({ r, i }))
111
+ .filter(({ r }) => r.releaseDigest !== undefined && (input.before === undefined || r.timestamp < input.before))
112
+ .sort((a, b) => (a.r.timestamp === b.r.timestamp ? b.i - a.i : a.r.timestamp < b.r.timestamp ? 1 : -1));
113
+ const seen = new Set<string>();
114
+ for (const { r } of ordered) {
115
+ const digest = r.releaseDigest!;
116
+ if (seen.has(digest) || input.exclude.has(digest)) continue;
117
+ seen.add(digest);
118
+ const check = await input.check(digest);
119
+ if (check.kept && !check.revoked) return digest;
120
+ }
121
+ return null;
122
+ }
123
+
124
+ /** The line that rolls `env` back to a release, by its label when it has one. */
125
+ export function releaseRollbackLine(env: string, releaseDigest: string, label: string | null | undefined): string {
126
+ return `chant components rollback ${env} --release ${label ?? releaseDigest}`;
127
+ }
128
+
129
+ /** The line that redeploys one component's recorded release in `env`. */
130
+ export function redeployLine(env: string, component: string): string {
131
+ return `chant components redeploy ${env} --component ${component}`;
132
+ }
133
+
134
+ /**
135
+ * The environment's mixed state, or null when it runs one release (or no
136
+ * release at all) and no component is unsettled.
137
+ */
138
+ export async function mixedEnvironment(input: {
139
+ env: string;
140
+ records: readonly ReleaseRecord[];
141
+ outcomes: readonly DeployOutcome[];
142
+ /** A release's label, or undefined. */
143
+ label: (releaseDigest: string) => Promise<string | undefined>;
144
+ check: (releaseDigest: string) => Promise<ReleaseCheck>;
145
+ }): Promise<MixedEnvironment | null> {
146
+ const { env, records } = input;
147
+ const recorded = latest(records);
148
+ const unsettled = unsettledComponents(records, input.outcomes);
149
+ const releases = new Set([...recorded.values()].map((r) => r.releaseDigest ?? null));
150
+ const divergent = releases.size > 1 && [...releases].some((d) => d !== null);
151
+ if (unsettled.size === 0 && !divergent) return null;
152
+
153
+ const labelOf = async (digest: string | null | undefined): Promise<string | null> => (digest ? ((await input.label(digest)) ?? null) : null);
154
+ const names = [...new Set([...recorded.keys(), ...unsettled.keys()])].sort();
155
+ const components: MixedComponent[] = [];
156
+ for (const component of names) {
157
+ const r = recorded.get(component);
158
+ const u = unsettled.get(component);
159
+ components.push({
160
+ component,
161
+ digest: r?.digest ?? null,
162
+ releaseDigest: r?.releaseDigest ?? null,
163
+ releaseLabel: await labelOf(r?.releaseDigest),
164
+ state: u ? u.state : "settled",
165
+ attempt: u
166
+ ? {
167
+ verb: u.outcome.verb,
168
+ runId: u.outcome.runId,
169
+ timestamp: u.outcome.timestamp,
170
+ digest: u.digest,
171
+ releaseDigest: u.releaseDigest,
172
+ releaseLabel: await labelOf(u.releaseDigest),
173
+ }
174
+ : null,
175
+ });
176
+ }
177
+
178
+ // The release the environment was moving to is not one to go back to: the
179
+ // one a run left components unsettled on, else, of the releases its
180
+ // components run, the one that reached the environment last.
181
+ const exclude = new Set<string>();
182
+ if (unsettled.size > 0) {
183
+ for (const u of unsettled.values()) if (u.releaseDigest) exclude.add(u.releaseDigest);
184
+ } else {
185
+ const firstSeen = new Map<string, string>();
186
+ for (const r of records) {
187
+ if (r.releaseDigest === undefined) continue;
188
+ const seen = firstSeen.get(r.releaseDigest);
189
+ if (seen === undefined || r.timestamp < seen) firstSeen.set(r.releaseDigest, r.timestamp);
190
+ }
191
+ let arrived: string | undefined;
192
+ for (const d of releases) {
193
+ if (d === null) continue;
194
+ if (arrived === undefined || firstSeen.get(d)! > firstSeen.get(arrived)!) arrived = d;
195
+ }
196
+ if (arrived !== undefined) exclude.add(arrived);
197
+ }
198
+ const target = await rollbackTarget({ records, exclude, check: input.check });
199
+ return {
200
+ components,
201
+ redeploy: components.filter((c) => c.state !== "settled" && c.digest !== null).map((c) => redeployLine(env, c.component)),
202
+ rollback: target ? releaseRollbackLine(env, target, await labelOf(target)) : null,
203
+ };
204
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * What a publish attaches to what it publishes (#4049): an SBOM and the
3
+ * release's provenance statement.
4
+ *
5
+ * The provenance statement is an in-toto Statement v1 whose predicate
6
+ * (`https://intentius.io/chant/release/v1`) names the release, its commit,
7
+ * and the cut that signed it, with the cut's ssh signature in the
8
+ * `chant-release` namespace. Anyone holding the repository checks that
9
+ * signature offline with `chant release verify`, against
10
+ * `.chant/allowed_signers` at the release's commit. The predicate also says
11
+ * whether the run had an OIDC token: in CI, the provenance a registry records
12
+ * (npm's `--provenance`) is signed with it, and from a workstation the
13
+ * release's signature is the only provenance there is.
14
+ *
15
+ * The SBOM is an SPDX or CycloneDX JSON document. A target attaches the file
16
+ * its declaration's `sbom` names; with none named, the OCI and forge targets
17
+ * write one from the member's `package-lock.json` or `pom.xml` with the
18
+ * hermetic writer `generate-sbom` uses (../components/verbs/bom-writer.ts),
19
+ * dated with the cut's time, so a publish run again writes the same bytes.
20
+ * `sbom: false` publishes without one. The npm target carries its SBOM in
21
+ * each package instead, since a registry keeps no file beside a package
22
+ * (./publish-npm.ts).
23
+ *
24
+ * Both are written as sorted JSON, so the same release attaches the same
25
+ * bytes on every run, and a resumed stage finds what an earlier run sent.
26
+ */
27
+
28
+ import { execFileSync } from "node:child_process";
29
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
30
+ import { dirname, join } from "node:path";
31
+ import { sortedJsonReplacer } from "../utils";
32
+ import { writeBom } from "../components/verbs/bom-writer";
33
+ import { parseNpmPackageLock, parsePomXml } from "../components/verbs/lockfile-sbom-generator";
34
+ import { SBOM_MEDIA_TYPES } from "../components/verbs/sbom-generator";
35
+ import { bytesDigest, checkReleaseDigest, memberFile } from "./publish-targets";
36
+ import { PublishTargetError, type PublishContext } from "./release-publish";
37
+ import { RELEASE_SEAL_NAMESPACE } from "../workspace/identity-gate-rule";
38
+
39
+ /** The in-toto statement type the provenance statement is. */
40
+ export const IN_TOTO_STATEMENT_TYPE = "https://in-toto.io/Statement/v1";
41
+ /** The predicate type of chant's release provenance. */
42
+ export const RELEASE_PREDICATE_TYPE = "https://intentius.io/chant/release/v1";
43
+ /** The media type an in-toto statement is attached under. */
44
+ export const IN_TOTO_MEDIA_TYPE = "application/vnd.in-toto+json";
45
+ /** The file name the provenance statement is attached under. */
46
+ export const PROVENANCE_FILE = "release.intoto.json";
47
+ /** The file name of an SBOM chant writes from a lockfile. */
48
+ export const GENERATED_SBOM_FILE = "sbom.spdx.json";
49
+ /** The names an npm package's own SBOM is looked for under, at its root, when the declaration names none. */
50
+ export const PACKAGE_SBOM_FILES = ["sbom.spdx.json", "sbom.cdx.json", "sbom.cyclonedx.json"] as const;
51
+
52
+ /** One file attached to what a target publishes. */
53
+ export interface Attachment {
54
+ kind: "sbom" | "provenance";
55
+ /** The file name it is attached under. */
56
+ name: string;
57
+ mediaType: string;
58
+ bytes: Uint8Array;
59
+ digest: string;
60
+ }
61
+
62
+ const json = (value: unknown): Uint8Array => new Uint8Array(Buffer.from(`${JSON.stringify(value, sortedJsonReplacer, 2)}\n`, "utf-8"));
63
+
64
+ const attachment = (kind: Attachment["kind"], name: string, mediaType: string, bytes: Uint8Array): Attachment => ({ kind, name, mediaType, bytes, digest: bytesDigest(bytes) });
65
+
66
+ /**
67
+ * The provenance statement for `subjects`, the digests of what the target
68
+ * publishes. Every value in it comes from the release, its cut and the run's
69
+ * OIDC check, never from the clock, so it is the same bytes on every run.
70
+ */
71
+ export function releaseProvenance(ctx: PublishContext, subjects: ReadonlyArray<{ name: string; digest: string }>): Attachment {
72
+ const cut = ctx.cut ?? null;
73
+ const statement = {
74
+ _type: IN_TOTO_STATEMENT_TYPE,
75
+ subject: subjects.map((s) => ({ name: s.name, digest: { [s.digest.split(":")[0] ?? "sha256"]: s.digest.split(":")[1] ?? s.digest } })),
76
+ predicateType: RELEASE_PREDICATE_TYPE,
77
+ predicate: {
78
+ releaseDigest: ctx.release.releaseDigest,
79
+ label: ctx.label,
80
+ member: ctx.release.member,
81
+ commit: ctx.release.commit,
82
+ cut: cut ? { actor: cut.actor, label: cut.label, timestamp: cut.timestamp, runId: cut.runId, signature: cut.signature, namespace: RELEASE_SEAL_NAMESPACE } : null,
83
+ provenance: ctx.provenance.oidc ? { kind: "oidc", ci: ctx.provenance.ci } : { kind: "signature", ci: ctx.provenance.ci },
84
+ },
85
+ };
86
+ return attachment("provenance", PROVENANCE_FILE, IN_TOTO_MEDIA_TYPE, json(statement));
87
+ }
88
+
89
+ /** The media type of an SBOM document, or null when `bytes` is neither SPDX nor CycloneDX JSON. */
90
+ export function sbomMediaType(bytes: Uint8Array): string | null {
91
+ let doc: { spdxVersion?: unknown; bomFormat?: unknown };
92
+ try {
93
+ doc = JSON.parse(Buffer.from(bytes).toString("utf-8")) as typeof doc;
94
+ } catch {
95
+ return null;
96
+ }
97
+ if (!doc || typeof doc !== "object") return null;
98
+ if (typeof doc.spdxVersion === "string") return SBOM_MEDIA_TYPES.spdx;
99
+ if (doc.bomFormat === "CycloneDX") return SBOM_MEDIA_TYPES.cyclonedx;
100
+ return null;
101
+ }
102
+
103
+ function invalid(where: string): PublishTargetError {
104
+ return new PublishTargetError("publish-sbom-invalid", `${where} is not an SPDX or CycloneDX JSON document`, "Point sbom at the SPDX or CycloneDX JSON the build writes.");
105
+ }
106
+
107
+ /** The lockfile nearest the member: `package-lock.json` or `pom.xml` in `cwd` or a directory above it inside its repository (only `cwd` outside one). */
108
+ function nearestLockfile(cwd: string): { kind: "npm" | "maven"; path: string } | null {
109
+ let top: string | null = null;
110
+ try {
111
+ top = realpathSync(execFileSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim());
112
+ } catch {
113
+ top = null;
114
+ }
115
+ let dir = realpathSync(cwd);
116
+ for (;;) {
117
+ for (const [kind, file] of [["npm", "package-lock.json"], ["maven", "pom.xml"]] as const) {
118
+ if (existsSync(join(dir, file))) return { kind, path: join(dir, file) };
119
+ }
120
+ const up = dirname(dir);
121
+ if (top === null || dir === top || up === dir || !up.startsWith(top)) return null;
122
+ dir = up;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * The SBOM a target attaches, for an OCI image or a forge release: the file
128
+ * `declared` names (relative to the member, checked against the release's
129
+ * digest when the release records it), else one written from the member's
130
+ * lockfile for `subject`. Null when `declared` is `false`. Throws
131
+ * `publish-sbom-missing` when there is neither.
132
+ */
133
+ export function memberSbom(ctx: PublishContext, declared: string | false | undefined, subject: string, target: string): Attachment | null {
134
+ if (declared === false) return null;
135
+ if (declared !== undefined) {
136
+ const abs = memberFile(ctx.cwd, declared);
137
+ const bytes = new Uint8Array(readFileSync(abs));
138
+ checkReleaseDigest(ctx.release, "asset", ctx.cwd, declared, [bytesDigest(bytes)]);
139
+ const mediaType = sbomMediaType(bytes);
140
+ if (!mediaType) throw invalid(declared);
141
+ return attachment("sbom", declared.split("/").pop() ?? declared, mediaType, bytes);
142
+ }
143
+ const lock = nearestLockfile(ctx.cwd);
144
+ if (!lock) {
145
+ throw new PublishTargetError(
146
+ "publish-sbom-missing",
147
+ `${target} attaches an SBOM to what it publishes, and its declaration names none and there is no package-lock.json or pom.xml in ${ctx.cwd} or above it to write one from`,
148
+ "Set sbom in the declaration to the SPDX or CycloneDX file the build writes, or sbom: false to publish without one.",
149
+ );
150
+ }
151
+ const content = readFileSync(lock.path, "utf-8");
152
+ const packages = lock.kind === "npm" ? parseNpmPackageLock(content) : parsePomXml(content);
153
+ const at = new Date(ctx.cut?.timestamp ?? 0);
154
+ const doc = writeBom(
155
+ "spdx",
156
+ { subjectName: subject, subjectId: ctx.release.releaseDigest, packages, generator: `chant-lockfile-sbom/${lock.kind === "npm" ? "package-lock.json" : "pom.xml"}` },
157
+ () => (Number.isNaN(at.getTime()) ? new Date(0) : at),
158
+ );
159
+ return attachment("sbom", GENERATED_SBOM_FILE, doc.mediaType, new Uint8Array(Buffer.from(doc.bytes, "utf-8")));
160
+ }
161
+
162
+ /**
163
+ * The SBOM an npm package carries at its root: the file `declared` names, else
164
+ * the first of {@link PACKAGE_SBOM_FILES} it holds, in a package directory or
165
+ * under `package/` in a packed `.tgz`. Null when `declared` is `false`.
166
+ * Throws `publish-sbom-missing` when the package has none, and
167
+ * `publish-sbom-invalid` when it is not SPDX or CycloneDX JSON.
168
+ */
169
+ export function packageSbom(abs: string, isDirectory: boolean, path: string, name: string, declared: string | false | undefined): { file: string; digest: string } | null {
170
+ if (declared === false) return null;
171
+ const candidates = declared !== undefined ? [declared] : [...PACKAGE_SBOM_FILES];
172
+ let listed: Set<string> | null = null;
173
+ if (!isDirectory) {
174
+ try {
175
+ listed = new Set(execFileSync("tar", ["-tzf", abs], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).split("\n").map((l) => l.trim()));
176
+ } catch {
177
+ listed = new Set();
178
+ }
179
+ }
180
+ for (const file of candidates) {
181
+ let bytes: Uint8Array | null = null;
182
+ if (isDirectory) {
183
+ if (existsSync(join(abs, file))) bytes = new Uint8Array(readFileSync(join(abs, file)));
184
+ } else if (listed!.has(`package/${file}`)) {
185
+ bytes = new Uint8Array(execFileSync("tar", ["-xzOf", abs, `package/${file}`], { stdio: ["ignore", "pipe", "ignore"] }));
186
+ }
187
+ if (bytes === null) continue;
188
+ if (!sbomMediaType(bytes)) throw invalid(`${file} in ${path}`);
189
+ return { file, digest: bytesDigest(bytes) };
190
+ }
191
+ throw new PublishTargetError(
192
+ "publish-sbom-missing",
193
+ `${name} (${path}) carries no SBOM: npm keeps no file beside a package, so its SBOM goes in it, and ${declared !== undefined ? declared : `none of ${PACKAGE_SBOM_FILES.join(", ")}`} is at its root`,
194
+ `Write one into the package before it is packed, such as npm sbom --sbom-format spdx > ${path}/sbom.spdx.json, and keep it in the package's files; or set sbom: false to publish without one.`,
195
+ );
196
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Credentials for publish targets (#4047), and the redactor every line a
3
+ * publish prints goes through.
4
+ *
5
+ * A target reads its credential from the environment of the run, by a name
6
+ * the declaration gives or the target's default, so a workstation sets it in
7
+ * its shell and CI in the job's secrets. chant never writes a credential to
8
+ * `chant/lifecycle`, to a file it keeps, or to its output: the value is held
9
+ * in memory for the requests that need it, and every line a target prints,
10
+ * and every error it throws, passes through {@link Redactor.redact} first.
11
+ *
12
+ * The redactor masks the value of each credential a target read, the values
13
+ * of the environment variables CI and registries use for tokens
14
+ * ({@link TOKEN_ENV_VARS}), and the token forms npm and HTTP headers write
15
+ * (`_authToken=...`, `Authorization: Bearer ...`).
16
+ */
17
+
18
+ /** Environment variables that hold a token in CI or for a registry. Their values are masked in all publish output. */
19
+ export const TOKEN_ENV_VARS = [
20
+ "NODE_AUTH_TOKEN",
21
+ "NPM_TOKEN",
22
+ "NPM_ID_TOKEN",
23
+ "SIGSTORE_ID_TOKEN",
24
+ "ACTIONS_ID_TOKEN_REQUEST_TOKEN",
25
+ "ACTIONS_RUNTIME_TOKEN",
26
+ "GITHUB_TOKEN",
27
+ "GH_TOKEN",
28
+ "GITLAB_TOKEN",
29
+ "CI_JOB_TOKEN",
30
+ "FORGEJO_TOKEN",
31
+ "GITEA_TOKEN",
32
+ "OCI_PASSWORD",
33
+ "DOCKER_PASSWORD",
34
+ ] as const;
35
+
36
+ /** What a masked value is replaced with. */
37
+ export const REDACTED = "[redacted]";
38
+
39
+ /** Masks credentials in text a publish prints. */
40
+ export interface Redactor {
41
+ /** Add a value to mask. Values shorter than 4 characters are not masked, since they would mask ordinary text. */
42
+ add(value: string | undefined | null): void;
43
+ redact(text: string): string;
44
+ }
45
+
46
+ /** A redactor that masks the token variables of `env` and every value added to it later. */
47
+ export function createRedactor(env: NodeJS.ProcessEnv = {}, names: readonly string[] = []): Redactor {
48
+ const values = new Set<string>();
49
+ const add = (v: string | undefined | null) => {
50
+ if (typeof v === "string" && v.trim().length >= 4) values.add(v.trim());
51
+ };
52
+ for (const name of [...TOKEN_ENV_VARS, ...names]) add(env[name]);
53
+ return {
54
+ add,
55
+ redact(text: string): string {
56
+ let out = text;
57
+ // Longest first, so a value that contains another is masked whole.
58
+ for (const v of [...values].sort((a, b) => b.length - a.length)) out = out.split(v).join(REDACTED);
59
+ return out
60
+ .replace(/(_authToken\s*=\s*)("?)[^\s"]+\2/g, `$1${REDACTED}`)
61
+ .replace(/(authorization:\s*(?:bearer|basic|token)\s+)[^\s"',]+/gi, `$1${REDACTED}`)
62
+ .replace(/(private-token:\s*|job-token:\s*)[^\s"',]+/gi, `$1${REDACTED}`);
63
+ },
64
+ };
65
+ }
66
+
67
+ /** A credential a target read, and the variable it came from. The value is never printed. */
68
+ export interface Credential {
69
+ value: string;
70
+ /** The environment variable, or `docker config` for one read from the docker config file. */
71
+ from: string;
72
+ }
73
+
74
+ /** The first of `names` set in `env`, added to `redactor`, or null. */
75
+ export function readCredential(env: NodeJS.ProcessEnv, names: readonly string[], redactor: Redactor): Credential | null {
76
+ for (const name of names) {
77
+ const value = env[name];
78
+ if (typeof value === "string" && value !== "") {
79
+ redactor.add(value);
80
+ return { value, from: name };
81
+ }
82
+ }
83
+ return null;
84
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Where a member declares its publish targets (#4047): `release.publish` in
3
+ * its `chant.config.ts`, a list of targets that `chant release publish` runs
4
+ * in order.
5
+ *
6
+ * ```ts
7
+ * export default {
8
+ * release: {
9
+ * publish: [
10
+ * { type: "npm", packages: ["packages/core", "packages/cli"] },
11
+ * { type: "oci", image: "dist/api.oci.tar", repository: "ghcr.io/acme/api", tags: ["{label}"], usernameEnv: "GITHUB_ACTOR", passwordEnv: "GITHUB_TOKEN" },
12
+ * { type: "forge", forge: "github", repo: "acme/api", assets: ["dist/api-linux-amd64.tar.gz"] },
13
+ * ],
14
+ * },
15
+ * };
16
+ * ```
17
+ *
18
+ * The config is the one at the release's commit: a publish with a target
19
+ * refuses a checkout whose `HEAD` is another commit. Paths are relative to
20
+ * the member's directory. A declaration names the environment variables its
21
+ * credentials are read from, never the credentials.
22
+ *
23
+ * This module imports nothing but zod, so ../config.ts can validate the block.
24
+ */
25
+
26
+ import { z } from "zod";
27
+
28
+ const envName = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/, "an environment variable name");
29
+
30
+ /** An SBOM file, or `false` to publish without one (#4049). */
31
+ const sbomField = z.union([z.string().min(1), z.literal(false)]);
32
+
33
+ /** npm: `npm publish` for each package, in the order given. */
34
+ export const NpmPublishSchema = z
35
+ .object({
36
+ type: z.literal("npm"),
37
+ /** Package directories or packed `.tgz` files, relative to the member, published in this order. */
38
+ packages: z.array(z.string().min(1)).min(1),
39
+ /** The registry, else the one npm's own config names. */
40
+ registry: z.string().url().optional(),
41
+ access: z.enum(["public", "restricted"]).optional(),
42
+ /**
43
+ * The SBOM each package carries at its root (#4049), a file name relative
44
+ * to the package. Default: the first of `sbom.spdx.json`, `sbom.cdx.json`,
45
+ * `sbom.cyclonedx.json` it holds. `false` publishes without one.
46
+ */
47
+ sbom: sbomField.optional(),
48
+ })
49
+ .strict();
50
+
51
+ /** An OCI registry such as GHCR or Docker Hub: the image is pushed by digest, then tagged. */
52
+ export const OciPublishSchema = z
53
+ .object({
54
+ type: z.literal("oci"),
55
+ /** An OCI image layout, as a directory or a tar file such as `docker save` writes, relative to the member. */
56
+ image: z.string().min(1),
57
+ /** `<registry host>/<name>`, such as `ghcr.io/acme/api` or `docker.io/acme/api`. */
58
+ repository: z.string().min(1),
59
+ /** Tags to point at the pushed digest. `{label}` is the release's label. Default: `["{label}"]`, or none for a release with no label. */
60
+ tags: z.array(z.string().min(1)).optional(),
61
+ /** The variable holding the registry user name. Default `OCI_USERNAME`. */
62
+ usernameEnv: envName.optional(),
63
+ /** The variable holding the registry password or token. Default `OCI_PASSWORD`. */
64
+ passwordEnv: envName.optional(),
65
+ /** The SBOM pushed as a referrer of the image (#4049), relative to the member. Default: one written from the member's lockfile. `false` publishes without one. */
66
+ sbom: sbomField.optional(),
67
+ })
68
+ .strict();
69
+
70
+ /** Release assets on a GitHub, GitLab or Forgejo release named by the release's tag. */
71
+ export const ForgePublishSchema = z
72
+ .object({
73
+ type: z.literal("forge"),
74
+ forge: z.enum(["github", "gitlab", "forgejo"]),
75
+ /** `owner/name` on GitHub and Forgejo; the project path or numeric id on GitLab. */
76
+ repo: z.string().min(1),
77
+ /** The API root: default `$GITHUB_API_URL` or `https://api.github.com` on GitHub, `$CI_API_V4_URL` or `https://gitlab.com/api/v4` on GitLab, `$GITHUB_API_URL` on Forgejo, which has no default. */
78
+ api: z.string().url().optional(),
79
+ /** Files to attach, relative to the member. */
80
+ assets: z.array(z.string().min(1)).min(1),
81
+ /** The forge release's tag. Default: the tag the cut made, `release/<label>` or `release/<member>/<label>`. */
82
+ tag: z.string().min(1).optional(),
83
+ /** The variable holding the token. Default `GITHUB_TOKEN`, `GITLAB_TOKEN` (then `CI_JOB_TOKEN`), or `FORGEJO_TOKEN`. */
84
+ tokenEnv: envName.optional(),
85
+ /** The SBOM attached to the release (#4049), relative to the member. Default: one written from the member's lockfile. `false` publishes without one. */
86
+ sbom: sbomField.optional(),
87
+ })
88
+ .strict();
89
+
90
+ export const PublishDeclarationSchema = z.discriminatedUnion("type", [NpmPublishSchema, OciPublishSchema, ForgePublishSchema]);
91
+
92
+ export type NpmPublishDeclaration = z.infer<typeof NpmPublishSchema>;
93
+ export type OciPublishDeclaration = z.infer<typeof OciPublishSchema>;
94
+ export type ForgePublishDeclaration = z.infer<typeof ForgePublishSchema>;
95
+ export type PublishDeclaration = z.infer<typeof PublishDeclarationSchema>;