@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
package/dist/op/op-waves-run.js
CHANGED
|
@@ -48,27 +48,20 @@
|
|
|
48
48
|
* question is answered, `chant run resume --op <name>` starts the job again
|
|
49
49
|
* (`pointGateResumable`, #3835).
|
|
50
50
|
*/
|
|
51
|
-
import { execFileSync
|
|
51
|
+
import { execFileSync } from "node:child_process";
|
|
52
52
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
53
53
|
import { dirname, isAbsolute, join, posix } from "node:path";
|
|
54
54
|
import { decideWaveGate, runWaveLoop, waveDigestMembers, waveSetDigest, writeWaveRecordFile, } from "../wave-engine.js";
|
|
55
55
|
import { approveCommand, evaluateGate, gitGateLedgerPort } from "./gate.js";
|
|
56
56
|
import { isApprovalSpent } from "../lifecycle/gate-applied.js";
|
|
57
|
+
import { samePlanDigest } from "../lifecycle/plan-digest.js";
|
|
57
58
|
import { isLifecycleLocalNote } from "../lifecycle/local-note.js";
|
|
58
59
|
import { ciRunIdFromEnv } from "./gate-run-locator.js";
|
|
60
|
+
import { quorumProblems, quorumRuleLine, quorumRulesFor, quorumWaiting, ruleNeedsProof, samePrincipal, trustQuorumJudge, } from "./gate-quorum.js";
|
|
59
61
|
import { OP_WAVE_DEFAULT_APPLY, OP_WAVE_GATE_POLICIES, OP_WAVE_HOOK_MOMENTS, GATE_APPROVAL_SOURCES, assertOpWaveHooks, assertOpWavesSpec, opWaveApplyHooksRunBy, opWaveGateName, opWaveHooksAt, opWavePointAt, opWavePointProblems, opWaveRecordPath, opWaveGroupRuns, opWaveGroupsProblem, opWaveShare, } from "./op-waves.js";
|
|
60
|
-
|
|
61
|
-
export
|
|
62
|
-
"version", "kind", "op", "gate", "timestamp", "expiresAt", "planDigest", "runId", "url", "resume",
|
|
63
|
-
"environment", "origin", "neverOverMcp", "approval", "approvalMode", "answer", "wave", "heldBy",
|
|
64
|
-
];
|
|
62
|
+
import { OP_WAVE_PENDING_RESERVED, opWaveArgv, pendingFields, readHoldRequest, runHookList, runStageHooks, spawnExec, } from "./stage-hooks.js";
|
|
63
|
+
export { OP_WAVE_PENDING_RESERVED, opWaveArgv, runStageHooks };
|
|
65
64
|
export const OP_WAVE_APPLY_OUTCOMES = ["stood-down", "held", "replan"];
|
|
66
|
-
const spawnExec = (argv, cwd, env) => {
|
|
67
|
-
const result = spawnSync(argv[0], argv.slice(1), { cwd, stdio: "inherit", ...(env ? { env: { ...process.env, ...env } } : {}) });
|
|
68
|
-
if (result.error)
|
|
69
|
-
throw result.error;
|
|
70
|
-
return result.status ?? 1;
|
|
71
|
-
};
|
|
72
65
|
const gitShowAtBase = (base, path, cwd) => {
|
|
73
66
|
let sha;
|
|
74
67
|
try {
|
|
@@ -126,7 +119,7 @@ export function readOpWavePolicy(specFile, name, base, cwd, show = gitShowAtBase
|
|
|
126
119
|
const spend = spec.approvals?.spend === true;
|
|
127
120
|
const neverOverMcp = spec.neverOverMcp === true;
|
|
128
121
|
// #3836: the gate's decision point, the spec's for a wave the base does not have yet.
|
|
129
|
-
const asks = pointAtBase(spec, name, `${specFile} at ${sha.slice(0, 12)}`);
|
|
122
|
+
const asks = { ...pointAtBase(spec, name, `${specFile} at ${sha.slice(0, 12)}`), ...quorumAtBase(spec, `${specFile} at ${sha.slice(0, 12)}`) };
|
|
130
123
|
const wave = spec.waves.find((w) => w.name === name);
|
|
131
124
|
if (!wave)
|
|
132
125
|
return { ...absent, spend, neverOverMcp, ...asks };
|
|
@@ -146,6 +139,16 @@ export function readOpWavePolicy(specFile, name, base, cwd, show = gitShowAtBase
|
|
|
146
139
|
}
|
|
147
140
|
return rules;
|
|
148
141
|
}
|
|
142
|
+
/** The approval rules at base (#4290), or why they cannot be read. A spec at base with none has none. */
|
|
143
|
+
function quorumAtBase(spec, where) {
|
|
144
|
+
const raw = spec.approvals?.quorum;
|
|
145
|
+
if (raw === undefined)
|
|
146
|
+
return {};
|
|
147
|
+
const problems = Array.isArray(raw) ? quorumProblems(raw, "approvals.quorum", { notAuthor: true }) : ["`approvals.quorum` must be a list of rules"];
|
|
148
|
+
if (problems.length > 0)
|
|
149
|
+
return { quorumRefused: `the approval rules in ${where} cannot be read (${problems.join("; ")})` };
|
|
150
|
+
return raw.length > 0 ? { quorum: raw } : {};
|
|
151
|
+
}
|
|
149
152
|
/** The point wave `name` asks at base (#3836), or why it cannot be asked. */
|
|
150
153
|
function pointAtBase(spec, name, where) {
|
|
151
154
|
const raw = spec.waves.find((w) => w.name === name)?.point ?? spec.point;
|
|
@@ -156,18 +159,6 @@ function pointAtBase(spec, name, where) {
|
|
|
156
159
|
return { pointRefused: `the decision point at base cannot be asked (${where}: ${problems.join("; ")})` };
|
|
157
160
|
return { point: opWavePointAt(spec, name) };
|
|
158
161
|
}
|
|
159
|
-
/** Fill one argv template. A placeholder whose value is not given is left as it is. */
|
|
160
|
-
export function opWaveArgv(template, values) {
|
|
161
|
-
const pairs = [
|
|
162
|
-
["{op}", values.op],
|
|
163
|
-
["{target}", values.target],
|
|
164
|
-
["{wave}", values.wave],
|
|
165
|
-
["{plans}", values.plans],
|
|
166
|
-
["{plan}", values.plan],
|
|
167
|
-
["{runs}", values.runs],
|
|
168
|
-
];
|
|
169
|
-
return template.map((part) => pairs.reduce((out, [key, value]) => (value === undefined ? out : out.replaceAll(key, value)), part));
|
|
170
|
-
}
|
|
171
162
|
/** Where a run's plan file goes. */
|
|
172
163
|
export function opWavePlanPath(spec, wave, target) {
|
|
173
164
|
return `.chant/op-waves/${spec.name}/wave-${wave}/${target.replace(/[^A-Za-z0-9._-]/g, "_")}.plan.json`;
|
|
@@ -200,11 +191,13 @@ function planRun(opts, waveName, run) {
|
|
|
200
191
|
return member(run.target, op, file);
|
|
201
192
|
}
|
|
202
193
|
function member(target, op, file) {
|
|
203
|
-
const denied
|
|
204
|
-
|
|
205
|
-
|
|
194
|
+
for (const key of ["denied", "held"]) {
|
|
195
|
+
const value = file[key];
|
|
196
|
+
if (value === undefined)
|
|
197
|
+
continue;
|
|
198
|
+
const d = value;
|
|
206
199
|
if (!d || typeof d !== "object" || !Array.isArray(d.rules) || d.rules.length === 0 || !d.rules.every((r) => typeof r === "string" && r.trim() !== "") || (d.reason !== undefined && typeof d.reason !== "string")) {
|
|
207
|
-
throw new Error(`the plan of ${target} has
|
|
200
|
+
throw new Error(`the plan of ${target} has ${key} ${JSON.stringify(value)}; ${key === "denied" ? "a denial" : "a hold"} is { "rules": ["<rule id>", ...], "reason"?: "<text>" }`);
|
|
208
201
|
}
|
|
209
202
|
}
|
|
210
203
|
return {
|
|
@@ -214,6 +207,7 @@ function member(target, op, file) {
|
|
|
214
207
|
...(file.destructive === true ? { destructive: true } : {}),
|
|
215
208
|
...(file.empty === true ? { empty: true } : {}),
|
|
216
209
|
...(file.denied ? { denied: { rules: file.denied.rules, ...(file.denied.reason ? { reason: file.denied.reason } : {}) } } : {}),
|
|
210
|
+
...(file.held ? { held: { rules: [...new Set(file.held.rules)].sort(), ...(file.held.reason ? { reason: file.held.reason } : {}) } } : {}),
|
|
217
211
|
};
|
|
218
212
|
}
|
|
219
213
|
/** Write the `{runs}` file: the runs, with their plans once planned. */
|
|
@@ -295,7 +289,7 @@ function planRuns(at, runs, discover = false) {
|
|
|
295
289
|
const members = runs.map((run) => {
|
|
296
290
|
const m = member(run.target, run.op ?? spec.op, byTarget.get(run.target));
|
|
297
291
|
const path = join(cwd, opWavePlanPath(spec, k, run.target));
|
|
298
|
-
writeFileSync(path, JSON.stringify({ planDigest: m.planDigest, ...(m.destructive ? { destructive: true } : {}), ...(m.empty ? { empty: true } : {}), ...(m.denied ? { denied: m.denied } : {}) }) + "\n");
|
|
292
|
+
writeFileSync(path, JSON.stringify({ planDigest: m.planDigest, ...(m.destructive ? { destructive: true } : {}), ...(m.empty ? { empty: true } : {}), ...(m.denied ? { denied: m.denied } : {}), ...(m.held ? { held: m.held } : {}) }) + "\n");
|
|
299
293
|
return m;
|
|
300
294
|
});
|
|
301
295
|
const overriders = Array.isArray(parsed) ? undefined : parsed.overriders;
|
|
@@ -335,19 +329,6 @@ function extraMembers(value, targets, where) {
|
|
|
335
329
|
}
|
|
336
330
|
return out.length > 0 ? out : undefined;
|
|
337
331
|
}
|
|
338
|
-
/** A plans file's `pending` (#3821): an object whose keys chant does not write itself, `description` aside. Undefined when absent or empty. */
|
|
339
|
-
function pendingFields(value, where) {
|
|
340
|
-
if (value === undefined)
|
|
341
|
-
return undefined;
|
|
342
|
-
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
343
|
-
throw new Error(`${where}: \`pending\` must be an object of fields for the pending fact`);
|
|
344
|
-
const reserved = Object.keys(value).filter((key) => OP_WAVE_PENDING_RESERVED.includes(key));
|
|
345
|
-
if (reserved.length > 0)
|
|
346
|
-
throw new Error(`${where}: \`pending\` sets ${reserved.join(", ")}, which chant writes itself`);
|
|
347
|
-
if ("description" in value && typeof value.description !== "string")
|
|
348
|
-
throw new Error(`${where}: pending.description must be a string`);
|
|
349
|
-
return Object.keys(value).length > 0 ? value : undefined;
|
|
350
|
-
}
|
|
351
332
|
/**
|
|
352
333
|
* A wave's set digest (#3821): {@link waveSetDigest} over its runs' plans,
|
|
353
334
|
* or under `digest.over: "changing"` over the runs whose plan is not empty,
|
|
@@ -357,81 +338,6 @@ function pendingFields(value, where) {
|
|
|
357
338
|
export function opWaveSetDigest(spec, members, extras = []) {
|
|
358
339
|
return waveSetDigest(waveDigestMembers(members, { ...(spec.digest?.over ? { over: spec.digest.over } : {}), extras }));
|
|
359
340
|
}
|
|
360
|
-
/** Read the hold file a hook wrote (#3821), or undefined when it wrote none or one that names no gate and digest. */
|
|
361
|
-
function readHoldRequest(path, log, label) {
|
|
362
|
-
if (!existsSync(path))
|
|
363
|
-
return undefined;
|
|
364
|
-
try {
|
|
365
|
-
const r = JSON.parse(readFileSync(path, "utf-8"));
|
|
366
|
-
if (typeof r.gate !== "string" || r.gate === "" || typeof r.digest !== "string" || r.digest === "")
|
|
367
|
-
throw new Error("it names no gate and digest");
|
|
368
|
-
if (r.op !== undefined && (typeof r.op !== "string" || r.op === ""))
|
|
369
|
-
throw new Error("its op is not a name");
|
|
370
|
-
return { gate: r.gate, digest: r.digest, ...(r.op ? { op: r.op } : {}), ...(typeof r.description === "string" ? { description: r.description } : {}), ...(r.pending ? { pending: pendingFields(r.pending, `the hold ${label} wrote`) ?? {} } : {}) };
|
|
371
|
-
}
|
|
372
|
-
catch (err) {
|
|
373
|
-
log(`${label} wrote a hold file that cannot be read (${err instanceof Error ? err.message : String(err)}), so it holds the wave on its set digest`);
|
|
374
|
-
return undefined;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
/**
|
|
378
|
-
* Run one moment's hooks, in order: each once for the wave, or once per run
|
|
379
|
-
* for a `perRun` hook. A hook that fails stops the rest, unless its
|
|
380
|
-
* `onFailure` is `approve`: then it holds the wave and the hooks after it run.
|
|
381
|
-
*/
|
|
382
|
-
function runHookList(at, moment, hooks, runs, runsPath, extraEnv = {}) {
|
|
383
|
-
const out = { holds: [], outcomes: [], otherGates: [] };
|
|
384
|
-
if (!hooks || hooks.length === 0)
|
|
385
|
-
return out;
|
|
386
|
-
const { name: specName, op, waveName, k, cwd, exec, log } = at;
|
|
387
|
-
// #3821: a hook that holds the wave on another gate names it in this file.
|
|
388
|
-
const holdPath = `.chant/op-waves/${specName}/wave-${k}/hold.json`;
|
|
389
|
-
const env = { CHANT_OP_WAVES: specName, CHANT_WAVE: waveName, CHANT_WAVE_NUMBER: String(k), CHANT_HOOK: moment, CHANT_RUNS: runsPath, CHANT_HOLD: holdPath, ...extraEnv };
|
|
390
|
-
for (const hook of hooks) {
|
|
391
|
-
const name = hook.name ?? (hook.command.join(" ").length > 60 ? `${hook.command.join(" ").slice(0, 57)}...` : hook.command.join(" "));
|
|
392
|
-
const calls = hook.perRun
|
|
393
|
-
? runs.map((run) => ({
|
|
394
|
-
target: run.target,
|
|
395
|
-
argv: opWaveArgv(hook.command, { op: run.op, wave: waveName, target: run.target, runs: runsPath, ...(run.plan ? { plan: run.plan } : {}) }),
|
|
396
|
-
env: { ...env, CHANT_TARGET: run.target, ...(run.plan ? { CHANT_PLAN: run.plan } : {}) },
|
|
397
|
-
}))
|
|
398
|
-
: [{ target: undefined, argv: opWaveArgv(hook.command, { op, wave: waveName, runs: runsPath }), env: env }];
|
|
399
|
-
for (const call of calls) {
|
|
400
|
-
const label = `${moment} hook ${name}${call.target ? ` (${call.target})` : ""}`;
|
|
401
|
-
let code;
|
|
402
|
-
rmSync(join(cwd, holdPath), { force: true });
|
|
403
|
-
try {
|
|
404
|
-
code = exec(call.argv, cwd, call.env);
|
|
405
|
-
}
|
|
406
|
-
catch (err) {
|
|
407
|
-
log(`${label} did not run: ${err instanceof Error ? err.message : String(err)}`);
|
|
408
|
-
code = 127;
|
|
409
|
-
}
|
|
410
|
-
const base = { moment, name, ...(call.target ? { target: call.target } : {}), exit: code };
|
|
411
|
-
if (code === 0) {
|
|
412
|
-
out.outcomes.push({ ...base, outcome: "passed" });
|
|
413
|
-
continue;
|
|
414
|
-
}
|
|
415
|
-
if (hook.onFailure === "approve") {
|
|
416
|
-
out.outcomes.push({ ...base, outcome: "held" });
|
|
417
|
-
const request = readHoldRequest(join(cwd, holdPath), log, label);
|
|
418
|
-
if (request) {
|
|
419
|
-
out.otherGates.push({ label, name, moment, ...(call.target ? { target: call.target } : {}), argv: call.argv, env: call.env, request });
|
|
420
|
-
log(`${label} exited ${code} and holds wave ${k} (${waveName}) on gate ${request.op ?? specName} / ${request.gate}, digest ${request.digest}`);
|
|
421
|
-
continue;
|
|
422
|
-
}
|
|
423
|
-
out.holds.push(label);
|
|
424
|
-
log(`${label} exited ${code}, so wave ${k} (${waveName}) waits for an approval of its set digest`);
|
|
425
|
-
continue;
|
|
426
|
-
}
|
|
427
|
-
out.outcomes.push({ ...base, outcome: "failed" });
|
|
428
|
-
out.failed = `${label} exited ${code}`;
|
|
429
|
-
log(out.failed);
|
|
430
|
-
return out;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
return out;
|
|
434
|
-
}
|
|
435
341
|
const placeOf = (ctx) => ({ name: ctx.spec.name, op: ctx.spec.op, waveName: ctx.wave.name, k: ctx.k, cwd: ctx.cwd, exec: ctx.exec, log: ctx.log });
|
|
436
342
|
const hookRuns = (spec, k, runs, members) => runs.map((run) => ({ target: run.target, op: run.op ?? spec.op, ...(members?.has(run.target) ? { plan: opWavePlanPath(spec, k, run.target) } : {}) }));
|
|
437
343
|
/**
|
|
@@ -508,6 +414,18 @@ async function runHooks(ctx, moment, hooks, runs, members, digest, extraEnv = {}
|
|
|
508
414
|
}
|
|
509
415
|
return back;
|
|
510
416
|
}
|
|
417
|
+
/**
|
|
418
|
+
* The hooks that run before a job plans (#4288): `before-observe`, then
|
|
419
|
+
* `before-plan`. A failure or a hold on another gate in the first ends it
|
|
420
|
+
* there; the holds of both add up.
|
|
421
|
+
*/
|
|
422
|
+
async function runBeforePlanHooks(ctx, hooks, runs) {
|
|
423
|
+
const observe = await runHooks(ctx, "before-observe", hooks?.["before-observe"], runs);
|
|
424
|
+
if (observe.failed || observe.waitingOn)
|
|
425
|
+
return observe;
|
|
426
|
+
const plan = await runHooks(ctx, "before-plan", hooks?.["before-plan"], runs);
|
|
427
|
+
return { ...plan, holds: [...observe.holds, ...plan.holds] };
|
|
428
|
+
}
|
|
511
429
|
/** How many gates one hook call holds a wave on in turn (#3847): the first hold and the holds its approved re-runs ask for. */
|
|
512
430
|
export const OP_WAVE_MAX_CHAINED_HOLDS = 10;
|
|
513
431
|
/**
|
|
@@ -827,7 +745,7 @@ async function planOpWave(options, { k, pass }) {
|
|
|
827
745
|
log(`${part} of wave ${k} (${wave.name}) holds none of the wave's runs; nothing to apply`);
|
|
828
746
|
return { end: result };
|
|
829
747
|
}
|
|
830
|
-
const before = await
|
|
748
|
+
const before = await runBeforePlanHooks(ctx, decision.hooks, slice);
|
|
831
749
|
if (before.failed)
|
|
832
750
|
return { end: failed(ctx, `a hook before the plan failed, so nothing in ${part} of wave ${k} was planned or applied`) };
|
|
833
751
|
if (before.waitingOn)
|
|
@@ -850,6 +768,14 @@ async function planOpWave(options, { k, pass }) {
|
|
|
850
768
|
// #3836: a point at base that cannot be asked fails the wave; the ledger never stands in for it.
|
|
851
769
|
if (rules.pointRefused)
|
|
852
770
|
return { end: failed(ctx, `wave ${k} (${wave.name}): ${rules.pointRefused}, so nothing in it was planned or applied`) };
|
|
771
|
+
// #4290: rules at base that cannot be read fail the wave; fewer approvers never stand in for them.
|
|
772
|
+
if (rules.quorumRefused)
|
|
773
|
+
return { end: failed(ctx, `wave ${k} (${wave.name}): ${rules.quorumRefused}, so nothing in it was planned or applied`) };
|
|
774
|
+
if (rules.quorum && rules.point)
|
|
775
|
+
return { end: failed(ctx, `wave ${k} (${wave.name}): approvals.quorum and a decision point are both set at ${base.slice(0, 12)}; a gate that asks a point takes its deciders from the point, so nothing in it was planned or applied`) };
|
|
776
|
+
if (JSON.stringify(spec.approvals?.quorum ?? null) !== JSON.stringify(rules.quorum ?? null)) {
|
|
777
|
+
log(`wave ${k} (${wave.name}): its approval rules are read at ${base.slice(0, 12)}, and this change's differ; the ones at base hold`);
|
|
778
|
+
}
|
|
853
779
|
const own = opWavePointAt(spec, wave.name);
|
|
854
780
|
if (JSON.stringify(own) !== JSON.stringify(rules.point)) {
|
|
855
781
|
log(`wave ${k} (${wave.name}): its gate's decision point is read at ${base.slice(0, 12)} (${rules.point?.name ?? "none"}), and this change's differs (${own?.name ?? "none"}); the one at base holds`);
|
|
@@ -880,7 +806,7 @@ async function planOpWave(options, { k, pass }) {
|
|
|
880
806
|
...(neverOverMcp ? { neverOverMcp: true } : {}),
|
|
881
807
|
...(rules.point ? { point: { ...rules.point } } : {}),
|
|
882
808
|
};
|
|
883
|
-
const before = await
|
|
809
|
+
const before = await runBeforePlanHooks(ctx, rules.hooks, wave.runs);
|
|
884
810
|
if (before.failed)
|
|
885
811
|
return { end: failed(ctx, `a hook before the plan failed, so nothing in wave ${k} was planned or applied`) };
|
|
886
812
|
if (before.waitingOn)
|
|
@@ -948,6 +874,10 @@ async function decideOpWave(options, { prior }, planned, digest) {
|
|
|
948
874
|
const { policy, source, base } = planned.rules;
|
|
949
875
|
decision.digest = digest;
|
|
950
876
|
decision.members = members;
|
|
877
|
+
// #4290: the rules at base whose targets glob matches one of the wave's runs.
|
|
878
|
+
const takes = planned.rules.quorum ? quorumRulesFor(planned.rules.quorum, members.map((m) => m.member)) : [];
|
|
879
|
+
if (takes.length > 0)
|
|
880
|
+
decision.quorum = takes;
|
|
951
881
|
if (extras)
|
|
952
882
|
decision.extras = extras;
|
|
953
883
|
if (pending)
|
|
@@ -956,6 +886,10 @@ async function decideOpWave(options, { prior }, planned, digest) {
|
|
|
956
886
|
if (after.failed)
|
|
957
887
|
return { end: failed(ctx, `a hook after the plan failed, so nothing in wave ${k} was applied`) };
|
|
958
888
|
holds.push(...planned.before, ...after.holds);
|
|
889
|
+
// #4294: a run the product's policy holds makes the wave wait for a person, as a held hook does.
|
|
890
|
+
for (const m of members)
|
|
891
|
+
if (m.held)
|
|
892
|
+
holds.push(`policy on ${m.member} (${m.held.rules.join(", ")})${m.held.reason ? `: ${m.held.reason}` : ""}`);
|
|
959
893
|
if (holds.length > 0)
|
|
960
894
|
decision.heldBy = holds;
|
|
961
895
|
if (after.waitingOn)
|
|
@@ -1225,6 +1159,8 @@ function failed(ctx, line) {
|
|
|
1225
1159
|
async function settleGate(options, ctx, decision) {
|
|
1226
1160
|
if (decision.point)
|
|
1227
1161
|
return settlePointGate(options, ctx, decision, decision.point);
|
|
1162
|
+
if (decision.quorum?.length)
|
|
1163
|
+
return settleQuorumGate(options, ctx, decision, decision.quorum);
|
|
1228
1164
|
const { spec, k, wave, cwd } = ctx;
|
|
1229
1165
|
const { gate, digest, approval, base, policy, members } = decision;
|
|
1230
1166
|
const { ledger, port } = await gatePort(options, cwd, gate, approval, base);
|
|
@@ -1280,6 +1216,120 @@ async function settleGate(options, ctx, decision) {
|
|
|
1280
1216
|
decision.status = "waiting";
|
|
1281
1217
|
decision.approve = approveCommand(spec.name, gate, undefined, digest, approval === "sealed");
|
|
1282
1218
|
}
|
|
1219
|
+
/**
|
|
1220
|
+
* Decide a gate under approval rules (#4290): the rules at base the wave's
|
|
1221
|
+
* targets take, each met by distinct human approvers of the digest. Roles
|
|
1222
|
+
* come from `.chant/trust.json` at base, for an approver a seal proves, or
|
|
1223
|
+
* under `pr-review` the merged pull request's review read from the forge in
|
|
1224
|
+
* this run. That review is written to the ledger once per approving
|
|
1225
|
+
* reviewer, so each counts on their own. `notAuthor` reads the author from
|
|
1226
|
+
* the same pull request; with none, no approver can show they are someone
|
|
1227
|
+
* else and the rule waits.
|
|
1228
|
+
*/
|
|
1229
|
+
async function settleQuorumGate(options, ctx, decision, rules) {
|
|
1230
|
+
const { spec, k, wave, cwd, log } = ctx;
|
|
1231
|
+
const { gate, digest, approval, base, policy, members } = decision;
|
|
1232
|
+
const { ledger, port } = await gatePort(options, cwd, gate, approval, base);
|
|
1233
|
+
const now = options.now ?? new Date().toISOString();
|
|
1234
|
+
const spent = decision.spend === "base" ? ((await ledger.readApplied?.(spec.name)) ?? []).filter((a) => a.gate === gate) : undefined;
|
|
1235
|
+
const review = await import("./gate-review.js");
|
|
1236
|
+
const trust = (options.trust ?? ((b) => review.trustPolicyAt(cwd, b)))(base);
|
|
1237
|
+
const label = `wave ${k} (${wave.name})`;
|
|
1238
|
+
// The merged pull request: its author for notAuthor, and its review under pr-review.
|
|
1239
|
+
let merged = null;
|
|
1240
|
+
let unknown;
|
|
1241
|
+
const asks = approval === "pr-review" || rules.some((r) => r.notAuthor);
|
|
1242
|
+
const head = asks ? (options.head ?? execFileSync("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8" }).trim()) : "";
|
|
1243
|
+
if (asks) {
|
|
1244
|
+
try {
|
|
1245
|
+
merged = await (options.reviews ?? review.reviewSourceFromEnv()).mergedReview(head);
|
|
1246
|
+
if (!merged)
|
|
1247
|
+
unknown = `no merged pull request produced ${head.slice(0, 12)}`;
|
|
1248
|
+
}
|
|
1249
|
+
catch (err) {
|
|
1250
|
+
unknown = `the forge's review could not be read: ${err instanceof Error ? err.message : String(err)}`;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
if (unknown && rules.some((r) => r.notAuthor))
|
|
1254
|
+
log(`${label}: ${unknown}, so the change's author is not known`);
|
|
1255
|
+
if (approval === "pr-review" && unknown)
|
|
1256
|
+
decision.review = { reason: unknown };
|
|
1257
|
+
let reviewed;
|
|
1258
|
+
if (approval === "pr-review" && merged) {
|
|
1259
|
+
const plans = merged.approvers.length === 0 ? null : await (options.headPlans ?? ((name, h) => readHeadPlans(name, h, cwd)))(spec.name, merged.head);
|
|
1260
|
+
const { reviewApproval } = await import("../gate-decision.js");
|
|
1261
|
+
const outcome = reviewApproval(merged, plans?.waves.find((w) => w.wave === k)?.digest, digest);
|
|
1262
|
+
decision.review = outcome.review;
|
|
1263
|
+
const reviewSpent = spent !== undefined && isApprovalSpent(spent, { gate, planDigest: digest, review: { head: merged.head } });
|
|
1264
|
+
if (reviewSpent)
|
|
1265
|
+
decision.review = { ...outcome.review, reason: `a wave already applied ${digest} under the review of #${merged.pr} at ${merged.head.slice(0, 12)}, so the review is spent` };
|
|
1266
|
+
if (outcome.approved && !reviewSpent) {
|
|
1267
|
+
reviewed = { head: merged.head, approvers: merged.approvers };
|
|
1268
|
+
// Each approving reviewer once, so the rules count them one by one.
|
|
1269
|
+
const had = (await ledger.read(spec.name)).resolutions.filter((r) => r.gate === gate && r.review?.head === merged.head && samePlanDigest(r.planDigest, digest));
|
|
1270
|
+
const note = `approved by the review of #${merged.pr} at ${merged.head.slice(0, 12)}: ${merged.approvers.join(", ")}`;
|
|
1271
|
+
for (const who of merged.approvers) {
|
|
1272
|
+
if (had.some((r) => samePrincipal(r.resolvedBy, who)))
|
|
1273
|
+
continue;
|
|
1274
|
+
await ledger.appendResolution?.({
|
|
1275
|
+
op: spec.name,
|
|
1276
|
+
gate,
|
|
1277
|
+
resolvedBy: who,
|
|
1278
|
+
timestamp: now,
|
|
1279
|
+
planDigest: digest,
|
|
1280
|
+
approver: { kind: "human" },
|
|
1281
|
+
gitSha: head,
|
|
1282
|
+
...(ciRunIdFromEnv() ? { runId: ciRunIdFromEnv() } : {}),
|
|
1283
|
+
note,
|
|
1284
|
+
...(merged.url ? { url: merged.url } : {}),
|
|
1285
|
+
review: { pr: merged.pr, head: merged.head, approvers: merged.approvers },
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
const judge = trustQuorumJudge({
|
|
1291
|
+
signers: trust,
|
|
1292
|
+
roles: trust.roles,
|
|
1293
|
+
...(merged ? { authors: [merged.author] } : {}),
|
|
1294
|
+
...(reviewed ? { review: reviewed } : {}),
|
|
1295
|
+
});
|
|
1296
|
+
const held = decision.heldBy?.length ? `, held by ${decision.heldBy.join(", ")}` : "";
|
|
1297
|
+
const check = await decideWaveGate(port, {
|
|
1298
|
+
op: spec.name,
|
|
1299
|
+
gate,
|
|
1300
|
+
digest,
|
|
1301
|
+
description: (typeof decision.pending?.description === "string" ? decision.pending.description : undefined) ?? `wave ${k} (${wave.name}): ${members.length} run${members.length === 1 ? "" : "s"}, gate ${policy}, approval ${approval}${held}`,
|
|
1302
|
+
now,
|
|
1303
|
+
...(spent ? { spent } : {}),
|
|
1304
|
+
fields: { ...decision.pending, approvalMode: approval },
|
|
1305
|
+
...(decision.neverOverMcp ? { neverOverMcp: true } : {}),
|
|
1306
|
+
approval: { quorum: rules, mode: "log-only" },
|
|
1307
|
+
judge,
|
|
1308
|
+
});
|
|
1309
|
+
if (check.satisfied) {
|
|
1310
|
+
const by = (check.approvals ?? [check.resolution]).map((r) => r.resolvedBy);
|
|
1311
|
+
decision.status = "approved";
|
|
1312
|
+
decision.via = reviewed && check.resolution.review?.head === reviewed.head ? "pr-review" : "ledger";
|
|
1313
|
+
decision.approvedBy = by.join(", ");
|
|
1314
|
+
decision.approvedAt = check.resolution.timestamp;
|
|
1315
|
+
log(`${label}: the approval rules at ${base.slice(0, 12)} are met by ${by.join(", ")}`);
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
logRefused(log, label, check);
|
|
1319
|
+
decision.pendingFact = check.pending;
|
|
1320
|
+
decision.recorded = check.recorded;
|
|
1321
|
+
decision.waitingSince = check.pending.timestamp;
|
|
1322
|
+
if (check.mismatch)
|
|
1323
|
+
decision.mismatch = check.mismatch;
|
|
1324
|
+
if (check.quorum?.rules) {
|
|
1325
|
+
decision.quorumProgress = check.quorum.rules;
|
|
1326
|
+
for (const p of check.quorum.rules)
|
|
1327
|
+
log(`${label}: approval rule ${quorumRuleLine(p)}`);
|
|
1328
|
+
log(`${label}: the approval rules at ${base.slice(0, 12)} are not met (${quorumWaiting(check.quorum.rules)}), so it waits for more approvals of ${digest}`);
|
|
1329
|
+
}
|
|
1330
|
+
decision.status = "waiting";
|
|
1331
|
+
decision.approve = approveCommand(spec.name, gate, undefined, digest, approval === "sealed" || rules.some(ruleNeedsProof));
|
|
1332
|
+
}
|
|
1283
1333
|
/**
|
|
1284
1334
|
* Decide a gate that asks a decision point (#3836), as an Op gate with a
|
|
1285
1335
|
* `point` is decided (#3170): ask it with `gate.planDigest` set to the set
|
package/dist/op/op-waves.d.ts
CHANGED
|
@@ -43,6 +43,14 @@
|
|
|
43
43
|
* `chant/lifecycle`, as terragucci's wave gate does, and that approval is
|
|
44
44
|
* spent: the same digest needs a new approval to apply again.
|
|
45
45
|
*
|
|
46
|
+
* ## Approval rules (#4290)
|
|
47
|
+
*
|
|
48
|
+
* With `approvals: { quorum: [...] }` (read at base), a wave takes each rule
|
|
49
|
+
* whose `targets` glob matches one of its runs' targets and waits until each
|
|
50
|
+
* is met: `count` distinct people, from `roles` when it names them (granted
|
|
51
|
+
* in `.chant/trust.json` at base), one of them not the author with
|
|
52
|
+
* `notAuthor`. See `./gate-quorum.ts`.
|
|
53
|
+
*
|
|
46
54
|
* ## Wave-level commands and hooks (#3762)
|
|
47
55
|
*
|
|
48
56
|
* A product whose tool plans a whole wave in one call (Terragrunt's `run
|
|
@@ -68,6 +76,7 @@
|
|
|
68
76
|
import type { OpEnvironment, OpSetupStep } from "../lexicon.js";
|
|
69
77
|
import type { CiRunner } from "../ci-jobs.js";
|
|
70
78
|
import { type GatePoint } from "./gate-point.js";
|
|
79
|
+
import { type GateQuorumRule } from "./gate-quorum.js";
|
|
71
80
|
import { type NotifyConfig } from "../notify.js";
|
|
72
81
|
/**
|
|
73
82
|
* The variables a wave job reads its notify secrets from, for the lexicons
|
|
@@ -128,13 +137,23 @@ export interface OpWaveHook {
|
|
|
128
137
|
onFailure?: OpWaveHookFailure;
|
|
129
138
|
/** Run once per run of the wave, rather than once for the wave. */
|
|
130
139
|
perRun?: boolean;
|
|
140
|
+
/**
|
|
141
|
+
* The runs the hook is for (#4288): targets, or patterns where `*` matches
|
|
142
|
+
* within one `/` segment and `**` across segments, as a job group's `runs`.
|
|
143
|
+
* A `perRun` hook runs for the matching runs only. A hook that runs once
|
|
144
|
+
* for the wave runs when any run matches, or when no run is known yet (a
|
|
145
|
+
* `discover` wave before its plan). Absent: every run.
|
|
146
|
+
*/
|
|
147
|
+
targets?: string[];
|
|
131
148
|
}
|
|
132
149
|
/** The stages a hook runs around. */
|
|
133
|
-
export type OpWaveHookStage = "plan" | "apply" | "gate";
|
|
150
|
+
export type OpWaveHookStage = "observe" | "plan" | "apply" | "gate";
|
|
134
151
|
/** When a hook runs. */
|
|
135
|
-
export type OpWaveHookMoment = "before-plan" | "after-plan" | "after-gate" | "before-apply" | "after-apply";
|
|
152
|
+
export type OpWaveHookMoment = "before-observe" | "before-plan" | "after-plan" | "after-gate" | "before-apply" | "after-apply";
|
|
136
153
|
/**
|
|
137
|
-
* Every moment, in the order a wave reaches them. `
|
|
154
|
+
* Every moment, in the order a wave reaches them. `before-observe` (#4288)
|
|
155
|
+
* runs before anything reads live state: ahead of `before-plan` in every job
|
|
156
|
+
* that plans, the deciding job's and each share's. `after-gate` (#3821) runs
|
|
138
157
|
* once the gate decided, whatever it decided: approved, let through,
|
|
139
158
|
* waiting, or refused by a policy denial.
|
|
140
159
|
*/
|
|
@@ -149,7 +168,9 @@ export type OpWaveApplyHooksRunner = "chant" | "apply";
|
|
|
149
168
|
export declare const OP_WAVE_APPLY_HOOKS_RUNNERS: readonly OpWaveApplyHooksRunner[];
|
|
150
169
|
/** Stage hooks (#3762), read from the spec at the base commit like the gate policy. */
|
|
151
170
|
export interface OpWaveHooks {
|
|
171
|
+
/** `before.observe` (#4288) runs before anything reads live state, ahead of `before.plan`. */
|
|
152
172
|
before?: {
|
|
173
|
+
observe?: OpWaveHook[];
|
|
153
174
|
plan?: OpWaveHook[];
|
|
154
175
|
apply?: OpWaveHook[];
|
|
155
176
|
};
|
|
@@ -418,6 +439,18 @@ export interface OpWavesApprovals {
|
|
|
418
439
|
* approval of a digest passes that digest as often as it is planned.
|
|
419
440
|
*/
|
|
420
441
|
spend?: boolean;
|
|
442
|
+
/**
|
|
443
|
+
* Approval rules (#4290): a wave takes each rule whose `targets` glob
|
|
444
|
+
* matches one of its runs' targets (a rule with no `targets` covers every
|
|
445
|
+
* wave), and applies once each is met by distinct human approvers of its
|
|
446
|
+
* set digest. Read at the base commit, with the roles from
|
|
447
|
+
* `.chant/trust.json` there, so a change cannot loosen the rules its own
|
|
448
|
+
* wave is judged by. A role, or `notAuthor`, counts only a proven
|
|
449
|
+
* approver: a seal that verifies against the signers at base, or under
|
|
450
|
+
* `pr-review` the merged pull request's review. `notAuthor` reads the
|
|
451
|
+
* author from that pull request. See `./gate-quorum.ts`.
|
|
452
|
+
*/
|
|
453
|
+
quorum?: GateQuorumRule[];
|
|
421
454
|
}
|
|
422
455
|
/** The scheduled resume job of an Op waves pipeline (#3683). */
|
|
423
456
|
export interface OpWavesResume {
|
|
@@ -462,6 +495,13 @@ export declare function assertOpWavesSpec(spec: OpWavesSpec): void;
|
|
|
462
495
|
* across segments.
|
|
463
496
|
*/
|
|
464
497
|
export declare function opWaveRunMatches(pattern: string, target: string): boolean;
|
|
498
|
+
/**
|
|
499
|
+
* The runs of `runs` hook `hook` is for (#4288): those its `targets` match,
|
|
500
|
+
* or every run when it names none.
|
|
501
|
+
*/
|
|
502
|
+
export declare function opWaveHookRuns<T extends {
|
|
503
|
+
target: string;
|
|
504
|
+
}>(hook: Pick<OpWaveHook, "targets">, runs: readonly T[]): T[];
|
|
465
505
|
/** The runs of `runs` that job group `group` holds (#3914). */
|
|
466
506
|
export declare function opWaveGroupRuns<T extends {
|
|
467
507
|
target: string;
|
|
@@ -473,6 +513,18 @@ export declare function opWaveGroupRuns<T extends {
|
|
|
473
513
|
export declare function opWaveGroupsProblem(wave: Pick<OpWave, "name" | "jobs">, targets: readonly string[]): string | undefined;
|
|
474
514
|
/** Refuse hooks no runner could run, naming what is wrong (#3762). */
|
|
475
515
|
export declare function assertOpWaveHooks(where: string, hooks: OpWaveHooks | undefined): void;
|
|
516
|
+
/**
|
|
517
|
+
* The moments a pull-request job runs hooks at (#4288): `pr-plan` and
|
|
518
|
+
* `pr-record` run the observe and plan moments, and `pr-apply` the apply
|
|
519
|
+
* moments too. The pull request's gate has no `after-gate` moment.
|
|
520
|
+
*/
|
|
521
|
+
export declare const PR_LOOP_HOOK_MOMENTS: readonly OpWaveHookMoment[];
|
|
522
|
+
/**
|
|
523
|
+
* Refuse `ci.hooks` a pull-request job could not run (#4288): what
|
|
524
|
+
* {@link assertOpWaveHooks} refuses, plus `after.gate` and `apply.runBy`,
|
|
525
|
+
* since chant itself runs a pull request's apply.
|
|
526
|
+
*/
|
|
527
|
+
export declare function assertPrLoopHooks(where: string, hooks: OpWaveHooks | undefined): void;
|
|
476
528
|
/** The hooks of each moment for wave `name`: the spec's, then the wave's own. */
|
|
477
529
|
export declare function opWaveHooksAt(spec: Pick<OpWavesSpec, "hooks" | "waves">, name: string): Record<OpWaveHookMoment, OpWaveHook[]>;
|
|
478
530
|
/** Who runs wave `name`'s apply-stage hooks (#3804): the wave's `hooks.apply.runBy`, else the spec's, else `chant`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"op-waves.d.ts","sourceRoot":"","sources":["../../src/op/op-waves.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"op-waves.d.ts","sourceRoot":"","sources":["../../src/op/op-waves.ts"],"names":[],"mappings":"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"}
|