@mutmutco/cli 4.5.34 → 4.5.35

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 +53 -15
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -16840,10 +16840,10 @@ var rollout_plan_default = {
16840
16840
  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)."
16841
16841
  },
16842
16842
  baseline: {
16843
- version: "4.5.34",
16844
- tag: "v4.5.34",
16845
- commit: "fe244fb48abc",
16846
- npm: "@mutmutco/cli@4.5.34"
16843
+ version: "4.5.35",
16844
+ tag: "v4.5.35",
16845
+ commit: "f10f5c65382f",
16846
+ npm: "@mutmutco/cli@4.5.35"
16847
16847
  },
16848
16848
  exitCriterion: "fleet-n-of-n",
16849
16849
  hubOnlyShortcut: "forbidden",
@@ -16860,14 +16860,14 @@ var rollout_plan_default = {
16860
16860
  repo: "mutmutco/mmi-hub",
16861
16861
  role: "canary",
16862
16862
  schedule: "train",
16863
- v3Target: "v4.5.34"
16863
+ v3Target: "v4.5.35"
16864
16864
  }
16865
16865
  ],
16866
16866
  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.",
16867
16867
  rollback: {
16868
16868
  independent: true,
16869
- mechanism: "npm dist-tag latest -> 4.5.34 and redeploy the Hub Lambda from tag v4.5.34 (fe244fb48abc); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16870
- v3Target: "v4.5.34 (@mutmutco/cli@4.5.34, tag commit fe244fb48abc \u2014 last known-good release carrying the repo-index v4-only contract)"
16869
+ mechanism: "npm dist-tag latest -> 4.5.35 and redeploy the Hub Lambda from tag v4.5.35 (f10f5c65382f); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16870
+ v3Target: "v4.5.35 (@mutmutco/cli@4.5.35, tag commit f10f5c65382f \u2014 last known-good release carrying the repo-index v4-only contract)"
16871
16871
  }
16872
16872
  },
