@mutmutco/cli 4.6.42 → 4.6.43
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 +54 -18
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -25273,6 +25273,31 @@ function missingPlaceholders(rendered) {
|
|
|
25273
25273
|
for (const m of rendered.matchAll(PLACEHOLDER_RE)) out.add(m[1]);
|
|
25274
25274
|
return [...out];
|
|
25275
25275
|
}
|
|
25276
|
+
function seedMatchesReleaseTrack(seed, releaseTrack) {
|
|
25277
|
+
if (!seed.releaseTracks?.length) return true;
|
|
25278
|
+
return releaseTrack != null && seed.releaseTracks.includes(releaseTrack);
|
|
25279
|
+
}
|
|
25280
|
+
var TRAIN_ONLY_BEGIN = "mmi-train-only:start";
|
|
25281
|
+
var TRAIN_ONLY_END = "mmi-train-only:end";
|
|
25282
|
+
function renderSelfSeedForTrack(content, releaseTrack) {
|
|
25283
|
+
if (releaseTrack !== "single") return content;
|
|
25284
|
+
const lines2 = content.split(/(?<=\n)/);
|
|
25285
|
+
const out = [];
|
|
25286
|
+
let skipping = false;
|
|
25287
|
+
for (const line of lines2) {
|
|
25288
|
+
if (!skipping && line.includes(TRAIN_ONLY_BEGIN)) {
|
|
25289
|
+
skipping = true;
|
|
25290
|
+
continue;
|
|
25291
|
+
}
|
|
25292
|
+
if (skipping) {
|
|
25293
|
+
if (line.includes(TRAIN_ONLY_END)) skipping = false;
|
|
25294
|
+
continue;
|
|
25295
|
+
}
|
|
25296
|
+
out.push(line);
|
|
25297
|
+
}
|
|
25298
|
+
if (skipping) throw new Error(`unterminated ${TRAIN_ONLY_BEGIN} region`);
|
|
25299
|
+
return out.join("");
|
|
25300
|
+
}
|
|
25276
25301
|
function isPropagatableSeed(seed) {
|
|
25277
25302
|
return seed.ownership === "org" && seed.source === "self" || seed.managedBlock != null;
|
|
25278
25303
|
}
|
|
@@ -26639,6 +26664,7 @@ function withDerivedRepoVars(vars, parsed, cls, releaseTrack, requiredCheckBranc
|
|
|
26639
26664
|
out.CLASS ??= cls;
|
|
26640
26665
|
out.PROJECT_OWNER ??= parsed.owner;
|
|
26641
26666
|
const track = releaseTrack ?? resolveBootstrapReleaseTrack(cls);
|
|
26667
|
+
out.RELEASE_TRACK ??= track;
|
|
26642
26668
|
const runtime = out.GATE_RUNTIME === "python" ? "python" : "node";
|
|
26643
26669
|
for (const [key, value] of Object.entries(gateSeedVars(cls, track, runtime, requiredCheckBranches, requiredChecks))) {
|
|
26644
26670
|
out[key] ??= value;
|
|
@@ -26998,7 +27024,10 @@ function renderBootstrapAncillaryPlan(labels, environments, exceptions = []) {
|
|
|
26998
27024
|
].join("\n");
|
|
26999
27025
|
}
|
|
27000
27026
|
function resolveSeedContent(seed, vars, readFile11) {
|
|
27001
|
-
if (seed.source === "self")
|
|
27027
|
+
if (seed.source === "self") {
|
|
27028
|
+
const own = readFile11(seed.target);
|
|
27029
|
+
return own == null ? null : renderSelfSeedForTrack(own, vars.RELEASE_TRACK);
|
|
27030
|
+
}
|
|
27002
27031
|
if (seed.source.startsWith("seed:")) {
|
|
27003
27032
|
const tmpl = readFile11(`skills/bootstrap/seeds/${seed.source.slice("seed:".length)}`);
|
|
27004
27033
|
return tmpl == null ? null : renderSeed(tmpl, vars);
|
|
@@ -28527,10 +28556,10 @@ var rollout_plan_default = {
|
|
|
28527
28556
|
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)."
|
|
28528
28557
|
},
|
|
28529
28558
|
baseline: {
|
|
28530
|
-
version: "4.6.
|
|
28531
|
-
tag: "v4.6.
|
|
28532
|
-
commit: "
|
|
28533
|
-
npm: "@mutmutco/cli@4.6.
|
|
28559
|
+
version: "4.6.43",
|
|
28560
|
+
tag: "v4.6.43",
|
|
28561
|
+
commit: "856f4651277d",
|
|
28562
|
+
npm: "@mutmutco/cli@4.6.43"
|
|
28534
28563
|
},
|
|
28535
28564
|
exitCriterion: "fleet-n-of-n",
|
|
28536
28565
|
hubOnlyShortcut: "forbidden",
|
|
@@ -28547,14 +28576,14 @@ var rollout_plan_default = {
|
|
|
28547
28576
|
repo: "mutmutco/mmi-hub",
|
|
28548
28577
|
role: "canary",
|
|
28549
28578
|
schedule: "train",
|
|
28550
|
-
v3Target: "v4.6.
|
|
28579
|
+
v3Target: "v4.6.43"
|
|
28551
28580
|
}
|
|
28552
28581
|
],
|
|
28553
28582
|
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.",
|
|
28554
28583
|
rollback: {
|
|
28555
28584
|
independent: true,
|
|
28556
|
-
mechanism: "npm dist-tag latest -> 4.6.
|
|
28557
|
-
v3Target: "v4.6.
|
|
28585
|
+
mechanism: "npm dist-tag latest -> 4.6.43 and redeploy the Hub Lambda from tag v4.6.43 (856f4651277d); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
28586
|
+
v3Target: "v4.6.43 (@mutmutco/cli@4.6.43, tag commit 856f4651277d \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
28558
28587
|
}
|
|
28559
28588
|
},
|
|
28560
28589
|
{
|
|
@@ -31670,17 +31699,17 @@ function isBundleOnlySuiteMember(repo, suite) {
|
|
|
31670
31699
|
if (!suite) return false;
|
|
31671
31700
|
return repo.split("/")[1]?.toLowerCase() !== suite.toLowerCase();
|
|
31672
31701
|
}
|
|
31702
|
+
var MM_GOES_LIVE = "MM-Work and its modules merge to development and it goes live; there is no release, rc or candidate stage.";
|
|
31673
31703
|
function suiteMemberRefusal(repo, meta, lane, solo = false, cut) {
|
|
31674
31704
|
const suite = meta?.suite;
|
|
31675
31705
|
if (!suite) return null;
|
|
31676
31706
|
if (!isBundleOnlySuiteMember(repo, suite)) {
|
|
31677
31707
|
if (solo || cut?.token === SUITE_LEAD_TOKEN && cut.repo.toLowerCase() === repo.toLowerCase()) return null;
|
|
31678
|
-
|
|
31679
|
-
return `${repo} is the ${suite} suite lead; a plain ${lane} would cut it without its members: ${route}. A lead-only ${lane} needs --suite-solo --owner-go.`;
|
|
31708
|
+
return `${repo} is the ${suite} suite lead; ${MM_GOES_LIVE} There is no ${lane} to run: land the change through a normal PR to development and state what landed.`;
|
|
31680
31709
|
}
|
|
31681
31710
|
if (authorisedSuiteCut(repo, meta, cut)) return null;
|
|
31682
31711
|
if (lane !== "rcand" && solo) return null;
|
|
31683
|
-
return `${repo} is part of the ${suite} suite;
|
|
31712
|
+
return `${repo} is part of the ${suite} suite; ${MM_GOES_LIVE} A suite member never asks for, proposes or prompts an rc, release or hotfix: there is none, so state what landed and stop.`;
|
|
31684
31713
|
}
|
|
31685
31714
|
|
|
31686
31715
|
// src/tenant-control-parse.ts
|
|
@@ -51008,7 +51037,7 @@ async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
|
|
|
51008
51037
|
detail: `expected: ${requiredProductRulesetRef} (apply as an active repo ruleset after bootstrap)`
|
|
51009
51038
|
});
|
|
51010
51039
|
}
|
|
51011
|
-
if (repoClass === "deployable") {
|
|
51040
|
+
if (repoClass === "deployable" && effectiveTrack !== "single") {
|
|
51012
51041
|
const trainScript = "scripts/next-version.mjs";
|
|
51013
51042
|
checks.push({ ok: await contentExists2(deps, repo, baseBranch, trainScript), label: `train tooling script exists: ${trainScript}` });
|
|
51014
51043
|
}
|
|
@@ -51734,7 +51763,9 @@ function registerBootstrapCommands(program3) {
|
|
|
51734
51763
|
for (const repo of targets) {
|
|
51735
51764
|
const cls = classOf(repo);
|
|
51736
51765
|
const meta = metaOf(repo);
|
|
51737
|
-
const
|
|
51766
|
+
const track = resolveReleaseTrack(metaWithDeclaredClass(meta, cls), void 0, repo);
|
|
51767
|
+
const seeds = byteComparableSeeds(manifest, cls).filter((s) => seedMatchesReleaseTrack(s, track));
|
|
51768
|
+
const trackHubContents = new Map([...hubContents].map(([t, c]) => [t, c == null ? c : renderSelfSeedForTrack(c, track)]));
|
|
51738
51769
|
seedsPerRepo = Math.max(seedsPerRepo, seeds.length);
|
|
51739
51770
|
const baseBranch = cls === "content" ? "main" : "development";
|
|
51740
51771
|
const reads = [];
|
|
@@ -51749,9 +51780,8 @@ function registerBootstrapCommands(program3) {
|
|
|
51749
51780
|
}
|
|
51750
51781
|
reads.push({ target: seed.target, content });
|
|
51751
51782
|
}
|
|
51752
|
-
findings.push(...auditRepoSeedDrift(repo, seeds,
|
|
51783
|
+
findings.push(...auditRepoSeedDrift(repo, seeds, trackHubContents, reads));
|
|
51753
51784
|
findings.push(...declaredRenderWaivers(manifest, repo));
|
|
51754
|
-
const track = resolveReleaseTrack(metaWithDeclaredClass(meta, cls), void 0, repo);
|
|
51755
51785
|
const github = await readBootstrapGitHubFacts(repo, track, controlClient);
|
|
51756
51786
|
const deployFacts = await fetchDeployFactsBySlug((repo.split("/").pop() ?? repo).toLowerCase(), registryClientDeps(await loadConfig()));
|
|
51757
51787
|
const controlRows = planBootstrapControlPlane({
|
|
@@ -52176,6 +52206,7 @@ function registerBootstrapCommands(program3) {
|
|
|
52176
52206
|
if (!seed.classes.includes(o.class)) continue;
|
|
52177
52207
|
if (!seedMatchesDeployModel(seed, applyDeployModel)) continue;
|
|
52178
52208
|
if (!seedMatchesProjectType(seed, applyProjectType)) continue;
|
|
52209
|
+
if (!seedMatchesReleaseTrack(seed, vars.RELEASE_TRACK)) continue;
|
|
52179
52210
|
const waiver = seed.waivers?.[slug];
|
|
52180
52211
|
if (waiver) {
|
|
52181
52212
|
actions.push({ action: "skip", target: seed.target, ownership: seed.ownership, reason: `waived: ${waiver}` });
|
|
@@ -52670,7 +52701,12 @@ LIVE apply to ${repo}:
|
|
|
52670
52701
|
const readSeedFile2 = (path2) => (0, import_node_fs40.existsSync)(path2) ? (0, import_node_fs40.readFileSync)(path2, "utf8") : null;
|
|
52671
52702
|
const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
|
|
52672
52703
|
const { projects, classOf, inClassRepos, canarySlug, sourcePilot } = await propagationScope(seed.classes, `target '${seed.target}'`);
|
|
52673
|
-
const
|
|
52704
|
+
const trackOf = (repo) => resolveReleaseTrack(
|
|
52705
|
+
projects.find((p) => (p.repos ?? []).some((r) => r.toLowerCase() === repo.toLowerCase())),
|
|
52706
|
+
void 0,
|
|
52707
|
+
repo
|
|
52708
|
+
);
|
|
52709
|
+
const repos = inClassRepos.filter((repo) => seedMatchesReleaseTrack(seed, trackOf(repo))).map((repo) => {
|
|
52674
52710
|
const slug = repo.split("/").pop().toLowerCase();
|
|
52675
52711
|
const waiver = seed.waivers?.[slug];
|
|
52676
52712
|
return { repo, slug, waiver };
|
|
@@ -52718,7 +52754,7 @@ LIVE apply to ${repo}:
|
|
|
52718
52754
|
}
|
|
52719
52755
|
desired = resolved.content;
|
|
52720
52756
|
} else {
|
|
52721
|
-
desired = hubContent;
|
|
52757
|
+
desired = renderSelfSeedForTrack(hubContent, trackOf(r.repo));
|
|
52722
52758
|
}
|
|
52723
52759
|
desiredByRepo.set(r.repo, desired);
|
|
52724
52760
|
const drift = compareSeedBytes(desired, content);
|
|
@@ -59669,7 +59705,7 @@ function branchRulesReadHint(path2) {
|
|
|
59669
59705
|
const match = new RegExp(`^repos/(${NAME})/(${NAME})/(?:rules/branches/[^/?#]+|branches/[^/?#]+/protection(?:/|$)|rulesets(?:/|$))`).exec(path2.split("?")[0]);
|
|
59670
59706
|
if (!match) return "";
|
|
59671
59707
|
const slug = `${match[1]}/${match[2]}`;
|
|
59672
|
-
return ` No raw rules, rulesets or branch-protection read is on the App allowlist, so retrying path or query variants cannot work \u2014 inspect a PR's required status contexts with \`MMI_ACTOR=app mmi-cli devops pr checks-required <pr> --repo ${slug} --json\`, or verify repository ruleset and branch-protection health with \`mmi-cli devops bootstrap verify --repo ${slug} --json\` and \`mmi-cli devops ci audit --repo ${slug} --json
|
|
59708
|
+
return ` No raw rules, rulesets or branch-protection read is on the App allowlist, so retrying path or query variants cannot work \u2014 inspect a PR's required status contexts with \`MMI_ACTOR=app mmi-cli devops pr checks-required <pr> --repo ${slug} --json\`, or verify repository ruleset and branch-protection health with \`mmi-cli devops bootstrap verify --repo ${slug} --json\` and \`mmi-cli devops ci audit --repo ${slug} --json\`. Stepping around it with a raw \`gh api repos/${slug}/rulesets/<id> > file\` is not a substitute \u2014 when that read fails it leaves an EMPTY file, and a later require/JSON.parse throws 'Unexpected end of JSON input' that looks like corrupt data while the real refusal is only on stderr; check the exit code and file size, or ask a human token holder for the raw ruleset body.`;
|
|
59673
59709
|
}
|
|
59674
59710
|
function commitsReadHint(path2) {
|
|
59675
59711
|
const clean4 = path2.split("?")[0];
|
package/package.json
CHANGED