@mutmutco/cli 3.45.0 → 3.47.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.
- package/dist/main.cjs +69 -32
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -14788,6 +14788,9 @@ function deriveHotfixState(f) {
|
|
|
14788
14788
|
if (!f.branchExists && !f.pr && !f.tagPushed && !f.releaseExists) {
|
|
14789
14789
|
return { state: "not-started", next: `mmi-cli hotfix start --from <pr#|sha> (would mint ${f.tag})` };
|
|
14790
14790
|
}
|
|
14791
|
+
if (!f.pr && !f.branchExists && f.tagPushed && f.releaseExists) {
|
|
14792
|
+
return { state: "complete", next: "nothing \u2014 that version is a completed release, not a hotfix in flight" };
|
|
14793
|
+
}
|
|
14791
14794
|
if (!f.pr) {
|
|
14792
14795
|
return { state: "branch-pushed (no PR)", next: `mmi-cli hotfix start --from <pr#|sha> (resumes at the PR step for ${f.tag})` };
|
|
14793
14796
|
}
|
|
@@ -20428,13 +20431,46 @@ async function pollGhPrChecks(prNumber, repoArgs) {
|
|
|
20428
20431
|
}
|
|
20429
20432
|
return state;
|
|
20430
20433
|
}
|
|
20431
|
-
|
|
20434
|
+
function ghFailureReason(e) {
|
|
20435
|
+
const err = e;
|
|
20436
|
+
const head = String(err?.message ?? "").split("\n")[0]?.trim() ?? "";
|
|
20437
|
+
const detail = `${err?.stderr ?? ""}
|
|
20438
|
+
${err?.stdout ?? ""}`.split("\n").map((line) => line.trim()).find((line) => line.length > 0);
|
|
20439
|
+
if (detail && head) return `${head} \u2014 ${detail}`;
|
|
20440
|
+
return detail || head || "unknown error";
|
|
20441
|
+
}
|
|
20442
|
+
var defaultGhMergeAutoIo = {
|
|
20443
|
+
gh: async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout
|
|
20444
|
+
};
|
|
20445
|
+
async function ghMergeAutoEnqueue(prNumber, repo, method, io = defaultGhMergeAutoIo) {
|
|
20432
20446
|
const args = repo ? ["--repo", repo] : [];
|
|
20433
|
-
const headRef = (await
|
|
20447
|
+
const headRef = (await io.gh(["pr", "view", prNumber, ...args, "--json", "headRefName", "--jq", ".headRefName"], GC_GH_TIMEOUT_MS2).catch(() => "")).trim();
|
|
20434
20448
|
const deleteBranch = !isProtectedBranch(headRef);
|
|
20435
|
-
const readMergeState = () => readGhPrStateWithRetry(
|
|
20449
|
+
const readMergeState = () => readGhPrStateWithRetry(
|
|
20450
|
+
() => io.gh(["pr", "view", prNumber, ...args, "--json", "state", "--jq", ".state"], GC_GH_TIMEOUT_MS2)
|
|
20451
|
+
);
|
|
20452
|
+
const confirmEnqueueOutcome = async () => {
|
|
20453
|
+
const stateRead = await readMergeState();
|
|
20454
|
+
if (!stateRead.ok) {
|
|
20455
|
+
return { mergeStatus: "failed", error: `could not read PR state after merge: ${stateRead.error}` };
|
|
20456
|
+
}
|
|
20457
|
+
if (stateRead.state === "MERGED") return { mergeStatus: "merged" };
|
|
20458
|
+
const confirmation = await confirmAutoMergeEnqueued({
|
|
20459
|
+
readAutoMergeRequest: async () => io.gh(["pr", "view", prNumber, ...args, "--json", "autoMergeRequest", "--jq", '.autoMergeRequest // ""'], GC_GH_TIMEOUT_MS2),
|
|
20460
|
+
readMerged: async () => {
|
|
20461
|
+
const s = await readMergeState();
|
|
20462
|
+
return s.ok && s.state === "MERGED";
|
|
20463
|
+
},
|
|
20464
|
+
reEnqueue: async () => {
|
|
20465
|
+
await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch }), GH_MUTATION_TIMEOUT_MS);
|
|
20466
|
+
}
|
|
20467
|
+
});
|
|
20468
|
+
if (confirmation === "merged") return { mergeStatus: "merged" };
|
|
20469
|
+
if (confirmation === "enqueued") return { mergeStatus: "auto-merge-enqueued" };
|
|
20470
|
+
return { mergeStatus: "failed", error: "auto-merge did not stick \u2014 GitHub reported no autoMergeRequest after enqueue; retry once the PR has a pending check" };
|
|
20471
|
+
};
|
|
20436
20472
|
try {
|
|
20437
|
-
await
|
|
20473
|
+
await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch }), GH_MUTATION_TIMEOUT_MS);
|
|
20438
20474
|
} catch (e) {
|
|
20439
20475
|
const message = String(e.message || "");
|
|
20440
20476
|
if (/already been merged/i.test(message)) return { mergeStatus: "merged" };
|
|
@@ -20442,50 +20478,51 @@ async function ghMergeAutoEnqueue(prNumber, repo, method) {
|
|
|
20442
20478
|
if (note) return { mergeStatus: "failed", error: note };
|
|
20443
20479
|
if (mergeAutoRejectedPrAlreadyClean(message)) {
|
|
20444
20480
|
try {
|
|
20445
|
-
await
|
|
20481
|
+
await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: false, deleteBranch }), GH_MUTATION_TIMEOUT_MS);
|
|
20446
20482
|
} catch (e2) {
|
|
20447
20483
|
const m2 = String(e2.message || "");
|
|
20448
20484
|
if (/already been merged/i.test(m2)) return { mergeStatus: "merged" };
|
|
20449
|
-
if (!ghPrMergeLocalBranchDeleteWarning(m2))
|
|
20485
|
+
if (!ghPrMergeLocalBranchDeleteWarning(m2)) {
|
|
20486
|
+
const afterDirect = await readMergeState();
|
|
20487
|
+
if (afterDirect.ok && afterDirect.state === "MERGED") return { mergeStatus: "merged" };
|
|
20488
|
+
if (!afterDirect.ok) {
|
|
20489
|
+
return { mergeStatus: "failed", error: `could not confirm PR state after clean-status fallback: ${afterDirect.error}` };
|
|
20490
|
+
}
|
|
20491
|
+
try {
|
|
20492
|
+
await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch }), GH_MUTATION_TIMEOUT_MS);
|
|
20493
|
+
} catch (e3) {
|
|
20494
|
+
const m3 = String(e3.message || "");
|
|
20495
|
+
if (/already been merged/i.test(m3)) return { mergeStatus: "merged" };
|
|
20496
|
+
const afterRetry = await readMergeState();
|
|
20497
|
+
if (afterRetry.ok && afterRetry.state === "MERGED") return { mergeStatus: "merged" };
|
|
20498
|
+
const stuck = (await io.gh(["pr", "view", prNumber, ...args, "--json", "autoMergeRequest", "--jq", '.autoMergeRequest // ""'], GC_GH_TIMEOUT_MS2).catch(() => "")).trim();
|
|
20499
|
+
if (stuck) return { mergeStatus: "auto-merge-enqueued" };
|
|
20500
|
+
return { mergeStatus: "failed", error: `${ghFailureReason(e2)} (re-enqueue also failed: ${ghFailureReason(e3)})` };
|
|
20501
|
+
}
|
|
20502
|
+
return confirmEnqueueOutcome();
|
|
20503
|
+
}
|
|
20450
20504
|
}
|
|
20451
|
-
const
|
|
20452
|
-
if (
|
|
20505
|
+
const stateRead = await readMergeState();
|
|
20506
|
+
if (stateRead.ok && stateRead.state === "MERGED") return { mergeStatus: "merged" };
|
|
20453
20507
|
return {
|
|
20454
20508
|
mergeStatus: "failed",
|
|
20455
|
-
error:
|
|
20509
|
+
error: stateRead.ok ? "direct merge did not land after clean-status fallback" : `could not confirm PR state after clean-status fallback: ${stateRead.error}`
|
|
20456
20510
|
};
|
|
20457
20511
|
}
|
|
20458
20512
|
if (ghPrMergeLocalBranchDeleteWarning(message)) {
|
|
20459
|
-
const
|
|
20460
|
-
if (
|
|
20513
|
+
const stateRead = await readMergeState();
|
|
20514
|
+
if (stateRead.ok && stateRead.state === "MERGED") return { mergeStatus: "merged" };
|
|
20461
20515
|
return {
|
|
20462
20516
|
mergeStatus: "failed",
|
|
20463
|
-
error:
|
|
20517
|
+
error: stateRead.ok ? message.split("\n")[0] : `could not confirm PR state after local branch cleanup warning: ${stateRead.error}`
|
|
20464
20518
|
};
|
|
20465
20519
|
}
|
|
20466
20520
|
if (!basePolicyBlocksImmediateMerge(message)) {
|
|
20467
|
-
return { mergeStatus: "failed", error:
|
|
20521
|
+
return { mergeStatus: "failed", error: ghFailureReason(e) };
|
|
20468
20522
|
}
|
|
20469
|
-
return { mergeStatus: "failed", error: `merge blocked: ${
|
|
20523
|
+
return { mergeStatus: "failed", error: `merge blocked: ${ghFailureReason(e)} \u2014 ensure checks are green` };
|
|
20470
20524
|
}
|
|
20471
|
-
|
|
20472
|
-
if (!stateRead.ok) {
|
|
20473
|
-
return { mergeStatus: "failed", error: `could not read PR state after merge: ${stateRead.error}` };
|
|
20474
|
-
}
|
|
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" };
|
|
20525
|
+
return confirmEnqueueOutcome();
|
|
20489
20526
|
}
|
|
20490
20527
|
async function remoteBranchExists2(branch, options = {}) {
|
|
20491
20528
|
return checkRemoteBranchExists(branch, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.47.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",
|