@mutmutco/cli 3.14.0 → 3.15.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 +85 -9
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -6456,6 +6456,9 @@ function basePolicyBlocksImmediateMerge(message) {
|
|
|
6456
6456
|
function ghPrMergeLocalBranchDeleteWarning(message) {
|
|
6457
6457
|
return /used by worktree|cannot delete branch/i.test(message);
|
|
6458
6458
|
}
|
|
6459
|
+
function mergeAutoRejectedPrAlreadyClean(message) {
|
|
6460
|
+
return /clean status \(enablePullRequestAutoMerge\)|is in clean status/i.test(message);
|
|
6461
|
+
}
|
|
6459
6462
|
async function checkRemoteBranchExists(branch, deps, options = {}) {
|
|
6460
6463
|
if (!branch) return void 0;
|
|
6461
6464
|
try {
|
|
@@ -11313,6 +11316,17 @@ async function preflight(deps, ctx, stage2, meta) {
|
|
|
11313
11316
|
throw new Error(`${ctx.repo} is not Hub-deployed (deployModel=none) \u2014 the release train does not apply; use the project's own release path`);
|
|
11314
11317
|
}
|
|
11315
11318
|
await deps.runSelf(["secrets", "preflight", "--stage", stage2, "--repo", ctx.repo]);
|
|
11319
|
+
if (stage2 === "main" && (model === "registry-publish" || meta.publishRequired === true)) {
|
|
11320
|
+
const publishDir = typeof meta.publishDir === "string" && meta.publishDir.trim() ? meta.publishDir.trim() : ".";
|
|
11321
|
+
const publishArgs = ["publish", ...publishDir !== "." ? [`./${publishDir}`] : [], "--dry-run", "--ignore-scripts"];
|
|
11322
|
+
try {
|
|
11323
|
+
await deps.run("npm", publishArgs);
|
|
11324
|
+
} catch (e) {
|
|
11325
|
+
throw new Error(
|
|
11326
|
+
`${ctx.repo}: pre-tag publish dry run failed for '${publishDir}' (npm ${publishArgs.join(" ")}) \u2014 refusing to tag a release that would fail to publish (#2753). Fix the publish surface (deleted dir, malformed package.json, missing files), then rerun. Underlying error: ${e instanceof Error ? e.message : String(e)}`
|
|
11327
|
+
);
|
|
11328
|
+
}
|
|
11329
|
+
}
|
|
11316
11330
|
return model;
|
|
11317
11331
|
}
|
|
11318
11332
|
async function preflightMergeToMain(deps, deployModel, remoteRef, blockingPrefix, realignMessage) {
|
|
@@ -18584,6 +18598,15 @@ function parseLinkedIssues(body) {
|
|
|
18584
18598
|
}
|
|
18585
18599
|
return [...refs].sort((a, b) => Number(a.slice(1)) - Number(b.slice(1)));
|
|
18586
18600
|
}
|
|
18601
|
+
function parseClosingIssues(body) {
|
|
18602
|
+
const refs = /* @__PURE__ */ new Set();
|
|
18603
|
+
const re = /\b(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s+#(\d+)\b/gi;
|
|
18604
|
+
let m;
|
|
18605
|
+
while ((m = re.exec(body)) !== null) {
|
|
18606
|
+
refs.add(`#${m[1]}`);
|
|
18607
|
+
}
|
|
18608
|
+
return [...refs].sort((a, b) => Number(a.slice(1)) - Number(b.slice(1)));
|
|
18609
|
+
}
|
|
18587
18610
|
function assertPositiveInt(label, n) {
|
|
18588
18611
|
const num = Number(n);
|
|
18589
18612
|
if (!Number.isInteger(num) || num <= 0) {
|
|
@@ -19286,6 +19309,27 @@ function repoFromSelector2(selector) {
|
|
|
19286
19309
|
async function loadConfigForBoardSelector(selector, repoOption) {
|
|
19287
19310
|
return loadConfigForRepo(repoFromSelector2(selector) ?? repoOption);
|
|
19288
19311
|
}
|
|
19312
|
+
async function advanceClosedIssuesToDone(prNumber, repoOption) {
|
|
19313
|
+
const repoArgs = repoOption ? ["--repo", repoOption] : [];
|
|
19314
|
+
let body;
|
|
19315
|
+
try {
|
|
19316
|
+
body = (await execFileP2("gh", ["pr", "view", prNumber, ...repoArgs, "--json", "body", "--jq", ".body"], { timeout: GC_GH_TIMEOUT_MS5 })).stdout;
|
|
19317
|
+
} catch {
|
|
19318
|
+
return void 0;
|
|
19319
|
+
}
|
|
19320
|
+
const closing = parseClosingIssues(body ?? "");
|
|
19321
|
+
if (closing.length === 0) return void 0;
|
|
19322
|
+
const results = [];
|
|
19323
|
+
for (const ref of closing) {
|
|
19324
|
+
try {
|
|
19325
|
+
const moved = await moveBoardItem({ config: await loadConfigForBoardSelector(ref, repoOption), selector: ref, status: "Done", repo: repoOption, allowPartial: true });
|
|
19326
|
+
results.push(moved.partial ? { issue: ref, moved: false, error: moved.warning } : { issue: ref, moved: true });
|
|
19327
|
+
} catch (e) {
|
|
19328
|
+
results.push({ issue: ref, moved: false, error: e.message });
|
|
19329
|
+
}
|
|
19330
|
+
}
|
|
19331
|
+
return results;
|
|
19332
|
+
}
|
|
19289
19333
|
function renderGcApplyResult(result) {
|
|
19290
19334
|
const lines = ["gc apply result:"];
|
|
19291
19335
|
lines.push(` branches removed: ${result.removedBranches.length ? result.removedBranches.join(", ") : "none"}`);
|
|
@@ -20922,6 +20966,21 @@ async function ghMergeAutoEnqueue(prNumber, repo, method) {
|
|
|
20922
20966
|
if (/already been merged/i.test(message)) return { mergeStatus: "merged" };
|
|
20923
20967
|
const note = timeoutKillNote(e, GH_MUTATION_TIMEOUT_MS);
|
|
20924
20968
|
if (note) return { mergeStatus: "failed", error: note };
|
|
20969
|
+
if (mergeAutoRejectedPrAlreadyClean(message)) {
|
|
20970
|
+
try {
|
|
20971
|
+
await execFileP2("gh", buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: false }), { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
20972
|
+
} catch (e2) {
|
|
20973
|
+
const m2 = String(e2.message || "");
|
|
20974
|
+
if (/already been merged/i.test(m2)) return { mergeStatus: "merged" };
|
|
20975
|
+
if (!ghPrMergeLocalBranchDeleteWarning(m2)) return { mergeStatus: "failed", error: m2.split("\n")[0] };
|
|
20976
|
+
}
|
|
20977
|
+
const stateRead2 = await readMergeState();
|
|
20978
|
+
if (stateRead2.ok && stateRead2.state === "MERGED") return { mergeStatus: "merged" };
|
|
20979
|
+
return {
|
|
20980
|
+
mergeStatus: "failed",
|
|
20981
|
+
error: stateRead2.ok ? "direct merge did not land after clean-status fallback" : `could not confirm PR state after clean-status fallback: ${stateRead2.error}`
|
|
20982
|
+
};
|
|
20983
|
+
}
|
|
20925
20984
|
if (ghPrMergeLocalBranchDeleteWarning(message)) {
|
|
20926
20985
|
const stateRead2 = await readMergeState();
|
|
20927
20986
|
if (stateRead2.ok && stateRead2.state === "MERGED") return { mergeStatus: "merged" };
|
|
@@ -21161,7 +21220,7 @@ pr.command("merge <number>").description("merge a PR (squash by default) and cle
|
|
|
21161
21220
|
const remoteBefore = await remoteBranchExists2(headRef);
|
|
21162
21221
|
let remoteDeleteAttempted = false;
|
|
21163
21222
|
let remoteNotAttemptedReason;
|
|
21164
|
-
await execFileP2("gh", buildPrMergeArgs({ number, repoArgs, method, auto: o.auto }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e) => {
|
|
21223
|
+
await execFileP2("gh", buildPrMergeArgs({ number, repoArgs, method, auto: o.auto }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch(async (e) => {
|
|
21165
21224
|
const message = String(e.message || "");
|
|
21166
21225
|
if (/already been merged/i.test(message)) {
|
|
21167
21226
|
remoteNotAttemptedReason = "pr-already-merged";
|
|
@@ -21169,6 +21228,19 @@ pr.command("merge <number>").description("merge a PR (squash by default) and cle
|
|
|
21169
21228
|
}
|
|
21170
21229
|
const note = timeoutKillNote(e, GH_MUTATION_TIMEOUT_MS);
|
|
21171
21230
|
if (note) throw new Error(`gh pr merge ${number}: ${note}`);
|
|
21231
|
+
if (o.auto && mergeAutoRejectedPrAlreadyClean(message)) {
|
|
21232
|
+
await execFileP2("gh", buildPrMergeArgs({ number, repoArgs, method, auto: false }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e2) => {
|
|
21233
|
+
const m2 = String(e2.message || "");
|
|
21234
|
+
if (/already been merged/i.test(m2)) {
|
|
21235
|
+
remoteNotAttemptedReason = "pr-already-merged";
|
|
21236
|
+
return;
|
|
21237
|
+
}
|
|
21238
|
+
const note2 = timeoutKillNote(e2, GH_MUTATION_TIMEOUT_MS);
|
|
21239
|
+
if (note2) throw new Error(`gh pr merge ${number}: ${note2}`);
|
|
21240
|
+
if (!ghPrMergeLocalBranchDeleteWarning(m2)) throw e2;
|
|
21241
|
+
});
|
|
21242
|
+
return;
|
|
21243
|
+
}
|
|
21172
21244
|
if (!o.auto && basePolicyBlocksImmediateMerge(message)) {
|
|
21173
21245
|
throw new Error(`gh pr merge ${number}: the base-branch policy blocks an immediate merge \u2014 re-run with --auto to merge once required checks pass.`);
|
|
21174
21246
|
}
|
|
@@ -21217,14 +21289,18 @@ pr.command("merge <number>").description("merge a PR (squash by default) and cle
|
|
|
21217
21289
|
}
|
|
21218
21290
|
};
|
|
21219
21291
|
}
|
|
21220
|
-
|
|
21221
|
-
|
|
21222
|
-
|
|
21223
|
-
|
|
21224
|
-
|
|
21225
|
-
|
|
21226
|
-
|
|
21227
|
-
|
|
21292
|
+
const boardAdvance = await advanceClosedIssuesToDone(number, o.repo);
|
|
21293
|
+
console.log(JSON.stringify({
|
|
21294
|
+
...buildPrMergeResultPayload({
|
|
21295
|
+
number,
|
|
21296
|
+
branch: headRef,
|
|
21297
|
+
method: method.slice(2),
|
|
21298
|
+
remoteBranch,
|
|
21299
|
+
localCleanup,
|
|
21300
|
+
housekeeping
|
|
21301
|
+
}),
|
|
21302
|
+
...boardAdvance ? { boardAdvance } : {}
|
|
21303
|
+
}));
|
|
21228
21304
|
});
|
|
21229
21305
|
registerQueryCommands(program2);
|
|
21230
21306
|
registerWorktreeCommands(program2);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.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",
|