@intentius/chant 0.124.34 → 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 +23 -8
- 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 +19 -1
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +17 -0
- package/dist/cli/registry.d.ts +14 -0
- 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.d.ts +4 -1
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +6 -3
- 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 +5 -1
- package/dist/graph-declared.d.ts.map +1 -1
- package/dist/graph-declared.js +7 -2
- 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 +225 -223
- package/dist/workspace/checks/ci-workflow.d.ts.map +1 -1
- package/dist/workspace/checks/ci-workflow.js +8 -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-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 +4 -4
- package/dist/workspace/ci-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-workflow.js +2 -2
- 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/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/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/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 +22 -8
- 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 +19 -1
- package/src/cli/parse-args.ts +13 -0
- package/src/cli/registry.ts +14 -0
- 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.test.ts +7 -0
- package/src/components/promote.ts +9 -4
- 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.test.ts +18 -0
- package/src/graph-declared.ts +8 -1
- 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/checks/ci-workflow.test.ts +12 -2
- package/src/workspace/checks/ci-workflow.ts +7 -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-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 +4 -4
- package/src/workspace/delivery-metrics.ts +12 -3
- package/src/workspace/graph-contract.test.ts +3 -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/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/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"}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* environment: every component of the release at the digest the environment
|
|
21
21
|
* ran from it, through the release gate `promote --release` binds (#4045).
|
|
22
22
|
*/
|
|
23
|
+
import { ENV_VAR } from "../../env.js";
|
|
23
24
|
import { dirname, join, resolve } from "node:path";
|
|
24
25
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
25
26
|
import { loadChantConfig } from "../../config.js";
|
|
@@ -73,9 +74,16 @@ function planJson(plan, removed) {
|
|
|
73
74
|
function resolveActor(ctx) {
|
|
74
75
|
return ctx.args.actor ?? process.env.GITHUB_ACTOR ?? process.env.GITLAB_USER_LOGIN ?? process.env.USER;
|
|
75
76
|
}
|
|
76
|
-
/**
|
|
77
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Config, params and the declared components, or the exit code of a refusal
|
|
79
|
+
* already printed. `env` is the environment being deployed: it is set as
|
|
80
|
+
* `CHANT_ENV` before the components load, as `--env` sets it for `chant run`
|
|
81
|
+
* (../main.ts), so a component that reads it (a stack name per environment)
|
|
82
|
+
* deploys to that environment and not to the one it was promoted from (#4222).
|
|
83
|
+
*/
|
|
84
|
+
async function loadProject(ctx, selector, env) {
|
|
78
85
|
const { args } = ctx;
|
|
86
|
+
process.env[ENV_VAR] = env;
|
|
79
87
|
const projectPath = resolve(".");
|
|
80
88
|
const { config } = await loadChantConfig(projectPath).catch(() => ({ config: {} }));
|
|
81
89
|
const paramsResolution = resolveCliBuildParams(config.buildParams, {
|
|
@@ -315,15 +323,21 @@ async function deployPlan(ctx, verb, plan, project, actor, record, under) {
|
|
|
315
323
|
const prepared = [];
|
|
316
324
|
const removed = new Map();
|
|
317
325
|
const refusals = [];
|
|
326
|
+
// #4222: a component with no artifact, deployed from the templates the release records, and its record's digest.
|
|
327
|
+
const templateDigests = new Map();
|
|
328
|
+
const { releaseTemplatesDigest } = await import("../../components/release-promote.js");
|
|
318
329
|
for (const item of plan.items) {
|
|
330
|
+
const templates = under ? releaseTemplatesDigest(under.target, item.component) : null;
|
|
319
331
|
// A rollback and a redeploy deploy a release this environment already
|
|
320
332
|
// received, so neither runs the publish step.
|
|
321
|
-
const result = withoutBuildSteps(byName.get(item.component), verb === "promote" ? {} : { pinDigest: item.digest, verb: `a ${verb}` });
|
|
333
|
+
const result = withoutBuildSteps(byName.get(item.component), { ...(verb === "promote" ? {} : { pinDigest: item.digest, verb: `a ${verb}` }), templatesPinned: templates !== null });
|
|
322
334
|
if ("error" in result)
|
|
323
335
|
refusals.push(result.error);
|
|
324
336
|
else {
|
|
325
337
|
prepared.push(result.component);
|
|
326
338
|
removed.set(item.component, result.removed);
|
|
339
|
+
if (result.templateOnly && templates !== null)
|
|
340
|
+
templateDigests.set(item.component, templates);
|
|
327
341
|
}
|
|
328
342
|
}
|
|
329
343
|
if (refusals.length > 0) {
|
|
@@ -573,7 +587,8 @@ async function deployPlan(ctx, verb, plan, project, actor, record, under) {
|
|
|
573
587
|
const approver = gateApprover(result);
|
|
574
588
|
const admission = gateAdmission(result);
|
|
575
589
|
const input = record(item, plan.to, { runId, ...(runOrigin ? { runOrigin } : {}), actor: actor, timestamp, ...(approver ? { approver } : {}), ...(admission ? { admission } : {}) });
|
|
576
|
-
const
|
|
590
|
+
const templates = templateDigests.get(item.component);
|
|
591
|
+
const { record: written } = await appendReleaseRecord(under ? { ...input, ...(templates ? { digest: templates } : {}), releaseDigest: under.release.releaseDigest } : input);
|
|
577
592
|
recorded.push(written);
|
|
578
593
|
}
|
|
579
594
|
if (outcome)
|
|
@@ -792,7 +807,7 @@ export async function runComponentsPromote(ctx) {
|
|
|
792
807
|
const who = actorOrRefuse(ctx);
|
|
793
808
|
if (typeof who === "number")
|
|
794
809
|
return who;
|
|
795
|
-
const project = await loadProject(ctx, args.component ?? "all");
|
|
810
|
+
const project = await loadProject(ctx, args.component ?? "all", to);
|
|
796
811
|
if (typeof project === "number")
|
|
797
812
|
return project;
|
|
798
813
|
// The member's `environments` array is its promotion order (#4030). It
|
|
@@ -874,7 +889,7 @@ export async function runComponentsRollback(ctx) {
|
|
|
874
889
|
return who;
|
|
875
890
|
if (args.release)
|
|
876
891
|
return runReleaseRollback(ctx, env, args.release, who.actor);
|
|
877
|
-
const project = await loadProject(ctx, args.component);
|
|
892
|
+
const project = await loadProject(ctx, args.component, env);
|
|
878
893
|
if (typeof project === "number")
|
|
879
894
|
return project;
|
|
880
895
|
await fetchLifecycle().catch(() => false);
|
|
@@ -904,7 +919,7 @@ export async function runComponentsRollback(ctx) {
|
|
|
904
919
|
* runs now, as `promote --release` binds them (#4045).
|
|
905
920
|
*/
|
|
906
921
|
async function runReleaseRollback(ctx, env, ref, actor) {
|
|
907
|
-
const project = await loadProject(ctx, "all");
|
|
922
|
+
const project = await loadProject(ctx, "all", env);
|
|
908
923
|
if (typeof project === "number")
|
|
909
924
|
return project;
|
|
910
925
|
await fetchLifecycle().catch(() => false);
|
|
@@ -942,7 +957,7 @@ export async function runComponentsRedeploy(ctx) {
|
|
|
942
957
|
const who = actorOrRefuse(ctx);
|
|
943
958
|
if (typeof who === "number")
|
|
944
959
|
return who;
|
|
945
|
-
const project = await loadProject(ctx, args.component);
|
|
960
|
+
const project = await loadProject(ctx, args.component, env);
|
|
946
961
|
if (typeof project === "number")
|
|
947
962
|
return project;
|
|
948
963
|
await fetchLifecycle().catch(() => false);
|
|
@@ -20,6 +20,11 @@
|
|
|
20
20
|
* release candidates [--env <env>] [--json]
|
|
21
21
|
* the green commits of trunk a release can be cut from, each with what it
|
|
22
22
|
* would ship, and what each environment runs (#4168 4.2)
|
|
23
|
+
* release compose <name> [--base <label|sha>] (--pr <n> | --work <id>)... [--yes] [--json]
|
|
24
|
+
* pick chosen trunk changes onto a base, each with a Chant-Composed-From
|
|
25
|
+
* trailer, on the pushed branch chant/compose/<name> (#4168 4.3); a dry
|
|
26
|
+
* run unless --yes. `release cut` takes a green commit of that branch,
|
|
27
|
+
* and `--channel <tag>` makes its publish move that tag, not latest
|
|
23
28
|
* release list [--json]
|
|
24
29
|
* every release of this member with its targets, its cuts and where it runs;
|
|
25
30
|
* `--json` prints the read contract's `releases` document
|
|
@@ -103,4 +108,11 @@ export declare function runReleaseExport(ctx: CommandContext): Promise<number>;
|
|
|
103
108
|
/** Internal: compile one environment, or build artifacts, in this process, and print the JSON the cut reads. */
|
|
104
109
|
export declare function runReleaseCompile(ctx: CommandContext): Promise<number>;
|
|
105
110
|
export declare function runReleaseUnknown(ctx: CommandContext): Promise<number>;
|
|
111
|
+
/**
|
|
112
|
+
* `release compose <name> [--base <label|sha>] (--pr <n> | --work <id>)... [--yes] [--json]`
|
|
113
|
+
* (#4168 4.3): pick the chosen trunk changes onto a base, on
|
|
114
|
+
* `chant/compose/<name>` (../../lifecycle/release-compose.ts). A dry run
|
|
115
|
+
* unless `--yes`, which moves the branch and pushes it so CI builds it.
|
|
116
|
+
*/
|
|
117
|
+
export declare function runReleaseCompose(ctx: CommandContext): Promise<number>;
|
|
106
118
|
//# sourceMappingURL=release.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"release.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/release.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"release.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/release.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAyBlD,0HAA0H;AAC1H,wBAAgB,YAAY,CAAC,GAAG,EAAE;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,uFAAuF;IACvF,YAAY,EAAE,OAAO,CAAC;CACvB,GAAG,MAAM,EAAE,CASX;AAED,wGAAwG;AACxG,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAG3D;AAiBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAqPxE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA4G5E;AAQD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC3E;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAsC/E;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA8BzE;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAyC3E;AAED,gHAAgH;AAChH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB5E;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAG5E;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAsD5E"}
|