@intentius/chant 0.124.32 → 0.124.33

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 (281) 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 +16 -1
  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 +81 -21
  22. package/dist/cli/parse-args.d.ts.map +1 -1
  23. package/dist/cli/parse-args.js +23 -1
  24. package/dist/cli/registry.d.ts +18 -4
  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/release-compile.d.ts +37 -2
  40. package/dist/components/release-compile.d.ts.map +1 -1
  41. package/dist/components/release-compile.js +64 -8
  42. package/dist/components/release-promote.d.ts +17 -3
  43. package/dist/components/release-promote.d.ts.map +1 -1
  44. package/dist/components/release-promote.js +79 -18
  45. package/dist/components/verbs/bom-writer.d.ts +1 -1
  46. package/dist/components/verbs/bom-writer.d.ts.map +1 -1
  47. package/dist/components/verbs/lockfile-sbom-generator.d.ts +20 -0
  48. package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -1
  49. package/dist/components/verbs/lockfile-sbom-generator.js +41 -4
  50. package/dist/components/wave-plan.d.ts +10 -0
  51. package/dist/components/wave-plan.d.ts.map +1 -1
  52. package/dist/components/wave-plan.js +32 -4
  53. package/dist/graph-detail.d.ts.map +1 -1
  54. package/dist/graph-detail.js +32 -7
  55. package/dist/health.d.ts +110 -0
  56. package/dist/health.d.ts.map +1 -0
  57. package/dist/health.js +105 -0
  58. package/dist/health.schema.json +191 -0
  59. package/dist/lexicon.d.ts +27 -0
  60. package/dist/lexicon.d.ts.map +1 -1
  61. package/dist/lifecycle/merge-record.d.ts +104 -0
  62. package/dist/lifecycle/merge-record.d.ts.map +1 -0
  63. package/dist/lifecycle/merge-record.js +102 -0
  64. package/dist/lifecycle/publish-attachments.d.ts +8 -5
  65. package/dist/lifecycle/publish-attachments.d.ts.map +1 -1
  66. package/dist/lifecycle/publish-attachments.js +27 -13
  67. package/dist/lifecycle/publish-npm.d.ts +3 -2
  68. package/dist/lifecycle/publish-npm.d.ts.map +1 -1
  69. package/dist/lifecycle/publish-npm.js +4 -3
  70. package/dist/lifecycle/release-at.d.ts +40 -0
  71. package/dist/lifecycle/release-at.d.ts.map +1 -0
  72. package/dist/lifecycle/release-at.js +83 -0
  73. package/dist/lifecycle/release-cut.d.ts.map +1 -1
  74. package/dist/lifecycle/release-cut.js +23 -12
  75. package/dist/lifecycle/release-publish.d.ts +28 -12
  76. package/dist/lifecycle/release-publish.d.ts.map +1 -1
  77. package/dist/lifecycle/release-publish.js +29 -17
  78. package/dist/lifecycle/release-store.d.ts +25 -2
  79. package/dist/lifecycle/release-store.d.ts.map +1 -1
  80. package/dist/lifecycle/release-store.js +40 -13
  81. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  82. package/dist/lifecycle/run-ledger.js +1 -0
  83. package/dist/lint/rules/op/index.d.ts +2 -1
  84. package/dist/lint/rules/op/index.d.ts.map +1 -1
  85. package/dist/lint/rules/op/index.js +4 -2
  86. package/dist/lint/rules/op/ops018-shell-image-digest.d.ts +10 -0
  87. package/dist/lint/rules/op/ops018-shell-image-digest.d.ts.map +1 -0
  88. package/dist/lint/rules/op/ops018-shell-image-digest.js +38 -0
  89. package/dist/op/activities/activity-contracts.d.ts +5 -0
  90. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  91. package/dist/op/activities/activity-contracts.js +3 -0
  92. package/dist/op/activities/shell.d.ts +38 -0
  93. package/dist/op/activities/shell.d.ts.map +1 -1
  94. package/dist/op/activities/shell.js +89 -3
  95. package/dist/op/builders.d.ts +7 -0
  96. package/dist/op/builders.d.ts.map +1 -1
  97. package/dist/op/builders.js +7 -0
  98. package/dist/op/runtime.d.ts +2 -0
  99. package/dist/op/runtime.d.ts.map +1 -1
  100. package/dist/pr-loop.d.ts +14 -3
  101. package/dist/pr-loop.d.ts.map +1 -1
  102. package/dist/pr-loop.js +15 -5
  103. package/dist/pr-report-url.d.ts +2 -2
  104. package/dist/pr-report-url.d.ts.map +1 -1
  105. package/dist/runner-start.d.ts +179 -0
  106. package/dist/runner-start.d.ts.map +1 -0
  107. package/dist/runner-start.js +277 -0
  108. package/dist/wave-runtime.mjs +235 -228
  109. package/dist/workspace/check.schema.json +70 -2
  110. package/dist/workspace/checks/security.d.ts +35 -0
  111. package/dist/workspace/checks/security.d.ts.map +1 -0
  112. package/dist/workspace/checks/security.js +305 -0
  113. package/dist/workspace/checks.d.ts +2 -0
  114. package/dist/workspace/checks.d.ts.map +1 -1
  115. package/dist/workspace/checks.js +5 -0
  116. package/dist/workspace/ci-builds.d.ts +163 -0
  117. package/dist/workspace/ci-builds.d.ts.map +1 -0
  118. package/dist/workspace/ci-builds.js +315 -0
  119. package/dist/workspace/ci-green-cli.d.ts +2 -1
  120. package/dist/workspace/ci-green-cli.d.ts.map +1 -1
  121. package/dist/workspace/ci-green-cli.js +25 -5
  122. package/dist/workspace/ci-green-forge.d.ts +12 -0
  123. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  124. package/dist/workspace/ci-green-forge.js +12 -0
  125. package/dist/workspace/ci-green-workflow.d.ts +55 -1
  126. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  127. package/dist/workspace/ci-green-workflow.js +128 -1
  128. package/dist/workspace/ci-green.d.ts +89 -4
  129. package/dist/workspace/ci-green.d.ts.map +1 -1
  130. package/dist/workspace/ci-green.js +310 -23
  131. package/dist/workspace/ci-last-green.schema.json +49 -0
  132. package/dist/workspace/ci-list.schema.json +35 -1
  133. package/dist/workspace/ci-run-index.d.ts +1 -1
  134. package/dist/workspace/ci-run-index.d.ts.map +1 -1
  135. package/dist/workspace/ci-run-index.schema.json +1 -1
  136. package/dist/workspace/ci-run.d.ts +12 -6
  137. package/dist/workspace/ci-run.d.ts.map +1 -1
  138. package/dist/workspace/ci-run.js +72 -16
  139. package/dist/workspace/ci-runner-start.d.ts +77 -0
  140. package/dist/workspace/ci-runner-start.d.ts.map +1 -0
  141. package/dist/workspace/ci-runner-start.js +185 -0
  142. package/dist/workspace/ci-serve.d.ts +76 -7
  143. package/dist/workspace/ci-serve.d.ts.map +1 -1
  144. package/dist/workspace/ci-serve.js +153 -19
  145. package/dist/workspace/declaration.d.ts +17 -0
  146. package/dist/workspace/declaration.d.ts.map +1 -1
  147. package/dist/workspace/declaration.js +3 -0
  148. package/dist/workspace/declaration.schema.json +23 -0
  149. package/dist/workspace/ls.d.ts +2 -0
  150. package/dist/workspace/ls.d.ts.map +1 -1
  151. package/dist/workspace/ls.js +1 -1
  152. package/dist/workspace/ls.schema.json +2 -1
  153. package/dist/workspace/member-pipeline.d.ts +1 -0
  154. package/dist/workspace/member-pipeline.d.ts.map +1 -1
  155. package/dist/workspace/member-pipeline.js +2 -0
  156. package/dist/workspace/merge-record.schema.json +49 -0
  157. package/dist/workspace/pr-report.schema.json +3 -3
  158. package/dist/workspace/reason-codes.d.ts +17 -0
  159. package/dist/workspace/reason-codes.d.ts.map +1 -1
  160. package/dist/workspace/reason-codes.js +18 -0
  161. package/dist/workspace/release-candidates.d.ts +129 -0
  162. package/dist/workspace/release-candidates.d.ts.map +1 -0
  163. package/dist/workspace/release-candidates.js +233 -0
  164. package/dist/workspace/release-candidates.schema.json +353 -0
  165. package/dist/workspace/security.d.ts +40 -0
  166. package/dist/workspace/security.d.ts.map +1 -0
  167. package/dist/workspace/security.js +99 -0
  168. package/package.json +8 -1
  169. package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +1 -1
  170. package/src/ci-jobs.ts +11 -2
  171. package/src/cli/handlers/build.test.ts +36 -0
  172. package/src/cli/handlers/build.ts +18 -3
  173. package/src/cli/handlers/lifecycle-health.test.ts +123 -0
  174. package/src/cli/handlers/lifecycle-health.ts +157 -0
  175. package/src/cli/handlers/lifecycle.ts +1 -1
  176. package/src/cli/handlers/pr-deploy-inputs.test.ts +209 -0
  177. package/src/cli/handlers/pr-record.test.ts +201 -0
  178. package/src/cli/handlers/pr.test.ts +22 -0
  179. package/src/cli/handlers/pr.ts +127 -4
  180. package/src/cli/handlers/promote.test.ts +17 -0
  181. package/src/cli/handlers/promote.ts +16 -1
  182. package/src/cli/handlers/release-cut-at.e2e.test.ts +115 -0
  183. package/src/cli/handlers/release-publish-targets.e2e.test.ts +2 -2
  184. package/src/cli/handlers/release-verify.e2e.test.ts +12 -10
  185. package/src/cli/handlers/release.e2e.test.ts +19 -28
  186. package/src/cli/handlers/release.ts +81 -11
  187. package/src/cli/main.ts +81 -21
  188. package/src/cli/parse-args.ts +17 -1
  189. package/src/cli/registry.ts +18 -4
  190. package/src/components/deploy-inputs.test.ts +108 -0
  191. package/src/components/deploy-inputs.ts +225 -0
  192. package/src/components/driver.ts +11 -0
  193. package/src/components/gate-plan.ts +9 -0
  194. package/src/components/pr-pipeline.ts +56 -15
  195. package/src/components/pr-run.ts +1 -1
  196. package/src/components/release-compile.test.ts +34 -0
  197. package/src/components/release-compile.ts +89 -8
  198. package/src/components/release-promote.test.ts +55 -2
  199. package/src/components/release-promote.ts +93 -23
  200. package/src/components/verbs/bom-writer.ts +1 -1
  201. package/src/components/verbs/lockfile-sbom-generator.test.ts +36 -0
  202. package/src/components/verbs/lockfile-sbom-generator.ts +43 -5
  203. package/src/components/wave-plan.ts +32 -4
  204. package/src/graph-detail.test.ts +57 -0
  205. package/src/graph-detail.ts +33 -7
  206. package/src/health.schema.json +191 -0
  207. package/src/health.test.ts +62 -0
  208. package/src/health.ts +178 -0
  209. package/src/lexicon.ts +41 -1
  210. package/src/lifecycle/merge-record.test.ts +111 -0
  211. package/src/lifecycle/merge-record.ts +154 -0
  212. package/src/lifecycle/publish-attachments.ts +25 -13
  213. package/src/lifecycle/publish-npm.ts +4 -3
  214. package/src/lifecycle/publish-targets.test.ts +34 -4
  215. package/src/lifecycle/release-at.test.ts +48 -0
  216. package/src/lifecycle/release-at.ts +100 -0
  217. package/src/lifecycle/release-cut.test.ts +41 -0
  218. package/src/lifecycle/release-cut.ts +22 -11
  219. package/src/lifecycle/release-publish.test.ts +43 -13
  220. package/src/lifecycle/release-publish.ts +38 -18
  221. package/src/lifecycle/release-store.ts +57 -13
  222. package/src/lifecycle/run-ledger.test.ts +13 -0
  223. package/src/lifecycle/run-ledger.ts +1 -0
  224. package/src/lint/rules/op/index.ts +4 -2
  225. package/src/lint/rules/op/ops018-shell-image-digest.test.ts +60 -0
  226. package/src/lint/rules/op/ops018-shell-image-digest.ts +48 -0
  227. package/src/op/activities/activity-contracts.ts +3 -0
  228. package/src/op/activities/shell-container.e2e.test.ts +67 -0
  229. package/src/op/activities/shell.test.ts +44 -1
  230. package/src/op/activities/shell.ts +110 -3
  231. package/src/op/builders.ts +7 -0
  232. package/src/op/runtime.ts +2 -0
  233. package/src/op/step-console.test.ts +4 -2
  234. package/src/pr-loop.test.ts +29 -0
  235. package/src/pr-loop.ts +26 -7
  236. package/src/pr-report-url.ts +2 -2
  237. package/src/runner-start-bundle.test.ts +29 -0
  238. package/src/runner-start.test.ts +179 -0
  239. package/src/runner-start.ts +329 -0
  240. package/src/workspace/check-contract.test.ts +3 -2
  241. package/src/workspace/check.schema.json +70 -2
  242. package/src/workspace/checks/security.test.ts +138 -0
  243. package/src/workspace/checks/security.ts +305 -0
  244. package/src/workspace/checks.ts +5 -0
  245. package/src/workspace/ci-builds.e2e.test.ts +208 -0
  246. package/src/workspace/ci-builds.test.ts +174 -0
  247. package/src/workspace/ci-builds.ts +385 -0
  248. package/src/workspace/ci-green-builds.test.ts +180 -0
  249. package/src/workspace/ci-green-cli.test.ts +54 -0
  250. package/src/workspace/ci-green-cli.ts +26 -5
  251. package/src/workspace/ci-green-forge.ts +24 -0
  252. package/src/workspace/ci-green-workflow.test.ts +84 -1
  253. package/src/workspace/ci-green-workflow.ts +139 -2
  254. package/src/workspace/ci-green.test.ts +5 -2
  255. package/src/workspace/ci-green.ts +359 -27
  256. package/src/workspace/ci-last-green.schema.json +49 -0
  257. package/src/workspace/ci-list.schema.json +35 -1
  258. package/src/workspace/ci-run-index.schema.json +1 -1
  259. package/src/workspace/ci-run-index.ts +1 -1
  260. package/src/workspace/ci-run.test.ts +24 -7
  261. package/src/workspace/ci-run.ts +78 -16
  262. package/src/workspace/ci-runner-start.test.ts +151 -0
  263. package/src/workspace/ci-runner-start.ts +193 -0
  264. package/src/workspace/ci-serve-builds.test.ts +86 -0
  265. package/src/workspace/ci-serve.test.ts +59 -0
  266. package/src/workspace/ci-serve.ts +177 -22
  267. package/src/workspace/declaration.schema.json +23 -0
  268. package/src/workspace/declaration.test.ts +17 -0
  269. package/src/workspace/declaration.ts +22 -1
  270. package/src/workspace/ls.schema.json +2 -1
  271. package/src/workspace/ls.ts +3 -2
  272. package/src/workspace/member-pipeline.ts +2 -1
  273. package/src/workspace/merge-record.schema.json +49 -0
  274. package/src/workspace/pr-report.schema.json +3 -3
  275. package/src/workspace/reason-codes.test.ts +2 -0
  276. package/src/workspace/reason-codes.ts +18 -0
  277. package/src/workspace/release-candidates.schema.json +353 -0
  278. package/src/workspace/release-candidates.test.ts +106 -0
  279. package/src/workspace/release-candidates.ts +315 -0
  280. package/src/workspace/security.test.ts +70 -0
  281. package/src/workspace/security.ts +118 -0
