@mutmutco/cli 2.28.0 → 2.28.1

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 (2) hide show
  1. package/dist/main.cjs +55 -12
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -7597,6 +7597,51 @@ async function discoverRequiredCheckContexts(deps, ctx, branch) {
7597
7597
  }
7598
7598
  return [...contexts];
7599
7599
  }
7600
+ async function findOpenAlignmentPr(deps, ctx) {
7601
+ const out = clean(await deps.run("gh", ["pr", "list", "--repo", ctx.repo, "--base", "development", "--head", "main", "--state", "open", "--json", "number,url"]));
7602
+ if (!out) return void 0;
7603
+ const rows = JSON.parse(out);
7604
+ const row = rows.find((r) => typeof r.number === "number" && typeof r.url === "string");
7605
+ return row ? { number: row.number, url: row.url } : void 0;
7606
+ }
7607
+ function parsePrNumber(url) {
7608
+ const n = Number.parseInt(url.split("/").pop() ?? "", 10);
7609
+ return Number.isFinite(n) ? n : void 0;
7610
+ }
7611
+ async function rollDevelopmentForward(deps, ctx, tag) {
7612
+ const required = await discoverRequiredCheckContexts(deps, ctx, "development");
7613
+ if (required.length === 0) {
7614
+ await deps.run("git", ["checkout", "development"]);
7615
+ await deps.run("git", ["pull", "--ff-only", "origin", "development"]);
7616
+ await deps.run("git", ["merge", "main", "--no-edit"]);
7617
+ await deps.run("git", ["push", "origin", "development"]);
7618
+ return { status: "pushed", note: "development rolled forward to the released main (development has no required checks)" };
7619
+ }
7620
+ const existing = await findOpenAlignmentPr(deps, ctx);
7621
+ if (existing) {
7622
+ return {
7623
+ status: "pr-pending",
7624
+ prNumber: existing.number,
7625
+ prUrl: existing.url,
7626
+ note: `alignment PR already open: ${existing.url} \u2014 land it with \`gh pr merge ${existing.number} --merge\``
7627
+ };
7628
+ }
7629
+ const ahead = clean(await deps.run("git", ["rev-list", "--count", "origin/development..main"]));
7630
+ if (ahead === "0") {
7631
+ return { status: "aligned", note: "development already contains the released main; nothing to roll forward" };
7632
+ }
7633
+ const body = `Carries the ${tag} release (including the version fold) from \`main\` back to \`development\`.
7634
+
7635
+ \`development\` requires status checks, so the release train opens this alignment PR instead of a direct push of the un-checked merge commit (#1143). Land it with a **true merge** (\`gh pr merge --merge\`, not squash) so the merge parentage survives and the misalignment guard stays satisfied.`;
7636
+ const url = clean(await deps.run("gh", ["pr", "create", "--repo", ctx.repo, "--base", "development", "--head", "main", "--title", `chore(release): align development to ${tag}`, "--body", body]));
7637
+ const number = parsePrNumber(url);
7638
+ return {
7639
+ status: "pr-pending",
7640
+ prNumber: number,
7641
+ prUrl: url || void 0,
7642
+ note: `development requires checks (${required.join(", ")}); opened alignment PR ${url || "(url unavailable)"} \u2014 land it with \`gh pr merge ${number ?? "<number>"} --merge\``
7643
+ };
7644
+ }
7600
7645
  function resolveContextState(context, checkRuns, statuses) {
7601
7646
  let sawFailure = false;
7602
7647
  for (const r of checkRuns) {
@@ -7855,10 +7900,7 @@ async function runTrainApply(command, deps, options = {}) {
7855
7900
  const announceNote2 = deps.announce ? (await deps.announce({ repo: ctx.repo, tag: tag2, summaryFile: options.announceSummaryFile })).note : void 0;
7856
7901
  const autoRunSince2 = (deps.now ?? Date.now)();
7857
7902
  const d2 = await dispatchDeploy(deps, ctx, "main", "main", deployModel2, watch, autoRunSince2, releaseSha2, "report");
7858
- await deps.run("git", ["checkout", "development"]);
7859
- await deps.run("git", ["pull", "--ff-only", "origin", "development"]);
7860
- await deps.run("git", ["merge", "main", "--no-edit"]);
7861
- await deps.run("git", ["push", "origin", "development"]);
7903
+ const devRollForward2 = await rollDevelopmentForward(deps, ctx, tag2);
7862
7904
  return {
7863
7905
  ...ctx,
7864
7906
  command,
@@ -7876,6 +7918,7 @@ async function runTrainApply(command, deps, options = {}) {
7876
7918
  deployStatus: d2.deployStatus,
7877
7919
  rcRetirement: "not-applicable",
7878
7920
  rcRetirementNote: "direct-track release skips rc; no rc runtime to retire",
7921
+ devRollForward: devRollForward2,
7879
7922
  announceNote: announceNote2,
7880
7923
  // #1062: --dev on a direct-track repo is a friendly no-op — it already releases from development.
7881
7924
  devNote: options.dev ? "--dev is a no-op on a direct-track repo \u2014 it already releases development -> main" : void 0,
@@ -7927,10 +7970,7 @@ async function runTrainApply(command, deps, options = {}) {
7927
7970
  const autoRunSince2 = (deps.now ?? Date.now)();
7928
7971
  const d2 = await dispatchDeploy(deps, ctx, "main", "main", deployModel2, watch, autoRunSince2, releaseSha2, "report");
7929
7972
  const retirement2 = await retireRcRuntime(deps, ctx, deployModel2, d2.deployStatus, rcShaAtRelease);
7930
- await deps.run("git", ["checkout", "development"]);
7931
- await deps.run("git", ["pull", "--ff-only", "origin", "development"]);
7932
- await deps.run("git", ["merge", "main", "--no-edit"]);
7933
- await deps.run("git", ["push", "origin", "development"]);
7973
+ const devRollForward2 = await rollDevelopmentForward(deps, ctx, tag2);
7934
7974
  let rcAlignment2;
7935
7975
  try {
7936
7976
  await deps.run("git", ["push", "origin", "main:rc"]);
@@ -7958,6 +7998,7 @@ async function runTrainApply(command, deps, options = {}) {
7958
7998
  rcRetirementNote: retirement2.note,
7959
7999
  rcRetirementCategory: retirement2.category,
7960
8000
  rcAlignment: rcAlignment2,
8001
+ devRollForward: devRollForward2,
7961
8002
  announceNote: announceNote2,
7962
8003
  devNote: "released development -> main (--dev), skipping the rc candidate",
7963
8004
  release: { tag: tag2, url: releaseUrl2, targetSha: releaseSha2 },
@@ -8007,10 +8048,7 @@ async function runTrainApply(command, deps, options = {}) {
8007
8048
  const announceNote = deps.announce ? (await deps.announce({ repo: ctx.repo, tag, summaryFile: options.announceSummaryFile })).note : void 0;
8008
8049
  const d = await dispatchDeploy(deps, ctx, "main", "main", deployModel, watch, autoRunSince, releaseSha, "report");
8009
8050
  const retirement = await retireRcRuntime(deps, ctx, deployModel, d.deployStatus, releasedRcSha);
8010
- await deps.run("git", ["checkout", "development"]);
8011
- await deps.run("git", ["pull", "--ff-only", "origin", "development"]);
8012
- await deps.run("git", ["merge", "main", "--no-edit"]);
8013
- await deps.run("git", ["push", "origin", "development"]);
8051
+ const devRollForward = await rollDevelopmentForward(deps, ctx, tag);
8014
8052
  let rcAlignment;
8015
8053
  try {
8016
8054
  await deps.run("git", ["push", "origin", "main:rc"]);
@@ -8038,6 +8076,7 @@ async function runTrainApply(command, deps, options = {}) {
8038
8076
  rcRetirementNote: retirement.note,
8039
8077
  rcRetirementCategory: retirement.category,
8040
8078
  rcAlignment,
8079
+ devRollForward,
8041
8080
  announceNote,
8042
8081
  release: { tag, url: releaseUrl, targetSha: releaseSha },
8043
8082
  environments
@@ -13212,6 +13251,10 @@ function renderTrainApply(commandName, r) {
13212
13251
  if (r.resumeNote) base = `${base}; ${r.resumeNote}`;
13213
13252
  if (r.devNote) base = `${base}; ${r.devNote}`;
13214
13253
  if (r.rcRetirement) base = `${base}; rc retirement: ${r.rcRetirement.toUpperCase()} (${r.rcRetirementNote ?? ""})`;
13254
+ if (r.devRollForward) {
13255
+ const f = r.devRollForward;
13256
+ base = f.status === "pr-pending" ? `${base}; dev roll-forward: ALIGNMENT PR PENDING \u2014 land it with \`gh pr merge ${f.prNumber ?? "<number>"} --merge\`${f.prUrl ? ` (${f.prUrl})` : ""}` : `${base}; dev roll-forward: ${f.note}`;
13257
+ }
13215
13258
  return r.announceNote ? `${base}; announce: ${r.announceNote}` : base;
13216
13259
  }
13217
13260
  function renderTenantRedeploy(r) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "2.28.0",
3
+ "version": "2.28.1",
4
4
  "description": "MMI Future CLI — delivers the org rules (whole-file), plus saga and KB access. The cross-IDE engine the plugin's SessionStart hook drives.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",