@mutmutco/cli 4.5.78 → 4.5.80
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 +82 -22
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -14366,7 +14366,7 @@ var PRODUCT_PENDING_RULESET_PREFIX = "mmi-product-required-checks-pending-";
|
|
|
14366
14366
|
var PRODUCT_GATE_CONTEXT = "gate";
|
|
14367
14367
|
var PRODUCT_RULESET_PATH = ".github/rulesets/mmi-product-required-checks.json";
|
|
14368
14368
|
function reseedProductRulesetStrictness(current, seed, declared = {}) {
|
|
14369
|
-
const
|
|
14369
|
+
const parse2 = (raw) => {
|
|
14370
14370
|
const policy2 = JSON.parse(raw);
|
|
14371
14371
|
if (!policy2 || typeof policy2 !== "object" || Array.isArray(policy2) || policy2.name !== PRODUCT_RULESET_NAME || policy2.target !== "branch" || !Array.isArray(policy2.rules) || policy2.rules.some((r) => !r || typeof r !== "object" || Array.isArray(r) || typeof r.type !== "string")) {
|
|
14372
14372
|
throw new Error("product ruleset must be a branch ruleset object with well-formed rules");
|
|
@@ -14378,11 +14378,11 @@ function reseedProductRulesetStrictness(current, seed, declared = {}) {
|
|
|
14378
14378
|
}
|
|
14379
14379
|
return { policy: policy2, parameters: parameters2 };
|
|
14380
14380
|
};
|
|
14381
|
-
const canonical =
|
|
14381
|
+
const canonical = parse2(seed);
|
|
14382
14382
|
const strict = canonical.parameters.strict_required_status_checks_policy;
|
|
14383
14383
|
if (typeof strict !== "boolean") throw new Error("canonical product ruleset must declare boolean strictness");
|
|
14384
14384
|
if (current === null) return seed;
|
|
14385
|
-
const { policy, parameters } =
|
|
14385
|
+
const { policy, parameters } = parse2(current);
|
|
14386
14386
|
let changed = parameters.strict_required_status_checks_policy !== strict;
|
|
14387
14387
|
parameters.strict_required_status_checks_policy = strict;
|
|
14388
14388
|
if (declared.requiredChecks) {
|
|
@@ -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.
|
|
17253
|
-
tag: "v4.5.
|
|
17254
|
-
commit: "
|
|
17255
|
-
npm: "@mutmutco/cli@4.5.
|
|
17252
|
+
version: "4.5.80",
|
|
17253
|
+
tag: "v4.5.80",
|
|
17254
|
+
commit: "a3829824b3c6",
|
|
17255
|
+
npm: "@mutmutco/cli@4.5.80"
|
|
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.
|
|
17272
|
+
v3Target: "v4.5.80"
|
|
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.
|
|
17279
|
-
v3Target: "v4.5.
|
|
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)"
|
|
17280
17280
|
}
|
|
17281
17281
|
},
|
|
17282
17282
|
{
|
|
@@ -20071,12 +20071,12 @@ async function isStrayUnreleasedTag(deps, tag, remoteSha, repo) {
|
|
|
20071
20071
|
return deps.run("gh", ["release", "view", tag, "--repo", repo, "--json", "tagName"]).then(() => false).catch((e) => /not found|HTTP 404/i.test(e instanceof Error ? e.message : String(e)));
|
|
20072
20072
|
}
|
|
20073
20073
|
function cmpReleaseTag(a, b) {
|
|
20074
|
-
const
|
|
20074
|
+
const parse2 = (t) => {
|
|
20075
20075
|
const m = /^v(\d+)\.(\d+)\.(\d+)$/.exec(t.trim());
|
|
20076
20076
|
return m ? { major: +m[1], minor: +m[2], patch: +m[3] } : null;
|
|
20077
20077
|
};
|
|
20078
|
-
const left =
|
|
20079
|
-
const right =
|
|
20078
|
+
const left = parse2(a);
|
|
20079
|
+
const right = parse2(b);
|
|
20080
20080
|
if (!left || !right) throw new Error(`cmpReleaseTag expects vX.Y.Z tags, got ${a} vs ${b}`);
|
|
20081
20081
|
return left.major - right.major || left.minor - right.minor || left.patch - right.patch;
|
|
20082
20082
|
}
|
|
@@ -26229,12 +26229,12 @@ function resumeFollowUpOf(state) {
|
|
|
26229
26229
|
return state === "aligned" ? "complete" : state;
|
|
26230
26230
|
}
|
|
26231
26231
|
function compareRcTagsNewestFirst(a, b) {
|
|
26232
|
-
const
|
|
26232
|
+
const parse2 = (tag) => {
|
|
26233
26233
|
const match = /^v(\d+)\.(\d+)\.(\d+)-rc\.(\d+)$/.exec(tag);
|
|
26234
26234
|
return match ? match.slice(1).map(Number) : [];
|
|
26235
26235
|
};
|
|
26236
|
-
const left =
|
|
26237
|
-
const right =
|
|
26236
|
+
const left = parse2(a);
|
|
26237
|
+
const right = parse2(b);
|
|
26238
26238
|
for (let i = 0; i < 4; i++) {
|
|
26239
26239
|
const delta = (right[i] ?? 0) - (left[i] ?? 0);
|
|
26240
26240
|
if (delta !== 0) return delta;
|
|
@@ -32638,6 +32638,7 @@ function assignWaves(repos, canarySlug) {
|
|
|
32638
32638
|
function statusFor(read, currentSha) {
|
|
32639
32639
|
if (!read) return { status: "pending", record: {} };
|
|
32640
32640
|
if (read.drift === "match") return { status: "match", record: { mergeSha: read.pr?.mergeSha } };
|
|
32641
|
+
if (read.prUnread) return { status: "red", record: {} };
|
|
32641
32642
|
const pr = read.pr;
|
|
32642
32643
|
if (!pr) return { status: "pending", record: {} };
|
|
32643
32644
|
if (pr.sourceSha && pr.sourceSha !== currentSha) return { status: "pending", record: {} };
|
|
@@ -35090,9 +35091,10 @@ LIVE apply to ${repo}:
|
|
|
35090
35091
|
desiredByRepo.set(r.repo, desired);
|
|
35091
35092
|
const drift = compareSeedBytes(desired, content);
|
|
35092
35093
|
let pr;
|
|
35094
|
+
let prUnread = false;
|
|
35093
35095
|
try {
|
|
35094
35096
|
const listBranchPrs = async (branch) => {
|
|
35095
|
-
const listed = await gh2(["pr", "list", "--repo", r.repo, "--head", branch, "--base", baseBranch, "--state", "all", "--json", "number,url,state,statusCheckRollup,mergeCommit,body,files", "--limit", "100"]);
|
|
35097
|
+
const listed = await gh2(["pr", "list", "--repo", r.repo, "--head", branch, "--base", baseBranch, "--state", "all", "--json", "number,url,state,statusCheckRollup,mergeCommit,body,files,headRefName", "--limit", "100"]);
|
|
35096
35098
|
return JSON.parse(listed.stdout || "[]");
|
|
35097
35099
|
};
|
|
35098
35100
|
const targetSpecific = (await listBranchPrs(propagationBranch(r.repo, seed.target))).map((p2) => ({ ...p2, files: p2.files?.map((f) => f.path) }));
|
|
@@ -35107,11 +35109,19 @@ LIVE apply to ${repo}:
|
|
|
35107
35109
|
const checks = rollup.length === 0 ? "none" : rollup.some((c) => c.conclusion === "FAILURE" || c.state === "FAILURE") ? "red" : rollup.every((c) => c.conclusion === "SUCCESS" || c.state === "SUCCESS") ? "success" : "pending";
|
|
35108
35110
|
const sourceSha = p.body?.match(/Propagates MMI-Hub@([0-9a-fA-F]{7,40})/)?.[1];
|
|
35109
35111
|
pr = { number: p.number, url: p.url, state: p.state === "MERGED" ? "merged" : p.state === "CLOSED" ? "closed" : "open", checks, mergeSha: p.mergeCommit?.oid, sourceSha };
|
|
35112
|
+
if (pr.state === "open" && sourceSha && sourceSha !== seedSource.sha && p.headRefName) {
|
|
35113
|
+
const head = await gh2(["api", `repos/${r.repo}/contents/${enc(seed.target)}?ref=${encodeURIComponent(p.headRefName)}`]);
|
|
35114
|
+
const parsedHead = JSON.parse(head.stdout);
|
|
35115
|
+
const headContent = parsedHead.encoding === "base64" && typeof parsedHead.content === "string" ? decodeGitHubContents(parsedHead.content) : null;
|
|
35116
|
+
if (compareSeedBytes(desired, headContent) === "match") pr.sourceSha = seedSource.sha;
|
|
35117
|
+
}
|
|
35110
35118
|
}
|
|
35111
|
-
} catch {
|
|
35112
|
-
|
|
35119
|
+
} catch (e) {
|
|
35120
|
+
prUnread = true;
|
|
35121
|
+
process.stderr.write(`bootstrap propagate: could not read ${r.repo}'s propagation PRs for ${seed.target}: ${e.message}
|
|
35122
|
+
`);
|
|
35113
35123
|
}
|
|
35114
|
-
reads.push({ repo: r.repo, drift: drift === "waived" ? "match" : drift, pr });
|
|
35124
|
+
reads.push({ repo: r.repo, drift: drift === "waived" ? "match" : drift, pr, prUnread });
|
|
35115
35125
|
}
|
|
35116
35126
|
let functionGateSatisfied = !isWorkflowSeed;
|
|
35117
35127
|
if (isWorkflowSeed && canarySlug) {
|
|
@@ -35627,10 +35637,41 @@ function formatSuiteStatus(report) {
|
|
|
35627
35637
|
var import_node_fs29 = require("node:fs");
|
|
35628
35638
|
var import_node_path29 = require("node:path");
|
|
35629
35639
|
var import_node_crypto13 = require("node:crypto");
|
|
35640
|
+
|
|
35641
|
+
// src/kit-version.ts
|
|
35642
|
+
var parse = (v) => /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(typeof v === "string" ? v.trim() : "");
|
|
35643
|
+
function kitSatisfies(shell2, module2) {
|
|
35644
|
+
const s = parse(shell2);
|
|
35645
|
+
const m = parse(module2);
|
|
35646
|
+
if (!s || !m) return true;
|
|
35647
|
+
for (let i = 1; i <= 3; i++) if (Number(s[i]) !== Number(m[i])) return Number(s[i]) > Number(m[i]);
|
|
35648
|
+
return !s[4] || Boolean(m[4]);
|
|
35649
|
+
}
|
|
35650
|
+
function kitFromBundle(js) {
|
|
35651
|
+
const direct = /export\s+(?:const|let|var)\s+__kit\s*=\s*["']([^"']+)["']/.exec(js);
|
|
35652
|
+
if (direct) return direct[1];
|
|
35653
|
+
const local = /[{,]\s*([\w$]+)\s+as\s+__kit\b/.exec(js)?.[1];
|
|
35654
|
+
if (!local) return void 0;
|
|
35655
|
+
const name = local.replace(/[$]/g, "\\$&");
|
|
35656
|
+
return new RegExp(`(?:^|[\\s;,{}])(?:const|let|var)?\\s*${name}\\s*=\\s*["']([^"']+)["']`).exec(js)?.[1];
|
|
35657
|
+
}
|
|
35658
|
+
function kitRefusals(shell2, modules) {
|
|
35659
|
+
return modules.filter((m) => !kitSatisfies(shell2, m.kit)).map((m) => `${m.id}: built on kit ${m.kit}, newer than the MM-Work shell's kit ${shell2}`);
|
|
35660
|
+
}
|
|
35661
|
+
|
|
35662
|
+
// src/suite-train.ts
|
|
35630
35663
|
var SCHEMA = "mmi-suite-cut/v1";
|
|
35631
35664
|
var SHA2 = /^[a-f0-9]{40}$/i;
|
|
35632
35665
|
var RC_TAG = /^v\d+\.\d+\.\d+-rc\.\d+$/;
|
|
35633
35666
|
var RELEASE_TAG = /^v\d+\.\d+\.\d+$/;
|
|
35667
|
+
async function kitCheck(deps, shellSha, bundles, where) {
|
|
35668
|
+
const shell2 = shellSha ? await deps.shellKit(shellSha) : void 0;
|
|
35669
|
+
const kits = await Promise.all(bundles.map(async (b) => ({ id: b.id, kit: b.url ? await deps.bundleKit(b.url) : void 0 })));
|
|
35670
|
+
const refused = kitRefusals(shell2, kits);
|
|
35671
|
+
if (refused.length) throw new Error(`kit check (${where}) refused; a module built on a newer kit crashes the older shell. Release the newer kit to MM-Work first (or rebuild the module on the shell's kit):
|
|
35672
|
+
${refused.join("\n")}`);
|
|
35673
|
+
return `kit check (${where}): shell kit ${shell2 ?? "unknown"}; ${kits.map((k) => `${k.id} __kit ${k.kit ?? "none"}`).join(", ") || "no modules"}`;
|
|
35674
|
+
}
|
|
35634
35675
|
function suitePins(record, bundles) {
|
|
35635
35676
|
return record.members.map((m) => m.carried ? { module: m.id, rc: m.rcPin, main: m.mainPin } : {
|
|
35636
35677
|
module: m.id,
|
|
@@ -35742,6 +35783,8 @@ async function suiteRcand(deps, opts) {
|
|
|
35742
35783
|
`${SUITE_REPO}: one PR for both rc/main pins, wait for merge`,
|
|
35743
35784
|
`${SUITE_REPO}: rcand`
|
|
35744
35785
|
];
|
|
35786
|
+
const lead = await deps.head(SUITE_REPO, "development");
|
|
35787
|
+
plan.push(await kitCheck(deps, lead, active.map((m) => ({ id: m.id, url: m.bundles.dev })), "dev bundles"));
|
|
35745
35788
|
if (opts.dryRun) return { plan };
|
|
35746
35789
|
let record = readSuiteCut(deps.recordPath);
|
|
35747
35790
|
if (record?.leadReleased && record.announcement === "pending") {
|
|
@@ -35863,6 +35906,7 @@ Each module must ship a live release that bind-mounts <deployPath>/data read-onl
|
|
|
35863
35906
|
throw new Error(`${member.repo}: live backend or development changed during the cut; pins unchanged`);
|
|
35864
35907
|
}
|
|
35865
35908
|
const pins = suitePins(record, bundlesOf(modules));
|
|
35909
|
+
await kitCheck(deps, record.leadDevelopment, pins.map((p) => ({ id: p.module, url: p.rc })), "rc bundles");
|
|
35866
35910
|
await deps.verifyLeadCut(record.leadDevelopment);
|
|
35867
35911
|
const pr = await deps.pin({ cut: record.leadDevelopment, pins, heldIds: record.heldMembers?.map((h) => h.id) });
|
|
35868
35912
|
await deps.verifyLeadCut(record.leadDevelopment);
|
|
@@ -46200,9 +46244,9 @@ function isMissingRef(message2) {
|
|
|
46200
46244
|
return /404|Not Found|No commit found for SHA/.test(message2);
|
|
46201
46245
|
}
|
|
46202
46246
|
function releaseTagsFrom(out) {
|
|
46203
|
-
const
|
|
46247
|
+
const parse2 = (t) => t.slice(1).split(".").map(Number);
|
|
46204
46248
|
return out.split(/\s+/).filter((t) => /^v\d+\.\d+\.\d+$/.test(t)).sort((a, b) => {
|
|
46205
|
-
const [x, y] = [
|
|
46249
|
+
const [x, y] = [parse2(a), parse2(b)];
|
|
46206
46250
|
return x[0] - y[0] || x[1] - y[1] || x[2] - y[2];
|
|
46207
46251
|
});
|
|
46208
46252
|
}
|
|
@@ -46493,6 +46537,22 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}) {
|
|
|
46493
46537
|
throw new Error(`${pin.module}: MM-Work ${ref} pins do not equal the tested cut (${row?.bundles.main ?? "no main pin"} vs ${pin.main}); release refused`);
|
|
46494
46538
|
}
|
|
46495
46539
|
},
|
|
46540
|
+
shellKit: async (sha) => {
|
|
46541
|
+
await exec("git", ["fetch", "--quiet", "origin", "--tags"], root, WATCH_TIMEOUT);
|
|
46542
|
+
try {
|
|
46543
|
+
return (await exec("git", ["describe", "--tags", "--match", "ds-v[0-9]*", "--abbrev=0", sha], root)).replace(/^ds-v/, "") || void 0;
|
|
46544
|
+
} catch {
|
|
46545
|
+
return void 0;
|
|
46546
|
+
}
|
|
46547
|
+
},
|
|
46548
|
+
bundleKit: async (url) => {
|
|
46549
|
+
try {
|
|
46550
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(GH_TIMEOUT) });
|
|
46551
|
+
return res.ok ? kitFromBundle(await res.text()) : void 0;
|
|
46552
|
+
} catch {
|
|
46553
|
+
return void 0;
|
|
46554
|
+
}
|
|
46555
|
+
},
|
|
46496
46556
|
release,
|
|
46497
46557
|
shippedTag,
|
|
46498
46558
|
sameContent: sameContent2,
|
package/package.json
CHANGED