@mutmutco/cli 3.105.3 → 3.105.5
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 +79 -6
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -20341,6 +20341,51 @@ async function deriveHotfixVersion(deps) {
|
|
|
20341
20341
|
function hotfixBranch(tag) {
|
|
20342
20342
|
return `hotfix/${tag}`;
|
|
20343
20343
|
}
|
|
20344
|
+
function pathIsTolerated(path2, tolerated) {
|
|
20345
|
+
return tolerated.some((entry) => {
|
|
20346
|
+
if (!entry) return false;
|
|
20347
|
+
if (path2 === entry) return true;
|
|
20348
|
+
const root = entry.endsWith("/") ? entry : `${entry}/`;
|
|
20349
|
+
return path2.startsWith(root);
|
|
20350
|
+
});
|
|
20351
|
+
}
|
|
20352
|
+
var HOTFIX_SKILL_TOLERATED_ROOTS = ["skills", ".kilo-plugin/skills"];
|
|
20353
|
+
async function cherryPickWithToleratedPaths(deps, sha, tolerated) {
|
|
20354
|
+
try {
|
|
20355
|
+
await deps.run("git", ["cherry-pick", "-x", sha]);
|
|
20356
|
+
return [];
|
|
20357
|
+
} catch (original) {
|
|
20358
|
+
const unmerged = (await deps.run("git", ["diff", "--name-only", "--diff-filter=U"])).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
20359
|
+
const blocking = unmerged.filter((f) => !pathIsTolerated(f, tolerated));
|
|
20360
|
+
if (unmerged.length === 0 || blocking.length > 0) {
|
|
20361
|
+
try {
|
|
20362
|
+
await deps.run("git", ["cherry-pick", "--abort"]);
|
|
20363
|
+
} catch {
|
|
20364
|
+
}
|
|
20365
|
+
const detail = original.message ?? String(original);
|
|
20366
|
+
if (unmerged.length === 0) {
|
|
20367
|
+
throw new Error(`cherry-pick of ${sha.slice(0, 7)} failed without conflicted paths \u2014 ${detail}`);
|
|
20368
|
+
}
|
|
20369
|
+
throw new Error(
|
|
20370
|
+
`cherry-pick of ${sha.slice(0, 7)} conflicted on untolerated path(s): ${blocking.join(", ")}` + (tolerated.length > 0 ? ` (regenerable paths are auto-resolved: ${tolerated.join(", ")})` : "") + ` \u2014 ${detail}`
|
|
20371
|
+
);
|
|
20372
|
+
}
|
|
20373
|
+
await deps.run("git", ["checkout", "--theirs", "--", ...unmerged]);
|
|
20374
|
+
await deps.run("git", ["add", "--", ...unmerged]);
|
|
20375
|
+
try {
|
|
20376
|
+
await deps.run("git", ["-c", "core.editor=true", "cherry-pick", "--continue"]);
|
|
20377
|
+
} catch (contErr) {
|
|
20378
|
+
try {
|
|
20379
|
+
await deps.run("git", ["cherry-pick", "--abort"]);
|
|
20380
|
+
} catch {
|
|
20381
|
+
}
|
|
20382
|
+
throw new Error(
|
|
20383
|
+
`cherry-pick continue after regenerable-path resolve failed for ${unmerged.join(", ")} \u2014 ${contErr.message ?? contErr}`
|
|
20384
|
+
);
|
|
20385
|
+
}
|
|
20386
|
+
return unmerged;
|
|
20387
|
+
}
|
|
20388
|
+
}
|
|
20344
20389
|
async function restoreAfterFailedPort(deps, opts) {
|
|
20345
20390
|
const { startBranch, branch, created } = opts;
|
|
20346
20391
|
if (!startBranch || startBranch === "HEAD" || startBranch === branch) {
|
|
@@ -20425,6 +20470,14 @@ async function hotfixBaseTagForRelease(deps, tag) {
|
|
|
20425
20470
|
const current = normalizeHotfixVersion(tag).tag;
|
|
20426
20471
|
return tags.find((t) => compareHotfixVersions(t, current) < 0) ?? tags.find((t) => t !== current) ?? "origin/main^";
|
|
20427
20472
|
}
|
|
20473
|
+
async function hotfixReleaseExists(deps, ctx, tag) {
|
|
20474
|
+
try {
|
|
20475
|
+
await deps.run("gh", ["release", "view", tag, "--repo", ctx.repo, "--json", "tagName"]);
|
|
20476
|
+
return true;
|
|
20477
|
+
} catch {
|
|
20478
|
+
return false;
|
|
20479
|
+
}
|
|
20480
|
+
}
|
|
20428
20481
|
async function runHotfixStart(deps, options) {
|
|
20429
20482
|
const ctx = await buildTrainApplyContext(deps);
|
|
20430
20483
|
const deployModel = await resolveHotfixDeployModel(deps, ctx);
|
|
@@ -20437,7 +20490,11 @@ async function runHotfixStart(deps, options) {
|
|
|
20437
20490
|
const branch = hotfixBranch(tag);
|
|
20438
20491
|
const notes = [];
|
|
20439
20492
|
const existingPr = await findHotfixPr(deps, ctx, tag);
|
|
20440
|
-
|
|
20493
|
+
const existingState = (existingPr?.state ?? "").toUpperCase();
|
|
20494
|
+
const releaseExists = existingState === "MERGED" ? await hotfixReleaseExists(deps, ctx, tag) : false;
|
|
20495
|
+
const reusable = existingState === "OPEN" || existingState === "MERGED" && releaseExists;
|
|
20496
|
+
if (existingPr && reusable) {
|
|
20497
|
+
const next = existingState === "MERGED" ? `next: mmi-cli hotfix release ${tag}` : `next: merge it, then mmi-cli hotfix release ${tag}`;
|
|
20441
20498
|
return {
|
|
20442
20499
|
...ctx,
|
|
20443
20500
|
command: "hotfix-start",
|
|
@@ -20447,10 +20504,21 @@ async function runHotfixStart(deps, options) {
|
|
|
20447
20504
|
source: options.from,
|
|
20448
20505
|
prUrl: existingPr.url,
|
|
20449
20506
|
reused: true,
|
|
20450
|
-
notes: [`hotfix PR for ${tag} already exists (#${existingPr.number}, ${existingPr.state}) \u2014 reused;
|
|
20507
|
+
notes: [`hotfix PR for ${tag} already exists (#${existingPr.number}, ${existingPr.state}) \u2014 reused; ${next}`]
|
|
20451
20508
|
};
|
|
20452
20509
|
}
|
|
20510
|
+
if (existingPr) {
|
|
20511
|
+
const reason = existingState === "MERGED" && !releaseExists ? `MERGED with no GitHub Release for ${tag} \u2014 recreating ${branch} from origin/main (#4381/#4383)` : `${existingPr.state} \u2014 recreating ${branch} from origin/main (#4369)`;
|
|
20512
|
+
notes.push(`prior hotfix PR #${existingPr.number} is ${reason}`);
|
|
20513
|
+
const staleRemote = clean3(await deps.run("git", ["ls-remote", "origin", `refs/heads/${branch}`]));
|
|
20514
|
+
if (staleRemote) {
|
|
20515
|
+
await deps.run("git", ["push", "origin", "--delete", branch]);
|
|
20516
|
+
notes.push(`deleted stale origin/${branch} left by the incomplete train`);
|
|
20517
|
+
}
|
|
20518
|
+
}
|
|
20453
20519
|
const { sha, label } = await resolveHotfixSource(deps, ctx, options.from);
|
|
20520
|
+
const foldPaths = deployModel === "hub-serverless" || deployModel === "registry-publish" ? await resolveFoldPaths(deps, deployModel) : [];
|
|
20521
|
+
const pickTolerated = deployModel === "hub-serverless" ? [...foldPaths, ...HOTFIX_SKILL_TOLERATED_ROOTS] : foldPaths;
|
|
20454
20522
|
if (deployModel === "hub-serverless") {
|
|
20455
20523
|
await deps.run("node", ["scripts/release-distribution.mjs", "verify-deps"]);
|
|
20456
20524
|
}
|
|
@@ -20464,9 +20532,11 @@ async function runHotfixStart(deps, options) {
|
|
|
20464
20532
|
const preexistingLocal = clean3(await deps.run("git", ["branch", "--list", branch]));
|
|
20465
20533
|
await deps.run("git", ["checkout", "-B", branch, "origin/main"]);
|
|
20466
20534
|
try {
|
|
20467
|
-
await deps
|
|
20535
|
+
const autoResolved = await cherryPickWithToleratedPaths(deps, sha, pickTolerated);
|
|
20536
|
+
if (autoResolved.length > 0) {
|
|
20537
|
+
notes.push(`auto-resolved regenerable cherry-pick conflict(s): ${autoResolved.join(", ")} (regenerated in bump step)`);
|
|
20538
|
+
}
|
|
20468
20539
|
} catch (e) {
|
|
20469
|
-
await deps.run("git", ["cherry-pick", "--abort"]);
|
|
20470
20540
|
const recovery = await restoreAfterFailedPort(deps, { startBranch, branch, created: !preexistingLocal });
|
|
20471
20541
|
throw new Error(
|
|
20472
20542
|
`cherry-pick of ${label} onto ${branch} conflicted \u2014 aborted; nothing was pushed. Land the conflict resolution on development through a normal PR, then rerun \`mmi-cli hotfix start --from <that PR's merge sha>\` \u2014 never hand-resolve the port onto main. ${recovery} (${e.message ?? e})`
|
|
@@ -20485,12 +20555,15 @@ async function runHotfixStart(deps, options) {
|
|
|
20485
20555
|
} else {
|
|
20486
20556
|
notes.push("distribution prepare produced no changes \u2014 nothing extra committed");
|
|
20487
20557
|
}
|
|
20558
|
+
} else if (deployModel === "registry-publish" && foldPaths.length > 0) {
|
|
20559
|
+
const foldNote = await foldReleaseVersion(deps, deployModel, tag, foldPaths);
|
|
20560
|
+
notes.push(foldNote);
|
|
20488
20561
|
} else {
|
|
20489
|
-
notes.push(`distribution bump skipped (deployModel=${deployModel}
|
|
20562
|
+
notes.push(`distribution bump skipped (deployModel=${deployModel})`);
|
|
20490
20563
|
}
|
|
20491
20564
|
await deps.run("git", ["push", "-u", "origin", branch]);
|
|
20492
20565
|
}
|
|
20493
|
-
const bumpNote = deployModel === "hub-serverless" ? " with the locked distribution bump" : "";
|
|
20566
|
+
const bumpNote = deployModel === "hub-serverless" ? " with the locked distribution bump" : deployModel === "registry-publish" ? " with the package version bump" : "";
|
|
20494
20567
|
const prUrl = clean3(await deps.run("gh", [
|
|
20495
20568
|
"pr",
|
|
20496
20569
|
"create",
|
package/package.json
CHANGED