@mutmutco/cli 4.2.6 → 4.2.7

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 +52 -18
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -11830,10 +11830,10 @@ var rollout_plan_default = {
11830
11830
  note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
11831
11831
  },
11832
11832
  baseline: {
11833
- version: "4.2.6",
11834
- tag: "v4.2.6",
11835
- commit: "608eebe09477",
11836
- npm: "@mutmutco/cli@4.2.6"
11833
+ version: "4.2.7",
11834
+ tag: "v4.2.7",
11835
+ commit: "bb37250e186c",
11836
+ npm: "@mutmutco/cli@4.2.7"
11837
11837
  },
11838
11838
  exitCriterion: "fleet-n-of-n",
11839
11839
  hubOnlyShortcut: "forbidden",
@@ -11850,14 +11850,14 @@ var rollout_plan_default = {
11850
11850
  repo: "mutmutco/mmi-hub",
11851
11851
  role: "canary",
11852
11852
  schedule: "train",
11853
- v3Target: "v4.2.6"
11853
+ v3Target: "v4.2.7"
11854
11854
  }
11855
11855
  ],
11856
11856
  rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
11857
11857
  rollback: {
11858
11858
  independent: true,
11859
- mechanism: "npm dist-tag latest -> 4.2.6 and redeploy the Hub Lambda from tag v4.2.6 (608eebe09477); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
11860
- v3Target: "v4.2.6 (@mutmutco/cli@4.2.6, tag commit 608eebe09477 \u2014 last known-good release carrying the repo-index v4-only contract)"
11859
+ mechanism: "npm dist-tag latest -> 4.2.7 and redeploy the Hub Lambda from tag v4.2.7 (bb37250e186c); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
11860
+ v3Target: "v4.2.7 (@mutmutco/cli@4.2.7, tag commit bb37250e186c \u2014 last known-good release carrying the repo-index v4-only contract)"
11861
11861
  }
11862
11862
  },
