@intentius/chant 0.124.30 → 0.124.32
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.
- package/bin/chant +7 -2
- package/dist/cli/commands/check-lexicon-plugin.d.ts +7 -4
- package/dist/cli/commands/check-lexicon-plugin.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-plugin.js +10 -7
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.js +18 -12
- package/dist/cli/commands/import.d.ts +10 -0
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/import.js +37 -0
- package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/project.js +4 -3
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/onboard.js +8 -4
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +2 -1
- package/dist/cli/dependencies.d.ts +18 -0
- package/dist/cli/dependencies.d.ts.map +1 -0
- package/dist/cli/dependencies.js +120 -0
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +23 -4
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +12 -4
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/graph.js +16 -6
- package/dist/cli/handlers/lint.d.ts.map +1 -1
- package/dist/cli/handlers/lint.js +6 -0
- package/dist/cli/handlers/operator.d.ts +2 -2
- package/dist/cli/handlers/operator.js +3 -3
- package/dist/cli/handlers/pr.d.ts +14 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +488 -32
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +3 -1
- package/dist/cli/handlers/run.d.ts +13 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +38 -15
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +74 -12
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +65 -0
- package/dist/cli/registry.d.ts +29 -2
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/registry.js +6 -2
- package/dist/codegen/generate-post-synth-barrel.js +2 -2
- package/dist/codegen/lexicon-regen.d.ts +4 -0
- package/dist/codegen/lexicon-regen.d.ts.map +1 -1
- package/dist/codegen/lexicon-regen.js +1 -1
- package/dist/codegen/pinned-upgrade.js +2 -2
- package/dist/components/capability.d.ts +44 -3
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/capability.js +21 -0
- package/dist/components/changed-files.d.ts +7 -1
- package/dist/components/changed-files.d.ts.map +1 -1
- package/dist/components/changed-files.js +9 -1
- package/dist/components/driver.d.ts +38 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +47 -6
- package/dist/components/fan-out-record.d.ts +6 -1
- package/dist/components/fan-out-record.d.ts.map +1 -1
- package/dist/components/fan-out-record.js +24 -0
- package/dist/components/fan-out-run.d.ts +20 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +21 -1
- package/dist/components/pr-pipeline.d.ts +65 -2
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +107 -15
- package/dist/components/pr-run.d.ts +127 -29
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +290 -70
- package/dist/components/wave-plan.d.ts +5 -2
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +12 -1
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/fold-import.js +16 -6
- package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
- package/dist/discovery/sandbox/bundle.js +6 -0
- package/dist/discovery/specifier-resolve.d.ts.map +1 -1
- package/dist/discovery/specifier-resolve.js +8 -2
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/fold/fold.js +68 -1
- package/dist/fold/subset.d.ts +37 -0
- package/dist/fold/subset.d.ts.map +1 -1
- package/dist/fold/subset.js +88 -0
- package/dist/lexicon-module.d.ts +10 -0
- package/dist/lexicon-module.d.ts.map +1 -1
- package/dist/lexicon-module.js +53 -8
- package/dist/lexicon-plugin-helpers.d.ts.map +1 -1
- package/dist/lexicon-plugin-helpers.js +6 -4
- package/dist/lexicon.d.ts +2 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/affected.d.ts.map +1 -1
- package/dist/lifecycle/affected.js +7 -1
- package/dist/lifecycle/release-ledger.d.ts +21 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.js +24 -0
- package/dist/lifecycle/work-dir.d.ts +45 -0
- package/dist/lifecycle/work-dir.d.ts.map +1 -0
- package/dist/lifecycle/work-dir.js +78 -0
- package/dist/lint/discover.d.ts +17 -9
- package/dist/lint/discover.d.ts.map +1 -1
- package/dist/lint/discover.js +62 -54
- package/dist/module-source.d.ts +57 -0
- package/dist/module-source.d.ts.map +1 -0
- package/dist/module-source.js +104 -0
- package/dist/op/activities/factory.js +1 -1
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/discover.js +13 -3
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/gate.js +3 -0
- package/dist/op/local-executor.d.ts +12 -0
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +22 -3
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +20 -5
- package/dist/op/operator.d.ts +2 -2
- package/dist/op/operator.js +2 -2
- package/dist/op/redact.d.ts +34 -0
- package/dist/op/redact.d.ts.map +1 -0
- package/dist/op/redact.js +94 -0
- package/dist/op/step-console.d.ts +2 -0
- package/dist/op/step-console.d.ts.map +1 -1
- package/dist/op/step-console.js +5 -2
- package/dist/op/steward.d.ts +6 -4
- package/dist/op/steward.d.ts.map +1 -1
- package/dist/op/steward.js +5 -3
- package/dist/op/work-lease-run.d.ts +20 -7
- package/dist/op/work-lease-run.d.ts.map +1 -1
- package/dist/op/work-lease-run.js +56 -12
- package/dist/pr-forge.d.ts +27 -0
- package/dist/pr-forge.d.ts.map +1 -1
- package/dist/pr-forge.js +51 -6
- package/dist/pr-loop.d.ts +141 -4
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +122 -6
- package/dist/pr-report-url.d.ts +10 -2
- package/dist/pr-report-url.d.ts.map +1 -1
- package/dist/pr-report-url.js +52 -6
- package/dist/ts-loader.d.ts +38 -4
- package/dist/ts-loader.d.ts.map +1 -1
- package/dist/ts-loader.js +190 -16
- package/dist/wave-runtime.mjs +208 -208
- package/dist/workspace/chant-migrations/fountain-retired-kinds.d.ts +34 -0
- package/dist/workspace/chant-migrations/fountain-retired-kinds.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/fountain-retired-kinds.js +176 -0
- package/dist/workspace/chant-migrations.d.ts.map +1 -1
- package/dist/workspace/chant-migrations.js +2 -0
- package/dist/workspace/checks/boxes.d.ts +6 -6
- package/dist/workspace/checks/boxes.js +9 -9
- package/dist/workspace/ci-green-forge.d.ts +6 -4
- package/dist/workspace/ci-green-forge.d.ts.map +1 -1
- package/dist/workspace/ci-green.d.ts +28 -12
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +49 -21
- package/dist/workspace/ci-list.schema.json +12 -6
- package/dist/workspace/ci-report-url.d.ts +18 -0
- package/dist/workspace/ci-report-url.d.ts.map +1 -0
- package/dist/workspace/ci-report-url.js +33 -0
- package/dist/workspace/ci-run-index.d.ts +108 -0
- package/dist/workspace/ci-run-index.d.ts.map +1 -0
- package/dist/workspace/ci-run-index.js +164 -0
- package/dist/workspace/ci-run-index.schema.json +52 -0
- package/dist/workspace/ci-run.d.ts +159 -9
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +419 -50
- package/dist/workspace/ci-runner-key.d.ts +133 -0
- package/dist/workspace/ci-runner-key.d.ts.map +1 -0
- package/dist/workspace/ci-runner-key.js +237 -0
- package/dist/workspace/ci-secrets.d.ts +65 -0
- package/dist/workspace/ci-secrets.d.ts.map +1 -0
- package/dist/workspace/ci-secrets.js +211 -0
- package/dist/workspace/ci-serve.d.ts +67 -4
- package/dist/workspace/ci-serve.d.ts.map +1 -1
- package/dist/workspace/ci-serve.js +240 -35
- package/dist/workspace/declaration.d.ts +8 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +11 -2
- package/dist/workspace/declaration.schema.json +7 -2
- package/dist/workspace/export.d.ts +1 -1
- package/dist/workspace/export.d.ts.map +1 -1
- package/dist/workspace/export.js +7 -3
- package/dist/workspace/ls.d.ts +6 -1
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/ls.js +1 -0
- package/dist/workspace/ls.schema.json +7 -1
- package/dist/workspace/pin-integrity.d.ts.map +1 -1
- package/dist/workspace/pin-integrity.js +3 -2
- package/dist/workspace/pr-report.schema.json +74 -7
- package/dist/workspace/reason-codes.d.ts +7 -3
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +9 -4
- package/dist/workspace/run-event.schema.json +3 -1
- package/dist/workspace/status.schema.json +3 -1
- package/dist/workspace/trust/trust-write.d.ts +109 -0
- package/dist/workspace/trust/trust-write.d.ts.map +1 -0
- package/dist/workspace/trust/trust-write.js +320 -0
- package/dist/workspace/trust-write.schema.json +265 -0
- package/dist/workspace/write-scope.d.ts +9 -0
- package/dist/workspace/write-scope.d.ts.map +1 -1
- package/dist/workspace/write-scope.js +11 -1
- package/package.json +1 -1
- package/src/__fixtures__/pr-loop/pr-apply-wave-waiting.note.golden.md +48 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +3 -3
- package/src/cli/commands/audit.test.ts +2 -2
- package/src/cli/commands/check-lexicon-plugin.test.ts +9 -0
- package/src/cli/commands/check-lexicon-plugin.ts +11 -8
- package/src/cli/commands/check-lexicon.test.ts +1 -1
- package/src/cli/commands/check-lexicon.ts +16 -11
- package/src/cli/commands/import.ts +40 -0
- package/src/cli/commands/init-lexicon/templates/project.ts +4 -3
- package/src/cli/commands/init-lexicon.test.ts +8 -5
- package/src/cli/commands/onboard.test.ts +45 -0
- package/src/cli/commands/onboard.ts +8 -4
- package/src/cli/commands/update.test.ts +6 -0
- package/src/cli/commands/update.ts +4 -1
- package/src/cli/compiled-cli.e2e.test.ts +76 -11
- package/src/cli/dependencies.e2e.test.ts +101 -0
- package/src/cli/dependencies.test.ts +93 -0
- package/src/cli/dependencies.ts +122 -0
- package/src/cli/handlers/build.test.ts +69 -0
- package/src/cli/handlers/build.ts +20 -4
- package/src/cli/handlers/fan-out.test.ts +73 -0
- package/src/cli/handlers/fan-out.ts +11 -4
- package/src/cli/handlers/graph.test.ts +76 -0
- package/src/cli/handlers/graph.ts +19 -12
- package/src/cli/handlers/lint.test.ts +42 -0
- package/src/cli/handlers/lint.ts +6 -0
- package/src/cli/handlers/operator.ts +3 -3
- package/src/cli/handlers/pr-waves.test.ts +449 -0
- package/src/cli/handlers/pr.test.ts +134 -0
- package/src/cli/handlers/pr.ts +523 -31
- package/src/cli/handlers/promote.test.ts +26 -1
- package/src/cli/handlers/promote.ts +3 -2
- package/src/cli/handlers/run-components-exit.test.ts +99 -0
- package/src/cli/handlers/run-signer.test.ts +79 -0
- package/src/cli/handlers/run.ts +34 -16
- package/src/cli/main.test.ts +19 -0
- package/src/cli/main.ts +73 -12
- package/src/cli/parse-args.ts +48 -0
- package/src/cli/registry.ts +35 -4
- package/src/codegen/generate-post-synth-barrel.ts +2 -2
- package/src/codegen/lexicon-regen.ts +1 -1
- package/src/codegen/pinned-upgrade.ts +2 -2
- package/src/codegen/post-synth-barrel-guards.test.ts +6 -11
- package/src/components/capability.ts +51 -3
- package/src/components/changed-files.test.ts +16 -1
- package/src/components/changed-files.ts +9 -1
- package/src/components/driver.test.ts +36 -1
- package/src/components/driver.ts +76 -4
- package/src/components/fan-out-record.ts +29 -1
- package/src/components/fan-out-run.test.ts +35 -0
- package/src/components/fan-out-run.ts +40 -1
- package/src/components/pr-pipeline.test.ts +100 -0
- package/src/components/pr-pipeline.ts +132 -17
- package/src/components/pr-run.test.ts +323 -18
- package/src/components/pr-run.ts +354 -75
- package/src/components/wave-plan.ts +16 -4
- package/src/discovery/fold-host-method.test.ts +87 -0
- package/src/discovery/fold-import.ts +15 -5
- package/src/discovery/fold-package-composite.test.ts +27 -0
- package/src/discovery/fold-specifiers.test.ts +8 -0
- package/src/discovery/sandbox/bundle.ts +6 -0
- package/src/discovery/specifier-resolve.ts +8 -2
- package/src/fold/fold.test.ts +72 -0
- package/src/fold/fold.ts +71 -0
- package/src/fold/subset.test.ts +31 -0
- package/src/fold/subset.ts +99 -0
- package/src/lexicon-dist.e2e.test.ts +254 -0
- package/src/lexicon-module.test.ts +47 -0
- package/src/lexicon-module.ts +49 -7
- package/src/lexicon-plugin-helpers.ts +6 -4
- package/src/lexicon.ts +2 -0
- package/src/lifecycle/affected.test.ts +7 -0
- package/src/lifecycle/affected.ts +6 -1
- package/src/lifecycle/release-ledger.ts +34 -0
- package/src/lifecycle/work-dir.test.ts +77 -0
- package/src/lifecycle/work-dir.ts +82 -0
- package/src/lint/discover.test.ts +46 -2
- package/src/lint/discover.ts +55 -49
- package/src/module-source.test.ts +85 -0
- package/src/module-source.ts +112 -0
- package/src/op/activities/factory.ts +1 -1
- package/src/op/discover-root.test.ts +31 -2
- package/src/op/discover.ts +14 -4
- package/src/op/gate.test.ts +30 -0
- package/src/op/gate.ts +3 -0
- package/src/op/local-executor.ts +31 -3
- package/src/op/op-waves-run.ts +20 -5
- package/src/op/op-waves.test.ts +49 -0
- package/src/op/operator.ts +2 -2
- package/src/op/redact.ts +93 -0
- package/src/op/step-console.ts +7 -2
- package/src/op/steward.ts +6 -4
- package/src/op/work-lease-run.test.ts +88 -2
- package/src/op/work-lease-run.ts +52 -12
- package/src/pr-forge-bundle.test.ts +2 -2
- package/src/pr-forge.test.ts +71 -0
- package/src/pr-forge.ts +70 -6
- package/src/pr-loop-bundle.test.ts +2 -1
- package/src/pr-loop.test.ts +117 -1
- package/src/pr-loop.ts +252 -10
- package/src/pr-report-url.ts +58 -6
- package/src/terraform/__fixtures__/advise.test.ts +21 -2
- package/src/ts-loader.test.ts +89 -1
- package/src/ts-loader.ts +180 -13
- package/src/workspace/changes.test.ts +1 -1
- package/src/workspace/chant-migrations/fountain-retired-kinds.test.ts +33 -0
- package/src/workspace/chant-migrations/fountain-retired-kinds.ts +190 -0
- package/src/workspace/chant-migrations.e2e.test.ts +5 -0
- package/src/workspace/chant-migrations.ts +2 -0
- package/src/workspace/checks/boxes.ts +9 -9
- package/src/workspace/ci-green-forge.ts +6 -4
- package/src/workspace/ci-green-runs.test.ts +53 -11
- package/src/workspace/ci-green.test.ts +1 -1
- package/src/workspace/ci-green.ts +50 -22
- package/src/workspace/ci-list.schema.json +12 -6
- package/src/workspace/ci-report-url.test.ts +220 -0
- package/src/workspace/ci-report-url.ts +33 -0
- package/src/workspace/ci-run-index.schema.json +52 -0
- package/src/workspace/ci-run-index.test.ts +392 -0
- package/src/workspace/ci-run-index.ts +206 -0
- package/src/workspace/ci-run.test.ts +520 -42
- package/src/workspace/ci-run.ts +483 -51
- package/src/workspace/ci-runner-key.test.ts +204 -0
- package/src/workspace/ci-runner-key.ts +275 -0
- package/src/workspace/ci-secrets.test.ts +106 -0
- package/src/workspace/ci-secrets.ts +213 -0
- package/src/workspace/ci-serve.test.ts +282 -39
- package/src/workspace/ci-serve.ts +231 -34
- package/src/workspace/declaration.schema.json +7 -2
- package/src/workspace/declaration.test.ts +23 -0
- package/src/workspace/declaration.ts +19 -3
- package/src/workspace/export.ts +6 -3
- package/src/workspace/ls.schema.json +7 -1
- package/src/workspace/ls.ts +7 -2
- package/src/workspace/pin-integrity.test.ts +5 -0
- package/src/workspace/pin-integrity.ts +3 -2
- package/src/workspace/pr-report.schema.json +74 -7
- package/src/workspace/reason-codes.test.ts +2 -0
- package/src/workspace/reason-codes.ts +9 -4
- package/src/workspace/run-event.schema.json +3 -1
- package/src/workspace/status.schema.json +3 -1
- package/src/workspace/trust/trust-write.test.ts +179 -0
- package/src/workspace/trust/trust-write.ts +377 -0
- package/src/workspace/trust-write.schema.json +265 -0
- package/src/workspace/write-scope.test.ts +29 -1
- package/src/workspace/write-scope.ts +12 -1
|
@@ -205,6 +205,25 @@ export interface FanOutRunOptions {
|
|
|
205
205
|
* the returned result cannot do.
|
|
206
206
|
*/
|
|
207
207
|
onComponentSettled?: (result: DriverComponentResult, outputs: Record<string, unknown> | undefined) => void;
|
|
208
|
+
/**
|
|
209
|
+
* Asked as each component's wave starts, with the outputs standing then
|
|
210
|
+
* (#3999). A reason holds the component back: it does not run, and nothing
|
|
211
|
+
* downstream of it runs either; each is listed in `held` and settles
|
|
212
|
+
* `waiting`. A pull request's apply holds a member whose plan read an
|
|
213
|
+
* upstream output that applied as another value, or that was known only
|
|
214
|
+
* after apply. Not with `waveGate`, which plans each wave after the one
|
|
215
|
+
* before it applied.
|
|
216
|
+
*/
|
|
217
|
+
hold?: (component: string, componentOutputs: Record<string, Record<string, unknown>>) => string | undefined;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** A component {@link FanOutRunOptions.hold} held back, or one downstream of it. */
|
|
221
|
+
export interface FanOutHeld {
|
|
222
|
+
component: string;
|
|
223
|
+
/** Why the hook held it, or, downstream, why it held the component named in `heldBy`. */
|
|
224
|
+
reason: string;
|
|
225
|
+
/** Downstream of a held component: that component. */
|
|
226
|
+
heldBy?: string;
|
|
208
227
|
}
|
|
209
228
|
|
|
210
229
|
export interface FanOutRunResult {
|
|
@@ -223,6 +242,8 @@ export interface FanOutRunResult {
|
|
|
223
242
|
gate?: PendingGateRecord;
|
|
224
243
|
/** With `waveGate`: each wave this attempt planned, in order, the one it stopped at included. */
|
|
225
244
|
waves?: WaveRecord[];
|
|
245
|
+
/** What {@link FanOutRunOptions.hold} held back, in plan order. Absent when nothing was. */
|
|
246
|
+
held?: FanOutHeld[];
|
|
226
247
|
}
|
|
227
248
|
|
|
228
249
|
/**
|
|
@@ -296,9 +317,21 @@ export async function runFanOut(
|
|
|
296
317
|
const completed: string[] = [];
|
|
297
318
|
const failed: string[] = [];
|
|
298
319
|
const blockedBy = new Map<string, string>();
|
|
320
|
+
const held = new Map<string, FanOutHeld>();
|
|
299
321
|
let gatedFact: PendingGateRecord | undefined;
|
|
300
322
|
|
|
301
323
|
const inPlan = new Set(active.order);
|
|
324
|
+
const waveOf = waveNumbers(active.waves);
|
|
325
|
+
/** Hold `root` and everything downstream of it that has not settled otherwise. */
|
|
326
|
+
const markHeld = (root: string, reason: string): void => {
|
|
327
|
+
held.set(root, { component: root, reason });
|
|
328
|
+
emit?.({ type: "component-settle", wave: waveOf.get(root)!, component: root, status: "waiting" });
|
|
329
|
+
for (const [component] of downstreamWithin(components, inPlan, [root])) {
|
|
330
|
+
if (held.has(component) || blockedBy.has(component) || failed.includes(component)) continue;
|
|
331
|
+
held.set(component, { component, reason, heldBy: root });
|
|
332
|
+
emit?.({ type: "component-settle", wave: waveOf.get(component)!, component, status: "waiting" });
|
|
333
|
+
}
|
|
334
|
+
};
|
|
302
335
|
/** Skip everything downstream of `root`, blaming `root` rather than the nearest edge. */
|
|
303
336
|
const markBlocked = (root: string): void => {
|
|
304
337
|
for (const [component, by] of downstreamWithin(components, inPlan, [root])) {
|
|
@@ -310,7 +343,12 @@ export async function runFanOut(
|
|
|
310
343
|
for (const [waveIndex, wave] of active.waves.entries()) {
|
|
311
344
|
const waveNum = waveIndex + 1;
|
|
312
345
|
// A component whose upstream failed in an earlier wave is not dispatched.
|
|
313
|
-
const
|
|
346
|
+
const candidates = wave.filter((name) => inPlan.has(name) && !blockedBy.has(name) && !held.has(name));
|
|
347
|
+
for (const name of candidates) {
|
|
348
|
+
const reason = options.hold?.(name, componentOutputs);
|
|
349
|
+
if (reason !== undefined) markHeld(name, reason);
|
|
350
|
+
}
|
|
351
|
+
const runnable = candidates.filter((name) => !held.has(name));
|
|
314
352
|
if (runnable.length === 0) continue;
|
|
315
353
|
|
|
316
354
|
emit?.({ type: "wave-start", wave: waveNum, components: runnable });
|
|
@@ -384,6 +422,7 @@ export async function runFanOut(
|
|
|
384
422
|
blocked,
|
|
385
423
|
componentOutputs,
|
|
386
424
|
...(gatedFact ? { gate: gatedFact } : {}),
|
|
425
|
+
...(held.size > 0 ? { held: active.order.filter((name) => held.has(name)).map((name) => held.get(name)!) } : {}),
|
|
387
426
|
};
|
|
388
427
|
}
|
|
389
428
|
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The apply job's base fallback (#4004), run in sh against a real
|
|
3
|
+
* repository. GitHub and Forgejo set `github.event.before`, and GitLab
|
|
4
|
+
* `CI_COMMIT_BEFORE_SHA`, to forty zeros on a branch's first push.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execFileSync } from "node:child_process";
|
|
8
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { afterAll, describe, expect, test } from "vitest";
|
|
12
|
+
import { EMPTY_TREE_SHA } from "./changed-files";
|
|
13
|
+
import { githubBaseFallback, gitlabBaseFallback, prLoopJobs } from "./pr-pipeline";
|
|
14
|
+
|
|
15
|
+
const ZEROS = "0".repeat(40);
|
|
16
|
+
const dir = mkdtempSync(join(tmpdir(), "chant-pr-base-"));
|
|
17
|
+
afterAll(() => rmSync(dir, { recursive: true, force: true }));
|
|
18
|
+
const ENV = { ...process.env, GIT_AUTHOR_NAME: "t", GIT_AUTHOR_EMAIL: "t@example.com", GIT_COMMITTER_NAME: "t", GIT_COMMITTER_EMAIL: "t@example.com" };
|
|
19
|
+
|
|
20
|
+
const git = (cwd: string, ...args: string[]): string => execFileSync("git", args, { cwd, encoding: "utf8", env: ENV }).trim();
|
|
21
|
+
|
|
22
|
+
/** A bare origin and a clone of it, `main` holding `commits` commits. */
|
|
23
|
+
function repo(name: string, commits: number): string {
|
|
24
|
+
const origin = join(dir, `${name}.git`);
|
|
25
|
+
const work = join(dir, name);
|
|
26
|
+
execFileSync("git", ["init", "-q", "--bare", "-b", "main", origin]);
|
|
27
|
+
execFileSync("git", ["init", "-q", "-b", "main", work]);
|
|
28
|
+
git(work, "remote", "add", "origin", origin);
|
|
29
|
+
for (let i = 0; i < commits; i++) {
|
|
30
|
+
writeFileSync(join(work, "f.txt"), `${i}\n`);
|
|
31
|
+
git(work, "add", "-A");
|
|
32
|
+
git(work, "commit", "-q", "-m", `c${i}`);
|
|
33
|
+
}
|
|
34
|
+
git(work, "push", "-q", "origin", "main");
|
|
35
|
+
return work;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** BASE_SHA after `line` runs in `cwd` with `env`. */
|
|
39
|
+
function settle(cwd: string, line: string, env: Record<string, string>): string {
|
|
40
|
+
return execFileSync("sh", ["-ec", `${line}\nprintf %s "$BASE_SHA"`], { cwd, encoding: "utf8", env: { ...ENV, ...env } });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe("githubBaseFallback (#4004)", () => {
|
|
44
|
+
test("a set base is kept", () => {
|
|
45
|
+
const work = repo("kept", 2);
|
|
46
|
+
const parent = git(work, "rev-parse", "HEAD^");
|
|
47
|
+
expect(settle(work, githubBaseFallback(), { BASE_SHA: parent, GITHUB_SHA: git(work, "rev-parse", "HEAD") })).toBe(parent);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("forty zeros or empty on the target branch's first push: the pushed commit's parent", () => {
|
|
51
|
+
const work = repo("first", 2);
|
|
52
|
+
const head = git(work, "rev-parse", "HEAD");
|
|
53
|
+
const parent = git(work, "rev-parse", "HEAD^");
|
|
54
|
+
expect(settle(work, githubBaseFallback(), { BASE_SHA: ZEROS, GITHUB_SHA: head })).toBe(parent);
|
|
55
|
+
expect(settle(work, githubBaseFallback(), { BASE_SHA: "", GITHUB_SHA: head })).toBe(parent);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("a branch's first push: the merge base with the target branch", () => {
|
|
59
|
+
const work = repo("branch", 1);
|
|
60
|
+
const fork = git(work, "rev-parse", "HEAD");
|
|
61
|
+
git(work, "checkout", "-q", "-b", "feature");
|
|
62
|
+
writeFileSync(join(work, "g.txt"), "g\n");
|
|
63
|
+
git(work, "add", "-A");
|
|
64
|
+
git(work, "commit", "-q", "-m", "feature");
|
|
65
|
+
expect(settle(work, githubBaseFallback(), { BASE_SHA: ZEROS, GITHUB_SHA: git(work, "rev-parse", "HEAD") })).toBe(fork);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("a root commit: the empty tree", () => {
|
|
69
|
+
const work = repo("root", 1);
|
|
70
|
+
expect(settle(work, githubBaseFallback(), { BASE_SHA: ZEROS, GITHUB_SHA: git(work, "rev-parse", "HEAD") })).toBe(EMPTY_TREE_SHA);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("a named branch is the one fetched", () => {
|
|
74
|
+
expect(githubBaseFallback()).toContain('git fetch origin "main"');
|
|
75
|
+
expect(githubBaseFallback("trunk")).toContain('git fetch origin "trunk"');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("gitlabBaseFallback", () => {
|
|
80
|
+
test("a root commit's first push: the empty tree, as on GitHub (#4004)", () => {
|
|
81
|
+
const work = repo("gitlab-root", 1);
|
|
82
|
+
expect(settle(work, gitlabBaseFallback("main"), { BASE_SHA: ZEROS, CI_COMMIT_SHA: git(work, "rev-parse", "HEAD") })).toBe(EMPTY_TREE_SHA);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("the merge request's diff base comes first", () => {
|
|
86
|
+
const work = repo("gitlab-mr", 2);
|
|
87
|
+
const parent = git(work, "rev-parse", "HEAD^");
|
|
88
|
+
expect(settle(work, gitlabBaseFallback("main"), { BASE_SHA: ZEROS, CI_MERGE_REQUEST_DIFF_BASE_SHA: parent, CI_COMMIT_SHA: git(work, "rev-parse", "HEAD") })).toBe(parent);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("prLoopJobs", () => {
|
|
93
|
+
test("every forge's apply settles its base first; no plan does", () => {
|
|
94
|
+
for (const forge of ["github", "forgejo", "gitlab"] as const) {
|
|
95
|
+
const [plan, apply] = prLoopJobs(forge, "prod", { branch: "trunk" });
|
|
96
|
+
expect(plan!.setup).toBeUndefined();
|
|
97
|
+
expect(apply!.setup).toEqual([forge === "gitlab" ? gitlabBaseFallback("trunk") : githubBaseFallback("trunk")]);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -42,6 +42,25 @@
|
|
|
42
42
|
* build is `chant build --env <env>`, or `buildCommand` when the templates
|
|
43
43
|
* come from several builds (`npm run build`).
|
|
44
44
|
*
|
|
45
|
+
* With `waveGate` (#4003) the apply runs a wave at a time: `pr-apply
|
|
46
|
+
* --wave-gate <gate>`, each wave planned after the one before it applied. A
|
|
47
|
+
* generator writes one apply job per wave of the component graph
|
|
48
|
+
* (`prLoopWaveJobs`), job `apply-wave-<k>` passing `--wave <k>` and needing
|
|
49
|
+
* the job before it, and the attempt record carries the approved set from
|
|
50
|
+
* one job to the next. A wave whose plan the approved change set covers
|
|
51
|
+
* applies under the pull request's approval; one whose plan moved stops at
|
|
52
|
+
* `<gate>-wave-<k>` (exit 3), and re-running its job after the approval
|
|
53
|
+
* carries on. `chant ci serve` runs the one apply job, which runs the waves
|
|
54
|
+
* in order until one stops.
|
|
55
|
+
*
|
|
56
|
+
* Each generated job runs its command through `chant ci run plan|apply
|
|
57
|
+
* --in-place` (#4156, `PrLoopJob.run`), so a forge job writes the same run
|
|
58
|
+
* record the chant runner does: Op `pr-plan` or `pr-apply`, signed with
|
|
59
|
+
* the runner key a forge secret puts in `CHANT_RUNNER_KEY`, and naming the
|
|
60
|
+
* forge's run as its `runOrigin`. `ci run` runs `command` in the job's own
|
|
61
|
+
* checkout with the job's environment, less the key. The plan passes
|
|
62
|
+
* `--no-push`, so it still writes nothing to the remote `chant/lifecycle`.
|
|
63
|
+
*
|
|
45
64
|
* In a workspace member (#3465) both commands run in the member's directory
|
|
46
65
|
* and pass `--member <name>`, so the member's gate (`pr-<number>-<member>`),
|
|
47
66
|
* note and statuses stay apart from every other member's on the same pull
|
|
@@ -52,6 +71,13 @@
|
|
|
52
71
|
import type { ForgeKind } from "../pr-forge";
|
|
53
72
|
import { PR_APPLY_GATE } from "../pr-loop";
|
|
54
73
|
import { CI_IMAGE, GITHUB_JOB_TOKEN } from "../ci-jobs";
|
|
74
|
+
import { EMPTY_TREE_SHA } from "./changed-files";
|
|
75
|
+
import { layerWaves } from "../gated-waves";
|
|
76
|
+
import type { DriverComponent } from "./driver";
|
|
77
|
+
import type { GatedWavePipelineOptions } from "./gated-wave-pipeline";
|
|
78
|
+
|
|
79
|
+
/** The variable `chant ci run` reads its runner key from (`RUNNER_KEY_ENV` in ../workspace/ci-green.ts). */
|
|
80
|
+
const RUNNER_KEY_ENV = "CHANT_RUNNER_KEY";
|
|
55
81
|
|
|
56
82
|
/** Where both jobs write their reports, kept as a CI artifact. */
|
|
57
83
|
export const PR_LOOP_REPORT_DIR = ".chant/pr";
|
|
@@ -125,6 +151,12 @@ export interface PrLoopPipelineOptions {
|
|
|
125
151
|
* `npm run build`. Setting it turns `build` on.
|
|
126
152
|
*/
|
|
127
153
|
buildCommand?: string;
|
|
154
|
+
/**
|
|
155
|
+
* A gate per wave on merge (#4003): the apply runs `pr-apply --wave-gate
|
|
156
|
+
* <waveGate>`, a wave at a time, and a generator writes one apply job per
|
|
157
|
+
* wave. Set from `--wave-gate` beside `--pr-loop` ({@link prLoopWithWaves}).
|
|
158
|
+
*/
|
|
159
|
+
waveGate?: string;
|
|
128
160
|
/**
|
|
129
161
|
* Which GitHub-shaped forge the workflow is for. Set by the forgejo
|
|
130
162
|
* generator, which reuses the github one; a caller never needs to.
|
|
@@ -148,6 +180,17 @@ export interface PrLoopJob {
|
|
|
148
180
|
setup?: string[];
|
|
149
181
|
/** With `build` (#4088): the build line and the lint line, run in this order before the command. */
|
|
150
182
|
build?: string[];
|
|
183
|
+
/** With `waveGate` (#4003): the wave a generated apply job runs, 1-based. */
|
|
184
|
+
wave?: number;
|
|
185
|
+
/**
|
|
186
|
+
* What the generated job runs in place of `command` (#4156): `chant ci run
|
|
187
|
+
* <job> --in-place` with the same environment, gate, forge, events and
|
|
188
|
+
* report URL, which runs `command` and records the run. `env` is the
|
|
189
|
+
* job's `env` and, on GitHub and Forgejo, `CHANT_RUNNER_KEY` from the
|
|
190
|
+
* repository secret of that name. GitLab hands its CI/CD variables to a
|
|
191
|
+
* job itself.
|
|
192
|
+
*/
|
|
193
|
+
run: { command: string; env: Record<string, string> };
|
|
151
194
|
}
|
|
152
195
|
|
|
153
196
|
/** The lines `build` (#4088) runs before a job's command: the build, then the lint. Empty without the option. */
|
|
@@ -176,29 +219,82 @@ export function prLoopJobs(forge: ForgeKind, env: string, options: PrLoopPipelin
|
|
|
176
219
|
...(github ? { GITHUB_TOKEN: GITHUB_JOB_TOKEN } : {}),
|
|
177
220
|
...(options.events ? { CHANT_RUN_EVENTS_DIR: PR_LOOP_EVENTS_DIR } : {}),
|
|
178
221
|
};
|
|
222
|
+
// #4156: the flags `chant ci run` rebuilds `command` from. It finds the member from the directory it runs in.
|
|
223
|
+
const ciRun = (job: "plan" | "apply"): string[] => [
|
|
224
|
+
"chant", "ci", "run", job, "--in-place", ...(job === "plan" ? ["--no-push"] : []),
|
|
225
|
+
"--base", '"$BASE_SHA"', ...(job === "plan" ? ["--pr", '"$PR_NUMBER"'] : []),
|
|
226
|
+
"--env", env, "--gate", gate, "--forge", forge,
|
|
227
|
+
...(options.waveGate ? ["--wave-gate", options.waveGate] : []),
|
|
228
|
+
...(job === "apply" && options.branch ? ["--branch", JSON.stringify(options.branch)] : []),
|
|
229
|
+
...(job === "apply" && options.requireReview === false ? ["--no-require-review"] : []),
|
|
230
|
+
...(options.events ? ["--events-json"] : []),
|
|
231
|
+
...(options.reportUrl ? ["--report-url", JSON.stringify(options.reportUrl)] : []),
|
|
232
|
+
];
|
|
233
|
+
const runEnv = (env: Record<string, string>): Record<string, string> => (github ? { ...env, [RUNNER_KEY_ENV]: `\${{ secrets.${RUNNER_KEY_ENV} }}` } : env);
|
|
234
|
+
const planEnv = github
|
|
235
|
+
? { BASE_SHA: "${{ github.event.pull_request.base.sha }}", PR_NUMBER: "${{ github.event.pull_request.number }}", ...jobEnv }
|
|
236
|
+
: { BASE_SHA: "$CI_MERGE_REQUEST_DIFF_BASE_SHA", PR_NUMBER: "$CI_MERGE_REQUEST_IID", ...jobEnv };
|
|
237
|
+
const applyEnv = github ? { BASE_SHA: "${{ github.event.before }}", ...jobEnv } : { BASE_SHA: "$CI_COMMIT_BEFORE_SHA", ...jobEnv };
|
|
179
238
|
return [
|
|
180
239
|
{
|
|
181
240
|
jobName: "plan",
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
: { BASE_SHA: "$CI_MERGE_REQUEST_DIFF_BASE_SHA", PR_NUMBER: "$CI_MERGE_REQUEST_IID", ...jobEnv },
|
|
241
|
+
// With `waveGate` the plan's note says the later waves may stop at their own gates (#4003).
|
|
242
|
+
command: ["chant", "components", "pr-plan", "--base", '"$BASE_SHA"', "--pr", '"$PR_NUMBER"', ...common, ...(options.waveGate ? ["--wave-gate", options.waveGate] : [])].join(" "),
|
|
243
|
+
env: planEnv,
|
|
186
244
|
...build,
|
|
245
|
+
run: { command: ciRun("plan").join(" "), env: runEnv(planEnv) },
|
|
187
246
|
},
|
|
188
247
|
{
|
|
189
248
|
jobName: "apply",
|
|
190
249
|
command: [
|
|
191
250
|
"chant", "components", "pr-apply", "--base", '"$BASE_SHA"', ...common,
|
|
192
251
|
...(options.requireReview === false ? [] : ["--require-review"]),
|
|
252
|
+
...(options.waveGate ? ["--wave-gate", options.waveGate] : []),
|
|
193
253
|
"--resume", PR_APPLY_RECORD,
|
|
194
254
|
].join(" "),
|
|
195
|
-
env:
|
|
196
|
-
|
|
255
|
+
env: applyEnv,
|
|
256
|
+
setup: [github ? githubBaseFallback(options.branch) : gitlabBaseFallback(options.branch)],
|
|
197
257
|
...build,
|
|
258
|
+
run: { command: ciRun("apply").join(" "), env: runEnv(applyEnv) },
|
|
198
259
|
},
|
|
199
260
|
];
|
|
200
261
|
}
|
|
201
262
|
|
|
263
|
+
/**
|
|
264
|
+
* The pull-request options with `--wave-gate` beside `--pr-loop` (#4003):
|
|
265
|
+
* the apply gets a gate per wave. A canary or a fixed base is refused, since
|
|
266
|
+
* the pull request's waves are its derivation's and each push is measured
|
|
267
|
+
* from the commit before it.
|
|
268
|
+
*/
|
|
269
|
+
export function prLoopWithWaves(prLoop: PrLoopPipelineOptions, gatedWaves?: GatedWavePipelineOptions): PrLoopPipelineOptions {
|
|
270
|
+
if (!gatedWaves) return prLoop;
|
|
271
|
+
if (gatedWaves.canary?.length) throw new Error("a pull-request pipeline's waves are the change's derivation, so it takes no --canary; drop --canary");
|
|
272
|
+
if (gatedWaves.base !== undefined) throw new Error("a pull-request pipeline measures each push from the commit before it, so it takes no --base; drop --base");
|
|
273
|
+
return { ...prLoop, waveGate: gatedWaves.gate };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** The name of the apply job for wave `wave` (#4003). */
|
|
277
|
+
export function prApplyWaveJobName(wave: number): string {
|
|
278
|
+
return `apply-wave-${wave}`;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* One apply job per wave of the component graph (#4003), each `apply`'s
|
|
283
|
+
* command with `--wave <k>`. A change selects a subset, which never layers
|
|
284
|
+
* deeper than the graph, so a job past the change's last wave finds nothing
|
|
285
|
+
* left and exits 0.
|
|
286
|
+
*/
|
|
287
|
+
export function prLoopWaveJobs(components: DriverComponent[], apply: PrLoopJob): PrLoopJob[] {
|
|
288
|
+
const count = Math.max(1, layerWaves(components.map((c) => ({ name: c.name, dependsOn: c.dependsOn ?? [] }))).length);
|
|
289
|
+
return Array.from({ length: count }, (_, i) => ({
|
|
290
|
+
...apply,
|
|
291
|
+
command: `${apply.command} --wave ${i + 1}`,
|
|
292
|
+
wave: i + 1,
|
|
293
|
+
// #4156: the job runs it through chant ci run, which appends the same --wave to pr-apply.
|
|
294
|
+
run: { ...apply.run, command: `${apply.run.command} --wave ${i + 1}` },
|
|
295
|
+
}));
|
|
296
|
+
}
|
|
297
|
+
|
|
202
298
|
/**
|
|
203
299
|
* The concurrency group (GitHub, Forgejo) or resource group (GitLab) that
|
|
204
300
|
* keeps one apply at a time per environment, and per member in a workspace.
|
|
@@ -227,19 +323,38 @@ export function prApplyRecordKey(env: string, commit: string, member?: string):
|
|
|
227
323
|
}
|
|
228
324
|
|
|
229
325
|
/**
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
326
|
+
* The shell that settles an unusable push base. `before` is empty or forty
|
|
327
|
+
* zeros on a branch's first push; then the base is `diffBase` when the forge
|
|
328
|
+
* gives one, else the merge base of `target` and `commit`, else `commit`'s
|
|
329
|
+
* parent when the merge base is `commit` itself, else, for a root commit, the
|
|
330
|
+
* empty tree.
|
|
234
331
|
*/
|
|
235
|
-
|
|
236
|
-
const
|
|
332
|
+
function pushBaseFallback(target: string, commit: string, diffBase?: string): string {
|
|
333
|
+
const fromBranch =
|
|
334
|
+
`git fetch origin ${target} && BASE_SHA="$(git merge-base FETCH_HEAD "${commit}")"; ` +
|
|
335
|
+
`if [ "$BASE_SHA" = "${commit}" ]; then BASE_SHA="$(git rev-parse --verify --quiet "${commit}^" || echo ${EMPTY_TREE_SHA})"; fi; `;
|
|
237
336
|
return (
|
|
238
337
|
'if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then ' +
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
`git fetch origin ${target} && BASE_SHA="$(git merge-base FETCH_HEAD "$CI_COMMIT_SHA")"; ` +
|
|
242
|
-
'if [ "$BASE_SHA" = "$CI_COMMIT_SHA" ]; then BASE_SHA="$(git rev-parse "$CI_COMMIT_SHA^")"; fi; ' +
|
|
243
|
-
"fi; fi; export BASE_SHA"
|
|
338
|
+
(diffBase ? `BASE_SHA="\${${diffBase}:-}"; if [ -z "$BASE_SHA" ]; then ${fromBranch}fi; ` : fromBranch) +
|
|
339
|
+
"fi; export BASE_SHA"
|
|
244
340
|
);
|
|
245
341
|
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* GitLab sets `CI_COMMIT_BEFORE_SHA` to forty zeros on a branch's first push.
|
|
345
|
+
* The line replaces it with the merge request's diff base when the pipeline
|
|
346
|
+
* has one, and otherwise as {@link pushBaseFallback} does.
|
|
347
|
+
*/
|
|
348
|
+
export function gitlabBaseFallback(branch?: string): string {
|
|
349
|
+
return pushBaseFallback(branch ? JSON.stringify(branch) : "$CI_DEFAULT_BRANCH", "$CI_COMMIT_SHA", "CI_MERGE_REQUEST_DIFF_BASE_SHA");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* GitHub and Forgejo set `github.event.before` to forty zeros on a branch's
|
|
354
|
+
* first push (#4004). The line replaces it as {@link pushBaseFallback} does,
|
|
355
|
+
* with the target branch `main` unless the pipeline names another, the
|
|
356
|
+
* generators' default.
|
|
357
|
+
*/
|
|
358
|
+
export function githubBaseFallback(branch?: string): string {
|
|
359
|
+
return pushBaseFallback(JSON.stringify(branch ?? "main"), "$GITHUB_SHA");
|
|
360
|
+
}
|