@mutmutco/cli 4.5.80 → 4.5.82

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 +92 -33
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -17249,10 +17249,10 @@ var rollout_plan_default = {
17249
17249
  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)."
17250
17250
  },
17251
17251
  baseline: {
17252
- version: "4.5.80",
17253
- tag: "v4.5.80",
17254
- commit: "a3829824b3c6",
17255
- npm: "@mutmutco/cli@4.5.80"
17252
+ version: "4.5.82",
17253
+ tag: "v4.5.82",
17254
+ commit: "d9d21d149b66",
17255
+ npm: "@mutmutco/cli@4.5.82"
17256
17256
  },
17257
17257
  exitCriterion: "fleet-n-of-n",
17258
17258
  hubOnlyShortcut: "forbidden",
@@ -17269,14 +17269,14 @@ var rollout_plan_default = {
17269
17269
  repo: "mutmutco/mmi-hub",
17270
17270
  role: "canary",
17271
17271
  schedule: "train",
17272
- v3Target: "v4.5.80"
17272
+ v3Target: "v4.5.82"
17273
17273
  }
17274
17274
  ],
17275
17275
  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.",
17276
17276
  rollback: {
17277
17277
  independent: true,
17278
- mechanism: "npm dist-tag latest -> 4.5.80 and redeploy the Hub Lambda from tag v4.5.80 (a3829824b3c6); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
17279
- v3Target: "v4.5.80 (@mutmutco/cli@4.5.80, tag commit a3829824b3c6 \u2014 last known-good release carrying the repo-index v4-only contract)"
17278
+ mechanism: "npm dist-tag latest -> 4.5.82 and redeploy the Hub Lambda from tag v4.5.82 (d9d21d149b66); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
17279
+ v3Target: "v4.5.82 (@mutmutco/cli@4.5.82, tag commit d9d21d149b66 \u2014 last known-good release carrying the repo-index v4-only contract)"
17280
17280
  }
17281
17281
  },
17282
17282
  {
@@ -21627,7 +21627,51 @@ async function dispatchCanary(deps, targetRepo2, nonce) {
21627
21627
  attempts.every((a) => isCanaryAccessBlockText(a)) ? `${detail} Every door answered with an access refusal \u2014 the caller may read MMI-Hub but not start its workflows.` : detail
21628
21628
  );
21629
21629
  }