11863
11863
  {
@@ -15880,15 +15880,35 @@ function parseGateNpmVersionPins(workflowBody) {
15880
15880
  if (!/runner-node-toolchain/.test(workflowBody)) return [];
15881
15881
  return [...workflowBody.matchAll(/^\s*npm-version:\s*['"]?(\d+(?:\.\d+\.\d+)?)/gm)].map((m) => m[1]);
15882
15882
  }
15883
+ function parseGateNpmInstallPins(workflowBody) {
15884
+ return [...workflowBody.matchAll(/npm\s+install\s+-g\s+npm@(\d+(?:\.\d+\.\d+)?)/g)].map((m) => m[1]);
15885
+ }
15886
+ function gateDeclaredNpmPins(gateFiles) {
15887
+ const files = gateFiles ?? [];
15888
+ return [
15889
+ ...files.flatMap((file) => parseGateNpmVersionPins(file.body)),
15890
+ ...files.flatMap((file) => parseGateNpmInstallPins(file.body))
15891
+ ];
15892
+ }
15893
+ function publishWorkflowNpm(files) {
15894
+ const publish = files?.find((f) => /(?:^|\/)publish\.ya?ml$/i.test(f.path));
15895
+ return publish ? parsePublishWorkflowNpmPin(publish.body) : void 0;
15896
+ }
15883
15897
  function resolveExpectedCiNpmFromWorkflows(gateFiles, allFiles) {
15884
- const declared = gateFiles?.flatMap((file) => parseGateNpmVersionPins(file.body)) ?? [];
15898
+ const declared = gateDeclaredNpmPins(gateFiles);
15885
15899
  if (declared.length) return declared[0];
15886
15900
  const fromGate = resolveGreenGateNpmFromGateWorkflows(gateFiles);
15887
15901
  if (fromGate) return fromGate;
15888
- const files = allFiles ?? gateFiles;
15889
- if (!files?.length) return void 0;
15890
- const publish = files.find((f) => /(?:^|\/)publish\.ya?ml$/i.test(f.path));
15891
- return publish ? parsePublishWorkflowNpmPin(publish.body) : void 0;
15902
+ return publishWorkflowNpm(allFiles ?? gateFiles);
15903
+ }
15904
+ function detectNpmPinDisagreement(gateFiles, allFiles) {
15905
+ const gateNpm = gateDeclaredNpmPins(gateFiles)[0];
15906
+ const publishNpm = publishWorkflowNpm(allFiles ?? gateFiles);
15907
+ if (!gateNpm || !publishNpm) return null;
15908
+ const gateMajor = npmMajor(gateNpm);
15909
+ const publishMajor = npmMajor(publishNpm);
15910
+ if (gateMajor === void 0 || publishMajor === void 0) return null;
15911
+ return gateMajor === publishMajor ? null : { gateNpm, publishNpm };
15892
15912
  }
15893
15913
  function npmMajor(version) {
15894
15914
  const match = /^(\d+)/.exec(version.trim());
@@ -15903,7 +15923,7 @@ function assertNpmMajorPreflight(opts) {
15903
15923
  if (localMajor === void 0 || expectedMajor === void 0) return;
15904
15924
  if (localMajor === expectedMajor) return;
15905
15925
  throw new Error(
15906
- `${opts.repo}: Step 0a npm-major preflight failed \u2014 local npm ${localNpm} (major ${localMajor}) \u2260 CI npm ${expectedNpm} (major ${expectedMajor}). Supported repair (preserves Node): \`npm install -g npm@${expectedNpm}\`; then verify with \`node -v && npm -v\` and rerun the same release command. This is an npm-major mismatch (#5666/#5616/#4578), not a lockfile or publish-surface problem. When gate.yml has no bundled npm pin, read the publish workflow npm pin; otherwise compare \`npm -v\` with the green gate log's toolchain-preflight line (#3446).`
15926
+ `${opts.repo}: Step 0a npm-major preflight failed \u2014 local npm ${localNpm} (major ${localMajor}) \u2260 CI npm ${expectedNpm} (major ${expectedMajor}). Supported repair (preserves Node): \`npm install -g npm@${expectedNpm}\`; then verify with \`node -v && npm -v\` and rerun the same release command. This is an npm-major mismatch (#5666/#5616/#4578), not a lockfile or publish-surface problem. The expected npm is the gate's own declared npm \u2014 an \`npm-version:\` input or an explicit \`npm install -g npm@X\` step (#5893) \u2014 and only a gate that declares neither falls back to a bundled-npm mapping or the publish workflow's pin; otherwise compare \`npm -v\` with the green gate log's toolchain-preflight line (#3446).`
15907
15927
  );
15908
15928
  }
15909
15929
  async function assertNpmMajorPreflightFromWorkflows(deps, repo) {
@@ -15915,6 +15935,12 @@ async function assertNpmMajorPreflightFromWorkflows(deps, repo) {
15915
15935
  } catch {
15916
15936
  return;
15917
15937
  }
15938
+ const disagreement = detectNpmPinDisagreement(gateFiles, allFiles);
15939
+ if (disagreement) {
15940
+ (deps.warn ?? ((m) => console.warn(m)))(
15941
+ `${repo}: repo defect \u2014 the gate declares npm ${disagreement.gateNpm} while the publish workflow pins npm ${disagreement.publishNpm}. The gate's declaration wins this preflight, but the publish lane will still run npm ${disagreement.publishNpm}. Align both pins in one PR.`
15942
+ );
15943
+ }
15918
15944
  assertNpmMajorPreflight({
15919
15945
  repo,
15920
15946
  localNpm,
@@ -31344,11 +31370,14 @@ async function mergeAutoEnqueueWithBody(prNumber, args, method, io, bodyFile) {
31344
31370
  }
31345
31371
  return confirmEnqueueOutcome();
31346
31372
  }
31373
+ function cleanupGitArgs(cwd, args) {
31374
+ return cwd ? ["-C", cwd, ...args] : args;
31375
+ }
31347
31376
  async function remoteBranchExists2(branch, options = {}) {
31348
31377
  if (!branch) return void 0;
31349
31378
  try {
31350
- if (options.prune) await execFileP2("git", ["fetch", "origin", "--prune"], { timeout: GIT_TIMEOUT_MS });
31351
- return (await execFileP2("git", ["ls-remote", "--heads", "origin", branch], { timeout: GIT_TIMEOUT_MS })).stdout.trim().length > 0;
31379
+ if (options.prune) await execFileP2("git", cleanupGitArgs(options.cwd, ["fetch", "origin", "--prune"]), { timeout: GIT_TIMEOUT_MS });
31380
+ return (await execFileP2("git", cleanupGitArgs(options.cwd, ["ls-remote", "--heads", "origin", branch]), { timeout: GIT_TIMEOUT_MS })).stdout.trim().length > 0;
31352
31381
  } catch {
31353
31382
  return void 0;
31354
31383
  }
@@ -40864,7 +40893,10 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
40864
40893
  gcAcknowledged: o.gc,
40865
40894
  expectedHeadOid: headRefOid,
40866
40895
  pathExists: (p) => (0, import_node_fs43.existsSync)(p),
40867
- execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout
40896
+ // #5899: pin cleanup git calls to the main checkout the task worktree this process may be
40897
+ // standing in is removed mid-cleanup, so a cwd-relative invocation fails with
40898
+ // 'fatal: not a git repository' and leaves a spurious partial-cleanup exit.
40899
+ execGit: async (args) => (await execFileP2("git", cleanupGitArgs(primaryRoot, args), { timeout: GIT_TIMEOUT_MS })).stdout
40868
40900
  });
40869
40901
  } catch (e) {
40870
40902
  localCleanup = {
@@ -40879,8 +40911,10 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
40879
40911
  const remoteBranch = await deleteMergedRemoteBranch({
40880
40912
  branch: headRef,
40881
40913
  existedBefore: remoteBefore,
40882
- execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
40883
- branchExists: remoteBranchExists2
40914
+ // #5899: this leg runs after worktree teardown anchor it to the main checkout so a cwd inside
40915
+ // the removed worktree cannot turn the delete into 'fatal: not a git repository' + manual remediation.
40916
+ execGit: async (args) => (await execFileP2("git", cleanupGitArgs(primaryRoot, args), { timeout: GIT_TIMEOUT_MS })).stdout,
40917
+ branchExists: (b) => remoteBranchExists2(b, { cwd: primaryRoot })
40884
40918
  });
40885
40919
  const worktreePartial = localCleanup?.worktree?.path && localCleanup.worktree.status !== "removed" ? {
40886
40920
  kind: "worktree-directory",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.2.6",
3
+ "version": "4.2.7",
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",