@@ -23,6 +23,7 @@ import { expandGlob } from "./glob";
23
23
  import { EXAMPLES_KIND, holdsChantProject } from "./kinds";
24
24
  import { parseJsonText, pointerToken, type TextLocation } from "./jsonc";
25
25
  import type { ReasonCode } from "./reason-codes";
26
+ import type { SecurityGroup } from "./security";
26
27
  import { joinPath, type WorkspaceTree } from "./tree";
27
28
  import { loadAjv2020 } from "./ajv2020";
28
29
  import { readChantPackageVersion } from "../package-version";
@@ -184,6 +185,13 @@ export interface AgentDeclaration {
184
185
  /** What a check run that finished as skipped counts as in a CI phase (#3573). */
185
186
  export type CiSkipped = "pass" | "fail";
186
187
 
188
+ /**
189
+ * Which commits a phase runs on (#4169): `every` first-parent commit of the
190
+ * branch, or only the `newest` one when a runner frees, the commits between
191
+ * left unbuilt and a red one bisected.
192
+ */
193
+ export type CiBuild = "every" | "newest";
194
+
187
195
  /** One phase of `ci.green`: the check runs that make it up (#3573, ws-103). */
188
196
  export interface CiPhase {
189
197
  name: string;
@@ -191,6 +199,12 @@ export interface CiPhase {
191
199
  runs: string[];
192
200
  /** `fail` (the default): a skipped run never passes. `pass`: it passes, for a job a path filter runs only on some commits. */
193
201
  skipped: CiSkipped;
202
+ /**
203
+ * Which commits the phase runs on (#4169), when the declaration says.
204
+ * Absent means `every`, and `ci workflow` then leaves the phase's
205
+ * workflows as they are.
206
+ */
207
+ build?: CiBuild;
194
208
  pointer: string;
195
209
  }
196
210
 
@@ -557,6 +571,8 @@ export interface Member {
557
571
  because: string | null;
558
572
  /** Whether `chant workspace export` takes the member (#2552, D10). False when the entry does not say. */
559
573
  travel: boolean;
574
+ /** The security groups this member adds to the declaration's top-level `security` (#4168 step 7), in file order; empty when it names none. */
575
+ security: SecurityGroup[];
560
576
  suppress: Suppression[];
561
577
  /** The entry's JSON Pointer in the file, for messages. */
562
578
  pointer: string;
@@ -624,6 +640,8 @@ export interface Declaration {
624
640
  * then uses 7.
625
641
  */
626
642
  metrics: { failureWindowDays?: number } | null;
643
+ /** The security groups the workspace opts into (#4168 section 8, ./security.ts), in file order; empty when the declaration has no `security` key. */
644
+ security: SecurityGroup[];
627
645
  /** The file, relative to the workspace root's tree (`chant.workspace.json` or `.jsonc`). */
628
646
  file: string;
629
647
  }
@@ -850,6 +868,7 @@ export function parseDeclaration(text: string, file: string, reader: string = re
850
868
  upstream: (e.upstream as string | undefined) ?? null,
851
869
  because: (e.because as string | undefined) ?? null,
852
870
  travel: e.travel === true,
871
+ security: [...((e.security as SecurityGroup[] | undefined) ?? [])],
853
872
  suppress,
854
873
  pointer,
855
874
  };
@@ -1002,6 +1021,7 @@ export function parseDeclaration(text: string, file: string, reader: string = re
1002
1021
  ci,
1003
1022
  retention: retentionOf(obj.retention),
1004
1023
  metrics: metricsOf(obj.metrics),
1024
+ security: [...((obj.security as SecurityGroup[] | undefined) ?? [])],
1005
1025
  file,
1006
1026
  };
1007
1027
  written.set(declaration, raw);
@@ -1096,7 +1116,7 @@ function retentionOf(raw: unknown): RetentionOverride | null {
1096
1116
 
1097
1117
  function ciOf(raw: unknown, at: (pointer: string, key?: boolean) => ErrorLocation): CiDeclaration | null {
1098
1118
  if (raw === undefined) return null;
1099
- const block = raw as { reportUrl?: string; green?: { branch: string; window?: string; phases: Record<string, string[] | { runs: string[]; skipped?: CiSkipped }>; require: string[]; signedSince?: string; source?: CiSource } };
1119
+ const block = raw as { reportUrl?: string; green?: { branch: string; window?: string; phases: Record<string, string[] | { runs: string[]; skipped?: CiSkipped; build?: CiBuild }>; require: string[]; signedSince?: string; source?: CiSource } };
1100
1120
  let reportUrl: string | null = null;
1101
1121
  if (block.reportUrl !== undefined) {
1102
1122
  // #4160: refused here as pr-plan and ci run refuse the flag, so a reader never gets a URL no job can write to.
@@ -1110,6 +1130,7 @@ function ciOf(raw: unknown, at: (pointer: string, key?: boolean) => ErrorLocatio
1110
1130
  name,
1111
1131
  runs: [...(Array.isArray(p) ? p : p.runs)],
1112
1132
  skipped: Array.isArray(p) ? "fail" : (p.skipped ?? "fail"),
1133
+ ...(!Array.isArray(p) && p.build !== undefined ? { build: p.build } : {}),
1113
1134
  pointer: `/ci/green/phases/${pointerToken(name)}`,
1114
1135
  }));
1115
1136
  for (const [i, name] of g.require.entries()) {
@@ -21,7 +21,8 @@
21
21
  "properties": {
22
22
  "name": { "type": "string" },
23
23
  "runs": { "type": "array", "items": { "type": "string" }, "description": "Check-run name patterns, * standing for any run of characters." },
24
- "skipped": { "enum": ["pass", "fail"], "description": "What a run that finished as skipped counts as." }
24
+ "skipped": { "enum": ["pass", "fail"], "description": "What a run that finished as skipped counts as." },
25
+ "build": { "enum": ["every", "newest"], "description": "Added in contract 1 by #4169, written by chant 0.124.33 and newer, and only when the declaration sets it: whether the phase runs on every first-parent commit or only the newest one when a runner frees. Absent means every." }
25
26
  }
26
27
  }
27
28
  },
@@ -142,7 +142,8 @@ export interface LsDiagram {
142
142
  export interface LsCiGreen {
143
143
  branch: string;
144
144
  window: string;
145
- phases: { name: string; runs: string[]; skipped: "pass" | "fail" }[];
145
+ /** `build` is present only when the declaration sets it (#4169). */
146
+ phases: { name: string; runs: string[]; skipped: "pass" | "fail"; build?: "every" | "newest" }[];
146
147
  require: string[];
147
148
  /** The first commit whose ci tags need a signed statement (#4031). Present only when the declaration sets it. */
148
149
  signedSince?: string;
@@ -328,7 +329,7 @@ function readListing(query: LsQuery): { doc: LsDocument; declaration?: Declarati
328
329
  ? {
329
330
  branch: declaration.ci.green.branch,
330
331
  window: declaration.ci.green.window,
331
- phases: declaration.ci.green.phases.map((p) => ({ name: p.name, runs: p.runs, skipped: p.skipped })),
332
+ phases: declaration.ci.green.phases.map((p) => ({ name: p.name, runs: p.runs, skipped: p.skipped, ...(p.build ? { build: p.build } : {}) })),
332
333
  require: declaration.ci.green.require,
333
334
  ...(declaration.ci.green.signedSince !== null ? { signedSince: declaration.ci.green.signedSince } : {}),
334
335
  ...(declaration.ci.green.source === "runs" ? { source: "runs" as const } : {}),
@@ -212,7 +212,7 @@ export interface ComponentPlanInput {
212
212
  params?: string[];
213
213
  paramsFile?: string;
214
214
  /** `--pr-loop`, with its `--gate` and `--branch` (#3465). */
215
- prLoop?: { gate?: string; branch?: string };
215
+ prLoop?: { gate?: string; branch?: string; applyOnMerge?: boolean };
216
216
  /** `--wave-gate` with its `--canary` and `--base` (#3049). */
217
217
  waveGate?: { gate: string; canary?: string[]; base?: string };
218
218
  /** `--image` (#4002). */
@@ -258,6 +258,7 @@ export function planMemberComponentPipeline(input: ComponentPlanInput): MemberCo
258
258
  parts.push("--pr-loop");
259
259
  if (input.prLoop.gate) parts.push("--gate", input.prLoop.gate);
260
260
  if (input.prLoop.branch) parts.push("--branch", input.prLoop.branch);
261
+ if (input.prLoop.applyOnMerge) parts.push("--apply-on-merge");
261
262
  }
262
263
  if (input.image) parts.push("--image", input.image);
263
264
  for (const line of input.beforeScript ?? []) parts.push("--before-script", line);
@@ -0,0 +1,49 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://intentius.io/chant/schemas/workspace/merge-record/v1/merge-record.schema.json",
4
+ "title": "chant merge record",
5
+ "description": "What landed on the target branch, keyed by commit: _merges/<commit>.json on chant/lifecycle (#4168, chant 0.124.33 and newer). The record job writes it on merge. It applies nothing; a release that carries the commit deploys it.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["$schema", "contract", "commit", "base", "env", "source", "op", "gate", "digest", "plan", "approvedBy", "components", "recordedAt"],
9
+ "properties": {
10
+ "$schema": { "const": "https://intentius.io/chant/schemas/workspace/merge-record/v1/merge-record.schema.json" },
11
+ "contract": { "const": 1 },
12
+ "commit": { "type": "string", "pattern": "^[0-9a-f]{40,64}$", "description": "The merged commit." },
13
+ "base": { "type": "string", "pattern": "^[0-9a-f]{40,64}$", "description": "The commit the change is measured from." },
14
+ "env": { "type": "string", "minLength": 1 },
15
+ "member": { "type": "string", "minLength": 1, "description": "The workspace member whose pipeline wrote it." },
16
+ "source": {
17
+ "description": "Where the change came from: the pull request that merged it, else the work items its Chant-Record: work:<id> trailers name, else a direct push.",
18
+ "oneOf": [
19
+ { "type": "object", "additionalProperties": false, "required": ["kind", "pr"], "properties": { "kind": { "const": "pull-request" }, "pr": { "type": "integer", "minimum": 1 } } },
20
+ {
21
+ "type": "object",
22
+ "additionalProperties": false,
23
+ "required": ["kind", "records"],
24
+ "properties": {
25
+ "kind": { "const": "work-item" },
26
+ "records": {
27
+ "type": "array",
28
+ "minItems": 1,
29
+ "items": { "type": "object", "additionalProperties": false, "required": ["kind", "id"], "properties": { "kind": { "const": "work" }, "id": { "type": "string", "minLength": 1 } } }
30
+ }
31
+ }
32
+ },
33
+ { "type": "object", "additionalProperties": false, "required": ["kind"], "properties": { "kind": { "const": "direct-push" } } }
34
+ ]
35
+ },
36
+ "op": { "type": ["string", "null"], "description": "The pull request's gate op, pr-<n> or pr-<n>-<member>; null without a pull request." },
37
+ "gate": { "type": "string", "minLength": 1 },
38
+ "digest": { "type": ["string", "null"], "pattern": "^(?:jcs1-)?sha256:[0-9a-f]{64}$", "description": "The change-set digest planned on the merged commit; null when nothing was planned. The change set is stored under _change-sets/." },
39
+ "plan": {
40
+ "enum": ["approved", "changed", "unapproved", "nothing", "plan-failed"],
41
+ "description": "approved: an approval stands for digest. changed: one stands for approvedDigest, so the plan moved after review; the record job's chant/record status fails. unapproved: none stands. nothing: no component deploys what the change touches. plan-failed: a member failed to plan."
42
+ },
43
+ "approvedDigest": { "type": "string", "pattern": "^(?:jcs1-)?sha256:[0-9a-f]{64}$" },
44
+ "approvedBy": { "type": "array", "items": { "type": "string" } },
45
+ "authorApproved": { "type": "array", "items": { "type": "string" }, "description": "Approvers who wrote the change (#4000), read with a forge." },
46
+ "components": { "type": "array", "items": { "type": "string" } },
47
+ "recordedAt": { "type": "string", "minLength": 1, "description": "ISO 8601." }
48
+ }
49
+ }
@@ -8,7 +8,7 @@
8
8
  "properties": {
9
9
  "$schema": { "const": "https://intentius.io/chant/schemas/workspace/pr-report/v1/pr-report.schema.json" },
10
10
  "contract": { "const": 1 },
11
- "stage": { "enum": ["plan", "apply"] },
11
+ "stage": { "enum": ["plan", "apply", "record"], "description": "plan: on the pull request. record (#4168, chant 0.124.33 and newer): on merge, the default; it plans the merged commit and writes the merge record, applying nothing. apply: on merge with --apply-on-merge." },
12
12
  "pr": {
13
13
  "type": ["integer", "null"],
14
14
  "minimum": 1,
@@ -33,8 +33,8 @@
33
33
  "description": "The change-set digest of what this stage planned: computePlanDigest(\"change-set\", ...) over every member's { member, planDigest }. What the gate binds."
34
34
  },
35
35
  "status": {
36
- "enum": ["planned", "plan-failed", "nothing", "applied", "failed", "wave-applied", "refused", "waiting"],
37
- "description": "planned: every member planned (plan stage). plan-failed: a member failed to plan, so nothing can apply. nothing: no component deploys what the change touches. applied: every member applied. failed: a member failed to apply and its dependents did not run. wave-applied (#4003, chant 0.124.32 and newer): with --wave-gate and --wave, the wave applied and a later wave is still to run. refused: nothing applied, see refusal. waiting (#3999): the approved plans applied except members held back after an upstream applied, which wait on an approval of the plan the last of rounds made."
36
+ "enum": ["planned", "recorded", "plan-failed", "nothing", "applied", "failed", "wave-applied", "refused", "waiting"],
37
+ "description": "planned: every member planned (plan stage). recorded (#4168, chant 0.124.33 and newer): every member planned on the merged commit and the merge record was written (record stage); approval says whether the plan is the approved one, and nothing applied. plan-failed: a member failed to plan, so nothing can apply. nothing: no component deploys what the change touches. applied: every member applied. failed: a member failed to apply and its dependents did not run. wave-applied (#4003, chant 0.124.32 and newer): with --wave-gate and --wave, the wave applied and a later wave is still to run. refused: nothing applied, see refusal. waiting (#3999): the approved plans applied except members held back after an upstream applied, which wait on an approval of the plan the last of rounds made."
38
38
  },
39
39
  "refusal": {
40
40
  "enum": ["not-approved", "plan-changed", "not-a-reviewer", "author-approved", "no-pull-request", "wave-not-approved", "wave-plan-changed", "earlier-wave"],
@@ -28,6 +28,7 @@ import { STATUS_ERROR_CODES, STATUS_GATE_REASON_CODES, STATUS_REASON_CODES, STAT
28
28
  import { WORK_WARNING_CODES } from "./work";
29
29
  import { BOX_FINDING_CODES } from "./checks/boxes";
30
30
  import { DIAGRAM_FINDING_CODES } from "./checks/diagrams";
31
+ import { SECURITY_FINDING_CODES } from "./checks/security";
31
32
  import { WORK_ERROR_CODES, WORK_HISTORY_ERROR_CODES, WORK_LEASE_REFUSALS } from "./work-cli";
32
33
  import { RUN_STATEMENT_ERROR_CODES, RUNS_ERROR_CODES, RUNS_REASON_CODES, RUNS_WRITE_ERROR_CODES } from "./runs-cli";
33
34
  import { RUN_STATEMENT_FAILURE_CODES } from "./trust/run-statement";
@@ -66,6 +67,7 @@ const PER_COMMAND: Record<string, readonly string[]> = {
66
67
  CHECK_ERROR_CODES,
67
68
  BOX_FINDING_CODES,
68
69
  DIAGRAM_FINDING_CODES,
70
+ SECURITY_FINDING_CODES,
69
71
  STATUS_REASON_CODES,
70
72
  STATUS_ERROR_CODES,
71
73
  STATUS_GATE_REASON_CODES,
@@ -229,6 +229,24 @@ export const REASONS = {
229
229
  "diagram-source-missing": "A declared diagram's source file does not exist in the tree read.",
230
230
  "diagram-render-missing": "A declared diagram's render file does not exist in the tree read.",
231
231
  "diagram-render-drift": "A declared diagram records a sourceHash, and the source's bytes now hash to something else: the render is stale for its source.",
232
+ // The security group checklists (check, #4168 step 7). Each is also a WSP finding.
233
+ "security-signers-missing": "The workspace opts into verified-releases, and there is no .chant/allowed_signers to verify a release's signature against.",
234
+ "security-release-gate-missing": "Under verified-releases, identity.gates has no release entry, or it names no class, so a cut is not held to a class of signers.",
235
+ "security-publish-allows-unsigned": "Under verified-releases, a pipeline file runs release publish with --allow-unsigned.",
236
+ "security-sbom-disabled": "Under supply-chain, a member's chant.config.ts sets sbom: false on a publish target.",
237
+ "security-sbom-unsourced": "Under supply-chain, a member publishes, names no sbom file and has no package-lock.json, pom.xml or Cargo.lock to write one from.",
238
+ "security-publish-no-provenance": "Under supply-chain, a pipeline file runs release publish without --require-provenance.",
239
+ "security-component-no-sbom": "Under supply-chain, a member's components have no generate-sbom step.",
240
+ "security-component-no-verify": "Under supply-chain, a member's components have no verify step.",
241
+ "security-signed-since-unset": "Under who-ran-it, ci.green sets no signedSince, or there is no ci.green block.",
242
+ "security-runner-untrusted": "Under who-ran-it, .chant/trust.json lists no runner key.",
243
+ "security-tick-no-key": "Under who-ran-it, a pipeline file runs ci tick with no runner key.",
244
+ "security-review-not-required": "Under review-rules, an apply job passes --no-require-review, which the group overrides.",
245
+ "security-attribution-any": "Under review-rules, identity.attribution is not identified.",
246
+ "security-gate-unsigned": "Under review-rules, an apply job names a gate identity.gates does not list, or identity.gates lists none.",
247
+ "security-runner-start-no-key": "Under isolated-runners, .chant/trust.json lists no key under runners for a provisioner to sign runner-start statements with.",
248
+ "security-runner-start-no-role": "Under isolated-runners, no principal under runners in .chant/trust.json holds the runner role.",
249
+ "security-trust-unreadable": "Under isolated-runners, .chant/trust.json can't be read, so no runner-start statement can be checked.",
232
250
  // A box that holds a credential (check, #2726). Each is also a WSP finding.
233
251
  "box-credential-declared": "A file in a box member's directory carries a literal secret: a credential's shape, or a literal where a credential goes. A variable or secret-manager reference is not one.",
234
252
  "box-capability-unbrokered": "A capability in a member's box block names no broker, so the box would hold its credential.",
@@ -0,0 +1,353 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://intentius.io/chant/schemas/workspace/release-candidates/v1/release-candidates.schema.json",
4
+ "title": "chant release candidates output",
5
+ "description": "What `chant release candidates --json` prints (#4168 4.2): the green commits of trunk a release can be cut from, newest first, each with the commits, work items, pull requests, merge records and flags it would ship since the previous candidate, and what each environment of the member runs. It reads local refs and tags and never fetches. Version 1 of the read contract. Readers ignore fields they do not know; a field is only ever added within a version. The codes are a closed list, each in the one closed list of `reason-codes.ts`. Contract version 1 of this document is written by chant 0.124.33 and newer.",
6
+ "oneOf": [
7
+ {
8
+ "$ref": "#/$defs/result"
9
+ },
10
+ {
11
+ "$ref": "#/$defs/failure"
12
+ }
13
+ ],
14
+ "$defs": {
15
+ "result": {
16
+ "type": "object",
17
+ "required": [
18
+ "$schema",
19
+ "contract",
20
+ "chant",
21
+ "member",
22
+ "branch",
23
+ "tip",
24
+ "environments",
25
+ "since",
26
+ "candidates",
27
+ "skipped",
28
+ "pending"
29
+ ],
30
+ "properties": {
31
+ "$schema": {
32
+ "const": "https://intentius.io/chant/schemas/workspace/release-candidates/v1/release-candidates.schema.json"
33
+ },
34
+ "contract": {
35
+ "const": 1
36
+ },
37
+ "chant": {
38
+ "type": "string"
39
+ },
40
+ "error": false,
41
+ "member": {
42
+ "type": [
43
+ "string",
44
+ "null"
45
+ ],
46
+ "description": "The workspace member read, or null for a project outside a workspace."
47
+ },
48
+ "branch": {
49
+ "type": "string",
50
+ "description": "The trunk walked: ci.green.branch, else what origin/HEAD names."
51
+ },
52
+ "tip": {
53
+ "type": [
54
+ "string",
55
+ "null"
56
+ ],
57
+ "description": "The branch's tip as this clone knows it, or null when it knows none."
58
+ },
59
+ "environments": {
60
+ "type": "array",
61
+ "items": {
62
+ "$ref": "#/$defs/environment"
63
+ },
64
+ "description": "The member's environments in promotion order, or the one --env names."
65
+ },
66
+ "since": {
67
+ "type": [
68
+ "string",
69
+ "null"
70
+ ],
71
+ "description": "The oldest commit an environment runs, where the walk stopped, or null when it read its limit of commits."
72
+ },
73
+ "candidates": {
74
+ "type": "array",
75
+ "items": {
76
+ "$ref": "#/$defs/candidate"
77
+ },
78
+ "description": "Green commits that are not revoked, newest first."
79
+ },
80
+ "skipped": {
81
+ "type": "array",
82
+ "items": {
83
+ "type": "object",
84
+ "required": [
85
+ "commit",
86
+ "subject",
87
+ "reason"
88
+ ],
89
+ "properties": {
90
+ "commit": {
91
+ "type": "string"
92
+ },
93
+ "subject": {
94
+ "type": "string"
95
+ },
96
+ "reason": {
97
+ "enum": [
98
+ "revoked"
99
+ ]
100
+ }
101
+ }
102
+ },
103
+ "description": "Revoked green commits, newest first. Their changes are in the next candidate."
104
+ },
105
+ "pending": {
106
+ "type": "array",
107
+ "items": {
108
+ "$ref": "#/$defs/change"
109
+ },
110
+ "description": "Commits after the newest candidate, newest first: not green yet, or red."
111
+ }
112
+ },
113
+ "description": "Exit code 0, with or without candidates."
114
+ },
115
+ "failure": {
116
+ "type": "object",
117
+ "required": [
118
+ "$schema",
119
+ "contract",
120
+ "chant",
121
+ "error"
122
+ ],
123
+ "properties": {
124
+ "$schema": {
125
+ "const": "https://intentius.io/chant/schemas/workspace/release-candidates/v1/release-candidates.schema.json"
126
+ },
127
+ "contract": {
128
+ "const": 1
129
+ },
130
+ "chant": {
131
+ "type": "string"
132
+ },
133
+ "error": {
134
+ "type": "object",
135
+ "required": [
136
+ "code",
137
+ "message"
138
+ ],
139
+ "properties": {
140
+ "code": {
141
+ "enum": [
142
+ "not-a-git-repository",
143
+ "ledger-unreadable"
144
+ ]
145
+ },
146
+ "message": {
147
+ "type": "string"
148
+ }
149
+ }
150
+ }
151
+ },
152
+ "description": "Exit code 1."
153
+ },
154
+ "environment": {
155
+ "type": "object",
156
+ "required": [
157
+ "env",
158
+ "running"
159
+ ],
160
+ "properties": {
161
+ "env": {
162
+ "type": "string"
163
+ },
164
+ "running": {
165
+ "type": "array",
166
+ "items": {
167
+ "$ref": "#/$defs/running"
168
+ },
169
+ "description": "The releases the environment's latest deploys name, newest cut first."
170
+ }
171
+ }
172
+ },
173
+ "running": {
174
+ "type": "object",
175
+ "required": [
176
+ "releaseDigest",
177
+ "label",
178
+ "commit",
179
+ "components"
180
+ ],
181
+ "properties": {
182
+ "releaseDigest": {
183
+ "type": "string"
184
+ },
185
+ "label": {
186
+ "type": [
187
+ "string",
188
+ "null"
189
+ ]
190
+ },
191
+ "commit": {
192
+ "type": [
193
+ "string",
194
+ "null"
195
+ ],
196
+ "description": "The commit it was cut from, or null when its release file is not on this clone's branch."
197
+ },
198
+ "components": {
199
+ "type": "array",
200
+ "items": {
201
+ "type": "string"
202
+ }
203
+ }
204
+ }
205
+ },
206
+ "candidate": {
207
+ "type": "object",
208
+ "required": [
209
+ "commit",
210
+ "subject",
211
+ "date",
212
+ "newFor",
213
+ "cut",
214
+ "changes",
215
+ "flags"
216
+ ],
217
+ "properties": {
218
+ "commit": {
219
+ "type": "string"
220
+ },
221
+ "subject": {
222
+ "type": "string"
223
+ },
224
+ "date": {
225
+ "type": "string"
226
+ },
227
+ "newFor": {
228
+ "type": "array",
229
+ "items": {
230
+ "type": "string"
231
+ },
232
+ "description": "The environments that run neither this commit nor a later one."
233
+ },
234
+ "cut": {
235
+ "type": "array",
236
+ "items": {
237
+ "type": "string"
238
+ },
239
+ "description": "The labels of releases already cut from it."
240
+ },
241
+ "changes": {
242
+ "type": "array",
243
+ "items": {
244
+ "$ref": "#/$defs/change"
245
+ },
246
+ "description": "The first-parent commits after the previous candidate, up to and including this one, newest first."
247
+ },
248
+ "flags": {
249
+ "type": "array",
250
+ "items": {
251
+ "$ref": "#/$defs/flag"
252
+ },
253
+ "description": "The flags of the declared flag kinds whose record files those commits change."
254
+ }
255
+ }
256
+ },
257
+ "change": {
258
+ "type": "object",
259
+ "required": [
260
+ "commit",
261
+ "subject",
262
+ "date",
263
+ "work",
264
+ "records",
265
+ "pr",
266
+ "merge"
267
+ ],
268
+ "properties": {
269
+ "commit": {
270
+ "type": "string"
271
+ },
272
+ "subject": {
273
+ "type": "string"
274
+ },
275
+ "date": {
276
+ "type": "string",
277
+ "description": "Author date, ISO 8601."
278
+ },
279
+ "work": {
280
+ "type": "array",
281
+ "items": {
282
+ "type": "string"
283
+ },
284
+ "description": "The ids its `Chant-Record: work:<id>` trailers name."
285
+ },
286
+ "records": {
287
+ "type": "array",
288
+ "items": {
289
+ "type": "string"
290
+ },
291
+ "description": "Every `Chant-Record` trailer, as `<kind>:<id>`."
292
+ },
293
+ "pr": {
294
+ "type": [
295
+ "integer",
296
+ "null"
297
+ ],
298
+ "description": "The pull request the merge record or the squash subject (`(#123)` at its end) names, or null."
299
+ },
300
+ "merge": {
301
+ "type": [
302
+ "object",
303
+ "null"
304
+ ],
305
+ "description": "The merge record `_merges/<sha>.json` on chant/lifecycle (#4168 4.1), as stored, or null when there is none."
306
+ }
307
+ },
308
+ "description": "One first-parent commit."
309
+ },
310
+ "flag": {
311
+ "type": "object",
312
+ "required": [
313
+ "id",
314
+ "member",
315
+ "param",
316
+ "path",
317
+ "values"
318
+ ],
319
+ "properties": {
320
+ "id": {
321
+ "type": "string"
322
+ },
323
+ "member": {
324
+ "type": "string"
325
+ },
326
+ "param": {
327
+ "type": "string"
328
+ },
329
+ "path": {
330
+ "type": "string",
331
+ "description": "The record file from the workspace root."
332
+ },
333
+ "values": {
334
+ "type": "object",
335
+ "additionalProperties": {
336
+ "anyOf": [
337
+ {
338
+ "type": "string"
339
+ },
340
+ {
341
+ "type": "number"
342
+ },
343
+ {
344
+ "type": "boolean"
345
+ }
346
+ ]
347
+ },
348
+ "description": "The value per environment, as the working tree's record writes it."
349
+ }
350
+ }
351
+ }
352
+ }
353
+ }