@intentius/chant 0.124.34 → 0.124.36

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.
Files changed (171) hide show
  1. package/dist/cli/handlers/graph.js +2 -2
  2. package/dist/cli/handlers/pr.d.ts.map +1 -1
  3. package/dist/cli/handlers/pr.js +147 -51
  4. package/dist/cli/handlers/promote.d.ts.map +1 -1
  5. package/dist/cli/handlers/promote.js +23 -8
  6. package/dist/cli/handlers/release.d.ts +12 -0
  7. package/dist/cli/handlers/release.d.ts.map +1 -1
  8. package/dist/cli/handlers/release.js +125 -10
  9. package/dist/cli/handlers/run.d.ts +3 -1
  10. package/dist/cli/handlers/run.d.ts.map +1 -1
  11. package/dist/cli/handlers/run.js +12 -2
  12. package/dist/cli/main.d.ts.map +1 -1
  13. package/dist/cli/main.js +19 -1
  14. package/dist/cli/parse-args.d.ts.map +1 -1
  15. package/dist/cli/parse-args.js +17 -0
  16. package/dist/cli/registry.d.ts +14 -0
  17. package/dist/cli/registry.d.ts.map +1 -1
  18. package/dist/components/auto-release.d.ts +8 -1
  19. package/dist/components/auto-release.d.ts.map +1 -1
  20. package/dist/components/auto-release.js +3 -2
  21. package/dist/components/green-cover.d.ts +77 -0
  22. package/dist/components/green-cover.d.ts.map +1 -0
  23. package/dist/components/green-cover.js +153 -0
  24. package/dist/components/pr-pipeline.d.ts +34 -0
  25. package/dist/components/pr-pipeline.d.ts.map +1 -1
  26. package/dist/components/pr-pipeline.js +39 -5
  27. package/dist/components/promote.d.ts +4 -1
  28. package/dist/components/promote.d.ts.map +1 -1
  29. package/dist/components/promote.js +6 -3
  30. package/dist/components/release-promote.d.ts +9 -0
  31. package/dist/components/release-promote.d.ts.map +1 -1
  32. package/dist/components/release-promote.js +15 -0
  33. package/dist/config.d.ts +3 -0
  34. package/dist/config.d.ts.map +1 -1
  35. package/dist/deep-observation.d.ts +11 -0
  36. package/dist/deep-observation.d.ts.map +1 -1
  37. package/dist/graph-declared.d.ts +5 -1
  38. package/dist/graph-declared.d.ts.map +1 -1
  39. package/dist/graph-declared.js +7 -2
  40. package/dist/graph-detail.d.ts.map +1 -1
  41. package/dist/graph-detail.js +30 -1
  42. package/dist/graph-ir.d.ts.map +1 -1
  43. package/dist/graph-ir.js +13 -2
  44. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +5 -0
  45. package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -1
  46. package/dist/lifecycle/__fixtures__/publish-fakes.js +20 -2
  47. package/dist/lifecycle/compose-refs.d.ts +52 -0
  48. package/dist/lifecycle/compose-refs.d.ts.map +1 -0
  49. package/dist/lifecycle/compose-refs.js +115 -0
  50. package/dist/lifecycle/deep-observe.d.ts.map +1 -1
  51. package/dist/lifecycle/deep-observe.js +3 -2
  52. package/dist/lifecycle/green-apply.d.ts +39 -0
  53. package/dist/lifecycle/green-apply.d.ts.map +1 -0
  54. package/dist/lifecycle/green-apply.js +51 -0
  55. package/dist/lifecycle/publish-declaration.d.ts +6 -0
  56. package/dist/lifecycle/publish-declaration.d.ts.map +1 -1
  57. package/dist/lifecycle/publish-declaration.js +21 -0
  58. package/dist/lifecycle/publish-npm.d.ts +11 -4
  59. package/dist/lifecycle/publish-npm.d.ts.map +1 -1
  60. package/dist/lifecycle/publish-npm.js +104 -14
  61. package/dist/lifecycle/release-compose.d.ts +91 -0
  62. package/dist/lifecycle/release-compose.d.ts.map +1 -0
  63. package/dist/lifecycle/release-compose.js +235 -0
  64. package/dist/lifecycle/release-cut.d.ts +8 -0
  65. package/dist/lifecycle/release-cut.d.ts.map +1 -1
  66. package/dist/lifecycle/release-cut.js +2 -0
  67. package/dist/lifecycle/release-publish.d.ts +6 -0
  68. package/dist/lifecycle/release-publish.d.ts.map +1 -1
  69. package/dist/lifecycle/release-publish.js +2 -1
  70. package/dist/lifecycle/release-store.d.ts +15 -0
  71. package/dist/lifecycle/release-store.d.ts.map +1 -1
  72. package/dist/lifecycle/release-store.js +5 -1
  73. package/dist/wave-runtime.mjs +225 -223
  74. package/dist/workspace/checks/ci-workflow.d.ts.map +1 -1
  75. package/dist/workspace/checks/ci-workflow.js +8 -1
  76. package/dist/workspace/ci-green-cli.d.ts.map +1 -1
  77. package/dist/workspace/ci-green-cli.js +5 -3
  78. package/dist/workspace/ci-green-workflow.d.ts +18 -2
  79. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  80. package/dist/workspace/ci-green-workflow.js +27 -5
  81. package/dist/workspace/ci-green.d.ts +31 -0
  82. package/dist/workspace/ci-green.d.ts.map +1 -1
  83. package/dist/workspace/ci-green.js +78 -4
  84. package/dist/workspace/ci-run.d.ts +6 -0
  85. package/dist/workspace/ci-run.d.ts.map +1 -1
  86. package/dist/workspace/ci-run.js +2 -1
  87. package/dist/workspace/ci-serve.d.ts +94 -13
  88. package/dist/workspace/ci-serve.d.ts.map +1 -1
  89. package/dist/workspace/ci-serve.js +248 -30
  90. package/dist/workspace/ci-workflow.d.ts +4 -4
  91. package/dist/workspace/ci-workflow.d.ts.map +1 -1
  92. package/dist/workspace/ci-workflow.js +2 -2
  93. package/dist/workspace/delivery-metrics.d.ts +3 -1
  94. package/dist/workspace/delivery-metrics.d.ts.map +1 -1
  95. package/dist/workspace/delivery-metrics.js +12 -3
  96. package/dist/workspace/member-commands.d.ts.map +1 -1
  97. package/dist/workspace/member-commands.js +4 -0
  98. package/dist/workspace/nested-graph.d.ts.map +1 -1
  99. package/dist/workspace/nested-graph.js +1 -0
  100. package/dist/workspace/release-candidates.d.ts +28 -0
  101. package/dist/workspace/release-candidates.d.ts.map +1 -1
  102. package/dist/workspace/release-candidates.js +38 -2
  103. package/dist/workspace/release-candidates.schema.json +51 -0
  104. package/dist/workspace/releases.schema.json +30 -0
  105. package/package.json +1 -1
  106. package/src/cli/handlers/graph.ts +2 -2
  107. package/src/cli/handlers/pr-green.test.ts +230 -0
  108. package/src/cli/handlers/pr.ts +148 -66
  109. package/src/cli/handlers/promote.test.ts +34 -0
  110. package/src/cli/handlers/promote.ts +22 -8
  111. package/src/cli/handlers/release-compose.e2e.test.ts +133 -0
  112. package/src/cli/handlers/release-publish-targets.e2e.test.ts +4 -1
  113. package/src/cli/handlers/release.ts +112 -10
  114. package/src/cli/handlers/run.ts +12 -1
  115. package/src/cli/main.ts +19 -1
  116. package/src/cli/parse-args.ts +13 -0
  117. package/src/cli/registry.ts +14 -0
  118. package/src/codegen/release-wiring.test.ts +56 -9
  119. package/src/components/auto-release.test.ts +22 -0
  120. package/src/components/auto-release.ts +9 -2
  121. package/src/components/green-cover.test.ts +170 -0
  122. package/src/components/green-cover.ts +186 -0
  123. package/src/components/pr-pipeline.test.ts +14 -4
  124. package/src/components/pr-pipeline.ts +56 -5
  125. package/src/components/promote.test.ts +7 -0
  126. package/src/components/promote.ts +9 -4
  127. package/src/components/release-promote.test.ts +24 -1
  128. package/src/components/release-promote.ts +15 -0
  129. package/src/deep-observation.ts +8 -0
  130. package/src/graph-declared.test.ts +18 -0
  131. package/src/graph-declared.ts +8 -1
  132. package/src/graph-detail.test.ts +68 -1
  133. package/src/graph-detail.ts +29 -1
  134. package/src/graph-ir.ts +12 -4
  135. package/src/lifecycle/__fixtures__/publish-fakes.ts +20 -2
  136. package/src/lifecycle/compose-refs.ts +125 -0
  137. package/src/lifecycle/deep-observe.test.ts +20 -0
  138. package/src/lifecycle/deep-observe.ts +4 -2
  139. package/src/lifecycle/green-apply.ts +71 -0
  140. package/src/lifecycle/publish-declaration.ts +21 -0
  141. package/src/lifecycle/publish-npm.ts +100 -14
  142. package/src/lifecycle/publish-targets.test.ts +108 -0
  143. package/src/lifecycle/release-compose.test.ts +125 -0
  144. package/src/lifecycle/release-compose.ts +312 -0
  145. package/src/lifecycle/release-cut.ts +6 -0
  146. package/src/lifecycle/release-publish.ts +8 -1
  147. package/src/lifecycle/release-store.ts +16 -1
  148. package/src/workspace/checks/ci-workflow.test.ts +12 -2
  149. package/src/workspace/checks/ci-workflow.ts +7 -1
  150. package/src/workspace/ci-green-cli.ts +5 -3
  151. package/src/workspace/ci-green-workflow.test.ts +52 -0
  152. package/src/workspace/ci-green-workflow.ts +34 -5
  153. package/src/workspace/ci-green.test.ts +33 -0
  154. package/src/workspace/ci-green.ts +85 -4
  155. package/src/workspace/ci-run.test.ts +18 -13
  156. package/src/workspace/ci-run.ts +8 -1
  157. package/src/workspace/ci-serve-land.e2e.test.ts +138 -0
  158. package/src/workspace/ci-serve-land.test.ts +176 -0
  159. package/src/workspace/ci-serve.test.ts +77 -43
  160. package/src/workspace/ci-serve.ts +268 -34
  161. package/src/workspace/ci-workflow.ts +4 -4
  162. package/src/workspace/delivery-metrics.ts +12 -3
  163. package/src/workspace/graph-contract.test.ts +3 -2
  164. package/src/workspace/member-commands.test.ts +11 -4
  165. package/src/workspace/member-commands.ts +3 -0
  166. package/src/workspace/nested-graph.test.ts +4 -0
  167. package/src/workspace/nested-graph.ts +1 -0
  168. package/src/workspace/release-candidates.schema.json +51 -0
  169. package/src/workspace/release-candidates.test.ts +30 -3
  170. package/src/workspace/release-candidates.ts +54 -2
  171. 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;AAuDH,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;AAuPD,uKAAuK;AACvK,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAG1E"}
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"}
@@ -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 check = await emitGateStep(events?.emit, { op, gate: p.gate }, () => decidePrGate(gitGateLedgerPort(), {
516
- op,
517
- gate: p.gate,
518
- digest: set.doc.digest,
519
- description: `pull request #${pr}: ${p.plan.order.join(", ")}`,
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 = reviewCheck(args, p, pr);
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: `pull request #${pr}, round ${round}: planned again after an upstream applied` }));
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 check = await emitGateStep(events?.emit, { op, gate: p.gate }, () => decidePrGate(gitGateLedgerPort(), { op, gate: p.gate, digest: set.doc.digest, description: `pull request #${pr}: ${p.plan.order.join(", ")}` }), (unapproved) => {
1124
- const a = approvalOf(unapproved);
1125
- return a.status === "changed"
1126
- ? describePlanChanged({ op, gate: p.gate, digest: set.doc.digest, approval: a })
1127
- : `No approval stands for this plan. Approve it: ${prApproveCommand({ op, gate: p.gate, digest: set.doc.digest })}`;
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 = reviewCheck(args, p, pr);
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: `pull request #${pr}, wave ${k} of ${total}: ${wavePlan.order.join(", ")}` }), (unapproved) => unapproved.mismatch
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;AA+CH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA4xBlD,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"}
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
- /** Config, params and the declared components, or the exit code of a refusal already printed. */
77
- async function loadProject(ctx, selector) {
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 { record: written } = await appendReleaseRecord(under ? { ...input, releaseDigest: under.release.releaseDigest } : input);
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;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,CAwNxE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA2G5E;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,CAiC/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"}
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"}