@mutmutco/cli 3.45.0 → 3.46.0

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 +66 -32
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -20428,13 +20428,46 @@ async function pollGhPrChecks(prNumber, repoArgs) {
20428
20428
  }
20429
20429
  return state;
20430
20430
  }
20431
- async function ghMergeAutoEnqueue(prNumber, repo, method) {
20431
+ function ghFailureReason(e) {
20432
+ const err = e;
20433
+ const head = String(err?.message ?? "").split("\n")[0]?.trim() ?? "";
20434
+ const detail = `${err?.stderr ?? ""}
20435
+ ${err?.stdout ?? ""}`.split("\n").map((line) => line.trim()).find((line) => line.length > 0);
20436
+ if (detail && head) return `${head} \u2014 ${detail}`;
20437
+ return detail || head || "unknown error";
20438
+ }
20439
+ var defaultGhMergeAutoIo = {
20440
+ gh: async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout
20441
+ };
20442
+ async function ghMergeAutoEnqueue(prNumber, repo, method, io = defaultGhMergeAutoIo) {
20432
20443
  const args = repo ? ["--repo", repo] : [];
20433
- const headRef = (await execFileP2("gh", ["pr", "view", prNumber, ...args, "--json", "headRefName", "--jq", ".headRefName"], { timeout: GC_GH_TIMEOUT_MS2 }).catch(() => ({ stdout: "" }))).stdout.trim();
20444
+ const headRef = (await io.gh(["pr", "view", prNumber, ...args, "--json", "headRefName", "--jq", ".headRefName"], GC_GH_TIMEOUT_MS2).catch(() => "")).trim();
20434
20445
  const deleteBranch = !isProtectedBranch(headRef);
20435
- const readMergeState = () => readGhPrStateWithRetry(async () => (await execFileP2("gh", ["pr", "view", prNumber, ...args, "--json", "state", "--jq", ".state"], { timeout: GC_GH_TIMEOUT_MS2 })).stdout);
20446
+ const readMergeState = () => readGhPrStateWithRetry(
20447
+ () => io.gh(["pr", "view", prNumber, ...args, "--json", "state", "--jq", ".state"], GC_GH_TIMEOUT_MS2)
20448
+ );
20449
+ const confirmEnqueueOutcome = async () => {
20450
+ const stateRead = await readMergeState();
20451
+ if (!stateRead.ok) {
20452
+ return { mergeStatus: "failed", error: `could not read PR state after merge: ${stateRead.error}` };
20453
+ }
20454
+ if (stateRead.state === "MERGED") return { mergeStatus: "merged" };
20455
+ const confirmation = await confirmAutoMergeEnqueued({
20456
+ readAutoMergeRequest: async () => io.gh(["pr", "view", prNumber, ...args, "--json", "autoMergeRequest", "--jq", '.autoMergeRequest // ""'], GC_GH_TIMEOUT_MS2),
20457
+ readMerged: async () => {
20458
+ const s = await readMergeState();
20459
+ return s.ok && s.state === "MERGED";
20460
+ },
20461
+ reEnqueue: async () => {
20462
+ await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch }), GH_MUTATION_TIMEOUT_MS);
20463
+ }
20464
+ });
20465
+ if (confirmation === "merged") return { mergeStatus: "merged" };
20466
+ if (confirmation === "enqueued") return { mergeStatus: "auto-merge-enqueued" };
20467
+ return { mergeStatus: "failed", error: "auto-merge did not stick \u2014 GitHub reported no autoMergeRequest after enqueue; retry once the PR has a pending check" };
20468
+ };
20436
20469
  try {
20437
- await execFileP2("gh", buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch }), { timeout: GH_MUTATION_TIMEOUT_MS });
20470
+ await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch }), GH_MUTATION_TIMEOUT_MS);
20438
20471
  } catch (e) {
20439
20472
  const message = String(e.message || "");
20440
20473
  if (/already been merged/i.test(message)) return { mergeStatus: "merged" };
@@ -20442,50 +20475,51 @@ async function ghMergeAutoEnqueue(prNumber, repo, method) {
20442
20475
  if (note) return { mergeStatus: "failed", error: note };
20443
20476
  if (mergeAutoRejectedPrAlreadyClean(message)) {
20444
20477
  try {
20445
- await execFileP2("gh", buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: false, deleteBranch }), { timeout: GH_MUTATION_TIMEOUT_MS });
20478
+ await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: false, deleteBranch }), GH_MUTATION_TIMEOUT_MS);
20446
20479
  } catch (e2) {
20447
20480
  const m2 = String(e2.message || "");
20448
20481
  if (/already been merged/i.test(m2)) return { mergeStatus: "merged" };
20449
- if (!ghPrMergeLocalBranchDeleteWarning(m2)) return { mergeStatus: "failed", error: m2.split("\n")[0] };
20482
+ if (!ghPrMergeLocalBranchDeleteWarning(m2)) {
20483
+ const afterDirect = await readMergeState();
20484
+ if (afterDirect.ok && afterDirect.state === "MERGED") return { mergeStatus: "merged" };
20485
+ if (!afterDirect.ok) {
20486
+ return { mergeStatus: "failed", error: `could not confirm PR state after clean-status fallback: ${afterDirect.error}` };
20487
+ }
20488
+ try {
20489
+ await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch }), GH_MUTATION_TIMEOUT_MS);
20490
+ } catch (e3) {
20491
+ const m3 = String(e3.message || "");
20492
+ if (/already been merged/i.test(m3)) return { mergeStatus: "merged" };
20493
+ const afterRetry = await readMergeState();
20494
+ if (afterRetry.ok && afterRetry.state === "MERGED") return { mergeStatus: "merged" };
20495
+ const stuck = (await io.gh(["pr", "view", prNumber, ...args, "--json", "autoMergeRequest", "--jq", '.autoMergeRequest // ""'], GC_GH_TIMEOUT_MS2).catch(() => "")).trim();
20496
+ if (stuck) return { mergeStatus: "auto-merge-enqueued" };
20497
+ return { mergeStatus: "failed", error: `${ghFailureReason(e2)} (re-enqueue also failed: ${ghFailureReason(e3)})` };
20498
+ }
20499
+ return confirmEnqueueOutcome();
20500
+ }
20450
20501
  }
