@intentius/chant 0.124.28 → 0.124.30

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 (449) 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 +18 -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 +67 -8
  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 +91 -10
  39. package/dist/cli/parse-args.d.ts.map +1 -1
  40. package/dist/cli/parse-args.js +73 -2
  41. package/dist/cli/registry.d.ts +32 -4
  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 +49 -0
  65. package/dist/components/pr-pipeline.d.ts.map +1 -1
  66. package/dist/components/pr-pipeline.js +38 -4
  67. package/dist/components/pr-run.d.ts +20 -2
  68. package/dist/components/pr-run.d.ts.map +1 -1
  69. package/dist/components/pr-run.js +26 -13
  70. package/dist/components/promote.d.ts +23 -0
  71. package/dist/components/promote.d.ts.map +1 -1
  72. package/dist/components/promote.js +60 -2
  73. package/dist/components/release-compile.d.ts +9 -0
  74. package/dist/components/release-compile.d.ts.map +1 -1
  75. package/dist/components/release-compile.js +9 -3
  76. package/dist/components/release-gate.d.ts +11 -1
  77. package/dist/components/release-gate.d.ts.map +1 -1
  78. package/dist/components/release-gate.js +10 -4
  79. package/dist/components/wave-plan.d.ts +16 -0
  80. package/dist/components/wave-plan.d.ts.map +1 -1
  81. package/dist/components/wave-plan.js +36 -2
  82. package/dist/config.d.ts +38 -0
  83. package/dist/config.d.ts.map +1 -1
  84. package/dist/config.js +2 -0
  85. package/dist/flag-params.d.ts +54 -0
  86. package/dist/flag-params.d.ts.map +1 -0
  87. package/dist/flag-params.js +130 -0
  88. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
  89. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
  90. package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
  91. package/dist/lifecycle/change-set-store.d.ts +2 -1
  92. package/dist/lifecycle/change-set-store.d.ts.map +1 -1
  93. package/dist/lifecycle/change-set-store.js +2 -1
  94. package/dist/lifecycle/delivery-metrics.d.ts +139 -0
  95. package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
  96. package/dist/lifecycle/delivery-metrics.js +145 -0
  97. package/dist/lifecycle/deploy-outcome.d.ts +81 -0
  98. package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
  99. package/dist/lifecycle/deploy-outcome.js +101 -0
  100. package/dist/lifecycle/gate-change-set.d.ts +10 -1
  101. package/dist/lifecycle/gate-change-set.d.ts.map +1 -1
  102. package/dist/lifecycle/gate-change-set.js +35 -3
  103. package/dist/lifecycle/gate-ledger.d.ts +2 -3
  104. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  105. package/dist/lifecycle/git.d.ts +30 -1
  106. package/dist/lifecycle/git.d.ts.map +1 -1
  107. package/dist/lifecycle/git.js +31 -5
  108. package/dist/lifecycle/index.d.ts +4 -0
  109. package/dist/lifecycle/index.d.ts.map +1 -1
  110. package/dist/lifecycle/index.js +4 -0
  111. package/dist/lifecycle/mixed-environment.d.ts +95 -0
  112. package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
  113. package/dist/lifecycle/mixed-environment.js +154 -0
  114. package/dist/lifecycle/publish-attachments.d.ts +79 -0
  115. package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
  116. package/dist/lifecycle/publish-attachments.js +179 -0
  117. package/dist/lifecycle/publish-credentials.d.ts +37 -0
  118. package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
  119. package/dist/lifecycle/publish-credentials.js +69 -0
  120. package/dist/lifecycle/publish-declaration.d.ts +97 -0
  121. package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
  122. package/dist/lifecycle/publish-declaration.js +83 -0
  123. package/dist/lifecycle/publish-forge.d.ts +53 -0
  124. package/dist/lifecycle/publish-forge.d.ts.map +1 -0
  125. package/dist/lifecycle/publish-forge.js +298 -0
  126. package/dist/lifecycle/publish-npm.d.ts +61 -0
  127. package/dist/lifecycle/publish-npm.d.ts.map +1 -0
  128. package/dist/lifecycle/publish-npm.js +242 -0
  129. package/dist/lifecycle/publish-oci.d.ts +64 -0
  130. package/dist/lifecycle/publish-oci.d.ts.map +1 -0
  131. package/dist/lifecycle/publish-oci.js +383 -0
  132. package/dist/lifecycle/publish-record.d.ts +66 -0
  133. package/dist/lifecycle/publish-record.d.ts.map +1 -0
  134. package/dist/lifecycle/publish-record.js +84 -0
  135. package/dist/lifecycle/publish-targets.d.ts +45 -0
  136. package/dist/lifecycle/publish-targets.d.ts.map +1 -0
  137. package/dist/lifecycle/publish-targets.js +85 -0
  138. package/dist/lifecycle/release-cut.d.ts +48 -1
  139. package/dist/lifecycle/release-cut.d.ts.map +1 -1
  140. package/dist/lifecycle/release-cut.js +116 -23
  141. package/dist/lifecycle/release-join.d.ts +95 -0
  142. package/dist/lifecycle/release-join.d.ts.map +1 -0
  143. package/dist/lifecycle/release-join.js +139 -0
  144. package/dist/lifecycle/release-ledger.d.ts +8 -3
  145. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  146. package/dist/lifecycle/release-ledger.js +2 -0
  147. package/dist/lifecycle/release-publish.d.ts +312 -0
  148. package/dist/lifecycle/release-publish.d.ts.map +1 -0
  149. package/dist/lifecycle/release-publish.js +335 -0
  150. package/dist/lifecycle/release-revocation.d.ts +80 -0
  151. package/dist/lifecycle/release-revocation.d.ts.map +1 -0
  152. package/dist/lifecycle/release-revocation.js +123 -0
  153. package/dist/lifecycle/release-store.d.ts +12 -2
  154. package/dist/lifecycle/release-store.d.ts.map +1 -1
  155. package/dist/lifecycle/release-store.js +4 -2
  156. package/dist/lifecycle/release-verify.d.ts +61 -0
  157. package/dist/lifecycle/release-verify.d.ts.map +1 -0
  158. package/dist/lifecycle/release-verify.js +92 -0
  159. package/dist/lifecycle/release-workstation.d.ts +91 -0
  160. package/dist/lifecycle/release-workstation.d.ts.map +1 -0
  161. package/dist/lifecycle/release-workstation.js +180 -0
  162. package/dist/lifecycle/retention-policy.d.ts +45 -0
  163. package/dist/lifecycle/retention-policy.d.ts.map +1 -0
  164. package/dist/lifecycle/retention-policy.js +43 -0
  165. package/dist/lifecycle/retention.d.ts +185 -0
  166. package/dist/lifecycle/retention.d.ts.map +1 -0
  167. package/dist/lifecycle/retention.js +400 -0
  168. package/dist/lifecycle/run-ledger.d.ts +1 -1
  169. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  170. package/dist/lifecycle/run-ledger.js +9 -0
  171. package/dist/op/activities/activity-contracts.d.ts +1 -0
  172. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  173. package/dist/op/activities/activity-contracts.js +1 -0
  174. package/dist/op/activities/shell.d.ts +6 -0
  175. package/dist/op/activities/shell.d.ts.map +1 -1
  176. package/dist/op/activities/shell.js +1 -1
  177. package/dist/op/local-executor.d.ts +23 -1
  178. package/dist/op/local-executor.d.ts.map +1 -1
  179. package/dist/op/local-executor.js +9 -3
  180. package/dist/op/runtime.d.ts +27 -0
  181. package/dist/op/runtime.d.ts.map +1 -1
  182. package/dist/op/runtime.js +20 -0
  183. package/dist/plan-summary.d.ts.map +1 -1
  184. package/dist/plan-summary.js +8 -2
  185. package/dist/pr-forge.d.ts +23 -0
  186. package/dist/pr-forge.d.ts.map +1 -1
  187. package/dist/pr-forge.js +44 -0
  188. package/dist/pr-loop.d.ts +17 -0
  189. package/dist/pr-loop.d.ts.map +1 -1
  190. package/dist/pr-loop.js +30 -4
  191. package/dist/pr-report-url.d.ts +82 -0
  192. package/dist/pr-report-url.d.ts.map +1 -0
  193. package/dist/pr-report-url.js +174 -0
  194. package/dist/provenance.d.ts +2 -2
  195. package/dist/provenance.d.ts.map +1 -1
  196. package/dist/run-event-store.d.ts +29 -2
  197. package/dist/run-event-store.d.ts.map +1 -1
  198. package/dist/run-event-store.js +50 -3
  199. package/dist/run-event-stream.d.ts +4 -3
  200. package/dist/run-event-stream.d.ts.map +1 -1
  201. package/dist/run-event-stream.js +7 -3
  202. package/dist/run-event.d.ts +34 -1
  203. package/dist/run-event.d.ts.map +1 -1
  204. package/dist/run-event.js +4 -0
  205. package/dist/wave-runtime.mjs +260 -229
  206. package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
  207. package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
  208. package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
  209. package/dist/workspace/change-set.schema.json +5 -1
  210. package/dist/workspace/check.schema.json +4 -2
  211. package/dist/workspace/checks/records.d.ts +9 -1
  212. package/dist/workspace/checks/records.d.ts.map +1 -1
  213. package/dist/workspace/checks/records.js +35 -1
  214. package/dist/workspace/checks.d.ts +1 -0
  215. package/dist/workspace/checks.d.ts.map +1 -1
  216. package/dist/workspace/checks.js +2 -1
  217. package/dist/workspace/ci-green-forge.d.ts +2 -0
  218. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  219. package/dist/workspace/ci-green.d.ts +10 -0
  220. package/dist/workspace/ci-green.d.ts.map +1 -1
  221. package/dist/workspace/ci-green.js +14 -5
  222. package/dist/workspace/ci-run.d.ts +222 -0
  223. package/dist/workspace/ci-run.d.ts.map +1 -0
  224. package/dist/workspace/ci-run.js +464 -0
  225. package/dist/workspace/ci-serve.d.ts +214 -0
  226. package/dist/workspace/ci-serve.d.ts.map +1 -0
  227. package/dist/workspace/ci-serve.js +412 -0
  228. package/dist/workspace/declaration.d.ts +16 -0
  229. package/dist/workspace/declaration.d.ts.map +1 -1
  230. package/dist/workspace/declaration.js +26 -0
  231. package/dist/workspace/declaration.schema.json +64 -0
  232. package/dist/workspace/declared-kinds.d.ts +16 -0
  233. package/dist/workspace/declared-kinds.d.ts.map +1 -1
  234. package/dist/workspace/declared-kinds.js +25 -0
  235. package/dist/workspace/delivery-metrics.d.ts +91 -0
  236. package/dist/workspace/delivery-metrics.d.ts.map +1 -0
  237. package/dist/workspace/delivery-metrics.js +217 -0
  238. package/dist/workspace/delivery-metrics.schema.json +519 -0
  239. package/dist/workspace/flags-read.d.ts +37 -0
  240. package/dist/workspace/flags-read.d.ts.map +1 -0
  241. package/dist/workspace/flags-read.js +103 -0
  242. package/dist/workspace/flags.d.ts +100 -0
  243. package/dist/workspace/flags.d.ts.map +1 -0
  244. package/dist/workspace/flags.js +98 -0
  245. package/dist/workspace/graph-cli.js +1 -1
  246. package/dist/workspace/intent-cli.d.ts +11 -2
  247. package/dist/workspace/intent-cli.d.ts.map +1 -1
  248. package/dist/workspace/intent-cli.js +54 -5
  249. package/dist/workspace/intent-trace.d.ts +219 -0
  250. package/dist/workspace/intent-trace.d.ts.map +1 -0
  251. package/dist/workspace/intent-trace.js +314 -0
  252. package/dist/workspace/intent.d.ts +30 -6
  253. package/dist/workspace/intent.d.ts.map +1 -1
  254. package/dist/workspace/intent.js +141 -21
  255. package/dist/workspace/intent.schema.json +702 -6
  256. package/dist/workspace/overlap.d.ts +146 -0
  257. package/dist/workspace/overlap.d.ts.map +1 -0
  258. package/dist/workspace/overlap.js +276 -0
  259. package/dist/workspace/pr-report.schema.json +5 -0
  260. package/dist/workspace/reason-codes.d.ts +2 -0
  261. package/dist/workspace/reason-codes.d.ts.map +1 -1
  262. package/dist/workspace/reason-codes.js +3 -0
  263. package/dist/workspace/records.d.ts +15 -2
  264. package/dist/workspace/records.d.ts.map +1 -1
  265. package/dist/workspace/records.js +32 -0
  266. package/dist/workspace/records.schema.json +21 -2
  267. package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  268. package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
  269. package/dist/workspace/releases.d.ts +9 -0
  270. package/dist/workspace/releases.d.ts.map +1 -1
  271. package/dist/workspace/releases.js +9 -0
  272. package/dist/workspace/releases.schema.json +123 -1
  273. package/dist/workspace/run-event.schema.json +24 -6
  274. package/dist/workspace/status-gates.d.ts +8 -0
  275. package/dist/workspace/status-gates.d.ts.map +1 -1
  276. package/dist/workspace/status-stewards.d.ts +2 -0
  277. package/dist/workspace/status-stewards.d.ts.map +1 -1
  278. package/dist/workspace/status-stewards.js +1 -0
  279. package/dist/workspace/status.d.ts +78 -0
  280. package/dist/workspace/status.d.ts.map +1 -1
  281. package/dist/workspace/status.js +170 -3
  282. package/dist/workspace/status.schema.json +210 -2
  283. package/dist/workspace/wip.d.ts.map +1 -1
  284. package/dist/workspace/wip.js +11 -3
  285. package/package.json +1 -1
  286. package/src/build-params.test.ts +46 -0
  287. package/src/build-params.ts +55 -1
  288. package/src/change-set-graph.ts +10 -2
  289. package/src/change-set.ts +14 -3
  290. package/src/cli/build-params-cli.ts +22 -9
  291. package/src/cli/commands/build.ts +6 -3
  292. package/src/cli/handlers/approve-release.test.ts +5 -1
  293. package/src/cli/handlers/approve-revoked.test.ts +155 -0
  294. package/src/cli/handlers/approve.ts +47 -0
  295. package/src/cli/handlers/build.test.ts +30 -0
  296. package/src/cli/handlers/build.ts +20 -4
  297. package/src/cli/handlers/fan-out.test.ts +262 -1
  298. package/src/cli/handlers/fan-out.ts +72 -8
  299. package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
  300. package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
  301. package/src/cli/handlers/lifecycle-gc.ts +146 -0
  302. package/src/cli/handlers/pr.test.ts +148 -1
  303. package/src/cli/handlers/pr.ts +144 -28
  304. package/src/cli/handlers/promote.test.ts +280 -2
  305. package/src/cli/handlers/promote.ts +210 -34
  306. package/src/cli/handlers/release-implicit.e2e.test.ts +12 -4
  307. package/src/cli/handlers/release-join.e2e.test.ts +185 -0
  308. package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
  309. package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
  310. package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
  311. package/src/cli/handlers/release.e2e.test.ts +146 -32
  312. package/src/cli/handlers/release.ts +415 -28
  313. package/src/cli/handlers/run-auto-release.test.ts +9 -0
  314. package/src/cli/handlers/run-release.test.ts +55 -0
  315. package/src/cli/handlers/run.ts +69 -5
  316. package/src/cli/main.test.ts +1 -0
  317. package/src/cli/main.ts +91 -10
  318. package/src/cli/parse-args.ts +54 -2
  319. package/src/cli/registry.ts +32 -4
  320. package/src/cli/run-events.ts +5 -2
  321. package/src/components/auto-release.test.ts +9 -0
  322. package/src/components/auto-release.ts +3 -0
  323. package/src/components/capability.ts +9 -0
  324. package/src/components/driver.test.ts +24 -0
  325. package/src/components/driver.ts +36 -0
  326. package/src/components/fan-out-inputs.test.ts +85 -0
  327. package/src/components/fan-out-inputs.ts +192 -0
  328. package/src/components/fan-out-record.test.ts +10 -0
  329. package/src/components/fan-out-record.ts +19 -0
  330. package/src/components/fan-out-run.ts +48 -0
  331. package/src/components/fan-out.ts +4 -0
  332. package/src/components/pr-pipeline.ts +67 -4
  333. package/src/components/pr-run.test.ts +127 -0
  334. package/src/components/pr-run.ts +32 -12
  335. package/src/components/promote.ts +68 -2
  336. package/src/components/release-compile.ts +17 -3
  337. package/src/components/release-gate.ts +18 -4
  338. package/src/components/wave-plan.ts +45 -2
  339. package/src/config.ts +8 -0
  340. package/src/flag-params.test.ts +144 -0
  341. package/src/flag-params.ts +134 -0
  342. package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
  343. package/src/lifecycle/change-set-store.ts +2 -1
  344. package/src/lifecycle/delivery-metrics.ts +263 -0
  345. package/src/lifecycle/deploy-outcome.ts +147 -0
  346. package/src/lifecycle/gate-change-set.ts +37 -6
  347. package/src/lifecycle/gate-ledger.ts +2 -2
  348. package/src/lifecycle/git.ts +61 -8
  349. package/src/lifecycle/index.ts +4 -0
  350. package/src/lifecycle/mixed-environment.test.ts +97 -0
  351. package/src/lifecycle/mixed-environment.ts +204 -0
  352. package/src/lifecycle/publish-attachments.ts +196 -0
  353. package/src/lifecycle/publish-credentials.ts +84 -0
  354. package/src/lifecycle/publish-declaration.ts +95 -0
  355. package/src/lifecycle/publish-forge.ts +325 -0
  356. package/src/lifecycle/publish-npm.ts +259 -0
  357. package/src/lifecycle/publish-oci.ts +396 -0
  358. package/src/lifecycle/publish-record.ts +113 -0
  359. package/src/lifecycle/publish-resume.test.ts +168 -0
  360. package/src/lifecycle/publish-targets.test.ts +494 -0
  361. package/src/lifecycle/publish-targets.ts +100 -0
  362. package/src/lifecycle/release-cut.test.ts +74 -2
  363. package/src/lifecycle/release-cut.ts +158 -22
  364. package/src/lifecycle/release-flag.test.ts +112 -0
  365. package/src/lifecycle/release-join.test.ts +68 -0
  366. package/src/lifecycle/release-join.ts +182 -0
  367. package/src/lifecycle/release-ledger.ts +9 -3
  368. package/src/lifecycle/release-publish.test.ts +225 -0
  369. package/src/lifecycle/release-publish.ts +568 -0
  370. package/src/lifecycle/release-revocation.ts +147 -0
  371. package/src/lifecycle/release-store.ts +15 -4
  372. package/src/lifecycle/release-verify.test.ts +110 -0
  373. package/src/lifecycle/release-verify.ts +120 -0
  374. package/src/lifecycle/release-workstation.test.ts +204 -0
  375. package/src/lifecycle/release-workstation.ts +216 -0
  376. package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
  377. package/src/lifecycle/retention-policy.ts +63 -0
  378. package/src/lifecycle/retention.test.ts +363 -0
  379. package/src/lifecycle/retention.ts +508 -0
  380. package/src/lifecycle/run-ledger.ts +7 -1
  381. package/src/op/activities/activity-contracts.ts +1 -0
  382. package/src/op/activities/shell.ts +7 -1
  383. package/src/op/local-executor.ts +27 -4
  384. package/src/op/runtime.ts +42 -0
  385. package/src/plan-summary.test.ts +20 -0
  386. package/src/plan-summary.ts +8 -1
  387. package/src/pr-forge.test.ts +25 -0
  388. package/src/pr-forge.ts +62 -0
  389. package/src/pr-loop.test.ts +13 -1
  390. package/src/pr-loop.ts +41 -4
  391. package/src/pr-report-url.test.ts +131 -0
  392. package/src/pr-report-url.ts +209 -0
  393. package/src/provenance.ts +2 -2
  394. package/src/run-event-store.ts +63 -3
  395. package/src/run-event-stream.ts +8 -4
  396. package/src/run-event.ts +38 -1
  397. package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
  398. package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
  399. package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
  400. package/src/workspace/change-set.schema.json +5 -1
  401. package/src/workspace/check.schema.json +4 -2
  402. package/src/workspace/checks/records.ts +35 -1
  403. package/src/workspace/checks.ts +2 -1
  404. package/src/workspace/ci-green-forge.ts +2 -0
  405. package/src/workspace/ci-green.ts +20 -5
  406. package/src/workspace/ci-run.test.ts +416 -0
  407. package/src/workspace/ci-run.ts +571 -0
  408. package/src/workspace/ci-serve.test.ts +268 -0
  409. package/src/workspace/ci-serve.ts +521 -0
  410. package/src/workspace/declaration.schema.json +64 -0
  411. package/src/workspace/declaration.test.ts +28 -0
  412. package/src/workspace/declaration.ts +38 -0
  413. package/src/workspace/declared-kinds.ts +40 -1
  414. package/src/workspace/delivery-metrics.schema.json +519 -0
  415. package/src/workspace/delivery-metrics.test.ts +236 -0
  416. package/src/workspace/delivery-metrics.ts +286 -0
  417. package/src/workspace/flags-read.ts +121 -0
  418. package/src/workspace/flags.test.ts +141 -0
  419. package/src/workspace/flags.ts +157 -0
  420. package/src/workspace/graph-cli.ts +1 -1
  421. package/src/workspace/intent-cli.ts +46 -5
  422. package/src/workspace/intent-trace.test.ts +173 -0
  423. package/src/workspace/intent-trace.ts +484 -0
  424. package/src/workspace/intent.schema.json +702 -6
  425. package/src/workspace/intent.ts +148 -25
  426. package/src/workspace/older-records.test.ts +412 -0
  427. package/src/workspace/overlap.test.ts +165 -0
  428. package/src/workspace/overlap.ts +340 -0
  429. package/src/workspace/pr-report.schema.json +5 -0
  430. package/src/workspace/read-contract.e2e.test.ts +2 -1
  431. package/src/workspace/reason-codes.test.ts +5 -0
  432. package/src/workspace/reason-codes.ts +3 -0
  433. package/src/workspace/record-assets.test.ts +2 -1
  434. package/src/workspace/records-contract.e2e.test.ts +2 -1
  435. package/src/workspace/records.schema.json +21 -2
  436. package/src/workspace/records.ts +36 -2
  437. package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
  438. package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
  439. package/src/workspace/releases.schema.json +123 -1
  440. package/src/workspace/releases.ts +11 -0
  441. package/src/workspace/run-event.schema.json +24 -6
  442. package/src/workspace/status-contract.test.ts +29 -0
  443. package/src/workspace/status-gates.ts +8 -0
  444. package/src/workspace/status-mixed.test.ts +152 -0
  445. package/src/workspace/status-revoked.test.ts +153 -0
  446. package/src/workspace/status-stewards.ts +3 -0
  447. package/src/workspace/status.schema.json +210 -2
  448. package/src/workspace/status.ts +254 -3
  449. package/src/workspace/wip.ts +11 -2
