@intentius/chant 0.124.32 → 0.124.34

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 (320) hide show
  1. package/dist/ci-jobs.d.ts +4 -0
  2. package/dist/ci-jobs.d.ts.map +1 -1
  3. package/dist/ci-jobs.js +9 -2
  4. package/dist/cli/handlers/build.d.ts.map +1 -1
  5. package/dist/cli/handlers/build.js +18 -3
  6. package/dist/cli/handlers/lifecycle-health.d.ts +16 -0
  7. package/dist/cli/handlers/lifecycle-health.d.ts.map +1 -0
  8. package/dist/cli/handlers/lifecycle-health.js +150 -0
  9. package/dist/cli/handlers/lifecycle.d.ts +26 -0
  10. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  11. package/dist/cli/handlers/lifecycle.js +1 -1
  12. package/dist/cli/handlers/pr.d.ts +2 -0
  13. package/dist/cli/handlers/pr.d.ts.map +1 -1
  14. package/dist/cli/handlers/pr.js +124 -2
  15. package/dist/cli/handlers/promote.d.ts.map +1 -1
  16. package/dist/cli/handlers/promote.js +277 -74
  17. package/dist/cli/handlers/release.d.ts +16 -1
  18. package/dist/cli/handlers/release.d.ts.map +1 -1
  19. package/dist/cli/handlers/release.js +83 -11
  20. package/dist/cli/main.d.ts.map +1 -1
  21. package/dist/cli/main.js +99 -22
  22. package/dist/cli/parse-args.d.ts.map +1 -1
  23. package/dist/cli/parse-args.js +30 -1
  24. package/dist/cli/registry.d.ts +21 -5
  25. package/dist/cli/registry.d.ts.map +1 -1
  26. package/dist/components/deploy-inputs.d.ts +91 -0
  27. package/dist/components/deploy-inputs.d.ts.map +1 -0
  28. package/dist/components/deploy-inputs.js +184 -0
  29. package/dist/components/driver.d.ts.map +1 -1
  30. package/dist/components/driver.js +9 -0
  31. package/dist/components/gate-plan.d.ts +8 -0
  32. package/dist/components/gate-plan.d.ts.map +1 -1
  33. package/dist/components/gate-plan.js +6 -0
  34. package/dist/components/pr-pipeline.d.ts +33 -10
  35. package/dist/components/pr-pipeline.d.ts.map +1 -1
  36. package/dist/components/pr-pipeline.js +42 -11
  37. package/dist/components/pr-run.d.ts +1 -1
  38. package/dist/components/pr-run.d.ts.map +1 -1
  39. package/dist/components/promote-rounds.d.ts +102 -0
  40. package/dist/components/promote-rounds.d.ts.map +1 -0
  41. package/dist/components/promote-rounds.js +174 -0
  42. package/dist/components/release-compile.d.ts +37 -2
  43. package/dist/components/release-compile.d.ts.map +1 -1
  44. package/dist/components/release-compile.js +64 -8
  45. package/dist/components/release-gate.d.ts +30 -0
  46. package/dist/components/release-gate.d.ts.map +1 -1
  47. package/dist/components/release-gate.js +92 -42
  48. package/dist/components/release-promote.d.ts +17 -3
  49. package/dist/components/release-promote.d.ts.map +1 -1
  50. package/dist/components/release-promote.js +79 -18
  51. package/dist/components/verbs/bom-writer.d.ts +1 -1
  52. package/dist/components/verbs/bom-writer.d.ts.map +1 -1
  53. package/dist/components/verbs/lockfile-sbom-generator.d.ts +20 -0
  54. package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -1
  55. package/dist/components/verbs/lockfile-sbom-generator.js +41 -4
  56. package/dist/components/wave-plan.d.ts +10 -0
  57. package/dist/components/wave-plan.d.ts.map +1 -1
  58. package/dist/components/wave-plan.js +32 -4
  59. package/dist/graph-declared.d.ts +4 -0
  60. package/dist/graph-declared.d.ts.map +1 -1
  61. package/dist/graph-declared.js +11 -1
  62. package/dist/graph-detail.d.ts.map +1 -1
  63. package/dist/graph-detail.js +32 -7
  64. package/dist/health.d.ts +110 -0
  65. package/dist/health.d.ts.map +1 -0
  66. package/dist/health.js +105 -0
  67. package/dist/health.schema.json +191 -0
  68. package/dist/lexicon.d.ts +27 -0
  69. package/dist/lexicon.d.ts.map +1 -1
  70. package/dist/lifecycle/merge-record.d.ts +104 -0
  71. package/dist/lifecycle/merge-record.d.ts.map +1 -0
  72. package/dist/lifecycle/merge-record.js +102 -0
  73. package/dist/lifecycle/publish-attachments.d.ts +8 -5
  74. package/dist/lifecycle/publish-attachments.d.ts.map +1 -1
  75. package/dist/lifecycle/publish-attachments.js +27 -13
  76. package/dist/lifecycle/publish-npm.d.ts +3 -2
  77. package/dist/lifecycle/publish-npm.d.ts.map +1 -1
  78. package/dist/lifecycle/publish-npm.js +4 -3
  79. package/dist/lifecycle/release-at.d.ts +40 -0
  80. package/dist/lifecycle/release-at.d.ts.map +1 -0
  81. package/dist/lifecycle/release-at.js +83 -0
  82. package/dist/lifecycle/release-cut.d.ts.map +1 -1
  83. package/dist/lifecycle/release-cut.js +23 -12
  84. package/dist/lifecycle/release-publish.d.ts +28 -12
  85. package/dist/lifecycle/release-publish.d.ts.map +1 -1
  86. package/dist/lifecycle/release-publish.js +29 -17
  87. package/dist/lifecycle/release-store.d.ts +25 -2
  88. package/dist/lifecycle/release-store.d.ts.map +1 -1
  89. package/dist/lifecycle/release-store.js +40 -13
  90. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  91. package/dist/lifecycle/run-ledger.js +1 -0
  92. package/dist/lint/rules/op/index.d.ts +2 -1
  93. package/dist/lint/rules/op/index.d.ts.map +1 -1
  94. package/dist/lint/rules/op/index.js +4 -2
  95. package/dist/lint/rules/op/ops018-shell-image-digest.d.ts +10 -0
  96. package/dist/lint/rules/op/ops018-shell-image-digest.d.ts.map +1 -0
  97. package/dist/lint/rules/op/ops018-shell-image-digest.js +38 -0
  98. package/dist/op/activities/activity-contracts.d.ts +5 -0
  99. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  100. package/dist/op/activities/activity-contracts.js +3 -0
  101. package/dist/op/activities/shell.d.ts +38 -0
  102. package/dist/op/activities/shell.d.ts.map +1 -1
  103. package/dist/op/activities/shell.js +89 -3
  104. package/dist/op/builders.d.ts +7 -0
  105. package/dist/op/builders.d.ts.map +1 -1
  106. package/dist/op/builders.js +7 -0
  107. package/dist/op/runtime.d.ts +2 -0
  108. package/dist/op/runtime.d.ts.map +1 -1
  109. package/dist/pr-loop.d.ts +14 -3
  110. package/dist/pr-loop.d.ts.map +1 -1
  111. package/dist/pr-loop.js +15 -5
  112. package/dist/pr-report-url.d.ts +2 -2
  113. package/dist/pr-report-url.d.ts.map +1 -1
  114. package/dist/runner-start.d.ts +179 -0
  115. package/dist/runner-start.d.ts.map +1 -0
  116. package/dist/runner-start.js +277 -0
  117. package/dist/wave-runtime.mjs +236 -229
  118. package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
  119. package/dist/workspace/__fixtures__/contract-repo.js +13 -2
  120. package/dist/workspace/check.schema.json +88 -2
  121. package/dist/workspace/checks/ci-workflow.d.ts +17 -0
  122. package/dist/workspace/checks/ci-workflow.d.ts.map +1 -0
  123. package/dist/workspace/checks/ci-workflow.js +123 -0
  124. package/dist/workspace/checks/security.d.ts +50 -0
  125. package/dist/workspace/checks/security.d.ts.map +1 -0
  126. package/dist/workspace/checks/security.js +305 -0
  127. package/dist/workspace/checks.d.ts +2 -0
  128. package/dist/workspace/checks.d.ts.map +1 -1
  129. package/dist/workspace/checks.js +8 -0
  130. package/dist/workspace/ci-builds.d.ts +163 -0
  131. package/dist/workspace/ci-builds.d.ts.map +1 -0
  132. package/dist/workspace/ci-builds.js +315 -0
  133. package/dist/workspace/ci-green-cli.d.ts +2 -1
  134. package/dist/workspace/ci-green-cli.d.ts.map +1 -1
  135. package/dist/workspace/ci-green-cli.js +25 -5
  136. package/dist/workspace/ci-green-forge.d.ts +12 -0
  137. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  138. package/dist/workspace/ci-green-forge.js +12 -0
  139. package/dist/workspace/ci-green-workflow.d.ts +55 -1
  140. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  141. package/dist/workspace/ci-green-workflow.js +128 -1
  142. package/dist/workspace/ci-green.d.ts +89 -4
  143. package/dist/workspace/ci-green.d.ts.map +1 -1
  144. package/dist/workspace/ci-green.js +310 -23
  145. package/dist/workspace/ci-init.d.ts +100 -0
  146. package/dist/workspace/ci-init.d.ts.map +1 -0
  147. package/dist/workspace/ci-init.js +434 -0
  148. package/dist/workspace/ci-last-green.schema.json +49 -0
  149. package/dist/workspace/ci-list.schema.json +35 -1
  150. package/dist/workspace/ci-run-index.d.ts +1 -1
  151. package/dist/workspace/ci-run-index.d.ts.map +1 -1
  152. package/dist/workspace/ci-run-index.schema.json +1 -1
  153. package/dist/workspace/ci-run.d.ts +12 -6
  154. package/dist/workspace/ci-run.d.ts.map +1 -1
  155. package/dist/workspace/ci-run.js +72 -16
  156. package/dist/workspace/ci-runner-start.d.ts +77 -0
  157. package/dist/workspace/ci-runner-start.d.ts.map +1 -0
  158. package/dist/workspace/ci-runner-start.js +185 -0
  159. package/dist/workspace/ci-serve.d.ts +76 -7
  160. package/dist/workspace/ci-serve.d.ts.map +1 -1
  161. package/dist/workspace/ci-serve.js +153 -19
  162. package/dist/workspace/ci-workflow.d.ts +91 -0
  163. package/dist/workspace/ci-workflow.d.ts.map +1 -0
  164. package/dist/workspace/ci-workflow.js +33 -0
  165. package/dist/workspace/declaration.d.ts +35 -0
  166. package/dist/workspace/declaration.d.ts.map +1 -1
  167. package/dist/workspace/declaration.js +12 -2
  168. package/dist/workspace/declaration.schema.json +51 -0
  169. package/dist/workspace/init.js +2 -2
  170. package/dist/workspace/ls.d.ts +2 -0
  171. package/dist/workspace/ls.d.ts.map +1 -1
  172. package/dist/workspace/ls.js +1 -1
  173. package/dist/workspace/ls.schema.json +2 -1
  174. package/dist/workspace/member-pipeline.d.ts +1 -0
  175. package/dist/workspace/member-pipeline.d.ts.map +1 -1
  176. package/dist/workspace/member-pipeline.js +2 -0
  177. package/dist/workspace/merge-record.schema.json +49 -0
  178. package/dist/workspace/pr-report.schema.json +3 -3
  179. package/dist/workspace/reason-codes.d.ts +26 -0
  180. package/dist/workspace/reason-codes.d.ts.map +1 -1
  181. package/dist/workspace/reason-codes.js +28 -0
  182. package/dist/workspace/release-candidates.d.ts +129 -0
  183. package/dist/workspace/release-candidates.d.ts.map +1 -0
  184. package/dist/workspace/release-candidates.js +233 -0
  185. package/dist/workspace/release-candidates.schema.json +353 -0
  186. package/dist/workspace/release-workflow.d.ts +31 -0
  187. package/dist/workspace/release-workflow.d.ts.map +1 -0
  188. package/dist/workspace/release-workflow.js +150 -0
  189. package/dist/workspace/security.d.ts +40 -0
  190. package/dist/workspace/security.d.ts.map +1 -0
  191. package/dist/workspace/security.js +99 -0
  192. package/package.json +8 -1
  193. package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +1 -1
  194. package/src/ci-jobs.ts +11 -2
  195. package/src/cli/handlers/build.test.ts +36 -0
  196. package/src/cli/handlers/build.ts +18 -3
  197. package/src/cli/handlers/lifecycle-health.test.ts +123 -0
  198. package/src/cli/handlers/lifecycle-health.ts +157 -0
  199. package/src/cli/handlers/lifecycle.ts +1 -1
  200. package/src/cli/handlers/pr-deploy-inputs.test.ts +209 -0
  201. package/src/cli/handlers/pr-record.test.ts +201 -0
  202. package/src/cli/handlers/pr.test.ts +22 -0
  203. package/src/cli/handlers/pr.ts +127 -4
  204. package/src/cli/handlers/promote.test.ts +17 -0
  205. package/src/cli/handlers/promote.ts +296 -84
  206. package/src/cli/handlers/release-cut-at.e2e.test.ts +115 -0
  207. package/src/cli/handlers/release-publish-targets.e2e.test.ts +2 -2
  208. package/src/cli/handlers/release-verify.e2e.test.ts +12 -10
  209. package/src/cli/handlers/release.e2e.test.ts +19 -28
  210. package/src/cli/handlers/release.ts +81 -11
  211. package/src/cli/main.ts +99 -22
  212. package/src/cli/parse-args.ts +22 -1
  213. package/src/cli/registry.ts +21 -5
  214. package/src/components/deploy-inputs.test.ts +108 -0
  215. package/src/components/deploy-inputs.ts +225 -0
  216. package/src/components/driver.ts +11 -0
  217. package/src/components/gate-plan.ts +9 -0
  218. package/src/components/pr-pipeline.ts +56 -15
  219. package/src/components/pr-run.ts +1 -1
  220. package/src/components/promote-rounds.test.ts +220 -0
  221. package/src/components/promote-rounds.ts +232 -0
  222. package/src/components/release-compile.test.ts +34 -0
  223. package/src/components/release-compile.ts +89 -8
  224. package/src/components/release-gate.test.ts +47 -0
  225. package/src/components/release-gate.ts +111 -46
  226. package/src/components/release-promote.test.ts +55 -2
  227. package/src/components/release-promote.ts +93 -23
  228. package/src/components/verbs/bom-writer.ts +1 -1
  229. package/src/components/verbs/lockfile-sbom-generator.test.ts +36 -0
  230. package/src/components/verbs/lockfile-sbom-generator.ts +43 -5
  231. package/src/components/wave-plan.ts +32 -4
  232. package/src/graph-declared-exports.test.ts +40 -0
  233. package/src/graph-declared.ts +9 -1
  234. package/src/graph-detail.test.ts +57 -0
  235. package/src/graph-detail.ts +33 -7
  236. package/src/health.schema.json +191 -0
  237. package/src/health.test.ts +62 -0
  238. package/src/health.ts +178 -0
  239. package/src/lexicon.ts +41 -1
  240. package/src/lifecycle/merge-record.test.ts +111 -0
  241. package/src/lifecycle/merge-record.ts +154 -0
  242. package/src/lifecycle/publish-attachments.ts +25 -13
  243. package/src/lifecycle/publish-npm.ts +4 -3
  244. package/src/lifecycle/publish-targets.test.ts +34 -4
  245. package/src/lifecycle/release-at.test.ts +48 -0
  246. package/src/lifecycle/release-at.ts +100 -0
  247. package/src/lifecycle/release-cut.test.ts +41 -0
  248. package/src/lifecycle/release-cut.ts +22 -11
  249. package/src/lifecycle/release-publish.test.ts +43 -13
  250. package/src/lifecycle/release-publish.ts +38 -18
  251. package/src/lifecycle/release-store.ts +57 -13
  252. package/src/lifecycle/run-ledger.test.ts +13 -0
  253. package/src/lifecycle/run-ledger.ts +1 -0
  254. package/src/lint/rules/op/index.ts +4 -2
  255. package/src/lint/rules/op/ops018-shell-image-digest.test.ts +60 -0
  256. package/src/lint/rules/op/ops018-shell-image-digest.ts +48 -0
  257. package/src/op/activities/activity-contracts.ts +3 -0
  258. package/src/op/activities/shell-container.e2e.test.ts +67 -0
  259. package/src/op/activities/shell.test.ts +44 -1
  260. package/src/op/activities/shell.ts +110 -3
  261. package/src/op/builders.ts +7 -0
  262. package/src/op/runtime.ts +2 -0
  263. package/src/op/step-console.test.ts +4 -2
  264. package/src/pr-loop.test.ts +29 -0
  265. package/src/pr-loop.ts +26 -7
  266. package/src/pr-report-url.ts +2 -2
  267. package/src/runner-start-bundle.test.ts +29 -0
  268. package/src/runner-start.test.ts +179 -0
  269. package/src/runner-start.ts +329 -0
  270. package/src/workspace/__fixtures__/contract-repo.ts +11 -2
  271. package/src/workspace/check-contract.test.ts +4 -2
  272. package/src/workspace/check.schema.json +88 -2
  273. package/src/workspace/checks/ci-workflow.test.ts +150 -0
  274. package/src/workspace/checks/ci-workflow.ts +129 -0
  275. package/src/workspace/checks/security.test.ts +138 -0
  276. package/src/workspace/checks/security.ts +305 -0
  277. package/src/workspace/checks.ts +8 -0
  278. package/src/workspace/ci-builds.e2e.test.ts +208 -0
  279. package/src/workspace/ci-builds.test.ts +174 -0
  280. package/src/workspace/ci-builds.ts +385 -0
  281. package/src/workspace/ci-green-builds.test.ts +180 -0
  282. package/src/workspace/ci-green-cli.test.ts +54 -0
  283. package/src/workspace/ci-green-cli.ts +26 -5
  284. package/src/workspace/ci-green-forge.ts +24 -0
  285. package/src/workspace/ci-green-workflow.test.ts +84 -1
  286. package/src/workspace/ci-green-workflow.ts +139 -2
  287. package/src/workspace/ci-green.test.ts +5 -2
  288. package/src/workspace/ci-green.ts +359 -27
  289. package/src/workspace/ci-init.test.ts +159 -0
  290. package/src/workspace/ci-init.ts +445 -0
  291. package/src/workspace/ci-last-green.schema.json +49 -0
  292. package/src/workspace/ci-list.schema.json +35 -1
  293. package/src/workspace/ci-report-url.test.ts +6 -1
  294. package/src/workspace/ci-run-index.schema.json +1 -1
  295. package/src/workspace/ci-run-index.ts +1 -1
  296. package/src/workspace/ci-run.test.ts +24 -7
  297. package/src/workspace/ci-run.ts +78 -16
  298. package/src/workspace/ci-runner-start.test.ts +151 -0
  299. package/src/workspace/ci-runner-start.ts +193 -0
  300. package/src/workspace/ci-serve-builds.test.ts +86 -0
  301. package/src/workspace/ci-serve.test.ts +59 -0
  302. package/src/workspace/ci-serve.ts +177 -22
  303. package/src/workspace/ci-workflow.ts +71 -0
  304. package/src/workspace/declaration.schema.json +51 -0
  305. package/src/workspace/declaration.test.ts +17 -0
  306. package/src/workspace/declaration.ts +53 -3
  307. package/src/workspace/init.ts +2 -2
  308. package/src/workspace/ls.schema.json +2 -1
  309. package/src/workspace/ls.ts +3 -2
  310. package/src/workspace/member-pipeline.ts +2 -1
  311. package/src/workspace/merge-record.schema.json +49 -0
  312. package/src/workspace/pr-report.schema.json +3 -3
  313. package/src/workspace/reason-codes.test.ts +4 -0
  314. package/src/workspace/reason-codes.ts +28 -0
  315. package/src/workspace/release-candidates.schema.json +353 -0
  316. package/src/workspace/release-candidates.test.ts +106 -0
  317. package/src/workspace/release-candidates.ts +315 -0
  318. package/src/workspace/release-workflow.ts +167 -0
  319. package/src/workspace/security.test.ts +70 -0
  320. package/src/workspace/security.ts +118 -0
