@mutmutco/cli 3.107.1 → 3.107.3

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 +71 -11
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -11982,6 +11982,7 @@ async function runPluginHeal(surface = detectSurface(process.env)) {
11982
11982
  }
11983
11983
 
11984
11984
  // src/train-apply.ts
11985
+ var TRAIN_BUMP_INTENTS = ["major", "minor", "patch"];
11985
11986
  function isCentralDispatchModel(model) {
11986
11987
  return model === "tenant-container" || model === "solo-container" || model === "static-cdn";
11987
11988
  }
@@ -13981,15 +13982,62 @@ async function runTrainApplyPipeline(mode, input) {
13981
13982
  environments
13982
13983
  };
13983
13984
  }
13985
+ async function latestLocalReleaseTag(deps) {
13986
+ const tags = clean2(await deps.run("git", ["tag", "--list", "v*"])).split("\n");
13987
+ let latest = { major: 0, minor: 0, patch: 0 };
13988
+ for (const tag of tags) {
13989
+ const m = /^v(\d+)\.(\d+)\.(\d+)$/.exec(tag.trim());
13990
+ if (!m) continue;
13991
+ const v = { major: +m[1], minor: +m[2], patch: +m[3] };
13992
+ const delta = v.major - latest.major || v.minor - latest.minor || v.patch - latest.patch;
13993
+ if (delta > 0) latest = v;
13994
+ }
13995
+ return latest;
13996
+ }
13997
+ async function resolveTrainBumpIntent(deps, env = process.env) {
13998
+ const explicitIntent = (env.MMI_BUMP_INTENT ?? "").trim();
13999
+ if (explicitIntent) {
14000
+ if (!TRAIN_BUMP_INTENTS.includes(explicitIntent)) {
14001
+ throw new Error("MMI_BUMP_INTENT must be one of major|minor|patch");
14002
+ }
14003
+ return explicitIntent;
14004
+ }
14005
+ const explicitVersion = (env.MMI_RELEASE_VERSION ?? "").trim();
14006
+ if (explicitVersion) {
14007
+ const m = /^v?(\d+)\.(\d+)\.(\d+)$/.exec(explicitVersion);
14008
+ if (!m) throw new Error(`MMI_RELEASE_VERSION must be X.Y.Z (got ${explicitVersion})`);
14009
+ const explicit = { major: +m[1], minor: +m[2], patch: +m[3] };
14010
+ const latest = await latestLocalReleaseTag(deps);
14011
+ const delta = explicit.major - latest.major || explicit.minor - latest.minor || explicit.patch - latest.patch;
14012
+ if (delta <= 0) {
14013
+ throw new Error(
14014
+ `MMI_RELEASE_VERSION ${explicitVersion} must be ahead of the latest release v${latest.major}.${latest.minor}.${latest.patch}`
14015
+ );
14016
+ }
14017
+ return explicit.major > latest.major ? "major" : explicit.minor > latest.minor ? "minor" : "patch";
14018
+ }
14019
+ try {
14020
+ return requireValue(
14021
+ clean2(await deps.run("node", ["scripts/next-version.mjs", "intent"])),
14022
+ "release bump intent"
14023
+ );
14024
+ } catch (e) {
14025
+ const message = e instanceof Error ? e.message : String(e);
14026
+ const usage = /usage: next-version\.mjs <([^>]+)>/.exec(message);
14027
+ if (usage && !usage[1].split("|").includes("intent")) {
14028
+ throw new Error(
14029
+ `release bump intent: this repo's seeded scripts/next-version.mjs predates the \`intent\` verb (its usage lists <${usage[1]}>), so unset-intent derivation cannot run here. Declare the intent explicitly and rerun \u2014 MMI_BUMP_INTENT=major|minor|patch (or MMI_RELEASE_VERSION=X.Y.Z for an exact target); the train then resolves it in the CLI without shelling this script. The durable fix is refreshing the seeded script from the Hub via seed propagation.`
14030
+ );
14031
+ }
14032
+ throw e;
14033
+ }
14034
+ }
13984
14035
  async function runTrainApply(command, deps, options = {}) {
13985
14036
  const watch = options.watch ?? false;
13986
14037
  const ctx = await buildTrainApplyContext(deps);
13987
14038
  await requireCleanTree(deps);
13988
14039
  await runGitRemoteRead(deps, ["fetch", "origin"]);
13989
- const bumpIntent = requireValue(
13990
- clean2(await deps.run("node", ["scripts/next-version.mjs", "intent"])),
13991
- "release bump intent"
13992
- );
14040
+ const bumpIntent = await resolveTrainBumpIntent(deps);
13993
14041
  const meta = requireProjectMetaForTrain(await loadProjectMeta(deps, ctx), ctx.repo);
13994
14042
  const branchHints = await loadReleaseTrackBranchHints(deps);
13995
14043
  const directTrack = isHubControlRepo(ctx.repo) || resolveReleaseTrack(meta, branchHints) === "direct";
@@ -22447,7 +22495,7 @@ async function runHotfixStart(deps, options) {
22447
22495
  if (!sources.some((s) => s.sha === resolved.sha)) sources.push(resolved);
22448
22496
  }
22449
22497
  const label = sources.map((s) => s.label).join(", ");
22450
- const foldPaths = deployModel === "hub-serverless" || deployModel === "registry-publish" ? await resolveFoldPaths(deps, deployModel) : [];
22498
+ const foldPaths = await resolveFoldPaths(deps, deployModel);
22451
22499
  const pickTolerated = deployModel === "hub-serverless" || deployModel === "registry-publish" ? [...foldPaths, ...HOTFIX_SKILL_TOLERATED_ROOTS] : foldPaths;
22452
22500
  if (deployModel === "hub-serverless") {
22453
22501
  await deps.run("node", ["scripts/release-distribution.mjs", "verify-deps"]);
@@ -22491,15 +22539,15 @@ async function runHotfixStart(deps, options) {
22491
22539
  } else {
22492
22540
  notes.push("distribution prepare produced no changes \u2014 nothing extra committed");
22493
22541
  }
22494
- } else if (deployModel === "registry-publish" && foldPaths.length > 0) {
22542
+ } else if (foldPaths.length > 0) {
22495
22543
  const foldNote = await foldReleaseVersion(deps, deployModel, tag, foldPaths);
22496
22544
  notes.push(foldNote);
22497
22545
  } else {
22498
- notes.push(`distribution bump skipped (deployModel=${deployModel})`);
22546
+ notes.push(`version fold skipped (deployModel=${deployModel} carries no version manifest \u2014 the tag is the version)`);
22499
22547
  }
22500
22548
  await deps.run("git", ["push", "-u", "origin", branch]);
22501
22549
  }