21630
+ var HOSTED_LABEL_RE = /\b(?:ubuntu|windows|macos)-(?:latest|\d[\w.-]*)\b/;
21631
+ var AUTOMATIC_TRIGGERS = /* @__PURE__ */ new Set(["push", "pull_request", "pull_request_target", "release", "schedule", "workflow_run", "merge_group", "create", "deployment", "repository_dispatch", "check_run", "check_suite", "issues", "issue_comment"]);
21632
+ function workflowTriggers(yaml) {
21633
+ const lines2 = yaml.split(/\r?\n/);
21634
+ const i = lines2.findIndex((l) => /^(?:on|"on"|'on'):(?:\s|$)/.test(l));
21635
+ if (i < 0) return [];
21636
+ const inline = lines2[i].replace(/^[^:]+:\s*/, "").replace(/\s+#.*$/, "").trim();
21637
+ if (inline) return inline.replace(/^\[|\]$/g, "").split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
21638
+ const out = [];
21639
+ for (const line of lines2.slice(i + 1)) {
21640
+ if (/^\S/.test(line)) break;
21641
+ const m = /^ {2}([A-Za-z_]+):/.exec(line) ?? /^ {2}- ([A-Za-z_]+)\s*$/.exec(line);
21642
+ if (m) out.push(m[1]);
21643
+ }
21644
+ return out;
21645
+ }
21646
+ function workflowNeedsHostedRunner(yaml) {
21647
+ const body = yaml.split(/\r?\n/).filter((l) => !/^\s*#/.test(l)).join("\n");
21648
+ return HOSTED_LABEL_RE.test(body) && workflowTriggers(yaml).some((t) => AUTOMATIC_TRIGGERS.has(t));
21649
+ }
21650
+ async function hostedWorkflowsOf(deps, repo) {
21651
+ const list = parseJson(
21652
+ await deps.run("gh", ["api", `repos/${repo}/contents/.github/workflows`]),
21653
+ `${repo} workflow listing`
21654
+ );
21655
+ const hosted = [];
21656
+ for (const f of list) {
21657
+ if (f.type !== "file" || !f.path || !/\.ya?ml$/.test(f.path) || f.name === CANARY_WORKFLOW) continue;
21658
+ const raw = parseJson(await deps.run("gh", ["api", `repos/${repo}/contents/${f.path}`]), `${repo} ${f.path}`);
21659
+ if (workflowNeedsHostedRunner(Buffer.from(raw.content ?? "", "base64").toString("utf8"))) hosted.push(f.path);
21660
+ }
21661
+ return hosted;
21662
+ }
21630
21663
  async function assertActionsJobsCanStart(deps, targetRepo2) {
21664
+ let hosted;
21665
+ try {
21666
+ hosted = await hostedWorkflowsOf(deps, targetRepo2);
21667
+ } catch {
21668
+ hosted = void 0;
21669
+ }
21670
+ if (hosted && hosted.length === 0) {
21671
+ process.stderr.write(`actions preflight: ${targetRepo2} has no automatically triggered GitHub-hosted job; hosted canary skipped (#7721)
21672
+ `);
21673
+ return;
21674
+ }
21631
21675
  const scanned = await scanRepoForBillingBlock(deps, targetRepo2);
21632
21676
  if (scanned) throw actionsBillingRefusal(scanned);
21633
21677
  if (targetRepo2.toLowerCase() !== CANARY_REPO.toLowerCase()) {
@@ -39718,42 +39762,57 @@ async function openSuitePinPr(deps, input) {
39718
39762
  const url = new URL(old);
39719
39763
  url.searchParams.set("v", tag);
39720
39764
  const pin = url.toString();
39721
- const branch = `suite-pin/${stage}-${member.id}-${tag}`;
39765
+ const branch = `suite-pin/${stage}-${member.id}`;
39766
+ const legacy = `${branch}-${tag}`;
39767
+ const title = `Pin ${member.id} ${stage} at ${tag}`;
39768
+ const tagOf = (t) => t.startsWith(`Pin ${member.id} ${stage} at `) ? t.slice(`Pin ${member.id} ${stage} at `.length) : void 0;
39769
+ const list = async (head) => JSON.parse(await deps.run("gh", ["pr", "list", "--repo", SUITE_REPO, "--head", head, "--state", "all", "--json", "url,state,title"], scratch || void 0));
39770
+ const onBranch = await list(branch);
39771
+ const legacyOpen = JSON.parse(await deps.run("gh", ["pr", "list", "--repo", SUITE_REPO, "--state", "open", "--limit", "200", "--json", "url,headRefName"])).flatMap((p) => {
39772
+ const t = p.headRefName.startsWith(`${branch}-v`) ? p.headRefName.slice(branch.length + 1) : void 0;
39773
+ return t ? [{ url: p.url, tag: t }] : [];
39774
+ });
39775
+ const newer = [
39776
+ ...onBranch.filter((p) => p.state !== "CLOSED").flatMap((p) => {
39777
+ const t = tagOf(p.title ?? "");
39778
+ return t && compareVersions(t, tag) > 0 ? [p] : [];
39779
+ }),
39780
+ ...legacyOpen.filter((p) => compareVersions(p.tag, tag) > 0).map((p) => ({ url: p.url, state: "OPEN" }))
39781
+ ];
39782
+ const exact = [...onBranch.filter((p) => tagOf(p.title ?? "") === tag), ...await list(legacy)];
39783
+ if (exact.some((p) => p.state === "CLOSED")) {
39784
+ if (newer.length) return { status: "superseded", pr: newer[0].url, url: pin };
39785
+ throw new Error(`${branch} for ${tag} was closed without landing`);
39786
+ }
39787
+ if (exact.some((p) => p.state === "MERGED")) {
39788
+ if (old !== pin) throw new Error(`${branch} merged but development still carries ${old}`);
39789
+ return { status: "merged", pr: exact[0].url, url: pin };
39790
+ }
39791
+ if (old === pin) return { status: "merged", pr: exact[0]?.url ?? `${SUITE_REPO} development already pinned`, url: pin };
39792
+ if (newer.length) return { status: "superseded", pr: newer[0].url, url: pin };
39793
+ const open2 = onBranch.find((p) => p.state === "OPEN");
39794
+ if (open2 && tagOf(open2.title ?? "") === tag) return { status: "pending", pr: open2.url, url: pin };
39722
39795
  await (0, import_promises6.mkdir)((0, import_node_path34.join)(deps.root, "tmp"), { recursive: true });
39723
39796
  scratch = await (0, import_promises6.mkdtemp)((0, import_node_path34.join)(deps.root, "tmp", "suite-pin-"));
39724
39797
  const run = (file, args) => deps.run(file, args, scratch);
39725
39798
  await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
39726
- const existing = JSON.parse(await run("gh", ["pr", "list", "--repo", SUITE_REPO, "--head", branch, "--state", "all", "--json", "url,state"]));
39727
- if (existing.some((pr2) => pr2.state === "CLOSED")) throw new Error(`${branch} was closed without landing`);
39728
- if (existing.some((pr2) => pr2.state === "MERGED")) {
39729
- if (old !== pin) throw new Error(`${branch} merged but development still carries ${old}`);
39730
- return { status: "merged", pr: existing[0].url, url: pin };
39731
- }
39732
- if (old === pin) return { status: "merged", pr: existing[0]?.url ?? `${SUITE_REPO} development already pinned`, url: pin };
39733
- if (existing.length) {
39734
- return { status: "pending", pr: existing[0].url, url: pin };
39735
- }
39736
39799
  await run("git", ["switch", "-c", branch]);
39737
39800
  await run("node", ["scripts/pin-modules.mjs", "--stage", stage, "--module", member.id, "--url", pin]);
39738
39801
  await run("git", ["add", "seed/modules.json"]);
39739
39802
  await run("git", ["commit", "-m", `Pin ${member.id} ${stage} at ${tag} (#7266)`]);
39740
- await run("git", ["push", "origin", branch]);
39741
- const pr = (await run("gh", [
39742
- "pr",
39743
- "create",
39744
- "--repo",
39745
- SUITE_REPO,
39746
- "--base",
39747
- "development",
39748
- "--head",
39749
- branch,
39750
- "--title",
39751
- `Pin ${member.id} ${stage} at ${tag}`,
39752
- "--body",
39753
- `Solo ${repo} release ${tag} (#7266). Pin ${stage} to the released bundle.`
39754
- ])).trim();
39803
+ await run("git", ["push", "--force", "origin", branch]);
39804
+ const body = `Solo ${repo} release ${tag} (#7266, #7714). Pin ${stage} to the released bundle.`;
39805
+ let pr;
39806
+ if (open2) {
39807
+ pr = open2.url;
39808
+ await run("gh", ["pr", "edit", pr, "--repo", SUITE_REPO, "--title", title, "--body", body]);
39809
+ } else {
39810
+ pr = (await run("gh", ["pr", "create", "--repo", SUITE_REPO, "--base", "development", "--head", branch, "--title", title, "--body", body])).trim();
39811
+ }
39755
39812
  if (!pr.includes("/pull/")) throw new Error(`gh pr create returned no PR URL: ${pr}`);
39756
39813
  await run("gh", ["pr", "merge", pr, "--repo", SUITE_REPO, "--auto", "--squash"]);
39814
+ for (const old2 of legacyOpen.filter((p) => compareVersions(p.tag, tag) < 0))
39815
+ await run("gh", ["pr", "close", old2.url, "--repo", SUITE_REPO, "--comment", `Superseded by ${pr} (${member.id} ${stage} at ${tag}).`]);
39757
39816
  return { status: "pending", pr, url: pin };
39758
39817
  } catch (e) {
39759
39818
  throw new Error(`suite pin PR FAILED for ${repo} ${tag}: ${e.message}; remedy: ${remedy}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.5.80",
3
+ "version": "4.5.82",
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",