@@ -0,0 +1,434 @@
1
+ /**
2
+ * `chant ci init --workflow <name> [--forge <forge>] [--security <group>]...
3
+ * [--yes]` (#4168 step 6, section 9): set a workspace up for a prescribed
4
+ * delivery workflow (../ci-workflow.ts).
5
+ *
6
+ * It proposes, prints the proposal as a diff, and writes only on `--yes` or a
7
+ * yes at the prompt. What it proposes comes from what `workspace check`'s
8
+ * WSP151 reports for the workspace as it would be with `ci.workflow` set, so
9
+ * a second run proposes only what is still missing, and nothing once the
10
+ * workspace has it all.
11
+ *
12
+ * - The declaration gains `ci.workflow`, a `ci.green` block when it has none
13
+ * (one phase per GitHub workflow, its jobs as the check runs, and the
14
+ * workflows a pull request runs as `preMerge`), and `preMerge` when the
15
+ * block has none. `--security` adds groups to the top-level `security`;
16
+ * without it no group is turned on, and the groups are listed.
17
+ * - A member that builds with chant and declares no component gets a
18
+ * `<member>.component.ts` stub with a gate and a TODO line, which WSP151
19
+ * keeps reporting until someone fills the stub in. A `chant.config.ts`
20
+ * that declares no environments gains `environments: ["staging", "prod"]`
21
+ * when its default export is a plain object literal.
22
+ * - The generated files are written by the generators that own them, run
23
+ * after the edits: `chant build --components --generate <forge> --pr-loop`
24
+ * in each member, `chant ci workflow` for the tick, and `chant release
25
+ * workflow` for the publish job. Each is named in the proposal before it
26
+ * runs.
27
+ *
28
+ * It never edits a file the diff did not show, and never overwrites a file
29
+ * it proposed to create that appeared in the meantime.
30
+ */
31
+ import { spawnSync } from "node:child_process";
32
+ import { existsSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
33
+ import { basename, join } from "node:path";
34
+ import { formatError, formatSuccess } from "../cli/format.js";
35
+ import { parseYAMLDocument } from "../yaml.js";
36
+ import { CI_WORKFLOW_CHECKS } from "./checks/ci-workflow.js";
37
+ import { CI_WORKFLOWS, STUB_MARKER, appliesOnMerge } from "./ci-workflow.js";
38
+ export { STUB_MARKER };
39
+ import { CI_GREEN_WORKFLOW_FILE, jobCheckRuns, readWorkflowSources, workflowsHolding } from "./ci-green-workflow.js";
40
+ import { findWorkspaceRoot } from "../project-root.js";
41
+ import { parseDeclaration, readDeclaration, WorkspaceReadError } from "./declaration.js";
42
+ import { staticPromotionOrder } from "./promotion-order.js";
43
+ import { SECURITY_GROUPS } from "./security.js";
44
+ import { gitTop, workingTree } from "./tree.js";
45
+ import { RELEASE_WORKFLOW_FORGES } from "./release-workflow.js";
46
+ const USAGE = "chant ci init --workflow trunk-curated|trunk-curated+apply-on-merge [--forge github|gitlab|forgejo] [--security <group>]... [--yes]";
47
+ /** The environments a config with none is given. */
48
+ export const DEFAULT_ENVIRONMENTS = ["staging", "prod"];
49
+ const memberDir = (dir) => (dir === "." ? "" : dir);
50
+ const join2 = (dir, file) => (dir === "" ? file : `${dir}/${file}`);
51
+ const isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
52
+ /** The forge whose pipeline files the repository has, or null. */
53
+ export function inferForge(repoRoot) {
54
+ if (existsSync(join(repoRoot, ".github", "workflows")))
55
+ return "github";
56
+ if (existsSync(join(repoRoot, ".forgejo", "workflows")))
57
+ return "forgejo";
58
+ if (existsSync(join(repoRoot, ".gitlab-ci.yml")))
59
+ return "gitlab";
60
+ return null;
61
+ }
62
+ /** The branch `origin/HEAD` names, else `main`. */
63
+ export function inferTrunk(repoRoot) {
64
+ const r = spawnSync("git", ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], { cwd: repoRoot, encoding: "utf-8" });
65
+ const ref = r.status === 0 ? r.stdout.trim() : "";
66
+ return ref.startsWith("origin/") ? ref.slice("origin/".length) : "main";
67
+ }
68
+ /** Whether a workflow document runs on a pull request. */
69
+ function onPullRequest(doc) {
70
+ if (!isObject(doc))
71
+ return false;
72
+ const on = doc.on ?? doc["true"];
73
+ if (typeof on === "string")
74
+ return on === "pull_request" || on === "pull_request_target";
75
+ if (Array.isArray(on))
76
+ return on.includes("pull_request") || on.includes("pull_request_target");
77
+ return isObject(on) && ("pull_request" in on || "pull_request_target" in on);
78
+ }
79
+ /**
80
+ * A `ci.green` block from the repository's GitHub workflows: a phase per
81
+ * workflow holding its jobs' check runs, all required, and those a pull
82
+ * request runs as `preMerge`. chant's own generated workflows are left out.
83
+ */
84
+ export function inferGreen(repoRoot) {
85
+ const phases = {};
86
+ const preMerge = [];
87
+ for (const s of readWorkflowSources(repoRoot, "")) {
88
+ if (/\/chant-[^/]*\.ya?ml$/.test(s.path))
89
+ continue;
90
+ let doc;
91
+ try {
92
+ doc = parseYAMLDocument(s.text);
93
+ }
94
+ catch {
95
+ continue;
96
+ }
97
+ const runs = jobCheckRuns(doc);
98
+ if (runs.length === 0)
99
+ continue;
100
+ const name = basename(s.path).replace(/\.ya?ml$/, "").replace(/[^A-Za-z0-9_.-]/g, "-").slice(0, 64) || "check";
101
+ phases[name] = runs;
102
+ if (onPullRequest(doc))
103
+ preMerge.push(name);
104
+ }
105
+ if (Object.keys(phases).length === 0) {
106
+ phases.check = ["check"];
107
+ preMerge.push("check");
108
+ }
109
+ return { branch: inferTrunk(repoRoot), phases, require: Object.keys(phases), preMerge: preMerge.length > 0 ? preMerge : [Object.keys(phases)[0]] };
110
+ }
111
+ /**
112
+ * The declaration's text with `next`'s top-level keys. A key the file lacks
113
+ * is added as text before the closing brace, so the rest keeps its layout; a
114
+ * key it has whose value changes rewrites the file, two-space indented.
115
+ */
116
+ export function editDeclarationText(text, raw, next) {
117
+ const whole = `${JSON.stringify(next, null, 2)}\n`;
118
+ const changed = Object.keys(next).filter((k) => JSON.stringify(raw[k]) !== JSON.stringify(next[k]));
119
+ if (changed.length === 0)
120
+ return text;
121
+ if (changed.some((k) => k in raw))
122
+ return whole;
123
+ const close = text.lastIndexOf("}");
124
+ if (close < 0)
125
+ return whole;
126
+ const head = text.slice(0, close).replace(/\s*$/, "");
127
+ const added = changed.map((k) => ` ${JSON.stringify(k)}: ${JSON.stringify(next[k], null, 2).replace(/\n/g, "\n ")}`).join(",\n");
128
+ const out = `${head}${head.endsWith("{") ? "" : ","}\n${added}\n${text.slice(close)}`;
129
+ try {
130
+ return JSON.stringify(JSON.parse(out)) === JSON.stringify(next) ? out : whole;
131
+ }
132
+ catch {
133
+ return whole;
134
+ }
135
+ }
136
+ /** An identifier for a member's stub export. */
137
+ function exportName(member) {
138
+ const parts = member.split(/[^A-Za-z0-9]+/).filter(Boolean);
139
+ const id = parts.map((p, i) => (i === 0 ? p.toLowerCase() : p[0].toUpperCase() + p.slice(1))).join("");
140
+ return /^[A-Za-z_]/.test(id) ? id : `component${id}`;
141
+ }
142
+ /** The component stub for a member that declares none: a gate and a TODO, so WSP151 keeps asking until it is filled in. */
143
+ export function componentStub(member, lastEnvironment) {
144
+ return [
145
+ `// ${STUB_MARKER}: say how ${member} is built and deployed, then delete this line.`,
146
+ "// The gate makes a promote wait on an approval; a release gate decides it",
147
+ `// for each environment, ${lastEnvironment} among them.`,
148
+ "// chant-disable COMP004 -- the release gate: the person promoting approves each environment with chant approve",
149
+ 'import { gate, phase, type Component } from "@intentius/chant/components/component";',
150
+ "",
151
+ `export const ${exportName(member)}: Component = {`,
152
+ ` name: ${JSON.stringify(member)},`,
153
+ " deploy: [",
154
+ ` phase("Release", [gate("release", { description: "Approve the release for ${lastEnvironment}." })]),`,
155
+ " ],",
156
+ "};",
157
+ "",
158
+ ].join("\n");
159
+ }
160
+ /** The config with `environments` added after `export default {`, or null when its default export is not a plain object literal. */
161
+ export function withEnvironments(config, environments = DEFAULT_ENVIRONMENTS) {
162
+ const m = /export\s+default\s*\{/.exec(config);
163
+ if (!m)
164
+ return null;
165
+ const at = m.index + m[0].length;
166
+ return `${config.slice(0, at)}\n environments: ${JSON.stringify(environments).replace(/,/g, ", ")},${config.slice(at)}`;
167
+ }
168
+ function read(tree, path) {
169
+ if (tree.stat(path) !== "file")
170
+ return null;
171
+ try {
172
+ return tree.read(path);
173
+ }
174
+ catch {
175
+ return null;
176
+ }
177
+ }
178
+ /** Run WSP151 over `declaration` and the tree as it is. */
179
+ function check(declaration, tree) {
180
+ return CI_WORKFLOW_CHECKS.flatMap((c) => c.check({ declaration, tree, groups: [], kinds: new Map(), kindProblems: [] }));
181
+ }
182
+ /**
183
+ * What `chant ci init` would write for the workspace at `root`, and the
184
+ * generators it would run. Reads only; {@link applyCiInit} writes.
185
+ */
186
+ export function proposeCiInit(root, options) {
187
+ const tree = workingTree(root);
188
+ const current = readDeclaration(tree);
189
+ if (current.file.endsWith(".jsonc")) {
190
+ throw new WorkspaceReadError("declaration-invalid", `${current.file} is JSONC, which chant ci init does not rewrite, since its comments would be lost; add ci.workflow by hand, then rerun chant ci init for the rest`);
191
+ }
192
+ const repoRoot = gitTop(root) ?? root;
193
+ const text = readFileSync(join(root, current.file), "utf-8");
194
+ const raw = JSON.parse(text);
195
+ const ci = isObject(raw.ci) ? { ...raw.ci } : {};
196
+ ci.workflow = options.workflow;
197
+ if (!isObject(ci.green))
198
+ ci.green = inferGreen(repoRoot);
199
+ else if (!Array.isArray(ci.green.preMerge) || ci.green.preMerge.length === 0) {
200
+ const inferred = inferGreen(repoRoot).preMerge;
201
+ const declared = Object.keys(ci.green.phases ?? {});
202
+ const pick = inferred.filter((p) => declared.includes(p));
203
+ ci.green = { ...ci.green, preMerge: pick.length > 0 ? pick : declared.slice(0, 1) };
204
+ }
205
+ const next = { ...raw, ci };
206
+ const security = [...new Set([...(raw.security ?? []), ...(options.security ?? [])])];
207
+ if (security.length > 0)
208
+ next.security = security;
209
+ const after = editDeclarationText(text, raw, next);
210
+ const proposed = parseDeclaration(after, current.file);
211
+ const files = [];
212
+ if (after !== text)
213
+ files.push({ path: current.file, before: text, after });
214
+ const notes = [];
215
+ const findings = check(proposed, tree);
216
+ const reported = (code, entity) => findings.some((f) => f.code === code && (entity === undefined || f.entity === entity));
217
+ const forge = options.forge ?? inferForge(repoRoot);
218
+ const releasing = proposed.members.filter((m) => (m.ciWorkflow ?? proposed.ci?.workflow) && (m.ciWorkflow !== null || tree.stat(join2(memberDir(m.dir), "chant.config.ts")) === "file"));
219
+ const firstEnv = new Map();
220
+ for (const m of releasing) {
221
+ const dir = memberDir(m.dir);
222
+ const configPath = join2(dir, "chant.config.ts");
223
+ const config = read(tree, configPath);
224
+ let order = config === null ? [] : (staticPromotionOrder(config) ?? null);
225
+ if (reported("ci-workflow-no-environments", m.name) && config !== null) {
226
+ const edited = withEnvironments(config);
227
+ if (edited) {
228
+ files.push({ path: configPath, before: config, after: edited });
229
+ order = DEFAULT_ENVIRONMENTS.map((name) => ({ name, canary: false }));
230
+ notes.push(`${configPath}: ${DEFAULT_ENVIRONMENTS.join(", ")} is a guess at the promotion order; edit it if yours differs.`);
231
+ }
232
+ else {
233
+ notes.push(`${configPath}: add environments, the promotion order; its default export is not a plain object, so chant ci init leaves it alone.`);
234
+ }
235
+ }
236
+ const names = (order ?? []).map((s) => s.name);
237
+ if (names.length > 0)
238
+ firstEnv.set(m.name, names[0]);
239
+ if (reported("ci-workflow-no-components", m.name)) {
240
+ const stubPath = join2(dir, `${m.name}.component.ts`);
241
+ if (!tree.stat(stubPath))
242
+ files.push({ path: stubPath, before: null, after: componentStub(m.name, names[names.length - 1] ?? "the last environment") });
243
+ }
244
+ for (const f of findings.filter((f) => f.code === "ci-workflow-ungated-env" && f.entity === m.name)) {
245
+ notes.push(`${f.treeFile}: add a gate() step, so a promote to the last environment waits on an approval; chant ci init does not edit a component you wrote.`);
246
+ }
247
+ }
248
+ const commands = [];
249
+ if (forge === null && (reported("ci-workflow-pipeline-missing") || reported("ci-workflow-pipeline-differs") || reported("ci-workflow-publish-missing"))) {
250
+ notes.push("No .github/workflows, .forgejo/workflows or .gitlab-ci.yml was found, so no pipeline is generated; rerun with --forge github|gitlab|forgejo.");
251
+ }
252
+ else if (forge !== null) {
253
+ if (reported("ci-workflow-pipeline-missing") || reported("ci-workflow-pipeline-differs")) {
254
+ for (const m of releasing) {
255
+ const env = firstEnv.get(m.name);
256
+ commands.push({
257
+ cwd: memberDir(m.dir) || ".",
258
+ // The root member builds alone: at a workspace root, chant build refuses without --root-only (WSP000).
259
+ args: ["build", "--components", "--generate", forge, "--pr-loop", ...(m.dir === "." ? ["--root-only"] : []), ...(env ? ["--env", env] : []), ...(appliesOnMerge(m.ciWorkflow ?? proposed.ci.workflow) ? ["--apply-on-merge"] : [])],
260
+ why: `the pull-request pipeline for ${m.name}: plan on a pull request, ${appliesOnMerge(m.ciWorkflow ?? proposed.ci.workflow) ? "apply" : "record"} on merge`,
261
+ });
262
+ }
263
+ if (forge === "github") {
264
+ // ci workflow refuses when no workflow holds a required phase's check run, so say so instead of running it.
265
+ const green = proposed.ci?.green;
266
+ const holding = green ? workflowsHolding(green, readWorkflowSources(repoRoot, CI_GREEN_WORKFLOW_FILE)).workflows : [];
267
+ if (holding.length > 0)
268
+ commands.push({ cwd: ".", args: ["ci", "workflow"], why: "the tick that tags green commits" });
269
+ else
270
+ notes.push("No workflow in .github/workflows runs a job of a required ci.green phase on the branch yet, so the tick is not generated; add your CI, name its jobs in ci.green.phases, then rerun chant ci init.");
271
+ }
272
+ else
273
+ notes.push(`${forge} has no check runs for ci tick to read; set ci.green.source to runs and run chant ci tick from a scheduled job (see the green-commits guide).`);
274
+ }
275
+ if (reported("ci-workflow-publish-missing"))
276
+ commands.push({ cwd: ".", args: ["release", "workflow", "--forge", forge], why: "the publish job for release/** tags" });
277
+ }
278
+ return { files, commands, notes, findings };
279
+ }
280
+ /** A unified-style diff of two texts, for the proposal. Line-based, from their longest common subsequence. */
281
+ export function lineDiff(path, before, after) {
282
+ const a = before === null ? [] : before.replace(/\n$/, "").split("\n");
283
+ const b = after.replace(/\n$/, "").split("\n");
284
+ const n = a.length;
285
+ const m = b.length;
286
+ const lcs = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
287
+ for (let i = n - 1; i >= 0; i--)
288
+ for (let j = m - 1; j >= 0; j--)
289
+ lcs[i][j] = a[i] === b[j] ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1]);
290
+ const out = [`--- ${before === null ? "/dev/null" : `a/${path}`}`, `+++ b/${path}`];
291
+ let i = 0;
292
+ let j = 0;
293
+ while (i < n || j < m) {
294
+ if (i < n && j < m && a[i] === b[j]) {
295
+ out.push(` ${a[i]}`);
296
+ i++;
297
+ j++;
298
+ }
299
+ else if (i < n && (j >= m || lcs[i + 1][j] >= lcs[i][j + 1]))
300
+ out.push(`- ${a[i++]}`);
301
+ else
302
+ out.push(`+ ${b[j++]}`);
303
+ }
304
+ // Keep three lines of context around each change.
305
+ const keep = new Set();
306
+ out.forEach((l, k) => {
307
+ if (k < 2 || l.startsWith("+ ") || l.startsWith("- "))
308
+ for (let d = -3; d <= 3; d++)
309
+ keep.add(k + d);
310
+ });
311
+ const shown = [];
312
+ let skipped = false;
313
+ out.forEach((l, k) => {
314
+ if (keep.has(k)) {
315
+ shown.push(l);
316
+ skipped = false;
317
+ }
318
+ else if (!skipped) {
319
+ shown.push(" ...");
320
+ skipped = true;
321
+ }
322
+ });
323
+ return shown.join("\n");
324
+ }
325
+ /** Write the proposal's files. A file it would create that now exists is refused, and nothing is written. */
326
+ export function applyCiInit(root, proposal) {
327
+ for (const f of proposal.files) {
328
+ if (f.before === null && existsSync(join(root, f.path)))
329
+ throw new Error(`${f.path} appeared after the proposal was made; nothing written`);
330
+ if (f.before !== null && readFileSync(join(root, f.path), "utf-8") !== f.before)
331
+ throw new Error(`${f.path} changed after the proposal was made; nothing written`);
332
+ }
333
+ for (const f of proposal.files)
334
+ writeFileSync(join(root, f.path), f.after, f.before === null ? { flag: "wx" } : {});
335
+ return proposal.files.map((f) => f.path);
336
+ }
337
+ async function confirm(question) {
338
+ const { createInterface } = await import("node:readline");
339
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
340
+ return new Promise((resolveAnswer) => {
341
+ rl.question(question, (answer) => {
342
+ resolveAnswer(/^y(es)?$/i.test(answer.trim()));
343
+ rl.close();
344
+ });
345
+ rl.on("close", () => resolveAnswer(false));
346
+ });
347
+ }
348
+ /** Run a chant command the way this process was run, so `npx chant` and a source checkout both work. */
349
+ function runChant(cwd, args) {
350
+ const r = spawnSync(process.execPath, [...process.execArgv, process.argv[1], ...args], { cwd, stdio: "inherit" });
351
+ return r.status ?? 1;
352
+ }
353
+ export async function runCiInit(ctx) {
354
+ const { args } = ctx;
355
+ const workflow = (args.ciWorkflows ?? [])[0];
356
+ if (!workflow || !CI_WORKFLOWS.includes(workflow) || (args.ciWorkflows ?? []).length > 1) {
357
+ console.error(formatError({ message: `--workflow names one of ${CI_WORKFLOWS.join(", ")}`, hint: USAGE }));
358
+ return 1;
359
+ }
360
+ if (args.forge !== undefined && !RELEASE_WORKFLOW_FORGES.includes(args.forge)) {
361
+ console.error(formatError({ message: `--forge ${JSON.stringify(args.forge)} is not one of ${RELEASE_WORKFLOW_FORGES.join(", ")}`, hint: USAGE }));
362
+ return 1;
363
+ }
364
+ const security = args.securityGroups ?? [];
365
+ const unknown = security.filter((g) => !SECURITY_GROUPS.includes(g));
366
+ if (unknown.length > 0) {
367
+ console.error(formatError({ message: `--security ${unknown.join(", ")} is not a group; the groups are ${SECURITY_GROUPS.join(", ")}`, hint: USAGE }));
368
+ return 1;
369
+ }
370
+ const found = findWorkspaceRoot(process.cwd());
371
+ if (!found) {
372
+ console.error(formatError({ message: "no chant.workspace.json between this directory and the git root", hint: "Run chant workspace init first, then chant ci init." }));
373
+ return 1;
374
+ }
375
+ const root = realpathSync(found.dir);
376
+ let proposal;
377
+ try {
378
+ proposal = proposeCiInit(root, { workflow: workflow, ...(args.forge ? { forge: args.forge } : {}), security: security });
379
+ }
380
+ catch (err) {
381
+ console.error(formatError({ message: `chant ci init: ${err instanceof WorkspaceReadError ? err.describe() : err.message}`, hint: USAGE }));
382
+ return 1;
383
+ }
384
+ if (proposal.files.length === 0 && proposal.commands.length === 0) {
385
+ for (const n of proposal.notes)
386
+ console.log(n);
387
+ console.log(formatSuccess(`The workspace has what ${workflow} needs; chant workspace check reports anything left.`));
388
+ return 0;
389
+ }
390
+ for (const f of proposal.files) {
391
+ console.log(lineDiff(f.path, f.before, f.after));
392
+ console.log("");
393
+ }
394
+ if (proposal.commands.length > 0) {
395
+ console.log("Then runs:");
396
+ for (const c of proposal.commands)
397
+ console.log(` (in ${c.cwd}) chant ${c.args.join(" ")} # ${c.why}`);
398
+ console.log("");
399
+ }
400
+ for (const n of proposal.notes)
401
+ console.log(`Note: ${n}`);
402
+ if (security.length === 0)
403
+ console.log(`No security group is turned on. Add one later with chant ci init --workflow ${workflow} --security <group>: ${SECURITY_GROUPS.join(", ")}.`);
404
+ console.log("");
405
+ let write = !!args.yes;
406
+ if (!write) {
407
+ if (!process.stdin.isTTY) {
408
+ console.log("Nothing written. Re-run with --yes to write it.");
409
+ return 0;
410
+ }
411
+ write = await confirm("Write these files and run the generators? [y/N] ");
412
+ }
413
+ if (!write) {
414
+ console.log("Nothing written.");
415
+ return 0;
416
+ }
417
+ try {
418
+ for (const path of applyCiInit(root, proposal))
419
+ console.log(`Wrote ${path}`);
420
+ }
421
+ catch (err) {
422
+ console.error(formatError({ message: `chant ci init: ${err.message}`, hint: "Rerun chant ci init to see a fresh proposal." }));
423
+ return 1;
424
+ }
425
+ for (const c of proposal.commands) {
426
+ const status = runChant(join(root, c.cwd), c.args);
427
+ if (status !== 0) {
428
+ console.error(formatError({ message: `chant ${c.args.join(" ")} failed in ${c.cwd}`, hint: "Fix what it reports and rerun chant ci init; it proposes only what is still missing." }));
429
+ return 1;
430
+ }
431
+ }
432
+ console.log(formatSuccess("Done. chant workspace check reports what the workflow still lacks, such as the component stubs to fill in."));
433
+ return 0;
434
+ }
@@ -104,6 +104,55 @@
104
104
  }