22502
- const bumpNote = deployModel === "hub-serverless" ? " with the locked distribution bump" : deployModel === "registry-publish" ? " with the package version bump" : "";
22550
+ const bumpNote = deployModel === "hub-serverless" ? " with the locked distribution bump" : foldPaths.length > 0 ? " with the package version bump" : "";
22503
22551
  const prUrl = clean3(await deps.run("gh", [
22504
22552
  "pr",
22505
22553
  "create",
@@ -22562,6 +22610,15 @@ async function watchReleaseRun(deps, ctx, workflow, sha) {
22562
22610
  function devFoldBranch(tag) {
22563
22611
  return `hotfix-fold/${tag}`;
22564
22612
  }
22613
+ function devFoldScratchBranch(tag, deps) {
22614
+ return `${devFoldBranch(tag)}--port-${(deps.now ?? Date.now)().toString(36)}`;
22615
+ }
22616
+ async function reapFoldScratchBranches(deps) {
22617
+ const listed = await deps.run("git", ["branch", "--list", "hotfix-fold/*--port-*", "--format=%(refname:short)"]).catch(() => "");
22618
+ for (const name of listed.split("\n").map((s) => s.trim()).filter(Boolean)) {
22619
+ await deps.run("git", ["branch", "-D", name]).catch(() => void 0);
22620
+ }
22621
+ }
22565
22622
  async function portFoldToDevelopment(deps, ctx, deployModel, tag) {
22566
22623
  const foldPaths = deployModel === "hub-serverless" || deployModel === "registry-publish" ? await resolveFoldPaths(deps, deployModel) : [];
22567
22624
  if (foldPaths.length === 0) return `fold port skipped (deployModel=${deployModel} folds no version manifest)`;
@@ -22585,14 +22642,16 @@ async function portFoldToDevelopment(deps, ctx, deployModel, tag) {
22585
22642
  const existing = JSON.parse(listed || "[]").filter((r) => r.state === "OPEN" || r.state === "MERGED").sort((a, b) => (b.number ?? 0) - (a.number ?? 0))[0];
22586
22643
  if (existing) return `development fold PR #${existing.number} for ${tag} is ${existing.state} \u2014 reused`;
22587
22644
  const previousRef = clean3(await deps.run("git", ["rev-parse", "--abbrev-ref", "HEAD"]));
22645
+ await reapFoldScratchBranches(deps);
22646
+ const scratch = devFoldScratchBranch(tag, deps);
22588
22647
  try {
22589
22648
  await deps.run("git", ["fetch", "origin", "development"]);
22590
- await deps.run("git", ["checkout", "-B", branch, "origin/development"]);
22649
+ await deps.run("git", ["checkout", "-B", scratch, "origin/development"]);
22591
22650
  const durableSource = clean3(await deps.run("git", ["merge-base", "HEAD", "origin/development"]));
22592
22651
  const foldNote = await foldReleaseVersion(deps, deployModel, tag, foldPaths, durableSource, false);
22593
22652
  const committed = clean3(await deps.run("git", ["rev-list", "--count", "origin/development..HEAD"]));
22594
22653
  if (committed === "0") return `development already carries ${tag} \u2014 no fold PR needed (${foldNote})`;
22595
- await deps.run("git", ["push", "-u", "origin", branch]);
22654
+ await deps.run("git", ["push", "origin", `${scratch}:refs/heads/${branch}`]);
22596
22655
  const prUrl = clean3(await deps.run("gh", [
22597
22656
  "pr",
22598
22657
  "create",
@@ -22612,6 +22671,7 @@ Generated by \`mmi-cli hotfix release\` \u2014 no main-parented commit is merged
22612
22671
  return `opened development fold PR ${prUrl} (${foldNote})`;
22613
22672
  } finally {
22614
22673
  if (previousRef && previousRef !== "HEAD") await deps.run("git", ["checkout", previousRef]).catch(() => void 0);
22674
+ await deps.run("git", ["branch", "-D", scratch]).catch(() => void 0);
22615
22675
  }
22616
22676
  }
22617
22677
  async function runHotfixRelease(deps, versionInput, options = {}) {
@@ -22743,7 +22803,7 @@ async function runHotfixRelease(deps, versionInput, options = {}) {
22743
22803
  try {
22744
22804
  foldNote = await portFoldToDevelopment(deps, ctx, deployModel, tag);
22745
22805
  } catch (e) {
22746
- foldNote = `development fold port FAILED: ${e.message ?? e} \u2014 the release stands; port it by hand: git checkout -B ${devFoldBranch(tag)} origin/development && node scripts/release-distribution.mjs prepare ${version}, then open a development-base PR`;
22806
+ foldNote = `development fold port FAILED: ${e.message ?? e} \u2014 the release stands; port it by hand on a fresh branch: git checkout -B ${devFoldBranch(tag)}-manual origin/development && node scripts/release-distribution.mjs prepare ${version}, then push it as the canonical head (git push origin ${devFoldBranch(tag)}-manual:refs/heads/${devFoldBranch(tag)}) and open a development-base PR`;
22747
22807
  }
22748
22808
  return {
22749
22809
  ...ctx,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "3.107.1",
3
+ "version": "3.107.3",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",