20451
- const stateRead2 = await readMergeState();
20452
- if (stateRead2.ok && stateRead2.state === "MERGED") return { mergeStatus: "merged" };
20502
+ const stateRead = await readMergeState();
20503
+ if (stateRead.ok && stateRead.state === "MERGED") return { mergeStatus: "merged" };
20453
20504
  return {
20454
20505
  mergeStatus: "failed",
20455
- error: stateRead2.ok ? "direct merge did not land after clean-status fallback" : `could not confirm PR state after clean-status fallback: ${stateRead2.error}`
20506
+ error: stateRead.ok ? "direct merge did not land after clean-status fallback" : `could not confirm PR state after clean-status fallback: ${stateRead.error}`
20456
20507
  };
20457
20508
  }
20458
20509
  if (ghPrMergeLocalBranchDeleteWarning(message)) {
20459
- const stateRead2 = await readMergeState();
20460
- if (stateRead2.ok && stateRead2.state === "MERGED") return { mergeStatus: "merged" };
20510
+ const stateRead = await readMergeState();
20511
+ if (stateRead.ok && stateRead.state === "MERGED") return { mergeStatus: "merged" };
20461
20512
  return {
20462
20513
  mergeStatus: "failed",
20463
- error: stateRead2.ok ? message.split("\n")[0] : `could not confirm PR state after local branch cleanup warning: ${stateRead2.error}`
20514
+ error: stateRead.ok ? message.split("\n")[0] : `could not confirm PR state after local branch cleanup warning: ${stateRead.error}`
20464
20515
  };
20465
20516
  }
20466
20517
  if (!basePolicyBlocksImmediateMerge(message)) {
20467
- return { mergeStatus: "failed", error: message.split("\n")[0] };
20518
+ return { mergeStatus: "failed", error: ghFailureReason(e) };
20468
20519
  }
20469
- return { mergeStatus: "failed", error: `merge blocked: ${message.split("\n")[0]} \u2014 ensure checks are green` };
20470
- }
20471
- const stateRead = await readMergeState();
20472
- if (!stateRead.ok) {
20473
- return { mergeStatus: "failed", error: `could not read PR state after merge: ${stateRead.error}` };
20520
+ return { mergeStatus: "failed", error: `merge blocked: ${ghFailureReason(e)} \u2014 ensure checks are green` };
20474
20521
  }
20475
- if (stateRead.state === "MERGED") return { mergeStatus: "merged" };
20476
- const confirmation = await confirmAutoMergeEnqueued({
20477
- readAutoMergeRequest: async () => (await execFileP2("gh", ["pr", "view", prNumber, ...args, "--json", "autoMergeRequest", "--jq", '.autoMergeRequest // ""'], { timeout: GC_GH_TIMEOUT_MS2 })).stdout,
20478
- readMerged: async () => {
20479
- const s = await readMergeState();
20480
- return s.ok && s.state === "MERGED";
20481
- },
20482
- reEnqueue: async () => {
20483
- await execFileP2("gh", buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch }), { timeout: GH_MUTATION_TIMEOUT_MS });
20484
- }
20485
- });
20486
- if (confirmation === "merged") return { mergeStatus: "merged" };
20487
- if (confirmation === "enqueued") return { mergeStatus: "auto-merge-enqueued" };
20488
- return { mergeStatus: "failed", error: "auto-merge did not stick \u2014 GitHub reported no autoMergeRequest after enqueue; retry once the PR has a pending check" };
20522
+ return confirmEnqueueOutcome();
20489
20523
  }
20490
20524
  async function remoteBranchExists2(branch, options = {}) {
20491
20525
  return checkRemoteBranchExists(branch, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "3.45.0",
3
+ "version": "3.46.0",
4
4
  "description": "MMI Future CLI — the org dev toolbox (board, registry, keyless secrets, release train, bootstrap, doctor) and the cross-IDE engine the plugin's session-start hook drives.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",