16873
16873
  {
@@ -34313,12 +34313,13 @@ var import_node_crypto11 = require("node:crypto");
34313
34313
  var SCHEMA = "mmi-suite-cut/v1";
34314
34314
  var SHA2 = /^[a-f0-9]{40}$/i;
34315
34315
  var RC_TAG = /^v\d+\.\d+\.\d+-rc\.\d+$/;
34316
+ var RELEASE_TAG = /^v\d+\.\d+\.\d+$/;
34316
34317
  function suitePins(record) {
34317
- return record.members.map((m) => ({
34318
+ return record.members.map((m) => m.carried ? { module: m.id, rc: m.rcPin, main: m.mainPin } : {
34318
34319
  module: m.id,
34319
34320
  rc: `https://${m.id === "katip" ? m.rcHost ?? m.host : m.host}/${m.id === "katip" ? "" : "rc/"}module.js?v=${m.tag}`,
34320
34321
  main: `https://${m.host}/module.js?v=${suiteReleaseTag(m.tag)}`
34321
- }));
34322
+ });
34322
34323
  }
34323
34324
  function suiteReleaseTag(rc) {
34324
34325
  if (!RC_TAG.test(rc)) throw new Error(`invalid rc tag ${rc}`);
@@ -34338,7 +34339,7 @@ function readSuiteCut(path2) {
34338
34339
  } catch {
34339
34340
  throw new Error(`suite cut record unreadable at ${path2}; repair it, never recut`);
34340
34341
  }
34341
- if (record.schema !== SCHEMA || record.lead !== SUITE_REPO || !SHA2.test(record.leadDevelopment) || !SHA2.test(record.leadPreviousRc) || !Array.isArray(record.members) || record.members.length === 0 || record.pinPr !== void 0 && (typeof record.pinPr !== "string" || !record.pinPr.startsWith("https://github.com/mutmutco/MM-Work/pull/")) || record.pinMerged !== void 0 && (record.pinMerged !== true && record.pinMerged !== false || !record.pinPr) || record.leadRcTag !== void 0 && (!RC_TAG.test(record.leadRcTag) || !SHA2.test(record.leadRcSha ?? "") || !record.pinMerged) || record.leadReleased !== void 0 && (record.leadReleased !== true || !record.leadRcTag) || record.announcement !== void 0 && (!["pending", "sent"].includes(record.announcement) || !record.leadReleased) || record.members.some((m) => !m.id || !m.repo || !m.backend || !m.host || !SHA2.test(m.development) || !SHA2.test(m.previousRc) || m.rcWasAbsent !== void 0 && m.rcWasAbsent !== true || m.tag !== void 0 && (!RC_TAG.test(m.tag) || !SHA2.test(m.sha ?? "")) || m.published !== void 0 && (m.published !== true || !m.tag) || m.released !== void 0 && (m.released !== true || !m.published) || m.backendAfterRelease !== void 0 && (!m.released || typeof m.backendAfterRelease !== "string")))
34342
+ if (record.schema !== SCHEMA || record.lead !== SUITE_REPO || !SHA2.test(record.leadDevelopment) || !SHA2.test(record.leadPreviousRc) || !Array.isArray(record.members) || record.members.length === 0 || record.pinPr !== void 0 && (typeof record.pinPr !== "string" || !record.pinPr.startsWith("https://github.com/mutmutco/MM-Work/pull/")) || record.pinMerged !== void 0 && (record.pinMerged !== true && record.pinMerged !== false || !record.pinPr) || record.leadRcTag !== void 0 && (!RC_TAG.test(record.leadRcTag) || !SHA2.test(record.leadRcSha ?? "") || !record.pinMerged) || record.leadReleased !== void 0 && (record.leadReleased !== true || !record.leadRcTag) || record.announcement !== void 0 && (!["pending", "sent"].includes(record.announcement) || !record.leadReleased) || record.members.some((m) => !m.id || !m.repo || !m.backend || !m.host || !SHA2.test(m.development) || !SHA2.test(m.previousRc) || m.rcWasAbsent !== void 0 && m.rcWasAbsent !== true || m.tag !== void 0 && (!RC_TAG.test(m.tag) || !SHA2.test(m.sha ?? "")) || m.published !== void 0 && (m.published !== true || !m.tag) || m.released !== void 0 && (m.released !== true || !m.published) || m.backendAfterRelease !== void 0 && (!m.released || typeof m.backendAfterRelease !== "string") || m.carried !== void 0 && (m.carried !== true || m.tag !== void 0 || m.published !== void 0 || !isPin(m.rcPin) || !isPin(m.mainPin)) || m.carried === void 0 && (m.rcPin !== void 0 || m.mainPin !== void 0)))
34342
34343
  throw new Error(`suite cut record has unknown or invalid state at ${path2}; repair it, never recut`);
34343
34344
  return record;
34344
34345
  }
@@ -34352,6 +34353,27 @@ function writeSuiteCut(path2, record) {
34352
34353
  throw new Error(`suite cut record write failed at ${path2}: ${e.message}; stop, verify the file before retrying`);
34353
34354
  }
34354
34355
  }