@@ -0,0 +1,95 @@
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
+ import type { ReleaseRecord } from "./release-ledger.js";
22
+ import type { DeployOutcome } from "./deploy-outcome.js";
23
+ /** One component of a mixed environment. */
24
+ export interface MixedComponent {
25
+ component: string;
26
+ /** The digest the release ledger records the component running, or null when it has no record in the environment. */
27
+ digest: string | null;
28
+ /** The release that record names, or null. */
29
+ releaseDigest: string | null;
30
+ releaseLabel: string | null;
31
+ /** `settled` when the ledger's record is what it runs; `failed` or `gated` when the newest run that named it left it so. */
32
+ state: "settled" | "failed" | "gated";
33
+ /** For an unsettled component, the run that left it so and what that run was deploying. Null when settled. */
34
+ attempt: {
35
+ verb: DeployOutcome["verb"];
36
+ runId: string;
37
+ timestamp: string;
38
+ digest: string;
39
+ releaseDigest: string | null;
40
+ releaseLabel: string | null;
41
+ } | null;
42
+ }
43
+ export interface MixedEnvironment {
44
+ /** Every component the ledger records or a run left unsettled, sorted by name. */
45
+ components: MixedComponent[];
46
+ /** `chant components redeploy <env> --component <c>` for each unsettled component the ledger records, which puts its recorded release back. */
47
+ redeploy: string[];
48
+ /** `chant components rollback <env> --release <label|digest>`, naming the release to go back to, or null when there is none to name. */
49
+ rollback: string | null;
50
+ }
51
+ /** What a reader knows about a release a rollback could go back to. */
52
+ export interface ReleaseCheck {
53
+ /** Its release file can still be read, so its templates can be deployed. */
54
+ kept: boolean;
55
+ /** Its commit is revoked (#4033), so it is refused. */
56
+ revoked: boolean;
57
+ }
58
+ /**
59
+ * The components a run left unsettled: for each, the newest outcome naming it
60
+ * `failed` or `gated` that no release record of it is newer than.
61
+ */
62
+ export declare function unsettledComponents(records: readonly ReleaseRecord[], outcomes: readonly DeployOutcome[]): Map<string, {
63
+ outcome: DeployOutcome;
64
+ state: "failed" | "gated";
65
+ digest: string;
66
+ releaseDigest: string | null;
67
+ }>;
68
+ /**
69
+ * The newest release the environment's ledger names that a rollback can go
70
+ * back to: not in `exclude`, still kept, and not revoked. With `before`, only
71
+ * records older than that time count. Null when there is none.
72
+ */
73
+ export declare function rollbackTarget(input: {
74
+ records: readonly ReleaseRecord[];
75
+ exclude: ReadonlySet<string>;
76
+ before?: string;
77
+ check: (releaseDigest: string) => Promise<ReleaseCheck>;
78
+ }): Promise<string | null>;
79
+ /** The line that rolls `env` back to a release, by its label when it has one. */
80
+ export declare function releaseRollbackLine(env: string, releaseDigest: string, label: string | null | undefined): string;
81
+ /** The line that redeploys one component's recorded release in `env`. */
82
+ export declare function redeployLine(env: string, component: string): string;
83
+ /**
84
+ * The environment's mixed state, or null when it runs one release (or no
85
+ * release at all) and no component is unsettled.
86
+ */
87
+ export declare function mixedEnvironment(input: {
88
+ env: string;
89
+ records: readonly ReleaseRecord[];
90
+ outcomes: readonly DeployOutcome[];
91
+ /** A release's label, or undefined. */
92
+ label: (releaseDigest: string) => Promise<string | undefined>;
93
+ check: (releaseDigest: string) => Promise<ReleaseCheck>;
94
+ }): Promise<MixedEnvironment | null>;
95
+ //# sourceMappingURL=mixed-environment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mixed-environment.d.ts","sourceRoot":"","sources":["../../src/lifecycle/mixed-environment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,4CAA4C;AAC5C,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,qHAAqH;IACrH,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,8CAA8C;IAC9C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,4HAA4H;IAC5H,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;IACtC,8GAA8G;IAC9G,OAAO,EAAE;QACP,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;QAC5B,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;KAC7B,GAAG,IAAI,CAAC;CACV;AAED,MAAM,WAAW,gBAAgB;IAC/B,kFAAkF;IAClF,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,+IAA+I;IAC/I,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,wIAAwI;IACxI,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,IAAI,EAAE,OAAO,CAAC;IACd,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAC;CAClB;AAWD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,aAAa,EAAE,EACjC,QAAQ,EAAE,SAAS,aAAa,EAAE,GACjC,GAAG,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAiBlH;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE;IAC1C,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAClC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CACzD,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAczB;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAEhH;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAClC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,uCAAuC;IACvC,KAAK,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC9D,KAAK,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CACzD,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA2DnC"}
@@ -0,0 +1,154 @@
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
+ function latest(records) {
22
+ const out = new Map();
23
+ for (const r of records) {
24
+ const current = out.get(r.component);
25
+ if (!current || r.timestamp >= current.timestamp)
26
+ out.set(r.component, r);
27
+ }
28
+ return out;
29
+ }
30
+ /**
31
+ * The components a run left unsettled: for each, the newest outcome naming it
32
+ * `failed` or `gated` that no release record of it is newer than.
33
+ */
34
+ export function unsettledComponents(records, outcomes) {
35
+ const newest = new Map();
36
+ for (const outcome of outcomes) {
37
+ for (const c of outcome.components) {
38
+ if (c.outcome !== "failed" && c.outcome !== "gated")
39
+ continue;
40
+ const seen = newest.get(c.component);
41
+ if (!seen || outcome.timestamp >= seen.outcome.timestamp) {
42
+ newest.set(c.component, { outcome, state: c.outcome, digest: c.digest, releaseDigest: c.releaseDigest });
43
+ }
44
+ }
45
+ }
46
+ const recorded = latest(records);
47
+ for (const [component, u] of newest) {
48
+ const r = recorded.get(component);
49
+ if (r && r.timestamp > u.outcome.timestamp)
50
+ newest.delete(component);
51
+ }
52
+ return newest;
53
+ }
54
+ /**
55
+ * The newest release the environment's ledger names that a rollback can go
56
+ * back to: not in `exclude`, still kept, and not revoked. With `before`, only
57
+ * records older than that time count. Null when there is none.
58
+ */
59
+ export async function rollbackTarget(input) {
60
+ const ordered = input.records
61
+ .map((r, i) => ({ r, i }))
62
+ .filter(({ r }) => r.releaseDigest !== undefined && (input.before === undefined || r.timestamp < input.before))
63
+ .sort((a, b) => (a.r.timestamp === b.r.timestamp ? b.i - a.i : a.r.timestamp < b.r.timestamp ? 1 : -1));
64
+ const seen = new Set();
65
+ for (const { r } of ordered) {
66
+ const digest = r.releaseDigest;
67
+ if (seen.has(digest) || input.exclude.has(digest))
68
+ continue;
69
+ seen.add(digest);
70
+ const check = await input.check(digest);
71
+ if (check.kept && !check.revoked)
72
+ return digest;
73
+ }
74
+ return null;
75
+ }
76
+ /** The line that rolls `env` back to a release, by its label when it has one. */
77
+ export function releaseRollbackLine(env, releaseDigest, label) {
78
+ return `chant components rollback ${env} --release ${label ?? releaseDigest}`;
79
+ }
80
+ /** The line that redeploys one component's recorded release in `env`. */
81
+ export function redeployLine(env, component) {
82
+ return `chant components redeploy ${env} --component ${component}`;
83
+ }
84
+ /**
85
+ * The environment's mixed state, or null when it runs one release (or no
86
+ * release at all) and no component is unsettled.
87
+ */
88
+ export async function mixedEnvironment(input) {
89
+ const { env, records } = input;
90
+ const recorded = latest(records);
91
+ const unsettled = unsettledComponents(records, input.outcomes);
92
+ const releases = new Set([...recorded.values()].map((r) => r.releaseDigest ?? null));
93
+ const divergent = releases.size > 1 && [...releases].some((d) => d !== null);
94
+ if (unsettled.size === 0 && !divergent)
95
+ return null;
96
+ const labelOf = async (digest) => (digest ? ((await input.label(digest)) ?? null) : null);
97
+ const names = [...new Set([...recorded.keys(), ...unsettled.keys()])].sort();
98
+ const components = [];
99
+ for (const component of names) {
100
+ const r = recorded.get(component);
101
+ const u = unsettled.get(component);
102
+ components.push({
103
+ component,
104
+ digest: r?.digest ?? null,
105
+ releaseDigest: r?.releaseDigest ?? null,
106
+ releaseLabel: await labelOf(r?.releaseDigest),
107
+ state: u ? u.state : "settled",
108
+ attempt: u
109
+ ? {
110
+ verb: u.outcome.verb,
111
+ runId: u.outcome.runId,
112
+ timestamp: u.outcome.timestamp,
113
+ digest: u.digest,
114
+ releaseDigest: u.releaseDigest,
115
+ releaseLabel: await labelOf(u.releaseDigest),
116
+ }
117
+ : null,
118
+ });
119
+ }
120
+ // The release the environment was moving to is not one to go back to: the
121
+ // one a run left components unsettled on, else, of the releases its
122
+ // components run, the one that reached the environment last.
123
+ const exclude = new Set();
124
+ if (unsettled.size > 0) {
125
+ for (const u of unsettled.values())
126
+ if (u.releaseDigest)
127
+ exclude.add(u.releaseDigest);
128
+ }
129
+ else {
130
+ const firstSeen = new Map();
131
+ for (const r of records) {
132
+ if (r.releaseDigest === undefined)
133
+ continue;
134
+ const seen = firstSeen.get(r.releaseDigest);
135
+ if (seen === undefined || r.timestamp < seen)
136
+ firstSeen.set(r.releaseDigest, r.timestamp);
137
+ }
138
+ let arrived;
139
+ for (const d of releases) {
140
+ if (d === null)
141
+ continue;
142
+ if (arrived === undefined || firstSeen.get(d) > firstSeen.get(arrived))
143
+ arrived = d;
144
+ }
145
+ if (arrived !== undefined)
146
+ exclude.add(arrived);
147
+ }
148
+ const target = await rollbackTarget({ records, exclude, check: input.check });
149
+ return {
150
+ components,
151
+ redeploy: components.filter((c) => c.state !== "settled" && c.digest !== null).map((c) => redeployLine(env, c.component)),
152
+ rollback: target ? releaseRollbackLine(env, target, await labelOf(target)) : null,
153
+ };
154
+ }
@@ -0,0 +1,79 @@
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
+ import { type PublishContext } from "./release-publish.js";
28
+ /** The in-toto statement type the provenance statement is. */
29
+ export declare const IN_TOTO_STATEMENT_TYPE = "https://in-toto.io/Statement/v1";
30
+ /** The predicate type of chant's release provenance. */
31
+ export declare const RELEASE_PREDICATE_TYPE = "https://intentius.io/chant/release/v1";
32
+ /** The media type an in-toto statement is attached under. */
33
+ export declare const IN_TOTO_MEDIA_TYPE = "application/vnd.in-toto+json";
34
+ /** The file name the provenance statement is attached under. */
35
+ export declare const PROVENANCE_FILE = "release.intoto.json";
36
+ /** The file name of an SBOM chant writes from a lockfile. */
37
+ export declare const GENERATED_SBOM_FILE = "sbom.spdx.json";
38
+ /** The names an npm package's own SBOM is looked for under, at its root, when the declaration names none. */
39
+ export declare const PACKAGE_SBOM_FILES: readonly ["sbom.spdx.json", "sbom.cdx.json", "sbom.cyclonedx.json"];
40
+ /** One file attached to what a target publishes. */
41
+ export interface Attachment {
42
+ kind: "sbom" | "provenance";
43
+ /** The file name it is attached under. */
44
+ name: string;
45
+ mediaType: string;
46
+ bytes: Uint8Array;
47
+ digest: string;
48
+ }
49
+ /**
50
+ * The provenance statement for `subjects`, the digests of what the target
51
+ * publishes. Every value in it comes from the release, its cut and the run's
52
+ * OIDC check, never from the clock, so it is the same bytes on every run.
53
+ */
54
+ export declare function releaseProvenance(ctx: PublishContext, subjects: ReadonlyArray<{
55
+ name: string;
56
+ digest: string;
57
+ }>): Attachment;
58
+ /** The media type of an SBOM document, or null when `bytes` is neither SPDX nor CycloneDX JSON. */
59
+ export declare function sbomMediaType(bytes: Uint8Array): string | null;
60
+ /**
61
+ * The SBOM a target attaches, for an OCI image or a forge release: the file
62
+ * `declared` names (relative to the member, checked against the release's
63
+ * digest when the release records it), else one written from the member's
64
+ * lockfile for `subject`. Null when `declared` is `false`. Throws
65
+ * `publish-sbom-missing` when there is neither.
66
+ */
67
+ export declare function memberSbom(ctx: PublishContext, declared: string | false | undefined, subject: string, target: string): Attachment | null;
68
+ /**
69
+ * The SBOM an npm package carries at its root: the file `declared` names, else
70
+ * the first of {@link PACKAGE_SBOM_FILES} it holds, in a package directory or
71
+ * under `package/` in a packed `.tgz`. Null when `declared` is `false`.
72
+ * Throws `publish-sbom-missing` when the package has none, and
73
+ * `publish-sbom-invalid` when it is not SPDX or CycloneDX JSON.
74
+ */
75
+ export declare function packageSbom(abs: string, isDirectory: boolean, path: string, name: string, declared: string | false | undefined): {
76
+ file: string;
77
+ digest: string;
78
+ } | null;
79
+ //# sourceMappingURL=publish-attachments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish-attachments.d.ts","sourceRoot":"","sources":["../../src/lifecycle/publish-attachments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAUH,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAG5E,8DAA8D;AAC9D,eAAO,MAAM,sBAAsB,oCAAoC,CAAC;AACxE,wDAAwD;AACxD,eAAO,MAAM,sBAAsB,0CAA0C,CAAC;AAC9E,6DAA6D;AAC7D,eAAO,MAAM,kBAAkB,iCAAiC,CAAC;AACjE,gEAAgE;AAChE,eAAO,MAAM,eAAe,wBAAwB,CAAC;AACrD,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AACpD,6GAA6G;AAC7G,eAAO,MAAM,kBAAkB,YAAI,gBAAgB,EAAE,eAAe,EAAE,qBAAqB,CAAU,CAAC;AAEtG,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,GAAG,YAAY,CAAC;IAC5B,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAMD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,UAAU,CAgB5H;AAED,mGAAmG;AACnG,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAW9D;AAyBD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA2BxI;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA2BxK"}
@@ -0,0 +1,179 @@
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
+ import { execFileSync } from "node:child_process";
28
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
29
+ import { dirname, join } from "node:path";
30
+ import { sortedJsonReplacer } from "../utils.js";
31
+ import { writeBom } from "../components/verbs/bom-writer.js";
32
+ import { parseNpmPackageLock, parsePomXml } from "../components/verbs/lockfile-sbom-generator.js";
33
+ import { SBOM_MEDIA_TYPES } from "../components/verbs/sbom-generator.js";
34
+ import { bytesDigest, checkReleaseDigest, memberFile } from "./publish-targets.js";
35
+ import { PublishTargetError } from "./release-publish.js";
36
+ import { RELEASE_SEAL_NAMESPACE } from "../workspace/identity-gate-rule.js";
37
+ /** The in-toto statement type the provenance statement is. */
38
+ export const IN_TOTO_STATEMENT_TYPE = "https://in-toto.io/Statement/v1";
39
+ /** The predicate type of chant's release provenance. */
40
+ export const RELEASE_PREDICATE_TYPE = "https://intentius.io/chant/release/v1";
41
+ /** The media type an in-toto statement is attached under. */
42
+ export const IN_TOTO_MEDIA_TYPE = "application/vnd.in-toto+json";
43
+ /** The file name the provenance statement is attached under. */
44
+ export const PROVENANCE_FILE = "release.intoto.json";
45
+ /** The file name of an SBOM chant writes from a lockfile. */
46
+ export const GENERATED_SBOM_FILE = "sbom.spdx.json";
47
+ /** The names an npm package's own SBOM is looked for under, at its root, when the declaration names none. */
48
+ export const PACKAGE_SBOM_FILES = ["sbom.spdx.json", "sbom.cdx.json", "sbom.cyclonedx.json"];
49
+ const json = (value) => new Uint8Array(Buffer.from(`${JSON.stringify(value, sortedJsonReplacer, 2)}\n`, "utf-8"));
50
+ const attachment = (kind, name, mediaType, bytes) => ({ kind, name, mediaType, bytes, digest: bytesDigest(bytes) });
51
+ /**
52
+ * The provenance statement for `subjects`, the digests of what the target
53
+ * publishes. Every value in it comes from the release, its cut and the run's
54
+ * OIDC check, never from the clock, so it is the same bytes on every run.
55
+ */
56
+ export function releaseProvenance(ctx, subjects) {
57
+ const cut = ctx.cut ?? null;
58
+ const statement = {
59
+ _type: IN_TOTO_STATEMENT_TYPE,
60
+ subject: subjects.map((s) => ({ name: s.name, digest: { [s.digest.split(":")[0] ?? "sha256"]: s.digest.split(":")[1] ?? s.digest } })),
61
+ predicateType: RELEASE_PREDICATE_TYPE,
62
+ predicate: {
63
+ releaseDigest: ctx.release.releaseDigest,
64
+ label: ctx.label,
65
+ member: ctx.release.member,
66
+ commit: ctx.release.commit,
67
+ cut: cut ? { actor: cut.actor, label: cut.label, timestamp: cut.timestamp, runId: cut.runId, signature: cut.signature, namespace: RELEASE_SEAL_NAMESPACE } : null,
68
+ provenance: ctx.provenance.oidc ? { kind: "oidc", ci: ctx.provenance.ci } : { kind: "signature", ci: ctx.provenance.ci },
69
+ },
70
+ };
71
+ return attachment("provenance", PROVENANCE_FILE, IN_TOTO_MEDIA_TYPE, json(statement));
72
+ }
73
+ /** The media type of an SBOM document, or null when `bytes` is neither SPDX nor CycloneDX JSON. */
74
+ export function sbomMediaType(bytes) {
75
+ let doc;
76
+ try {
77
+ doc = JSON.parse(Buffer.from(bytes).toString("utf-8"));
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ if (!doc || typeof doc !== "object")
83
+ return null;
84
+ if (typeof doc.spdxVersion === "string")
85
+ return SBOM_MEDIA_TYPES.spdx;
86
+ if (doc.bomFormat === "CycloneDX")
87
+ return SBOM_MEDIA_TYPES.cyclonedx;
88
+ return null;
89
+ }
90
+ function invalid(where) {
91
+ 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.");
92
+ }
93
+ /** 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). */
94
+ function nearestLockfile(cwd) {
95
+ let top = null;
96
+ try {
97
+ top = realpathSync(execFileSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim());
98
+ }
99
+ catch {
100
+ top = null;
101
+ }
102
+ let dir = realpathSync(cwd);
103
+ for (;;) {
104
+ for (const [kind, file] of [["npm", "package-lock.json"], ["maven", "pom.xml"]]) {
105
+ if (existsSync(join(dir, file)))
106
+ return { kind, path: join(dir, file) };
107
+ }
108
+ const up = dirname(dir);
109
+ if (top === null || dir === top || up === dir || !up.startsWith(top))
110
+ return null;
111
+ dir = up;
112
+ }
113
+ }
114
+ /**
115
+ * The SBOM a target attaches, for an OCI image or a forge release: the file
116
+ * `declared` names (relative to the member, checked against the release's
117
+ * digest when the release records it), else one written from the member's
118
+ * lockfile for `subject`. Null when `declared` is `false`. Throws
119
+ * `publish-sbom-missing` when there is neither.
120
+ */
121
+ export function memberSbom(ctx, declared, subject, target) {
122
+ if (declared === false)
123
+ return null;
124
+ if (declared !== undefined) {
125
+ const abs = memberFile(ctx.cwd, declared);
126
+ const bytes = new Uint8Array(readFileSync(abs));
127
+ checkReleaseDigest(ctx.release, "asset", ctx.cwd, declared, [bytesDigest(bytes)]);
128
+ const mediaType = sbomMediaType(bytes);
129
+ if (!mediaType)
130
+ throw invalid(declared);
131
+ return attachment("sbom", declared.split("/").pop() ?? declared, mediaType, bytes);
132
+ }
133
+ const lock = nearestLockfile(ctx.cwd);
134
+ if (!lock) {
135
+ throw new PublishTargetError("publish-sbom-missing", `${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`, "Set sbom in the declaration to the SPDX or CycloneDX file the build writes, or sbom: false to publish without one.");
136
+ }
137
+ const content = readFileSync(lock.path, "utf-8");
138
+ const packages = lock.kind === "npm" ? parseNpmPackageLock(content) : parsePomXml(content);
139
+ const at = new Date(ctx.cut?.timestamp ?? 0);
140
+ const doc = writeBom("spdx", { subjectName: subject, subjectId: ctx.release.releaseDigest, packages, generator: `chant-lockfile-sbom/${lock.kind === "npm" ? "package-lock.json" : "pom.xml"}` }, () => (Number.isNaN(at.getTime()) ? new Date(0) : at));
141
+ return attachment("sbom", GENERATED_SBOM_FILE, doc.mediaType, new Uint8Array(Buffer.from(doc.bytes, "utf-8")));
142
+ }
143
+ /**
144
+ * The SBOM an npm package carries at its root: the file `declared` names, else
145
+ * the first of {@link PACKAGE_SBOM_FILES} it holds, in a package directory or
146
+ * under `package/` in a packed `.tgz`. Null when `declared` is `false`.
147
+ * Throws `publish-sbom-missing` when the package has none, and
148
+ * `publish-sbom-invalid` when it is not SPDX or CycloneDX JSON.
149
+ */
150
+ export function packageSbom(abs, isDirectory, path, name, declared) {
151
+ if (declared === false)
152
+ return null;
153
+ const candidates = declared !== undefined ? [declared] : [...PACKAGE_SBOM_FILES];
154
+ let listed = null;
155
+ if (!isDirectory) {
156
+ try {
157
+ listed = new Set(execFileSync("tar", ["-tzf", abs], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).split("\n").map((l) => l.trim()));
158
+ }
159
+ catch {
160
+ listed = new Set();
161
+ }
162
+ }
163
+ for (const file of candidates) {
164
+ let bytes = null;
165
+ if (isDirectory) {
166
+ if (existsSync(join(abs, file)))
167
+ bytes = new Uint8Array(readFileSync(join(abs, file)));
168
+ }
169
+ else if (listed.has(`package/${file}`)) {
170
+ bytes = new Uint8Array(execFileSync("tar", ["-xzOf", abs, `package/${file}`], { stdio: ["ignore", "pipe", "ignore"] }));
171
+ }
172
+ if (bytes === null)
173
+ continue;
174
+ if (!sbomMediaType(bytes))
175
+ throw invalid(`${file} in ${path}`);
176
+ return { file, digest: bytesDigest(bytes) };
177
+ }
178
+ throw new PublishTargetError("publish-sbom-missing", `${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`, `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.`);
179
+ }
@@ -0,0 +1,37 @@
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
+ /** Environment variables that hold a token in CI or for a registry. Their values are masked in all publish output. */
18
+ export declare const TOKEN_ENV_VARS: readonly ["NODE_AUTH_TOKEN", "NPM_TOKEN", "NPM_ID_TOKEN", "SIGSTORE_ID_TOKEN", "ACTIONS_ID_TOKEN_REQUEST_TOKEN", "ACTIONS_RUNTIME_TOKEN", "GITHUB_TOKEN", "GH_TOKEN", "GITLAB_TOKEN", "CI_JOB_TOKEN", "FORGEJO_TOKEN", "GITEA_TOKEN", "OCI_PASSWORD", "DOCKER_PASSWORD"];
19
+ /** What a masked value is replaced with. */
20
+ export declare const REDACTED = "[redacted]";
21
+ /** Masks credentials in text a publish prints. */
22
+ export interface Redactor {
23
+ /** Add a value to mask. Values shorter than 4 characters are not masked, since they would mask ordinary text. */
24
+ add(value: string | undefined | null): void;
25
+ redact(text: string): string;
26
+ }
27
+ /** A redactor that masks the token variables of `env` and every value added to it later. */
28
+ export declare function createRedactor(env?: NodeJS.ProcessEnv, names?: readonly string[]): Redactor;
29
+ /** A credential a target read, and the variable it came from. The value is never printed. */
30
+ export interface Credential {
31
+ value: string;
32
+ /** The environment variable, or `docker config` for one read from the docker config file. */
33
+ from: string;
34
+ }
35
+ /** The first of `names` set in `env`, added to `redactor`, or null. */
36
+ export declare function readCredential(env: NodeJS.ProcessEnv, names: readonly string[], redactor: Redactor): Credential | null;
37
+ //# sourceMappingURL=publish-credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish-credentials.d.ts","sourceRoot":"","sources":["../../src/lifecycle/publish-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,sHAAsH;AACtH,eAAO,MAAM,cAAc,YACzB,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,gCAAgC,EAChC,uBAAuB,EACvB,cAAc,EACd,UAAU,EACV,cAAc,EACd,cAAc,EACd,eAAe,EACf,aAAa,EACb,cAAc,EACd,iBAAiB,CACT,CAAC;AAEX,4CAA4C;AAC5C,eAAO,MAAM,QAAQ,eAAe,CAAC;AAErC,kDAAkD;AAClD,MAAM,WAAW,QAAQ;IACvB,iHAAiH;IACjH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;IAC5C,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAC9B;AAED,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAe,EAAE,KAAK,GAAE,SAAS,MAAM,EAAO,GAAG,QAAQ,CAkBnG;AAED,6FAA6F;AAC7F,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAC;CACd;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,QAAQ,EAAE,QAAQ,GAAG,UAAU,GAAG,IAAI,CAStH"}
@@ -0,0 +1,69 @@
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
+ /** Environment variables that hold a token in CI or for a registry. Their values are masked in all publish output. */
18
+ export const TOKEN_ENV_VARS = [
19
+ "NODE_AUTH_TOKEN",
20
+ "NPM_TOKEN",
21
+ "NPM_ID_TOKEN",
22
+ "SIGSTORE_ID_TOKEN",
23
+ "ACTIONS_ID_TOKEN_REQUEST_TOKEN",
24
+ "ACTIONS_RUNTIME_TOKEN",
25
+ "GITHUB_TOKEN",
26
+ "GH_TOKEN",
27
+ "GITLAB_TOKEN",
28
+ "CI_JOB_TOKEN",
29
+ "FORGEJO_TOKEN",
30
+ "GITEA_TOKEN",
31
+ "OCI_PASSWORD",
32
+ "DOCKER_PASSWORD",
33
+ ];
34
+ /** What a masked value is replaced with. */
35
+ export const REDACTED = "[redacted]";
36
+ /** A redactor that masks the token variables of `env` and every value added to it later. */
37
+ export function createRedactor(env = {}, names = []) {
38
+ const values = new Set();
39
+ const add = (v) => {
40
+ if (typeof v === "string" && v.trim().length >= 4)
41
+ values.add(v.trim());
42
+ };
43
+ for (const name of [...TOKEN_ENV_VARS, ...names])
44
+ add(env[name]);
45
+ return {
46
+ add,
47
+ redact(text) {
48
+ let out = text;
49
+ // Longest first, so a value that contains another is masked whole.
50
+ for (const v of [...values].sort((a, b) => b.length - a.length))
51
+ out = out.split(v).join(REDACTED);
52
+ return out
53
+ .replace(/(_authToken\s*=\s*)("?)[^\s"]+\2/g, `$1${REDACTED}`)
54
+ .replace(/(authorization:\s*(?:bearer|basic|token)\s+)[^\s"',]+/gi, `$1${REDACTED}`)
55
+ .replace(/(private-token:\s*|job-token:\s*)[^\s"',]+/gi, `$1${REDACTED}`);
56
+ },
57
+ };
58
+ }
59
+ /** The first of `names` set in `env`, added to `redactor`, or null. */
60
+ export function readCredential(env, names, redactor) {
61
+ for (const name of names) {
62
+ const value = env[name];
63
+ if (typeof value === "string" && value !== "") {
64
+ redactor.add(value);
65
+ return { value, from: name };
66
+ }
67
+ }
68
+ return null;
69
+ }