105
105
  ]
106
106
  },
107
+ "newer": {
108
+ "type": "array",
109
+ "description": "Added within contract version 1 by #4169, written by chant 0.124.33 and newer. The first-parent commits walked past, newest first and above the one found, each with what its _ci/<sha>.json record on the local chant/lifecycle says (null when it has none), so a reader can say why the newest commits are not green. At most 50.",
110
+ "items": {
111
+ "type": "object",
112
+ "required": [
113
+ "sha",
114
+ "record"
115
+ ],
116
+ "properties": {
117
+ "sha": {
118
+ "type": "string",
119
+ "pattern": "^[0-9a-f]{40,64}$"
120
+ },
121
+ "record": {
122
+ "oneOf": [
123
+ {
124
+ "type": "null"
125
+ },
126
+ {
127
+ "type": "object",
128
+ "required": [
129
+ "state"
130
+ ],
131
+ "properties": {
132
+ "state": {
133
+ "enum": [
134
+ "green",
135
+ "red",
136
+ "unbuilt",
137
+ "pending"
138
+ ]
139
+ },
140
+ "reason": {
141
+ "type": "string"
142
+ },
143
+ "coveredBy": {
144
+ "type": "string"
145
+ },
146
+ "culprit": {
147
+ "type": "string"
148
+ }
149
+ }
150
+ }
151
+ ]
152
+ }
153
+ }
154
+ }
155
+ },
107
156
  "ignored": {
108
157
  "type": "array",
109
158
  "description": "Added within contract version 1 by #4031, written by chant 0.124.28 and newer. The tags on the commits walked, newest first and down to the one found, that do not count for want of a valid statement. Empty when ci.green.signedSince is not set.",
@@ -284,9 +284,10 @@
284
284
  "pass",
285
285
  "fail",
286
286
  "pending",
287
+ "unbuilt",
287
288
  "unrecorded"
288
289
  ],
