@intentius/chant 0.124.33 → 0.124.35
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/cli/handlers/graph.js +2 -2
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +147 -51
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +284 -81
- package/dist/cli/handlers/release.d.ts +12 -0
- package/dist/cli/handlers/release.d.ts.map +1 -1
- package/dist/cli/handlers/release.js +125 -10
- package/dist/cli/handlers/run.d.ts +3 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +12 -2
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +37 -2
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +24 -0
- package/dist/cli/registry.d.ts +17 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/auto-release.d.ts +8 -1
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/auto-release.js +3 -2
- package/dist/components/green-cover.d.ts +77 -0
- package/dist/components/green-cover.d.ts.map +1 -0
- package/dist/components/green-cover.js +153 -0
- package/dist/components/pr-pipeline.d.ts +34 -0
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +39 -5
- package/dist/components/promote-rounds.d.ts +102 -0
- package/dist/components/promote-rounds.d.ts.map +1 -0
- package/dist/components/promote-rounds.js +174 -0
- package/dist/components/promote.d.ts +4 -1
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +6 -3
- package/dist/components/release-gate.d.ts +30 -0
- package/dist/components/release-gate.d.ts.map +1 -1
- package/dist/components/release-gate.js +92 -42
- package/dist/components/release-promote.d.ts +9 -0
- package/dist/components/release-promote.d.ts.map +1 -1
- package/dist/components/release-promote.js +15 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/deep-observation.d.ts +11 -0
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/graph-declared.d.ts +9 -1
- package/dist/graph-declared.d.ts.map +1 -1
- package/dist/graph-declared.js +18 -3
- package/dist/graph-detail.d.ts.map +1 -1
- package/dist/graph-detail.js +30 -1
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-ir.js +13 -2
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -1
- package/dist/lifecycle/__fixtures__/publish-fakes.js +5 -1
- package/dist/lifecycle/compose-refs.d.ts +52 -0
- package/dist/lifecycle/compose-refs.d.ts.map +1 -0
- package/dist/lifecycle/compose-refs.js +115 -0
- package/dist/lifecycle/deep-observe.d.ts.map +1 -1
- package/dist/lifecycle/deep-observe.js +3 -2
- package/dist/lifecycle/green-apply.d.ts +39 -0
- package/dist/lifecycle/green-apply.d.ts.map +1 -0
- package/dist/lifecycle/green-apply.js +51 -0
- package/dist/lifecycle/publish-declaration.d.ts +4 -0
- package/dist/lifecycle/publish-declaration.d.ts.map +1 -1
- package/dist/lifecycle/publish-declaration.js +15 -0
- package/dist/lifecycle/publish-npm.d.ts +7 -4
- package/dist/lifecycle/publish-npm.d.ts.map +1 -1
- package/dist/lifecycle/publish-npm.js +60 -13
- package/dist/lifecycle/release-compose.d.ts +91 -0
- package/dist/lifecycle/release-compose.d.ts.map +1 -0
- package/dist/lifecycle/release-compose.js +235 -0
- package/dist/lifecycle/release-cut.d.ts +8 -0
- package/dist/lifecycle/release-cut.d.ts.map +1 -1
- package/dist/lifecycle/release-cut.js +2 -0
- package/dist/lifecycle/release-publish.d.ts +2 -0
- package/dist/lifecycle/release-publish.d.ts.map +1 -1
- package/dist/lifecycle/release-publish.js +2 -1
- package/dist/lifecycle/release-store.d.ts +15 -0
- package/dist/lifecycle/release-store.d.ts.map +1 -1
- package/dist/lifecycle/release-store.js +5 -1
- package/dist/wave-runtime.mjs +226 -224
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.js +13 -2
- package/dist/workspace/check.schema.json +20 -2
- package/dist/workspace/checks/ci-workflow.d.ts +17 -0
- package/dist/workspace/checks/ci-workflow.d.ts.map +1 -0
- package/dist/workspace/checks/ci-workflow.js +130 -0
- package/dist/workspace/checks/security.d.ts +16 -1
- package/dist/workspace/checks/security.d.ts.map +1 -1
- package/dist/workspace/checks/security.js +4 -4
- package/dist/workspace/checks.d.ts +1 -1
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +4 -1
- package/dist/workspace/ci-green-cli.d.ts.map +1 -1
- package/dist/workspace/ci-green-cli.js +5 -3
- package/dist/workspace/ci-green-workflow.d.ts +18 -2
- package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-green-workflow.js +27 -5
- package/dist/workspace/ci-green.d.ts +31 -0
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +78 -4
- package/dist/workspace/ci-init.d.ts +100 -0
- package/dist/workspace/ci-init.d.ts.map +1 -0
- package/dist/workspace/ci-init.js +434 -0
- package/dist/workspace/ci-run.d.ts +6 -0
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +2 -1
- package/dist/workspace/ci-serve.d.ts +94 -13
- package/dist/workspace/ci-serve.d.ts.map +1 -1
- package/dist/workspace/ci-serve.js +248 -30
- package/dist/workspace/ci-workflow.d.ts +91 -0
- package/dist/workspace/ci-workflow.d.ts.map +1 -0
- package/dist/workspace/ci-workflow.js +33 -0
- package/dist/workspace/declaration.d.ts +18 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +9 -2
- package/dist/workspace/declaration.schema.json +28 -0
- package/dist/workspace/delivery-metrics.d.ts +3 -1
- package/dist/workspace/delivery-metrics.d.ts.map +1 -1
- package/dist/workspace/delivery-metrics.js +12 -3
- package/dist/workspace/init.js +2 -2
- package/dist/workspace/member-commands.d.ts.map +1 -1
- package/dist/workspace/member-commands.js +4 -0
- package/dist/workspace/nested-graph.d.ts.map +1 -1
- package/dist/workspace/nested-graph.js +1 -0
- package/dist/workspace/reason-codes.d.ts +9 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +10 -0
- package/dist/workspace/release-candidates.d.ts +28 -0
- package/dist/workspace/release-candidates.d.ts.map +1 -1
- package/dist/workspace/release-candidates.js +38 -2
- package/dist/workspace/release-candidates.schema.json +51 -0
- package/dist/workspace/release-workflow.d.ts +31 -0
- package/dist/workspace/release-workflow.d.ts.map +1 -0
- package/dist/workspace/release-workflow.js +150 -0
- package/dist/workspace/releases.schema.json +30 -0
- package/package.json +1 -1
- package/src/cli/handlers/graph.ts +2 -2
- package/src/cli/handlers/pr-green.test.ts +230 -0
- package/src/cli/handlers/pr.ts +148 -66
- package/src/cli/handlers/promote.test.ts +34 -0
- package/src/cli/handlers/promote.ts +302 -91
- package/src/cli/handlers/release-compose.e2e.test.ts +133 -0
- package/src/cli/handlers/release.ts +112 -10
- package/src/cli/handlers/run.ts +12 -1
- package/src/cli/main.ts +37 -2
- package/src/cli/parse-args.ts +18 -0
- package/src/cli/registry.ts +17 -1
- package/src/codegen/release-wiring.test.ts +56 -9
- package/src/components/auto-release.test.ts +22 -0
- package/src/components/auto-release.ts +9 -2
- package/src/components/green-cover.test.ts +170 -0
- package/src/components/green-cover.ts +186 -0
- package/src/components/pr-pipeline.test.ts +14 -4
- package/src/components/pr-pipeline.ts +56 -5
- package/src/components/promote-rounds.test.ts +220 -0
- package/src/components/promote-rounds.ts +232 -0
- package/src/components/promote.test.ts +7 -0
- package/src/components/promote.ts +9 -4
- package/src/components/release-gate.test.ts +47 -0
- package/src/components/release-gate.ts +111 -46
- package/src/components/release-promote.test.ts +24 -1
- package/src/components/release-promote.ts +15 -0
- package/src/deep-observation.ts +8 -0
- package/src/graph-declared-exports.test.ts +40 -0
- package/src/graph-declared.test.ts +18 -0
- package/src/graph-declared.ts +17 -2
- package/src/graph-detail.test.ts +68 -1
- package/src/graph-detail.ts +29 -1
- package/src/graph-ir.ts +12 -4
- package/src/lifecycle/__fixtures__/publish-fakes.ts +5 -1
- package/src/lifecycle/compose-refs.ts +125 -0
- package/src/lifecycle/deep-observe.test.ts +20 -0
- package/src/lifecycle/deep-observe.ts +4 -2
- package/src/lifecycle/green-apply.ts +71 -0
- package/src/lifecycle/publish-declaration.ts +15 -0
- package/src/lifecycle/publish-npm.ts +55 -13
- package/src/lifecycle/publish-targets.test.ts +45 -0
- package/src/lifecycle/release-compose.test.ts +125 -0
- package/src/lifecycle/release-compose.ts +312 -0
- package/src/lifecycle/release-cut.ts +6 -0
- package/src/lifecycle/release-publish.ts +4 -1
- package/src/lifecycle/release-store.ts +16 -1
- package/src/workspace/__fixtures__/contract-repo.ts +11 -2
- package/src/workspace/check-contract.test.ts +3 -2
- package/src/workspace/check.schema.json +20 -2
- package/src/workspace/checks/ci-workflow.test.ts +160 -0
- package/src/workspace/checks/ci-workflow.ts +135 -0
- package/src/workspace/checks/security.ts +4 -4
- package/src/workspace/checks.ts +4 -1
- package/src/workspace/ci-green-cli.ts +5 -3
- package/src/workspace/ci-green-workflow.test.ts +52 -0
- package/src/workspace/ci-green-workflow.ts +34 -5
- package/src/workspace/ci-green.test.ts +33 -0
- package/src/workspace/ci-green.ts +85 -4
- package/src/workspace/ci-init.test.ts +159 -0
- package/src/workspace/ci-init.ts +445 -0
- package/src/workspace/ci-report-url.test.ts +6 -1
- package/src/workspace/ci-run.test.ts +18 -13
- package/src/workspace/ci-run.ts +8 -1
- package/src/workspace/ci-serve-land.e2e.test.ts +138 -0
- package/src/workspace/ci-serve-land.test.ts +176 -0
- package/src/workspace/ci-serve.test.ts +77 -43
- package/src/workspace/ci-serve.ts +268 -34
- package/src/workspace/ci-workflow.ts +71 -0
- package/src/workspace/declaration.schema.json +28 -0
- package/src/workspace/declaration.ts +32 -3
- package/src/workspace/delivery-metrics.ts +12 -3
- package/src/workspace/graph-contract.test.ts +3 -2
- package/src/workspace/init.ts +2 -2
- package/src/workspace/member-commands.test.ts +11 -4
- package/src/workspace/member-commands.ts +3 -0
- package/src/workspace/nested-graph.test.ts +4 -0
- package/src/workspace/nested-graph.ts +1 -0
- package/src/workspace/reason-codes.test.ts +2 -0
- package/src/workspace/reason-codes.ts +10 -0
- package/src/workspace/release-candidates.schema.json +51 -0
- package/src/workspace/release-candidates.test.ts +30 -3
- package/src/workspace/release-candidates.ts +54 -2
- package/src/workspace/release-workflow.ts +167 -0
- package/src/workspace/releases.schema.json +30 -0
|
@@ -406,7 +406,7 @@ async function runGraphLive(ctx, format) {
|
|
|
406
406
|
// no stack carries a `src`.
|
|
407
407
|
const scopedStacks = stacks.filter((s) => s.src);
|
|
408
408
|
if (scopedStacks.length > 0) {
|
|
409
|
-
const declaredIr = await buildDeclaredPerStack(scopedStacks, projectPath);
|
|
409
|
+
const declaredIr = await buildDeclaredPerStack(scopedStacks, projectPath, (g, entities) => withLexiconEdges(g, plugins, entities));
|
|
410
410
|
ir =
|
|
411
411
|
args.overlayAnchor === "live"
|
|
412
412
|
? overlayGraphs(ir, declaredIr, overlayOpts)
|
|
@@ -432,7 +432,7 @@ async function runGraphLive(ctx, format) {
|
|
|
432
432
|
}
|
|
433
433
|
else {
|
|
434
434
|
await mergeGraphOps(declared.entities, declared.sourceFiles, projectPath);
|
|
435
|
-
const declaredIr = buildGraphIr(declared.entities, projectPath);
|
|
435
|
+
const declaredIr = withLexiconEdges(buildGraphIr(declared.entities, projectPath), plugins, declared.entities);
|
|
436
436
|
ir =
|
|
437
437
|
args.overlayAnchor === "live"
|
|
438
438
|
? overlayGraphs(ir, declaredIr, overlayOpts)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/pr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;
|
|
1
|
+
{"version":3,"file":"pr.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/pr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAwDH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAMlD,qDAAqD;AACrD,eAAO,MAAM,aAAa,cAAc,CAAC;AAqSzC,mKAAmK;AACnK,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAGxE;AAyDD,yLAAyL;AACzL,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAGzE;AAwOD,uKAAuK;AACvK,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAG1E"}
|
package/dist/cli/handlers/pr.js
CHANGED
|
@@ -86,6 +86,7 @@ import { formatError, formatInfo, formatSuccess, formatWarning } from "../format
|
|
|
86
86
|
import { reportUninstalledDependencies } from "../dependencies.js";
|
|
87
87
|
import { findWorkspaceRoot } from "../../project-root.js";
|
|
88
88
|
import { resolveChangedUnits } from "./fan-out.js";
|
|
89
|
+
import { coverByMerges, greenOp } from "../../components/green-cover.js";
|
|
89
90
|
import { GATED_EXIT_CODE, autoReleaseRun, recordAutoReleasesForRun } from "./run.js";
|
|
90
91
|
import { failedRunExitCode } from "../../components/driver.js";
|
|
91
92
|
/** Where the reports land unless `--output` says. */
|
|
@@ -430,11 +431,20 @@ async function prApply(ctx, stage) {
|
|
|
430
431
|
return fail("--wave runs one wave of a --wave-gate apply, so it needs --wave-gate <name>");
|
|
431
432
|
if (args.wave !== undefined && args.wave < 1)
|
|
432
433
|
return fail(`--wave counts from 1, not ${args.wave}`);
|
|
434
|
+
// #4168 step 9: apply on merge applies a green commit, measured from the one applied before it.
|
|
435
|
+
let green;
|
|
436
|
+
if (args.green) {
|
|
437
|
+
const target = await greenTargetOf(ctx);
|
|
438
|
+
if (typeof target === "number")
|
|
439
|
+
return target;
|
|
440
|
+
green = target;
|
|
441
|
+
ctx = { ...ctx, args: { ...ctx.args, base: green.base, head: green.commit } };
|
|
442
|
+
}
|
|
433
443
|
const p = await prepare(ctx, "apply");
|
|
434
444
|
if (typeof p === "number")
|
|
435
445
|
return p;
|
|
436
446
|
let pr = args.pr ?? null;
|
|
437
|
-
if (pr === null && p.forge && p.plan.order.length > 0) {
|
|
447
|
+
if (pr === null && !green && p.forge && p.plan.order.length > 0) {
|
|
438
448
|
try {
|
|
439
449
|
pr = await p.forge.pullRequestFor(p.head);
|
|
440
450
|
}
|
|
@@ -442,7 +452,7 @@ async function prApply(ctx, stage) {
|
|
|
442
452
|
return fail(`could not ask ${p.forge.kind} which pull request merged ${p.head}: ${err instanceof Error ? err.message : String(err)}`);
|
|
443
453
|
}
|
|
444
454
|
}
|
|
445
|
-
const op = pr === null ? "pr-unknown" : prOp(pr, p.member);
|
|
455
|
+
const op = green ? greenOp(green.commit, p.member) : pr === null ? "pr-unknown" : prOp(pr, p.member);
|
|
446
456
|
// The run id the release records carry, chosen before the run so that
|
|
447
457
|
// `--events-json` (#4041, #4007) names the run by it too.
|
|
448
458
|
const release = autoReleaseRun(`${op}-${Date.now()}`);
|
|
@@ -451,7 +461,7 @@ async function prApply(ctx, stage) {
|
|
|
451
461
|
stage.stream = events;
|
|
452
462
|
events?.emit({ type: "run-start", waves: p.plan.waves });
|
|
453
463
|
if (args.waveGate)
|
|
454
|
-
return prApplyWaves(ctx, p, { pr, op, release, upload, events });
|
|
464
|
+
return prApplyWaves(ctx, p, { pr, op, release, upload, events, ...(green ? { green } : {}) });
|
|
455
465
|
// `--resume` (#3464): an earlier attempt at this change on this commit
|
|
456
466
|
// applied part of an approved set. When the members left are covered by
|
|
457
467
|
// it and its approval still stands, finish it under that approval.
|
|
@@ -486,7 +496,7 @@ async function prApply(ctx, stage) {
|
|
|
486
496
|
if (p.plan.order.length === 0) {
|
|
487
497
|
return stop(prReport({ ...base, approval: { status: "pending" }, status: "nothing" }), 0);
|
|
488
498
|
}
|
|
489
|
-
if (pr === null) {
|
|
499
|
+
if (pr === null && !green) {
|
|
490
500
|
return stop(prReport({
|
|
491
501
|
...base,
|
|
492
502
|
approval: { status: "pending" },
|
|
@@ -508,35 +518,18 @@ async function prApply(ctx, stage) {
|
|
|
508
518
|
// one a pending fact waits on, can be read once this job is gone.
|
|
509
519
|
await storeChangeSetForGate(ctx, p, set.doc);
|
|
510
520
|
let approval;
|
|
521
|
+
let via = "gate";
|
|
511
522
|
if (resumed) {
|
|
512
523
|
approval = resumed.approval;
|
|
513
524
|
}
|
|
514
525
|
else {
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}), (unapproved) => {
|
|
521
|
-
const a = approvalOf(unapproved);
|
|
522
|
-
return a.status === "changed"
|
|
523
|
-
? describePlanChanged({ op, gate: p.gate, digest: set.doc.digest, approval: a })
|
|
524
|
-
: `No approval stands for this plan. Approve it: ${prApproveCommand({ op, gate: p.gate, digest: set.doc.digest })}`;
|
|
525
|
-
});
|
|
526
|
-
approval = approvalOf(check);
|
|
527
|
-
if (!check.satisfied) {
|
|
528
|
-
const partial = { ...base, approval, status: "refused" };
|
|
529
|
-
const report = approval.status === "changed"
|
|
530
|
-
? prReport({ ...partial, refusal: "plan-changed", message: describePlanChanged({ op, gate: p.gate, digest: set.doc.digest, approval }) })
|
|
531
|
-
: prReport({
|
|
532
|
-
...partial,
|
|
533
|
-
refusal: "not-approved",
|
|
534
|
-
message: `No approval stands for this plan, so nothing was applied. Approve it: ${prApproveCommand({ op, gate: p.gate, digest: set.doc.digest })}; then run the apply again.`,
|
|
535
|
-
});
|
|
536
|
-
return stop(report, GATED_EXIT_CODE);
|
|
537
|
-
}
|
|
526
|
+
const decided = await decideApplyGate(p, { op, pr, set, events, ...(green ? { green } : {}) });
|
|
527
|
+
approval = decided.approval;
|
|
528
|
+
if (!decided.satisfied)
|
|
529
|
+
return stop(prReport({ ...base, approval, status: "refused", refusal: decided.refusal, message: decided.message }), GATED_EXIT_CODE);
|
|
530
|
+
via = decided.via;
|
|
538
531
|
}
|
|
539
|
-
const reviewOf =
|
|
532
|
+
const reviewOf = reviewFor(args, p, pr, green);
|
|
540
533
|
const review = await reviewOf(approval.approvedBy ?? [], "The plan");
|
|
541
534
|
if (review && "error" in review)
|
|
542
535
|
return fail(review.error);
|
|
@@ -584,7 +577,7 @@ async function prApply(ctx, stage) {
|
|
|
584
577
|
...roundLedger,
|
|
585
578
|
...(roundLedger.approvalRule ? { approvalRule: (g, at) => roundLedger.approvalRule(g === gate ? p.gate : g, at) } : {}),
|
|
586
579
|
};
|
|
587
|
-
const check = await emitGateStep(events?.emit, { op, gate }, () => decidePrGate(port, { op, gate, digest: planned.doc.digest, description:
|
|
580
|
+
const check = await emitGateStep(events?.emit, { op, gate }, () => decidePrGate(port, { op, gate, digest: planned.doc.digest, description: `${changeLabel(pr, green)}, round ${round}: planned again after an upstream applied` }));
|
|
588
581
|
const roundApproval = approvalOf(check);
|
|
589
582
|
if (!check.satisfied)
|
|
590
583
|
return { satisfied: false, approval: roundApproval };
|
|
@@ -644,6 +637,8 @@ async function prApply(ctx, stage) {
|
|
|
644
637
|
// a person. A step that refused its plan because the target moved since
|
|
645
638
|
// the approval exits 3, as a moved digest does before anything applies (#3997).
|
|
646
639
|
const exit = applied.status === "applied" ? 0 : applied.status === "waiting" ? GATED_EXIT_CODE : failedRunExitCode(applied.run.results);
|
|
640
|
+
if (green && applied.status === "applied")
|
|
641
|
+
await recordGreenApplied(p, green, set.doc.digest, approval, via);
|
|
647
642
|
const code = await stop(report, exit, applied.run.status === "gated" ? "gated" : undefined);
|
|
648
643
|
if (applied.status === "applied")
|
|
649
644
|
console.error(formatSuccess(`applied the plan ${report.digest} approved by ${(approval.approvedBy ?? []).join(", ")}`));
|
|
@@ -763,6 +758,115 @@ async function prRecord(ctx, stage) {
|
|
|
763
758
|
const stop = stopper(ctx, p, events, upload);
|
|
764
759
|
return stop(report, status === "plan-failed" ? 1 : 0);
|
|
765
760
|
}
|
|
761
|
+
/** What a gate's description and messages call the change: the pull request, or the green commit (#4168 step 9). */
|
|
762
|
+
function changeLabel(pr, green) {
|
|
763
|
+
return green ? `green ${green.commit.slice(0, 12)}` : `pull request #${pr}`;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* `pr-apply --green` (#4168 step 9): the green commit at HEAD, the last one
|
|
767
|
+
* applied to the environment, and the merges between with their approvals.
|
|
768
|
+
* Exits 0 when HEAD is the green commit last applied.
|
|
769
|
+
*/
|
|
770
|
+
async function greenTargetOf(ctx) {
|
|
771
|
+
const { args } = ctx;
|
|
772
|
+
if (args.pr !== undefined)
|
|
773
|
+
return fail("--green applies a green commit and every merge since the last one applied, so it takes no --pr; the merge records name the pull requests");
|
|
774
|
+
const { resolveGreenTarget } = await import("../../components/green-cover.js");
|
|
775
|
+
const env = args.env ?? "local";
|
|
776
|
+
const resolved = await resolveGreenTarget({ cwd: resolve("."), env, ...(args.base ? { base: args.base } : {}), gate: args.gate ?? PR_APPLY_GATE });
|
|
777
|
+
if (resolved.status === "error")
|
|
778
|
+
return fail(resolved.message, resolved.hint);
|
|
779
|
+
if (resolved.status === "applied") {
|
|
780
|
+
console.error(formatInfo(`${resolved.commit.slice(0, 12)} is the green commit last applied to ${env}, so there is nothing to apply`));
|
|
781
|
+
return 0;
|
|
782
|
+
}
|
|
783
|
+
const t = resolved.target;
|
|
784
|
+
const from = { applied: "the green commit last applied", flag: "--base", "previous-green": "the green commit before it", parent: "its parent" }[t.baseFrom];
|
|
785
|
+
console.error(formatInfo(`applying green ${t.commit.slice(0, 12)}, measured from ${t.base.slice(0, 12)} (${from}): ${t.merges.length} commit${t.merges.length === 1 ? "" : "s"}, ${t.approved.length} with an approved plan`));
|
|
786
|
+
return t;
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* The first plan's gate. For one pull request, its gate on the plan's digest.
|
|
790
|
+
* For a green commit (#4168 step 9), the approved plans of the merges since
|
|
791
|
+
* the last applied green commit when they cover every member, and otherwise
|
|
792
|
+
* a gate of the green commit's own on the combined digest.
|
|
793
|
+
*/
|
|
794
|
+
async function decideApplyGate(p, at) {
|
|
795
|
+
const { op, pr, set, events, green } = at;
|
|
796
|
+
let uncovered = [];
|
|
797
|
+
if (green) {
|
|
798
|
+
const cover = coverByMerges(set.doc, green.approved);
|
|
799
|
+
if (cover.covered) {
|
|
800
|
+
console.error(formatInfo(`the approved plans of ${cover.coveredBy.length} merge${cover.coveredBy.length === 1 ? "" : "s"} cover the plan at ${green.commit.slice(0, 12)}, so it applies under them`));
|
|
801
|
+
return { approval: { status: "approved", approvedBy: cover.approvedBy }, satisfied: true, via: "merges" };
|
|
802
|
+
}
|
|
803
|
+
uncovered = cover.uncovered;
|
|
804
|
+
console.error(formatInfo(`the merged changes' approvals do not cover the plan at ${green.commit.slice(0, 12)}: ${uncovered.join("; ")}`));
|
|
805
|
+
}
|
|
806
|
+
const digest = set.doc.digest;
|
|
807
|
+
const check = await emitGateStep(events?.emit, { op, gate: p.gate }, () => decidePrGate(gitGateLedgerPort(), { op, gate: p.gate, digest, description: `${changeLabel(pr, green)}: ${p.plan.order.join(", ")}` }), (unapproved) => {
|
|
808
|
+
const a = approvalOf(unapproved);
|
|
809
|
+
return a.status === "changed"
|
|
810
|
+
? describePlanChanged({ op, gate: p.gate, digest, approval: a })
|
|
811
|
+
: `No approval stands for this plan. Approve it: ${prApproveCommand({ op, gate: p.gate, digest })}`;
|
|
812
|
+
});
|
|
813
|
+
const approval = approvalOf(check);
|
|
814
|
+
if (check.satisfied)
|
|
815
|
+
return { approval, satisfied: true, via: "gate" };
|
|
816
|
+
if (approval.status === "changed")
|
|
817
|
+
return { approval, satisfied: false, refusal: "plan-changed", message: describePlanChanged({ op, gate: p.gate, digest, approval }) };
|
|
818
|
+
const why = uncovered.length > 0 ? ` The approvals of the merges since the last applied green commit do not cover it: ${uncovered.join("; ")}.` : "";
|
|
819
|
+
return {
|
|
820
|
+
approval,
|
|
821
|
+
satisfied: false,
|
|
822
|
+
refusal: "not-approved",
|
|
823
|
+
message: `No approval stands for this plan, so nothing was applied.${why} Approve it: ${prApproveCommand({ op, gate: p.gate, digest })}; then run the apply again.`,
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* The `--require-review` check for the apply. A green commit (#4168 step 9)
|
|
828
|
+
* has no pull request of its own, so its approvers are checked against every
|
|
829
|
+
* pull request merged since the last applied green commit for authorship
|
|
830
|
+
* only: whoever wrote one of them never counts.
|
|
831
|
+
*/
|
|
832
|
+
function reviewFor(args, p, pr, green) {
|
|
833
|
+
if (!green)
|
|
834
|
+
return reviewCheck(args, p, pr);
|
|
835
|
+
const checks = green.prs.map((n) => reviewCheck(args, p, n, true));
|
|
836
|
+
return async (approvedBy, what) => {
|
|
837
|
+
for (const check of checks) {
|
|
838
|
+
const stop = await check(approvedBy, what);
|
|
839
|
+
if (stop)
|
|
840
|
+
return stop;
|
|
841
|
+
}
|
|
842
|
+
return undefined;
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
/** Record the green commit as applied to the environment (#4168 step 9), and push `chant/lifecycle`. A failure warns. */
|
|
846
|
+
async function recordGreenApplied(p, green, digest, approval, via) {
|
|
847
|
+
const { writeGreenApply, GREEN_APPLY_CONTRACT } = await import("../../lifecycle/green-apply.js");
|
|
848
|
+
const { pushLifecycleReport } = await import("../../lifecycle/git.js");
|
|
849
|
+
try {
|
|
850
|
+
await writeGreenApply({
|
|
851
|
+
contract: GREEN_APPLY_CONTRACT,
|
|
852
|
+
env: p.env,
|
|
853
|
+
...(p.member ? { member: p.member } : {}),
|
|
854
|
+
commit: green.commit,
|
|
855
|
+
base: green.base,
|
|
856
|
+
digest,
|
|
857
|
+
approval: via,
|
|
858
|
+
approvedBy: approval.approvedBy ?? [],
|
|
859
|
+
merges: green.merges,
|
|
860
|
+
appliedAt: new Date().toISOString(),
|
|
861
|
+
});
|
|
862
|
+
const pushed = await pushLifecycleReport();
|
|
863
|
+
if (pushed.pushWarning)
|
|
864
|
+
console.error(formatWarning({ message: pushed.pushWarning }));
|
|
865
|
+
}
|
|
866
|
+
catch (err) {
|
|
867
|
+
console.error(formatWarning({ message: `could not record ${green.commit.slice(0, 12)} as applied to ${p.env}: ${err instanceof Error ? err.message : String(err)}` }));
|
|
868
|
+
}
|
|
869
|
+
}
|
|
766
870
|
/** How a stage stops: settle its events, write and publish the report, say how it ended, and return `code`. */
|
|
767
871
|
function stopper(ctx, p, events, upload) {
|
|
768
872
|
return async (report, code, settle) => {
|
|
@@ -781,7 +885,7 @@ function stopper(ctx, p, events, upload) {
|
|
|
781
885
|
* authors are read once, at the first call. Without `--require-review` every
|
|
782
886
|
* approval counts.
|
|
783
887
|
*/
|
|
784
|
-
function reviewCheck(args, p, pr) {
|
|
888
|
+
function reviewCheck(args, p, pr, authorsOnly = false) {
|
|
785
889
|
let read;
|
|
786
890
|
return async (approvedBy, what) => {
|
|
787
891
|
const forge = p.forge;
|
|
@@ -810,7 +914,7 @@ function reviewCheck(args, p, pr) {
|
|
|
810
914
|
`Have someone else approve the pull request and the plan, then run the apply again.`,
|
|
811
915
|
};
|
|
812
916
|
}
|
|
813
|
-
const strangers = approversWithoutReview(approvedBy, reviewers);
|
|
917
|
+
const strangers = authorsOnly ? [] : approversWithoutReview(approvedBy, reviewers);
|
|
814
918
|
if (strangers.length > 0) {
|
|
815
919
|
return {
|
|
816
920
|
refusal: "not-a-reviewer",
|
|
@@ -1035,7 +1139,7 @@ function inWave(body, wave) {
|
|
|
1035
1139
|
*/
|
|
1036
1140
|
async function prApplyWaves(ctx, p, at) {
|
|
1037
1141
|
const { args } = ctx;
|
|
1038
|
-
const { pr, op, events, upload } = at;
|
|
1142
|
+
const { pr, op, events, upload, green } = at;
|
|
1039
1143
|
const waveGate = args.waveGate;
|
|
1040
1144
|
const only = args.wave;
|
|
1041
1145
|
const total = p.plan.waves.length;
|
|
@@ -1043,7 +1147,7 @@ async function prApplyWaves(ctx, p, at) {
|
|
|
1043
1147
|
const log = applyLog();
|
|
1044
1148
|
log({ type: "run-start", waves: p.plan.waves });
|
|
1045
1149
|
const reportBase = { stage: "apply", pr, env: p.env, ...(p.member ? { member: p.member } : {}), base: p.base, head: p.head, op, gate: p.gate, plan: p.plan, signal: p.signal, waveGate };
|
|
1046
|
-
if (p.plan.order.length === 0 || pr === null) {
|
|
1150
|
+
if (p.plan.order.length === 0 || (pr === null && !green)) {
|
|
1047
1151
|
const set = await planAll(p, events?.emit);
|
|
1048
1152
|
if (p.plan.order.length === 0)
|
|
1049
1153
|
return stop(prReport({ ...reportBase, set, approval: { status: "pending" }, status: "nothing" }), 0);
|
|
@@ -1081,6 +1185,7 @@ async function prApplyWaves(ctx, p, at) {
|
|
|
1081
1185
|
? { doc: carry.record.changeSet, members: carry.record.members, plans: {}, outputs: carry.record.planOutputs, failed: false }
|
|
1082
1186
|
: undefined;
|
|
1083
1187
|
let approval = carry?.approval ?? { status: "pending" };
|
|
1188
|
+
let via = "gate";
|
|
1084
1189
|
const report = (input) => {
|
|
1085
1190
|
const set = approved ?? { doc: composeChangeSet([]), members: [], plans: {}, outputs: {}, failed: false };
|
|
1086
1191
|
const members = set.members.map((m) => {
|
|
@@ -1120,26 +1225,15 @@ async function prApplyWaves(ctx, p, at) {
|
|
|
1120
1225
|
return stop(report({ status: "plan-failed", message: "A member failed to plan, so there is no plan to compare with the approved one. Nothing was applied." }), 1);
|
|
1121
1226
|
}
|
|
1122
1227
|
await storeChangeSetForGate(ctx, p, set.doc);
|
|
1123
|
-
const
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
});
|
|
1129
|
-
approval = approvalOf(check);
|
|
1130
|
-
if (!check.satisfied) {
|
|
1131
|
-
return stop(approval.status === "changed"
|
|
1132
|
-
? report({ status: "refused", refusal: "plan-changed", message: describePlanChanged({ op, gate: p.gate, digest: set.doc.digest, approval }) })
|
|
1133
|
-
: report({
|
|
1134
|
-
status: "refused",
|
|
1135
|
-
refusal: "not-approved",
|
|
1136
|
-
message: `No approval stands for this plan, so nothing was applied. Approve it: ${prApproveCommand({ op, gate: p.gate, digest: set.doc.digest })}; then run the apply again.`,
|
|
1137
|
-
}), GATED_EXIT_CODE);
|
|
1138
|
-
}
|
|
1228
|
+
const decided = await decideApplyGate(p, { op, pr, set, events, ...(green ? { green } : {}) });
|
|
1229
|
+
approval = decided.approval;
|
|
1230
|
+
if (!decided.satisfied)
|
|
1231
|
+
return stop(report({ status: "refused", refusal: decided.refusal, message: decided.message }), GATED_EXIT_CODE);
|
|
1232
|
+
via = decided.via;
|
|
1139
1233
|
planned = set;
|
|
1140
1234
|
}
|
|
1141
1235
|
const approvedSet = approved;
|
|
1142
|
-
const review =
|
|
1236
|
+
const review = reviewFor(args, p, pr, green);
|
|
1143
1237
|
const prReview = await review(approval.approvedBy ?? [], "The plan");
|
|
1144
1238
|
if (prReview && "error" in prReview)
|
|
1145
1239
|
return fail(prReview.error);
|
|
@@ -1223,7 +1317,7 @@ async function prApplyWaves(ctx, p, at) {
|
|
|
1223
1317
|
await storeChangeSetForGate(ctx, p, fresh.doc);
|
|
1224
1318
|
writeWaveChangeSet(ctx, k, fresh.doc, upload);
|
|
1225
1319
|
const approveLine = prApproveCommand({ op, gate, digest });
|
|
1226
|
-
const check = await emitGateStep(events?.emit, { op, gate }, () => decidePrGate(gitGateLedgerPort(), { op, gate, digest, description:
|
|
1320
|
+
const check = await emitGateStep(events?.emit, { op, gate }, () => decidePrGate(gitGateLedgerPort(), { op, gate, digest, description: `${changeLabel(pr, green)}, wave ${k} of ${total}: ${wavePlan.order.join(", ")}` }), (unapproved) => unapproved.mismatch
|
|
1227
1321
|
? `wave ${k}'s plan changed after its approval. Approve the plan now: ${approveLine}`
|
|
1228
1322
|
: `wave ${k}'s plan is not the one the pull request approved. Approve it: ${approveLine}`);
|
|
1229
1323
|
const waveApproval = approvalOf(check);
|
|
@@ -1300,6 +1394,8 @@ async function prApplyWaves(ctx, p, at) {
|
|
|
1300
1394
|
console.error(formatSuccess(`applied wave ${k} of ${total} under ${entry.approval === "pull-request" ? `the pull request's approval of ${approvedSet.doc.digest}` : `${entry.gate}, approved for ${entry.digest}`} by ${(entry.approvedBy ?? []).join(", ")}`));
|
|
1301
1395
|
}
|
|
1302
1396
|
const done = p.plan.order.every((name) => completed.has(name));
|
|
1397
|
+
if (green && done)
|
|
1398
|
+
await recordGreenApplied(p, green, approvedSet.doc.digest, approval, via);
|
|
1303
1399
|
return stop(report({ status: done ? "applied" : "wave-applied" }), 0);
|
|
1304
1400
|
}
|
|
1305
1401
|
/** A wave's new plan (#4003), beside the report as `change-set-wave-<k>.json`, for whoever approves its gate. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promote.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/promote.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;
|
|
1
|
+
{"version":3,"file":"promote.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/promote.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAgDH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAyyBlD,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA2E/E;AAED,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAsChF;AAsCD,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA6BhF"}
|