@intentius/chant 0.124.40 → 0.124.42
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/dist/__fixtures__/oidc-emulator.d.ts +49 -0
- package/dist/__fixtures__/oidc-emulator.d.ts.map +1 -0
- package/dist/__fixtures__/oidc-emulator.js +89 -0
- package/dist/ci-handover.d.ts +117 -0
- package/dist/ci-handover.d.ts.map +1 -0
- package/dist/ci-handover.js +217 -0
- package/dist/ci-identity.d.ts +190 -0
- package/dist/ci-identity.d.ts.map +1 -0
- package/dist/ci-identity.js +219 -0
- package/dist/cli/handlers/approve.d.ts +2 -1
- package/dist/cli/handlers/approve.d.ts.map +1 -1
- package/dist/cli/handlers/approve.js +20 -6
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +18 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +42 -5
- package/dist/cli/handlers/gate-resume.d.ts +3 -0
- package/dist/cli/handlers/gate-resume.d.ts.map +1 -1
- package/dist/cli/handlers/gate-resume.js +15 -2
- package/dist/cli/handlers/lock.d.ts +8 -0
- package/dist/cli/handlers/lock.d.ts.map +1 -0
- package/dist/cli/handlers/lock.js +197 -0
- package/dist/cli/handlers/pr-env.d.ts +34 -0
- package/dist/cli/handlers/pr-env.d.ts.map +1 -0
- package/dist/cli/handlers/pr-env.js +436 -0
- package/dist/cli/handlers/pr.d.ts +76 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +615 -55
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +9 -0
- package/dist/cli/handlers/query.d.ts +6 -0
- package/dist/cli/handlers/query.d.ts.map +1 -0
- package/dist/cli/handlers/query.js +80 -0
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +11 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +49 -4
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +46 -2
- package/dist/cli/registry.d.ts +31 -2
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/capability.d.ts +47 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/capability.js +44 -0
- package/dist/components/cli-support.d.ts +6 -0
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/cli-support.js +8 -1
- package/dist/components/driver.d.ts +10 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +52 -2
- package/dist/components/fan-out-run.d.ts +37 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +44 -4
- package/dist/components/gate-plan.d.ts +2 -0
- package/dist/components/gate-plan.d.ts.map +1 -1
- package/dist/components/gate-plan.js +1 -0
- package/dist/components/pr-environment.d.ts +298 -0
- package/dist/components/pr-environment.d.ts.map +1 -0
- package/dist/components/pr-environment.js +406 -0
- package/dist/components/pr-hooks.d.ts +66 -0
- package/dist/components/pr-hooks.d.ts.map +1 -0
- package/dist/components/pr-hooks.js +119 -0
- package/dist/components/pr-pipeline.d.ts +92 -2
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +150 -21
- package/dist/components/pr-policy.d.ts +109 -0
- package/dist/components/pr-policy.d.ts.map +1 -0
- package/dist/components/pr-policy.js +150 -0
- package/dist/components/pr-run.d.ts +6 -0
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +8 -3
- package/dist/components/wave-plan.d.ts +14 -1
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +34 -2
- package/dist/gate-decision.d.ts +15 -2
- package/dist/gate-decision.d.ts.map +1 -1
- package/dist/gate-decision.js +17 -2
- package/dist/gate-policy.d.ts +140 -0
- package/dist/gate-policy.d.ts.map +1 -0
- package/dist/gate-policy.js +242 -0
- package/dist/lifecycle/gate-checkpoint.d.ts +1 -1
- package/dist/lifecycle/gate-checkpoint.js +1 -1
- package/dist/{workspace/trust → lifecycle}/gate-seal.d.ts +1 -1
- package/dist/lifecycle/gate-seal.d.ts.map +1 -0
- package/dist/lifecycle/query.d.ts +68 -0
- package/dist/lifecycle/query.d.ts.map +1 -0
- package/dist/lifecycle/query.js +246 -0
- package/dist/lifecycle/run-ledger.d.ts +13 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +16 -0
- package/dist/lifecycle/target-lock.d.ts +97 -0
- package/dist/lifecycle/target-lock.d.ts.map +1 -0
- package/dist/lifecycle/target-lock.js +286 -0
- package/dist/op/activities/activity-contracts.d.ts +30 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.js +26 -0
- package/dist/op/activities/index.d.ts +2 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/index.js +4 -0
- package/dist/op/activities/pr-refs.d.ts +10 -0
- package/dist/op/activities/pr-refs.d.ts.map +1 -0
- package/dist/op/activities/pr-refs.js +26 -0
- package/dist/op/activities/predict-behaviour.d.ts +2 -4
- package/dist/op/activities/predict-behaviour.d.ts.map +1 -1
- package/dist/op/activities/predict-behaviour.js +2 -21
- package/dist/op/activities/review-agent.d.ts +137 -0
- package/dist/op/activities/review-agent.d.ts.map +1 -0
- package/dist/op/activities/review-agent.js +266 -0
- package/dist/op/builders.d.ts +9 -0
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +11 -0
- package/dist/op/cron.d.ts +8 -0
- package/dist/op/cron.d.ts.map +1 -1
- package/dist/op/cron.js +51 -0
- package/dist/op/gate-approval.d.ts +20 -13
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-approval.js +13 -18
- package/dist/op/gate-quorum.d.ts +131 -0
- package/dist/op/gate-quorum.d.ts.map +1 -0
- package/dist/op/gate-quorum.js +183 -0
- package/dist/op/gate-resume.d.ts +4 -2
- package/dist/op/gate-resume.d.ts.map +1 -1
- package/dist/op/gate-resume.js +5 -4
- package/dist/op/gate-review.js +1 -1
- package/dist/op/gate.d.ts +32 -4
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/gate.js +40 -24
- package/dist/op/index.d.ts +6 -4
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/index.js +4 -3
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +9 -3
- package/dist/op/op-waves-run.d.ts +28 -43
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +170 -120
- package/dist/op/op-waves.d.ts +55 -3
- package/dist/op/op-waves.d.ts.map +1 -1
- package/dist/op/op-waves.js +63 -8
- package/dist/op/schedule-overdue.d.ts +57 -0
- package/dist/op/schedule-overdue.d.ts.map +1 -0
- package/dist/op/schedule-overdue.js +138 -0
- package/dist/op/stage-hooks.d.ts +132 -0
- package/dist/op/stage-hooks.d.ts.map +1 -0
- package/dist/op/stage-hooks.js +142 -0
- package/dist/override-gate.d.ts +2 -2
- package/dist/override-gate.d.ts.map +1 -1
- package/dist/override-gate.js +1 -1
- package/dist/pr-forge.d.ts +64 -0
- package/dist/pr-forge.d.ts.map +1 -1
- package/dist/pr-forge.js +331 -44
- package/dist/pr-loop.d.ts +107 -1
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +84 -3
- package/dist/pr-post.d.ts +72 -0
- package/dist/pr-post.d.ts.map +1 -0
- package/dist/pr-post.js +134 -0
- package/dist/pr-requires.d.ts +38 -0
- package/dist/pr-requires.d.ts.map +1 -0
- package/dist/pr-requires.js +48 -0
- package/dist/wave-blocked.d.ts +79 -0
- package/dist/wave-blocked.d.ts.map +1 -0
- package/dist/wave-blocked.js +245 -0
- package/dist/wave-engine.d.ts +6 -0
- package/dist/wave-engine.d.ts.map +1 -1
- package/dist/wave-engine.js +2 -0
- package/dist/wave-runtime.mjs +243 -243
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts +1 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/two-member-workspace.js +1 -1
- package/dist/workspace/checks/ci-workflow.d.ts.map +1 -1
- package/dist/workspace/checks/ci-workflow.js +19 -4
- package/dist/workspace/ci-branches.d.ts +51 -0
- package/dist/workspace/ci-branches.d.ts.map +1 -0
- package/dist/workspace/ci-branches.js +69 -0
- package/dist/workspace/ci-cloud-identity.d.ts +11 -0
- package/dist/workspace/ci-cloud-identity.d.ts.map +1 -0
- package/dist/workspace/ci-cloud-identity.js +24 -0
- package/dist/workspace/ci-init.d.ts.map +1 -1
- package/dist/workspace/ci-init.js +5 -4
- package/dist/workspace/ci-locks.d.ts +12 -0
- package/dist/workspace/ci-locks.d.ts.map +1 -0
- package/dist/workspace/ci-locks.js +6 -0
- package/dist/workspace/ci-run.d.ts +17 -1
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +66 -4
- package/dist/workspace/ci-workflow-at.d.ts +14 -0
- package/dist/workspace/ci-workflow-at.d.ts.map +1 -0
- package/dist/workspace/ci-workflow-at.js +17 -0
- package/dist/workspace/ci-workflow.d.ts +11 -3
- package/dist/workspace/ci-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-workflow.js +10 -2
- package/dist/workspace/declaration.d.ts +69 -3
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +72 -1
- package/dist/workspace/declaration.schema.json +149 -2
- package/dist/workspace/identity-gate-rule.d.ts +1 -1
- package/dist/workspace/identity-gate-rule.d.ts.map +1 -1
- package/dist/workspace/identity-gate-rule.js +1 -1
- package/dist/workspace/identity.d.ts +1 -1
- package/dist/workspace/identity.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.js +8 -5
- package/dist/workspace/member-pipeline.d.ts +6 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -1
- package/dist/workspace/member-pipeline.js +23 -0
- package/dist/workspace/pr-report.schema.json +94 -3
- package/dist/workspace/reason-codes.d.ts +1 -1
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +1 -1
- package/dist/workspace/security.d.ts.map +1 -1
- package/dist/workspace/security.js +4 -0
- package/dist/workspace/status-gates.d.ts +3 -2
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status-gates.js +8 -7
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +4 -1
- package/dist/workspace/trust/seal.d.ts +2 -2
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/seal.js +2 -2
- package/package.json +1 -1
- package/src/__fixtures__/oidc-emulator.ts +118 -0
- package/src/ci-handover.test.ts +209 -0
- package/src/ci-handover.ts +237 -0
- package/src/ci-identity.test.ts +201 -0
- package/src/ci-identity.ts +310 -0
- package/src/cli/handlers/approve.ts +18 -5
- package/src/cli/handlers/build.ts +18 -1
- package/src/cli/handlers/fan-out.ts +43 -6
- package/src/cli/handlers/gate-resume.ts +16 -2
- package/src/cli/handlers/lock.test.ts +98 -0
- package/src/cli/handlers/lock.ts +202 -0
- package/src/cli/handlers/operator.test.ts +3 -1
- package/src/cli/handlers/pr-before-merge.test.ts +276 -0
- package/src/cli/handlers/pr-env.ts +462 -0
- package/src/cli/handlers/pr-waves-blocked.test.ts +247 -0
- package/src/cli/handlers/pr.test.ts +184 -0
- package/src/cli/handlers/pr.ts +605 -61
- package/src/cli/handlers/promote.ts +9 -0
- package/src/cli/handlers/query.ts +78 -0
- package/src/cli/handlers/run.ts +10 -0
- package/src/cli/main.ts +49 -4
- package/src/cli/parse-args.ts +34 -2
- package/src/cli/registry.ts +31 -2
- package/src/components/capability-targets.test.ts +162 -0
- package/src/components/capability.ts +71 -0
- package/src/components/cli-support.ts +14 -1
- package/src/components/driver.ts +54 -3
- package/src/components/fan-out-run.ts +58 -3
- package/src/components/fan-out-waves.test.ts +45 -1
- package/src/components/gate-plan.ts +3 -0
- package/src/components/pr-environment.test.ts +249 -0
- package/src/components/pr-environment.ts +538 -0
- package/src/components/pr-hooks.test.ts +118 -0
- package/src/components/pr-hooks.ts +157 -0
- package/src/components/pr-pipeline.ts +194 -21
- package/src/components/pr-policy.test.ts +145 -0
- package/src/components/pr-policy.ts +211 -0
- package/src/components/pr-run.ts +14 -2
- package/src/components/wave-plan.ts +38 -3
- package/src/gate-decision-bundle.test.ts +4 -3
- package/src/gate-decision.ts +39 -2
- package/src/gate-policy-bundle.test.ts +28 -0
- package/src/gate-policy.test.ts +162 -0
- package/src/gate-policy.ts +298 -0
- package/src/lifecycle/gate-checkpoint.ts +1 -1
- package/src/{workspace/trust → lifecycle}/gate-seal.ts +1 -1
- package/src/lifecycle/query.test.ts +91 -0
- package/src/lifecycle/query.ts +315 -0
- package/src/lifecycle/run-ledger.ts +18 -0
- package/src/lifecycle/target-lock.test.ts +195 -0
- package/src/lifecycle/target-lock.ts +347 -0
- package/src/op/activities/activity-contracts.ts +31 -0
- package/src/op/activities/index.ts +6 -0
- package/src/op/activities/pr-refs.ts +28 -0
- package/src/op/activities/predict-behaviour.ts +3 -23
- package/src/op/activities/review-agent.test.ts +240 -0
- package/src/op/activities/review-agent.ts +355 -0
- package/src/op/builders.ts +13 -0
- package/src/op/cron.ts +44 -0
- package/src/op/gate-alias.test.ts +2 -2
- package/src/op/gate-approval.test.ts +30 -9
- package/src/op/gate-approval.ts +23 -26
- package/src/op/gate-quorum.test.ts +139 -0
- package/src/op/gate-quorum.ts +248 -0
- package/src/op/gate-resume.ts +6 -3
- package/src/op/gate-review.ts +1 -1
- package/src/op/gate.ts +64 -23
- package/src/op/index.ts +13 -1
- package/src/op/local-executor.ts +9 -2
- package/src/op/op-waves-hook-targets.test.ts +126 -0
- package/src/op/op-waves-policy-hold.test.ts +66 -0
- package/src/op/op-waves-quorum.test.ts +101 -0
- package/src/op/op-waves-run.ts +197 -183
- package/src/op/op-waves.ts +83 -10
- package/src/op/schedule-overdue.test.ts +220 -0
- package/src/op/schedule-overdue.ts +161 -0
- package/src/op/stage-hooks.ts +251 -0
- package/src/override-gate-bundle.test.ts +1 -1
- package/src/override-gate.ts +2 -2
- package/src/pr-forge-bundle.test.ts +2 -2
- package/src/pr-forge.test.ts +33 -0
- package/src/pr-forge.ts +347 -35
- package/src/pr-loop-bundle.test.ts +2 -2
- package/src/pr-loop.test.ts +58 -0
- package/src/pr-loop.ts +170 -4
- package/src/pr-post.test.ts +141 -0
- package/src/pr-post.ts +159 -0
- package/src/pr-requires.test.ts +184 -0
- package/src/pr-requires.ts +79 -0
- package/src/review-agent-bundle.test.ts +35 -0
- package/src/stage-hooks-bundle.test.ts +38 -0
- package/src/wave-blocked-bundle.test.ts +25 -0
- package/src/wave-blocked.test.ts +146 -0
- package/src/wave-blocked.ts +271 -0
- package/src/wave-engine.ts +8 -0
- package/src/workspace/__fixtures__/two-member-workspace.ts +2 -2
- package/src/workspace/checks/ci-workflow.test.ts +9 -0
- package/src/workspace/checks/ci-workflow.ts +18 -4
- package/src/workspace/ci-branches.test.ts +62 -0
- package/src/workspace/ci-branches.ts +79 -0
- package/src/workspace/ci-cloud-identity.test.ts +53 -0
- package/src/workspace/ci-cloud-identity.ts +23 -0
- package/src/workspace/ci-init.ts +5 -4
- package/src/workspace/ci-locks.ts +16 -0
- package/src/workspace/ci-run-index.test.ts +2 -1
- package/src/workspace/ci-run.test.ts +107 -8
- package/src/workspace/ci-run.ts +74 -5
- package/src/workspace/ci-workflow-at.ts +23 -0
- package/src/workspace/ci-workflow.ts +13 -2
- package/src/workspace/declaration.schema.json +149 -2
- package/src/workspace/declaration.test.ts +76 -0
- package/src/workspace/declaration.ts +140 -5
- package/src/workspace/identity-gate-rule.ts +1 -1
- package/src/workspace/identity.ts +1 -1
- package/src/workspace/lineage-upgrade.ts +11 -8
- package/src/workspace/member-pipeline.test.ts +29 -0
- package/src/workspace/member-pipeline.ts +30 -0
- package/src/workspace/pr-report.schema.json +94 -3
- package/src/workspace/reason-codes.ts +1 -1
- package/src/workspace/security.ts +4 -0
- package/src/workspace/status-gates.ts +9 -6
- package/src/workspace/status.ts +4 -1
- package/src/workspace/trust/gate-seal.test.ts +1 -1
- package/src/workspace/trust/seal.ts +2 -2
- package/dist/workspace/trust/gate-seal.d.ts.map +0 -1
- /package/dist/{workspace/trust → lifecycle}/gate-seal.js +0 -0
|
@@ -55,7 +55,8 @@ import { samePlanDigest } from "../../lifecycle/plan-digest.js";
|
|
|
55
55
|
import { affectedStacks } from "../../lifecycle/affected.js";
|
|
56
56
|
import { changedFilesBetween, lexiconChangedUnits } from "../../components/changed-files.js";
|
|
57
57
|
import { deriveFanOut, fanOutRegistry } from "../../components/fan-out-support.js";
|
|
58
|
-
import { EarlierWaveNotAppliedError, runFanOut } from "../../components/fan-out-run.js";
|
|
58
|
+
import { EarlierWaveNotAppliedError, EarlierWaveWaitingError, fanOutGateDigest, runFanOut } from "../../components/fan-out-run.js";
|
|
59
|
+
import { parseComponentTargets } from "../../components/capability.js";
|
|
59
60
|
import { readFanOutAttempt, writeFanOutAttempt } from "../../components/fan-out-record.js";
|
|
60
61
|
import { componentInputDigests, progressThatCarries } from "../../components/fan-out-inputs.js";
|
|
61
62
|
import { appliedDigestOf } from "../../components/auto-release.js";
|
|
@@ -248,6 +249,28 @@ async function fanOut(ctx, projectPath, config) {
|
|
|
248
249
|
hint: "They are outside this fan-out. Deploy them another way, or give a component a step that names them.",
|
|
249
250
|
}));
|
|
250
251
|
}
|
|
252
|
+
// #4300: `--target <component>:<address>`, the addresses a component's steps narrow to.
|
|
253
|
+
let targets;
|
|
254
|
+
if (args.targets && args.targets.length > 0) {
|
|
255
|
+
try {
|
|
256
|
+
targets = parseComponentTargets(args.targets, new Set(derived.components.map((c) => c.name)));
|
|
257
|
+
}
|
|
258
|
+
catch (err) {
|
|
259
|
+
console.error(formatError({ message: err instanceof Error ? err.message : String(err) }));
|
|
260
|
+
return 1;
|
|
261
|
+
}
|
|
262
|
+
const running = new Set(derived.plan.order);
|
|
263
|
+
const outside = Object.keys(targets).filter((name) => !running.has(name));
|
|
264
|
+
if (outside.length > 0) {
|
|
265
|
+
console.error(formatError({
|
|
266
|
+
message: `--target names ${outside.join(", ")}, which this fan-out does not run`,
|
|
267
|
+
hint: "A target narrows a component the change selects. Drop the --target, or change what the fan-out selects.",
|
|
268
|
+
}));
|
|
269
|
+
return 1;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// The fan-out's identity: the derivation's digest, with the targets in it (#4300).
|
|
273
|
+
const runDigest = fanOutGateDigest(derived.plan.digest, targets);
|
|
251
274
|
const gate = args.gate ? { op: FAN_OUT_GATE_OP, gate: args.gate } : undefined;
|
|
252
275
|
const waveGate = args.waveGate
|
|
253
276
|
? { op: FAN_OUT_GATE_OP, gate: args.waveGate, ...(args.wave !== undefined ? { only: args.wave } : {}) }
|
|
@@ -267,6 +290,7 @@ async function fanOut(ctx, projectPath, config) {
|
|
|
267
290
|
let priorOutputs = {};
|
|
268
291
|
let priorWaves = [];
|
|
269
292
|
let priorCarried = {};
|
|
293
|
+
let priorFailed = [];
|
|
270
294
|
let progress;
|
|
271
295
|
if (resumePath) {
|
|
272
296
|
let attempt;
|
|
@@ -277,7 +301,7 @@ async function fanOut(ctx, projectPath, config) {
|
|
|
277
301
|
console.error(formatError({ message: `--resume: could not read "${args.resume}": ${err instanceof Error ? err.message : String(err)}` }));
|
|
278
302
|
return 1;
|
|
279
303
|
}
|
|
280
|
-
if (attempt && !samePlanDigest(attempt.digest,
|
|
304
|
+
if (attempt && !samePlanDigest(attempt.digest, runDigest)) {
|
|
281
305
|
console.error(formatWarning({
|
|
282
306
|
message: `--resume: "${args.resume}" records a different fan-out (${attempt.digest || "no digest"}), so its progress does not apply here`,
|
|
283
307
|
hint: "The derivation changed, which makes this a different change. Everything selected will run.",
|
|
@@ -311,6 +335,12 @@ async function fanOut(ctx, projectPath, config) {
|
|
|
311
335
|
priorOutputs = Object.fromEntries(Object.entries(attempt.outputs).filter(([name]) => carried.has(name)));
|
|
312
336
|
progress = { completed: split.carried };
|
|
313
337
|
priorWaves = attempt.waves ?? [];
|
|
338
|
+
// #4291: what failed stays in the record until it applies, so each later wave's job blocks its dependents and runs the rest.
|
|
339
|
+
priorFailed = attempt.failed.filter((name) => !carried.has(name));
|
|
340
|
+
if (waveGate?.only !== undefined) {
|
|
341
|
+
waveGate.failedEarlier = priorFailed;
|
|
342
|
+
waveGate.waitingEarlier = priorWaves.filter((w) => w.status === "gated").map((w) => ({ wave: w.wave, gate: w.gate, components: w.components }));
|
|
343
|
+
}
|
|
314
344
|
// What steps kept per member (#3459), such as a choudoufu root's wave
|
|
315
345
|
// resume file. Each step decides whether what it kept still applies.
|
|
316
346
|
priorCarried = attempt.carried ?? {};
|
|
@@ -370,7 +400,7 @@ async function fanOut(ctx, projectPath, config) {
|
|
|
370
400
|
// repeated command picks up from there instead of redoing them.
|
|
371
401
|
const completedSoFar = new Set(priorCompleted);
|
|
372
402
|
const inputsSoFar = { ...priorInputs };
|
|
373
|
-
const failedSoFar =
|
|
403
|
+
const failedSoFar = new Set(priorFailed);
|
|
374
404
|
const outputsSoFar = { ...priorOutputs };
|
|
375
405
|
let wavesSoFar = priorWaves;
|
|
376
406
|
const carriedSoFar = { ...priorCarried };
|
|
@@ -378,7 +408,7 @@ async function fanOut(ctx, projectPath, config) {
|
|
|
378
408
|
if (!resumePath)
|
|
379
409
|
return;
|
|
380
410
|
const attempt = {
|
|
381
|
-
digest:
|
|
411
|
+
digest: runDigest,
|
|
382
412
|
completed: [...completedSoFar].sort(),
|
|
383
413
|
failed: [...failedSoFar].sort(),
|
|
384
414
|
outputs: outputsSoFar,
|
|
@@ -398,6 +428,7 @@ async function fanOut(ctx, projectPath, config) {
|
|
|
398
428
|
env,
|
|
399
429
|
releaseIdentity: () => (revision ? { revision } : {}),
|
|
400
430
|
componentOutputs,
|
|
431
|
+
...(targets ? { targets } : {}),
|
|
401
432
|
...(gate ? { gate } : {}),
|
|
402
433
|
...(waveGate ? { waveGate } : {}),
|
|
403
434
|
...(waveGate
|
|
@@ -429,9 +460,10 @@ async function fanOut(ctx, projectPath, config) {
|
|
|
429
460
|
inputsSoFar[settled.component] = inputsNow[settled.component];
|
|
430
461
|
if (outputs)
|
|
431
462
|
outputsSoFar[settled.component] = outputs;
|
|
463
|
+
failedSoFar.delete(settled.component);
|
|
432
464
|
}
|
|
433
465
|
else if (settled.status === "fail") {
|
|
434
|
-
failedSoFar.
|
|
466
|
+
failedSoFar.add(settled.component);
|
|
435
467
|
}
|
|
436
468
|
recordAttempt();
|
|
437
469
|
},
|
|
@@ -442,6 +474,11 @@ async function fanOut(ctx, projectPath, config) {
|
|
|
442
474
|
catch (err) {
|
|
443
475
|
events?.settle("failed", err instanceof Error ? err.message : String(err));
|
|
444
476
|
// `--wave <n>` reached before an earlier wave applied: nothing ran.
|
|
477
|
+
// #4291: an earlier wave waits at its gate, so this one waits too.
|
|
478
|
+
if (err instanceof EarlierWaveWaitingError) {
|
|
479
|
+
console.error(formatInfo(err.message));
|
|
480
|
+
return GATED_EXIT_CODE;
|
|
481
|
+
}
|
|
445
482
|
if (err instanceof EarlierWaveNotAppliedError) {
|
|
446
483
|
console.error(formatError({ message: err.message }));
|
|
447
484
|
return 1;
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { type GateResolutionRecord, type PendingGateRecord } from "../../lifecycle/gate-ledger.js";
|
|
21
21
|
import { type AppliedGateLine, type FinishedGateRecord } from "../../lifecycle/gate-applied.js";
|
|
22
|
+
import { type GateQuorumJudge } from "../../op/gate-quorum.js";
|
|
22
23
|
import { type GateResumeOutcome } from "../../op/gate-resume.js";
|
|
23
24
|
import { type GateResumeCandidate, type GateResumeReason } from "../../op/gate-resume-plan.js";
|
|
24
25
|
import type { GateRunLocator } from "../../op/gate-run-locator.js";
|
|
@@ -77,6 +78,8 @@ export interface ResumeGateOptions {
|
|
|
77
78
|
/** The lines of `_gates/<op>/applied.jsonl` (#3858). Read from `chant/lifecycle` by {@link resumeOpGates} when not given. */
|
|
78
79
|
applied?: readonly AppliedGateLine[];
|
|
79
80
|
finished?: readonly FinishedGateRecord[];
|
|
81
|
+
/** Proves approvers for approval rules that name roles (#4290). Read from the trust policy at base when a fact needs one and none is given. */
|
|
82
|
+
judge?: GateQuorumJudge;
|
|
80
83
|
}
|
|
81
84
|
/** Every question the workspace at `cwd` lists, by id, the lifecycle ledger's included. Empty when the workspace can't be read. */
|
|
82
85
|
export declare function workspaceQuestions(cwd: string): Promise<Map<string, PointQuestionState>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate-resume.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/gate-resume.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACvB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAyC,KAAK,eAAe,EAAE,KAAK,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGpI,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAmB,KAAK,mBAAmB,EAAE,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,MAAM,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACtC;;;OAGG;IACH,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,GAAG,cAAc,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAC;IAClG,0FAA0F;IAC1F,MAAM,EAAE,gBAAgB,CAAC;IACzB;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,KAAK,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACrC,iGAAiG;IACjG,OAAO,CAAC,EAAE,mBAAmB,CAAC,SAAS,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAClE;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACpD,6HAA6H;IAC7H,OAAO,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACrC,QAAQ,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"gate-resume.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/gate-resume.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,9 +21,20 @@ import { GATES_DIR, readGateLedger, } from "../../lifecycle/gate-ledger.js";
|
|
|
21
21
|
import { appliedGatePath, parseAppliedGateFile } from "../../lifecycle/gate-applied.js";
|
|
22
22
|
import { listFilesInDir, pushLifecycleStatus, readBlobFromPath, requireLifecycleLedger } from "../../lifecycle/git.js";
|
|
23
23
|
import { parseGateSubject } from "../../op/gate-point.js";
|
|
24
|
+
import { quorumRules, ruleNeedsProof, trustQuorumJudge } from "../../op/gate-quorum.js";
|
|
24
25
|
import { resolutionAnswering, resumeGateRun, resumeTokenFromEnv, } from "../../op/gate-resume.js";
|
|
25
26
|
import { planGateResumes } from "../../op/gate-resume-plan.js";
|
|
26
27
|
import { formatError, formatInfo, formatSuccess, formatWarning } from "../format.js";
|
|
28
|
+
/** The judge a fact's approval rules are met by (#4290): the trust policy at base, read only when a rule names roles or notAuthor. */
|
|
29
|
+
async function judgeFor(pending, opts) {
|
|
30
|
+
if (opts.judge)
|
|
31
|
+
return opts.judge;
|
|
32
|
+
if (!pending.some((p) => quorumRules(p.approval?.quorum).some(ruleNeedsProof)))
|
|
33
|
+
return undefined;
|
|
34
|
+
const { scopeSource } = await import("../../workspace/write-scope.js");
|
|
35
|
+
const { policy } = scopeSource(process.cwd());
|
|
36
|
+
return trustQuorumJudge({ signers: policy, roles: policy.roles });
|
|
37
|
+
}
|
|
27
38
|
/** Every question the workspace at `cwd` lists, by id, the lifecycle ledger's included. Empty when the workspace can't be read. */
|
|
28
39
|
export async function workspaceQuestions(cwd) {
|
|
29
40
|
const { workspacePoints } = await import("../../workspace/points-cli.js");
|
|
@@ -34,6 +45,7 @@ export async function workspaceQuestions(cwd) {
|
|
|
34
45
|
}
|
|
35
46
|
/** Resume the job behind one standing pending fact, when an approval answers it and is not spent. */
|
|
36
47
|
export async function resumeStanding(standing, resolutions, opts = {}, pending = [standing]) {
|
|
48
|
+
const judge = await judgeFor([standing], opts);
|
|
37
49
|
const [candidate] = planGateResumes({
|
|
38
50
|
op: standing.op,
|
|
39
51
|
pending: [standing],
|
|
@@ -41,7 +53,7 @@ export async function resumeStanding(standing, resolutions, opts = {}, pending =
|
|
|
41
53
|
applied: opts.applied ?? [],
|
|
42
54
|
now: standing.timestamp,
|
|
43
55
|
...(opts.questions ? { questions: opts.questions } : {}),
|
|
44
|
-
answering: (r, s) => resolutionAnswering(r, s, pending),
|
|
56
|
+
answering: (r, s) => resolutionAnswering(r, s, pending, judge),
|
|
45
57
|
});
|
|
46
58
|
return resumeCandidate(candidate, opts);
|
|
47
59
|
}
|
|
@@ -127,6 +139,7 @@ export async function resumeOpGates(op, gate, opts = {}) {
|
|
|
127
139
|
const now = opts.now ?? new Date().toISOString();
|
|
128
140
|
// Read the workspace's questions once, and only when a fact waits on one (#3835).
|
|
129
141
|
const questions = opts.questions ?? (ledger.pending.some((p) => p.answer) ? await workspaceQuestions(process.cwd()) : undefined);
|
|
142
|
+
const judge = await judgeFor(ledger.pending, opts);
|
|
130
143
|
const candidates = planGateResumes({
|
|
131
144
|
op,
|
|
132
145
|
pending: ledger.pending,
|
|
@@ -136,7 +149,7 @@ export async function resumeOpGates(op, gate, opts = {}) {
|
|
|
136
149
|
now,
|
|
137
150
|
...(gate !== undefined ? { gate } : {}),
|
|
138
151
|
...(questions ? { questions } : {}),
|
|
139
|
-
answering: resolutionAnswering,
|
|
152
|
+
answering: (r, s, p) => resolutionAnswering(r, s, p, judge),
|
|
140
153
|
});
|
|
141
154
|
const reports = [];
|
|
142
155
|
for (const c of candidates)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CommandContext } from "../registry.js";
|
|
2
|
+
/** The status `chant lock --head <sha> --forge <kind>` posts. */
|
|
3
|
+
export declare const LOCK_STATUS_CONTEXT = "chant/lock";
|
|
4
|
+
/** chant lock */
|
|
5
|
+
export declare function runLock(ctx: CommandContext): Promise<number>;
|
|
6
|
+
/** chant unlock */
|
|
7
|
+
export declare function runUnlock(ctx: CommandContext): Promise<number>;
|
|
8
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/lock.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAkBlD,iEAAiE;AACjE,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAwDhD,iBAAiB;AACjB,wBAAsB,OAAO,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA6ElE;AAED,mBAAmB;AACnB,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBpE"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant lock` and `chant unlock` (#4293): take, list and release the target
|
|
3
|
+
* locks a pull request holds (../../lifecycle/target-lock.ts).
|
|
4
|
+
*
|
|
5
|
+
* ```
|
|
6
|
+
* chant lock <target>... --pr <n> [--stage plan|apply|lock] [--head <sha>] [--by <who>] [--gate <name>] [--forge <kind>] [--json]
|
|
7
|
+
* chant lock --from-report <pr-plan.json> --pr <n> [--stage plan] [--forge <kind>] [--json]
|
|
8
|
+
* chant lock --list [--gate <name>] [--json]
|
|
9
|
+
* chant unlock --pr <n> [<target>...] [--gate <name>] [--json]
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* The targets are names: the components `chant components pr-plan` selects,
|
|
13
|
+
* which are what `pr-apply` checks under `ci.locks`, or any other name a
|
|
14
|
+
* pipeline locks by. `--from-report` reads them from a pull-request report
|
|
15
|
+
* (`selection.waves`), so a lock job that runs the target branch's code can
|
|
16
|
+
* lock what the plan job, which ran the pull request's code, selected.
|
|
17
|
+
*
|
|
18
|
+
* `--forge` reads which holders are still open, so a lock whose pull
|
|
19
|
+
* request merged or closed is taken over, and, with `--head`, posts the
|
|
20
|
+
* `chant/lock` status on that commit. Without it every lock counts as held.
|
|
21
|
+
*
|
|
22
|
+
* Exit 0 when the locks are taken or released, 3 when another open pull
|
|
23
|
+
* request holds a target (nothing is locked), 1 on an error.
|
|
24
|
+
*/
|
|
25
|
+
import { readFileSync } from "node:fs";
|
|
26
|
+
import { resolve } from "node:path";
|
|
27
|
+
import { formatError, formatInfo, formatSuccess, formatWarning } from "../format.js";
|
|
28
|
+
import { FORGE_KINDS, forgeFromEnv } from "../../pr-forge.js";
|
|
29
|
+
import { PR_APPLY_GATE } from "../../pr-loop.js";
|
|
30
|
+
import { describeHeldTargetLocks, readTargetLocks, releaseTargetLocks, takeTargetLocks, targetLockGateProblem, targetLockPathAt, TARGET_LOCK_STAGES, } from "../../lifecycle/target-lock.js";
|
|
31
|
+
/** The exit for a target another open pull request holds: 3, as a run stopped at a gate exits (`GATED_EXIT_CODE` in ./run.ts). */
|
|
32
|
+
const HELD_EXIT_CODE = 3;
|
|
33
|
+
/** The status `chant lock --head <sha> --forge <kind>` posts. */
|
|
34
|
+
export const LOCK_STATUS_CONTEXT = "chant/lock";
|
|
35
|
+
const fail = (message, hint) => {
|
|
36
|
+
console.error(formatError({ message, ...(hint ? { hint } : {}) }));
|
|
37
|
+
return 1;
|
|
38
|
+
};
|
|
39
|
+
/** The targets named after the command word. */
|
|
40
|
+
function targetsOf(ctx) {
|
|
41
|
+
return (ctx.args.positionals ?? []).filter((t) => t !== "");
|
|
42
|
+
}
|
|
43
|
+
function gateOf(ctx) {
|
|
44
|
+
const gate = ctx.args.gate ?? PR_APPLY_GATE;
|
|
45
|
+
const problem = targetLockGateProblem(gate);
|
|
46
|
+
return problem ? { error: problem } : gate;
|
|
47
|
+
}
|
|
48
|
+
function forgeOf(ctx) {
|
|
49
|
+
const kind = ctx.args.forge;
|
|
50
|
+
if (kind === undefined)
|
|
51
|
+
return undefined;
|
|
52
|
+
if (!FORGE_KINDS.includes(kind))
|
|
53
|
+
return { error: `--forge ${kind} is not one of ${FORGE_KINDS.join(", ")}` };
|
|
54
|
+
try {
|
|
55
|
+
return forgeFromEnv(kind);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const prName = (forge) => (n) => (forge?.kind === "gitlab" ? `merge request !${n}` : `pull request ${n}`);
|
|
62
|
+
/** The components a `pr-plan.json` selected, and the head it planned, checked against `--pr`. */
|
|
63
|
+
function fromReport(file, pr) {
|
|
64
|
+
let doc;
|
|
65
|
+
try {
|
|
66
|
+
doc = JSON.parse(readFileSync(resolve(file), "utf8"));
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
return { error: `--from-report: could not read "${file}": ${err instanceof Error ? err.message : String(err)}` };
|
|
70
|
+
}
|
|
71
|
+
if (doc.pr !== pr)
|
|
72
|
+
return { error: `--from-report: "${file}" is the report of ${doc.pr === null || doc.pr === undefined ? "no pull request" : `pull request ${String(doc.pr)}`}, not ${pr}` };
|
|
73
|
+
const waves = doc.selection?.waves;
|
|
74
|
+
if (!Array.isArray(waves) || !waves.every((w) => Array.isArray(w) && w.every((n) => typeof n === "string"))) {
|
|
75
|
+
return { error: `--from-report: "${file}" has no selection.waves; pass a pr-plan.json that chant components pr-plan wrote` };
|
|
76
|
+
}
|
|
77
|
+
return { targets: waves.flat(), ...(typeof doc.head === "string" ? { head: doc.head } : {}) };
|
|
78
|
+
}
|
|
79
|
+
async function postStatus(forge, head, state, description) {
|
|
80
|
+
if (!forge || !head)
|
|
81
|
+
return;
|
|
82
|
+
try {
|
|
83
|
+
await forge.setStatus(head, { context: LOCK_STATUS_CONTEXT, state, description: description.length > 140 ? `${description.slice(0, 137)}...` : description, ...(forge.runUrl ? { url: forge.runUrl } : {}) });
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
console.error(formatWarning({ message: `could not post ${LOCK_STATUS_CONTEXT} on ${head}: ${err instanceof Error ? err.message : String(err)}` }));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** chant lock */
|
|
90
|
+
export async function runLock(ctx) {
|
|
91
|
+
const { args } = ctx;
|
|
92
|
+
const gate = gateOf(ctx);
|
|
93
|
+
if (typeof gate !== "string")
|
|
94
|
+
return fail(gate.error);
|
|
95
|
+
if (args.list) {
|
|
96
|
+
if (targetsOf(ctx).length > 0 || args.pr !== undefined)
|
|
97
|
+
return fail("--list prints every lock; drop the targets and --pr");
|
|
98
|
+
let file;
|
|
99
|
+
try {
|
|
100
|
+
file = await readTargetLocks(gate);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
104
|
+
}
|
|
105
|
+
const rows = Object.entries(file.locks).sort(([a], [b]) => a.localeCompare(b));
|
|
106
|
+
if (args.json) {
|
|
107
|
+
console.log(JSON.stringify({ gate, path: await targetLockPathAt(gate), locks: Object.fromEntries(rows) }, null, 2));
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
if (rows.length === 0)
|
|
111
|
+
console.error(formatInfo(`no target locks on ${gate}`));
|
|
112
|
+
for (const [target, l] of rows)
|
|
113
|
+
console.log(`${target}\tpull request ${l.pr}\t${l.stage}\t${l.by}\t${l.at}`);
|
|
114
|
+
return 0;
|
|
115
|
+
}
|
|
116
|
+
if (args.pr === undefined)
|
|
117
|
+
return fail("chant lock needs the pull or merge request that holds the locks: --pr <n>", "chant lock --list prints the locks held now.");
|
|
118
|
+
const stage = (args.lockStage ?? "lock");
|
|
119
|
+
if (!TARGET_LOCK_STAGES.includes(stage))
|
|
120
|
+
return fail(`--stage ${args.lockStage} is not one of ${TARGET_LOCK_STAGES.join(", ")}`);
|
|
121
|
+
let targets = targetsOf(ctx);
|
|
122
|
+
let head = args.head;
|
|
123
|
+
if (args.fromReport) {
|
|
124
|
+
if (targets.length > 0)
|
|
125
|
+
return fail("--from-report reads the targets from the report; drop the names");
|
|
126
|
+
const read = fromReport(args.fromReport, args.pr);
|
|
127
|
+
if ("error" in read)
|
|
128
|
+
return fail(read.error);
|
|
129
|
+
targets = read.targets;
|
|
130
|
+
head ?? (head = read.head);
|
|
131
|
+
}
|
|
132
|
+
else if (targets.length === 0 && stage !== "plan") {
|
|
133
|
+
return fail("chant lock needs the targets to lock: chant lock <target>... --pr <n>", "Or --from-report <pr-plan.json> to lock what a plan selected.");
|
|
134
|
+
}
|
|
135
|
+
const forge = forgeOf(ctx);
|
|
136
|
+
if (forge && "error" in forge)
|
|
137
|
+
return fail(forge.error);
|
|
138
|
+
const isOpen = forge?.pullRequestOpen?.bind(forge);
|
|
139
|
+
const by = args.by ?? process.env.GITHUB_ACTOR ?? process.env.GITLAB_USER_LOGIN ?? process.env.USER ?? "unknown";
|
|
140
|
+
const name = prName(forge);
|
|
141
|
+
let result;
|
|
142
|
+
try {
|
|
143
|
+
result = await takeTargetLocks(gate, targets, { pr: args.pr, by, at: new Date().toISOString(), stage, ...(head ? { head } : {}) }, { ...(isOpen ? { isOpen } : {}), releaseStalePlan: stage === "plan" });
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
return fail(`could not take the locks of ${gate}: ${err instanceof Error ? err.message : String(err)}`);
|
|
147
|
+
}
|
|
148
|
+
if (!result.ok) {
|
|
149
|
+
const holders = [...new Set(result.held.map((h) => h.pr))].sort((a, b) => a - b);
|
|
150
|
+
const text = `${describeHeldTargetLocks(result.held, name)}, so ${name(args.pr)} is not locked`;
|
|
151
|
+
await postStatus(forge, head, "failure", `locked by ${holders.map(name).join(", ")}: ${result.held.map((h) => h.target).join(", ")}`);
|
|
152
|
+
if (args.json)
|
|
153
|
+
console.log(JSON.stringify({ gate, pr: args.pr, ok: false, held: result.held }, null, 2));
|
|
154
|
+
console.error(formatError({ message: text, hint: `Lock again once ${holders.length === 1 ? "it merges or closes" : "they merge or close"}, or after chant unlock --pr ${holders[0]}.${isOpen ? "" : " Without --forge every lock counts as held."}` }));
|
|
155
|
+
return HELD_EXIT_CODE;
|
|
156
|
+
}
|
|
157
|
+
const holds = result.taken.length > 0 ? `holds ${result.taken.join(", ")}` : "reaches no target";
|
|
158
|
+
await postStatus(forge, head, "success", holds);
|
|
159
|
+
if (args.json) {
|
|
160
|
+
console.log(JSON.stringify({ gate, pr: args.pr, ok: true, stage, taken: result.taken, released: result.released, expired: result.expired, pushed: result.pushed }, null, 2));
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
console.error(formatSuccess(`${name(args.pr)} ${holds} on ${gate}`));
|
|
164
|
+
if (result.released.length > 0)
|
|
165
|
+
console.error(formatInfo(`released ${result.released.join(", ")}, which it no longer reaches`));
|
|
166
|
+
if (result.expired.length > 0)
|
|
167
|
+
console.error(formatInfo(`took over ${result.expired.join(", ")} from pull requests that merged or closed`));
|
|
168
|
+
if (!result.pushed && (result.taken.length > 0 || result.released.length > 0))
|
|
169
|
+
console.error(formatInfo("chant/lifecycle was not pushed: this repository has no remote, or recording is off"));
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
/** chant unlock */
|
|
173
|
+
export async function runUnlock(ctx) {
|
|
174
|
+
const { args } = ctx;
|
|
175
|
+
const gate = gateOf(ctx);
|
|
176
|
+
if (typeof gate !== "string")
|
|
177
|
+
return fail(gate.error);
|
|
178
|
+
if (args.pr === undefined)
|
|
179
|
+
return fail("chant unlock needs the pull or merge request whose locks it releases: --pr <n>");
|
|
180
|
+
const targets = targetsOf(ctx);
|
|
181
|
+
let result;
|
|
182
|
+
try {
|
|
183
|
+
result = await releaseTargetLocks(gate, args.pr, targets.length > 0 ? { targets } : {});
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
return fail(`could not release the locks of ${gate}: ${err instanceof Error ? err.message : String(err)}`);
|
|
187
|
+
}
|
|
188
|
+
if (args.json) {
|
|
189
|
+
console.log(JSON.stringify({ gate, pr: args.pr, released: result.released, pushed: result.pushed }, null, 2));
|
|
190
|
+
return 0;
|
|
191
|
+
}
|
|
192
|
+
if (result.released.length === 0)
|
|
193
|
+
console.error(formatInfo(`pull request ${args.pr} holds no lock${targets.length > 0 ? ` on ${targets.join(", ")}` : ""} on ${gate}`));
|
|
194
|
+
else
|
|
195
|
+
console.error(formatSuccess(`released the locks pull request ${args.pr} held on ${result.released.join(", ")}`));
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant components pr-env up|down|sweep` (#4298): an environment per open
|
|
3
|
+
* pull request, `pr-<n>`, derived from `ci.prEnvironment.template`
|
|
4
|
+
* (../../components/pr-environment.ts says what each does and why).
|
|
5
|
+
*
|
|
6
|
+
* ```
|
|
7
|
+
* chant components pr-env up --base <ref> --pr <n> [--forge <kind>] [--member <name>] [--output <dir>] [--no-push]
|
|
8
|
+
* chant approve pr-<n> pr-env --plan <digest> --approver <you> --sign
|
|
9
|
+
* chant components pr-env down --pr <n> [--reason closed|expired] [--forge <kind>] [--member <name>]
|
|
10
|
+
* chant components pr-env post --pr <n> --head <sha> [--result <r>] --forge <kind>
|
|
11
|
+
* chant components pr-env sweep [--forge <kind>]
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* In the generated jobs (#4295) `up` runs the change's code with no forge
|
|
15
|
+
* token, so it passes `--no-push` and hands its report and record on, and
|
|
16
|
+
* `post` runs in a fresh job at the target branch's commit, appends the
|
|
17
|
+
* record and posts the note.
|
|
18
|
+
*
|
|
19
|
+
* `up` prepares the change as `pr-plan` does, with `--env pr-<n>` and the
|
|
20
|
+
* build parameter `ownership.env` reads set to `pr-<n>`, so every resource
|
|
21
|
+
* the apply creates carries that environment's marker. It applies only when
|
|
22
|
+
* the plan is empty or an approval of its digest stands under gate `pr-env`,
|
|
23
|
+
* since a non-empty plan never applies with no person approving (ws-080).
|
|
24
|
+
*
|
|
25
|
+
* `down` reads the environment's record on chant/lifecycle, checks it names
|
|
26
|
+
* this pull request's environment, and tears down what `planTeardown` finds
|
|
27
|
+
* under the record's marker, after dropping any entry whose marker is not
|
|
28
|
+
* exactly that one. No gate holds it: closing the pull request is the
|
|
29
|
+
* decision, as is the TTL the base set for `sweep`.
|
|
30
|
+
*/
|
|
31
|
+
import type { CommandContext } from "../registry.js";
|
|
32
|
+
/** chant components pr-env <up|down|sweep> */
|
|
33
|
+
export declare function runComponentsPrEnv(ctx: CommandContext): Promise<number>;
|
|
34
|
+
//# sourceMappingURL=pr-env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pr-env.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/pr-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AA0CH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AASlD,8CAA8C;AAC9C,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAO7E"}
|