289
- "description": "pass and fail come from the tags (a green tag records the phases that passed, a revoked tag the one that failed) or, for an undecided commit, from the forge under --forge, or from the signed Op runs on the local chant/lifecycle when the source is chant runs (#4044). pending is such a read with runs still going or not yet reported. unrecorded means no tag holds the result and nothing was read."
290
+ "description": "pass and fail come from the tags (a green tag records the phases that passed, a revoked tag the one that failed) or, for an undecided commit, from the forge under --forge, or from the signed Op runs on the local chant/lifecycle when the source is chant runs (#4044). pending is such a read with runs still going or not yet reported. unbuilt (#4169, chant 0.124.33 and newer) is such a read whose runs were cancelled. unrecorded means no tag holds the result and nothing was read."
290
291
  },
291
292
  "runs": {
292
293
  "type": "array",
@@ -406,6 +407,39 @@
406
407
  }
407
408
  ]
408
409
  },
410
+ "record": {
411
+ "description": "Added within contract version 1 by #4169, written by chant 0.124.33 and newer. What the commit's _ci/<sha>.json record on the local chant/lifecycle says, the tick's last word on it, or null when it has none. state is green, red, unbuilt or pending. reason, coveredBy and culprit come from the required phases: why a phase is unbuilt or still pending (cancelled, superseded, retrying, requested, request-unserved or aged-out), the newer commit whose run covers a superseded phase, and the commit a bisect found broke a phase. Each is absent when no phase has one.",
412
+ "oneOf": [
413
+ {
414
+ "type": "null"
415
+ },
416
+ {
417
+ "type": "object",
418
+ "required": [
419
+ "state"
420
+ ],
421
+ "properties": {
422
+ "state": {
423
+ "enum": [
424
+ "green",
425
+ "red",
426
+ "unbuilt",
427
+ "pending"
428
+ ]
429
+ },
430
+ "reason": {
431
+ "type": "string"
432
+ },
433
+ "coveredBy": {
434
+ "type": "string"
435
+ },
436
+ "culprit": {
437
+ "type": "string"
438
+ }
439
+ }
440
+ }
441
+ ]
442
+ },
409
443
  "ignored": {
410
444
  "type": "array",
411
445
  "description": "Added within contract version 1 by #4031, written by chant 0.124.28 and newer. The commit's ci tags that do not count for want of a valid statement signed by a runner key. A tag on any commit that is not a strict first-parent ancestor of ci.green.signedSince needs one; empty when signedSince is not set. state, green and revoked count only the tags that are not ignored.",
@@ -38,7 +38,7 @@ export declare const CI_RUN_INDEX_KEY = "runs/index.json";
38
38
  export declare const CI_RUN_INDEX_LIMIT = 200;
39
39
  /** One job the runner ran. */
40
40
  export interface CiRunIndexEntry {
41
- job: "plan" | "apply";
41
+ job: "plan" | "apply" | "record";
42
42
  /** The pull request, or null when the job found none. */
43
43
  pr: number | null;
44
44
  commit: string;
@@ -1 +1 @@
1
- {"version":3,"file":"ci-run-index.d.ts","sourceRoot":"","sources":["../../src/workspace/ci-run-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGtD,OAAO,EAAwC,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAG3H,eAAO,MAAM,sBAAsB,0FAA0F,CAAC;AAC9H,eAAO,MAAM,qBAAqB,IAAI,CAAC;AACvC,6CAA6C;AAC7C,eAAO,MAAM,kBAAkB,aAAa,CAAC;AAC7C,qDAAqD;AACrD,eAAO,MAAM,gBAAgB,oBAAoB,CAAC;AAClD,8DAA8D;AAC9D,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,8BAA8B;AAC9B,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACtB,yDAAyD;IACzD,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,IAAI,GAAG,MAAM,CAAC;IACtB,qEAAqE;IACrE,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,SAAS,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,sBAAsB,CAAC;IACvC,QAAQ,EAAE,OAAO,qBAAqB,CAAC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,IAAI,EAAE,eAAe,EAAE,CAAC;CACzB;AAID,2GAA2G;AAC3G,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAc7G;AAED,qGAAqG;AACrG,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,CAEtH;AAKD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,GAAG,CAAC,EAAE,SAAS,CAAC;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuChB;AAED,gHAAgH;AAChH,wBAAsB,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,GAAE,SAA6B,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAQzH;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAS1D;AAID,iEAAiE;AACjE,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAepE"}
1
+ {"version":3,"file":"ci-run-index.d.ts","sourceRoot":"","sources":["../../src/workspace/ci-run-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGtD,OAAO,EAAwC,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAG3H,eAAO,MAAM,sBAAsB,0FAA0F,CAAC;AAC9H,eAAO,MAAM,qBAAqB,IAAI,CAAC;AACvC,6CAA6C;AAC7C,eAAO,MAAM,kBAAkB,aAAa,CAAC;AAC7C,qDAAqD;AACrD,eAAO,MAAM,gBAAgB,oBAAoB,CAAC;AAClD,8DAA8D;AAC9D,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,8BAA8B;AAC9B,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IACjC,yDAAyD;IACzD,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,IAAI,GAAG,MAAM,CAAC;IACtB,qEAAqE;IACrE,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,SAAS,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,sBAAsB,CAAC;IACvC,QAAQ,EAAE,OAAO,qBAAqB,CAAC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,IAAI,EAAE,eAAe,EAAE,CAAC;CACzB;AAID,2GAA2G;AAC3G,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAc7G;AAED,qGAAqG;AACrG,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,CAEtH;AAKD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,GAAG,CAAC,EAAE,SAAS,CAAC;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuChB;AAED,gHAAgH;AAChH,wBAAsB,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,GAAE,SAA6B,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAQzH;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAS1D;AAID,iEAAiE;AACjE,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAepE"}
@@ -23,7 +23,7 @@
23
23
  "if": { "properties": { "status": { "const": "fail" } } },
24
24
  "then": { "required": ["failure"], "properties": { "failure": { "type": "object" } } },
25
25
  "properties": {
26
- "job": { "enum": ["plan", "apply"] },
26
+ "job": { "enum": ["plan", "apply", "record"], "description": "record (#4168, chant 0.124.33 and newer): the merge record job." },
27
27
  "pr": { "type": ["integer", "null"], "minimum": 1, "description": "The pull or merge request number: the one the job's report names, else --pr. null when neither has one." },
28
28
  "commit": { "type": "string", "pattern": "^[0-9a-f]{40,64}$", "description": "The full commit the job ran at." },
29
29
  "status": { "enum": ["ok", "fail"] },
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `chant ci run plan|apply` (#4094, epic #4101): run one pull-request job as
2
+ * `chant ci run plan|record|apply` (#4094, epic #4101, record #4168): run one pull-request job as
3
3
  * a pipeline Op, the first slice of the chant runner.
4
4
  *
5
5
  * The job is the typed value `prLoopJobs` (../components/pr-pipeline.ts)
@@ -78,7 +78,7 @@ import type { OpRunFailureReason, OpRunRecord } from "../op/runtime.js";
78
78
  import type { OpConfig } from "../op/types.js";
79
79
  import { type Redact } from "../op/redact.js";
80
80
  import { type CiSecrets } from "./ci-secrets.js";
81
- /** The two jobs a pull-request pipeline has. */
81
+ /** The jobs a pull-request pipeline has: plan, and on merge record (#4168) or, opted in, apply. */
82
82
  export type CiJobName = PrLoopJob["jobName"];
83
83
  export declare const CI_JOB_NAMES: readonly CiJobName[];
84
84
  /** The Op each job is recorded as, which `ci.green` phases match. */
@@ -193,7 +193,7 @@ export interface CiStatusConfig {
193
193
  urlTemplate?: string;
194
194
  }
195
195
  /**
196
- * A job's status context: `chant/pr-plan` for the plan, and for the apply
196
+ * A job's status context: `chant/pr-plan` for the plan, `chant/pr-record` for the record (#4168), and for the apply
197
197
  * `chant/<gate>`, which is `chant/pr-apply` unless `--gate` renamed the gate.
198
198
  * pr-plan's own status is `chant/plan` (../pr-loop.ts), a different context.
199
199
  */
@@ -304,6 +304,12 @@ export interface CiRunRequest {
304
304
  * `runOrigin: { forge: "local" }` and `runtime: "local"`.
305
305
  */
306
306
  local?: boolean;
307
+ /**
308
+ * `--require-runner-start` (#4147): refuse the job before its first step
309
+ * unless a runner-start statement, signed by a runner key
310
+ * `.chant/trust.json` at `base` lists, names this forge job.
311
+ */
312
+ requireRunnerStart?: boolean;
307
313
  /**
308
314
  * `--wave <k>` (#4003, #4156): the one wave a generated apply job runs, as
309
315
  * `pr-apply --wave <k>`. Needs `--wave-gate`.
@@ -361,12 +367,12 @@ export declare function formatCiRun(r: CiRunResult): string;
361
367
  * The generator's options from `ci run`'s and `ci serve`'s flags, as `chant
362
368
  * build --components --generate <lexicon> --pr-loop` takes them: `--gate`,
363
369
  * `--branch`, `--events-json`, `--report-url`, `--pr-build`,
364
- * `--pr-build-command`, `--wave-gate` (#4003) and `--no-require-review`
365
- * (#4156).
370
+ * `--pr-build-command`, `--wave-gate` (#4003) and `--require-review`
371
+ * (#4168; `--no-require-review`, the default, is still accepted).
366
372
  */
367
373
  export declare function ciLoopOptions(args: CommandContext["args"]): PrLoopPipelineOptions;
368
374
  /** The secrets file `--secrets` names, else the one `CHANT_CI_SECRETS` names, read; undefined when neither names one (#4096). */
369
375
  export declare function ciSecretsOf(flag: string | undefined, env?: Record<string, string | undefined>): CiSecrets | undefined;
370
- /** `chant ci run plan|apply` (#4094). */
376
+ /** `chant ci run plan|record|apply` (#4094, #4168). */
371
377
  export declare function runCiRun(ctx: CommandContext): Promise<number>;
372
378
  //# sourceMappingURL=ci-run.d.ts.map