34356
+ function isPin(url) {
34357
+ return typeof url === "string" && url.startsWith("https://");
34358
+ }
34359
+ function pinVersion(url) {
34360
+ try {
34361
+ return url ? new URL(url).searchParams.get("v") ?? "" : "";
34362
+ } catch {
34363
+ return "";
34364
+ }
34365
+ }
34366
+ async function carriedPins(deps, member, bundles) {
34367
+ const main = bundles.main;
34368
+ if (!main || !RELEASE_TAG.test(pinVersion(main))) throw new Error(`${member.repo}: nothing new since rc, but its MM-Work main pin ${main ?? "(none)"} has no ?v=vX.Y.Z release; pin it before the suite cut`);
34369
+ for (const url of [bundles.rc, main]) {
34370
+ const tag = pinVersion(url);
34371
+ if (!RC_TAG.test(tag) && !RELEASE_TAG.test(tag)) continue;
34372
+ const sha = await deps.tagSha(member.repo, tag);
34373
+ if (sha && await deps.contains(member.repo, member.development, sha)) return { rcPin: url, mainPin: main };
34374
+ }
34375
+ throw new Error(`${member.repo}: nothing new since rc, but no pinned MM-Work bundle carries development ${member.development.slice(0, 12)}; pin it before the suite cut`);
34376
+ }
34355
34377
  async function suiteRcand(deps, opts) {
34356
34378
  if (!opts.ownerGo) throw new Error("suite rcand requires --owner-go (rc uses live data); nothing was written");
34357
34379
  const members = suiteMembers(await deps.registry());
@@ -34364,8 +34386,6 @@ async function suiteRcand(deps, opts) {
34364
34386
  if (opts.dryRun) return { plan };
34365
34387
  let record = readSuiteCut(deps.recordPath);
34366
34388
  if (record?.announcement === "sent" && record.leadReleased) {
34367
- const heads = await Promise.all(modules.map(async (m) => [await deps.head(m.repo, "development"), await deps.head(m.repo, "rc")]));
34368
- if (heads.some(([dev, rc]) => dev === rc)) throw new Error("a member has no new development commits since rc; no new suite cut started");
34369
34389
  const archive = `${deps.recordPath}.${record.leadRcTag}.json`;
34370
34390
  if ((0, import_node_fs29.existsSync)(archive)) throw new Error(`suite cut archive already exists at ${archive}; inspect before a new cut`);
34371
34391
  (0, import_node_fs29.renameSync)(deps.recordPath, archive);
@@ -34390,9 +34410,22 @@ async function suiteRcand(deps, opts) {
34390
34410
  if (!record) throw new Error("suite cut record unavailable after initialization");
34391
34411
  if (record.members.length !== modules.length || modules.some((m) => !record.members.some((r) => r.repo === m.repo && r.id === m.id)))
34392
34412
  throw new Error("suite roster changed since cut began; refuse to omit or add a member");
34413
+ const quiet = [];
34414
+ for (const member of record.members) {
34415
+ if (!member.tag && !member.carried && await deps.contains(member.repo, member.development, member.previousRc)) quiet.push(member);
34416
+ }
34417
+ if (record.members.every((m) => m.carried || quiet.includes(m))) {
34418
+ (0, import_node_fs29.rmSync)(deps.recordPath, { force: true });
34419
+ throw new Error("nothing to promote: no MM-Work module has new development commits since its rc; no suite cut started");
34420
+ }
34421
+ for (const member of quiet) {
34422
+ Object.assign(member, { carried: true }, await carriedPins(deps, member, modules.find((m) => m.repo === member.repo).bundles));
34423
+ writeSuiteCut(deps.recordPath, record);
34424
+ }
34393
34425
  for (const member of record.members) {
34394
34426
  if ((await deps.head(member.repo, "development"))?.toLowerCase() !== member.development.toLowerCase())
34395
34427
  throw new Error(`${member.repo}: development moved after the cut snapshot; refuse to add untested code`);
34428
+ if (member.carried) continue;
34396
34429
  if (!member.tag) {
34397
34430
  const cut = await deps.rcand(member.repo, member.previousRc, member.rcWasAbsent);
34398
34431
  if (!RC_TAG.test(cut.tag) || !SHA2.test(cut.sha) || cut.sha.toLowerCase() === member.previousRc.toLowerCase()) throw new Error(`${member.repo}: rcand did not return a new immutable rc tag/SHA`);
@@ -34443,6 +34476,11 @@ async function suiteRelease(deps, ownerGo) {
34443
34476
  if (members.length !== record.members.length || members.some((m) => !record.members.some((r) => r.repo === m.repo && r.id === m.id))) throw new Error("suite roster changed after the cut; release refused");
34444
34477
  await deps.verifyPins(suitePins(record));
34445
34478
  for (const member of record.members) {
34479
+ if (member.carried) {
34480
+ const live2 = await deps.backend({ repo: member.repo, id: member.id, bundles: {} });
34481
+ if (live2.version !== member.backend) throw new Error(`${member.repo}: live backend changed after the cut (${member.backend} \u2192 ${live2.version}); release refused`);
34482
+ continue;
34483
+ }
34446
34484
  const [sha, live, rcHead] = await Promise.all([deps.tagSha(member.repo, member.tag), deps.backend({ repo: member.repo, id: member.id, bundles: {} }), deps.head(member.repo, "rc")]);
34447
34485
  if (!sha || sha.toLowerCase() !== member.sha?.toLowerCase() || !member.released && rcHead?.toLowerCase() !== member.sha?.toLowerCase()) throw new Error(`${member.repo}: rc tag/branch ${member.tag} changed after the cut; release refused`);
34448
34486
  const katipReleaseSha = member.id === "katip" ? await deps.tagSha(member.repo, suiteReleaseTag(member.tag)) : void 0;
@@ -34452,7 +34490,7 @@ async function suiteRelease(deps, ownerGo) {
34452
34490
  }
34453
34491
  if ((await deps.tagSha(SUITE_REPO, record.leadRcTag))?.toLowerCase() !== record.leadRcSha?.toLowerCase()) throw new Error("MM-Work rc tag changed after the cut; release refused");
34454
34492
  for (const member of record.members) {
34455
- if (member.released) continue;
34493
+ if (member.released || member.carried) continue;
34456
34494
  await deps.release(member.repo, suiteReleaseTag(member.tag), member.sha);
34457
34495
  if (member.id === "katip") {
34458
34496
  const expected = await deps.tagSha(member.repo, suiteReleaseTag(member.tag));
@@ -44471,7 +44509,7 @@ function registerSuiteCommands(program3) {
44471
44509
  console.log(o.json ? JSON.stringify(result, null, 2) : [
44472
44510
  `suite rcand ${o.dryRun ? "dry-run" : "receipt"}:`,
44473
44511
  ...result.plan.map((line) => ` - ${line}`),
44474
- ...result.record?.members.map((m) => ` - ${m.repo}: ${m.tag} (${m.sha?.slice(0, 12)}) ${m.published ? "served" : "pending"}`) ?? [],
44512
+ ...result.record?.members.map((m) => m.carried ? ` - ${m.repo}: carried (nothing new) at ${m.mainPin}` : ` - ${m.repo}: ${m.tag} (${m.sha?.slice(0, 12)}) ${m.published ? "served" : "pending"}`) ?? [],
44475
44513
  ...result.record ? [` - pin PR ${result.record.pinPr}: merged; MM-Work ${result.record.leadRcTag}`] : []
44476
44514
  ].join("\n"));
44477
44515
  } catch (e) {
@@ -44494,7 +44532,7 @@ function registerSuiteCommands(program3) {
44494
44532
  }
44495
44533
  console.log(o.json ? JSON.stringify(result, null, 2) : [
44496
44534
  `suite release: ${result.leadRcTag} \u2192 main`,
44497
- ...result.members.map((m) => ` - ${m.repo}: ${m.tag} \u2192 main ${m.released ? "released" : "pending"}`),
44535
+ ...result.members.map((m) => m.carried ? ` - ${m.repo}: carried, untouched` : ` - ${m.repo}: ${m.tag} \u2192 main ${m.released ? "released" : "pending"}`),
44498
44536
  ` - MM-Work: ${result.leadReleased ? "released" : "pending"}; announcement: ${result.announcement}`
44499
44537
  ].join("\n"));
44500
44538
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.5.34",
3
+ "version": "4.5.35",
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",