@mutmutco/cli 4.5.185 → 4.5.187
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 +77 -18
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -19193,10 +19193,10 @@ var rollout_plan_default = {
|
|
|
19193
19193
|
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)."
|
|
19194
19194
|
},
|
|
19195
19195
|
baseline: {
|
|
19196
|
-
version: "4.5.
|
|
19197
|
-
tag: "v4.5.
|
|
19198
|
-
commit: "
|
|
19199
|
-
npm: "@mutmutco/cli@4.5.
|
|
19196
|
+
version: "4.5.187",
|
|
19197
|
+
tag: "v4.5.187",
|
|
19198
|
+
commit: "725e17b3adb5",
|
|
19199
|
+
npm: "@mutmutco/cli@4.5.187"
|
|
19200
19200
|
},
|
|
19201
19201
|
exitCriterion: "fleet-n-of-n",
|
|
19202
19202
|
hubOnlyShortcut: "forbidden",
|
|
@@ -19213,14 +19213,14 @@ var rollout_plan_default = {
|
|
|
19213
19213
|
repo: "mutmutco/mmi-hub",
|
|
19214
19214
|
role: "canary",
|
|
19215
19215
|
schedule: "train",
|
|
19216
|
-
v3Target: "v4.5.
|
|
19216
|
+
v3Target: "v4.5.187"
|
|
19217
19217
|
}
|
|
19218
19218
|
],
|
|
19219
19219
|
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.",
|
|
19220
19220
|
rollback: {
|
|
19221
19221
|
independent: true,
|
|
19222
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
19223
|
-
v3Target: "v4.5.
|
|
19222
|
+
mechanism: "npm dist-tag latest -> 4.5.187 and redeploy the Hub Lambda from tag v4.5.187 (725e17b3adb5); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
19223
|
+
v3Target: "v4.5.187 (@mutmutco/cli@4.5.187, tag commit 725e17b3adb5 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
19224
19224
|
}
|
|
19225
19225
|
},
|
|
19226
19226
|
{
|
|
@@ -23886,6 +23886,7 @@ async function dispatchCanary(deps, targetRepo2, nonce) {
|
|
|
23886
23886
|
);
|
|
23887
23887
|
}
|
|
23888
23888
|
var HOSTED_LABEL_RE = /\b(?:ubuntu|windows|macos)-(?:latest|\d[\w.-]*)\b/;
|
|
23889
|
+
var FALLBACK_EXPR_GLOBAL_RE = /\$\{\{\s*vars\.MMI_RUNNER_FALLBACK\s*==\s*'hosted'\s*&&\s*'(?:ubuntu|windows|macos)-(?:latest|\d[\w.-]*)'\s*\|\|\s*fromJSON\('\[[^\n']+\]'\)\s*\}\}/g;
|
|
23889
23890
|
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"]);
|
|
23890
23891
|
function workflowTriggers(yaml) {
|
|
23891
23892
|
const lines2 = yaml.split(/\r?\n/);
|
|
@@ -23902,7 +23903,7 @@ function workflowTriggers(yaml) {
|
|
|
23902
23903
|
return out;
|
|
23903
23904
|
}
|
|
23904
23905
|
function workflowNeedsHostedRunner(yaml) {
|
|
23905
|
-
const body = yaml.split(/\r?\n/).filter((l) => !/^\s*#/.test(l)).join("\n");
|
|
23906
|
+
const body = yaml.split(/\r?\n/).filter((l) => !/^\s*#/.test(l)).join("\n").replace(FALLBACK_EXPR_GLOBAL_RE, "");
|
|
23906
23907
|
return HOSTED_LABEL_RE.test(body) && workflowTriggers(yaml).some((t) => AUTOMATIC_TRIGGERS.has(t));
|
|
23907
23908
|
}
|
|
23908
23909
|
async function hostedWorkflowsOf(deps, repo) {
|
|
@@ -26867,6 +26868,8 @@ function enforceBumpRules(input) {
|
|
|
26867
26868
|
return decision();
|
|
26868
26869
|
}
|
|
26869
26870
|
if (proposal.breaking.length && effective === "patch") {
|
|
26871
|
+
if (input.suite && input.revamp) return decision();
|
|
26872
|
+
if (input.suite) throw new Error(`range carries breaking changes (${evidence}): suite versions auto-mint as patch (#8224), so release it as an owner-approved revamp with --owner-go --big, or ship a shim (#8666)`);
|
|
26870
26873
|
throw new Error(input.preOneZero ? `range carries breaking changes (${evidence}): this repo is held on 0.x.y, so declare MMI_BUMP_INTENT=minor with MMI_BUMP_REASON, or ship a shim (patch) (#8499)` : `range carries breaking changes (${evidence}): ship a shim (patch) or declare an owner-gated major (MMI_BUMP_INTENT=major, MMI_BUMP_REASON, MMI_MAJOR_APPROVED=<version>) (#8499)`);
|
|
26871
26874
|
}
|
|
26872
26875
|
if (proposal.breaking.length && !input.preOneZero) {
|
|
@@ -29959,7 +29962,7 @@ async function resolveTrainBumpIntent(deps, env = process.env, latestRelease, pr
|
|
|
29959
29962
|
}
|
|
29960
29963
|
return "patch";
|
|
29961
29964
|
}
|
|
29962
|
-
async function enforceTrainBump(deps, env, intent, preOneZero, source) {
|
|
29965
|
+
async function enforceTrainBump(deps, env, intent, preOneZero, source, suite = { suite: false, revamp: false }) {
|
|
29963
29966
|
const raw = await deps.run("git", ["log", `origin/main..${source}`, RANGE_LOG_FORMAT]);
|
|
29964
29967
|
const proposal = proposeBump(parseRangeLog(raw), { preOneZero });
|
|
29965
29968
|
let majorTarget;
|
|
@@ -29968,7 +29971,7 @@ async function enforceTrainBump(deps, env, intent, preOneZero, source) {
|
|
|
29968
29971
|
const latest = await latestLocalReleaseTag(deps, void 0, preOneZero);
|
|
29969
29972
|
majorTarget = explicit || `${latest.major + 1}.0.0`;
|
|
29970
29973
|
}
|
|
29971
|
-
return enforceBumpRules({ intent, env, proposal, preOneZero, majorTarget });
|
|
29974
|
+
return enforceBumpRules({ intent, env, proposal, preOneZero, majorTarget, ...suite });
|
|
29972
29975
|
}
|
|
29973
29976
|
async function resolveHotfixVersion(deps, env = process.env, preOneZero = false) {
|
|
29974
29977
|
const latest = await latestLocalReleaseTag(deps, "origin/main", preOneZero);
|
|
@@ -30023,7 +30026,8 @@ async function runTrainApplyLocked(command, deps, options = {}) {
|
|
|
30023
30026
|
process.env,
|
|
30024
30027
|
declaredIntent,
|
|
30025
30028
|
preOneZeroInForce(meta),
|
|
30026
|
-
command === "release" && !directTrack && options.dev !== true ? "origin/rc" : "origin/development"
|
|
30029
|
+
command === "release" && !directTrack && options.dev !== true ? "origin/rc" : "origin/development",
|
|
30030
|
+
{ suite: !!meta.suite, revamp: command === "release" && options.big === true }
|
|
30027
30031
|
);
|
|
30028
30032
|
const bumpIntent = bump.intent;
|
|
30029
30033
|
const bumpReceipt = { intent: bump.intent, reason: bump.reason, proposed: bump.proposed, breaking: bump.breaking };
|
|
@@ -30069,6 +30073,33 @@ function registryRepoKey(repo) {
|
|
|
30069
30073
|
return repo.toLowerCase();
|
|
30070
30074
|
}
|
|
30071
30075
|
|
|
30076
|
+
// ../shared/runner-fallback.ts
|
|
30077
|
+
var RUNNER_FALLBACK_EXPR_RE = /^\$\{\{\s*vars\.MMI_RUNNER_FALLBACK\s*==\s*'hosted'\s*&&\s*'((?:ubuntu|windows|macos)-(?:latest|\d[\w.-]*))'\s*\|\|\s*fromJSON\('(\[[^\n']+\])'\)\s*\}\}$/;
|
|
30078
|
+
var FLEET_ONLY_RUNNER_JOBS = {
|
|
30079
|
+
"backup-health.yml:judge": "SSH backup inventory; hosted source-IP access unverified",
|
|
30080
|
+
"browser-pass.yml:browser-pass": "owner-bounded two-lane browser capacity",
|
|
30081
|
+
"app-publish.yml:publish": "SSH tenant publish; hosted source-IP access unverified",
|
|
30082
|
+
"get-publish.yml:publish": "SSH installer publish; hosted source-IP access unverified",
|
|
30083
|
+
"get-publish-desktop.yml:publish": "SSH installer publish; hosted source-IP access unverified",
|
|
30084
|
+
"hetzner-knownhosts-pin.yml:pin": "SSH host-key probe; hosted source-IP access unverified",
|
|
30085
|
+
"module-rc-publish.yml:publish": "SSH live-container snapshot and tenant persistent state",
|
|
30086
|
+
"tenant-control.yml:control": "SSH tenant control; hosted source-IP access unverified",
|
|
30087
|
+
"tenant-deploy.yml:deploy": "SSH tenant deploy; hosted source-IP access unverified",
|
|
30088
|
+
"tenant-reconcile.yml:reconcile": "SSH tenant reconciliation; hosted source-IP access unverified"
|
|
30089
|
+
};
|
|
30090
|
+
function parseRunnerFallbackExpression(runsOn) {
|
|
30091
|
+
const match = RUNNER_FALLBACK_EXPR_RE.exec(runsOn.trim());
|
|
30092
|
+
if (!match) return void 0;
|
|
30093
|
+
try {
|
|
30094
|
+
const parsed = JSON.parse(match[2]);
|
|
30095
|
+
if (!Array.isArray(parsed) || !parsed.length || !parsed.every((item) => typeof item === "string")) return void 0;
|
|
30096
|
+
if (!parsed.some((item) => item.toLowerCase() === "self-hosted")) return void 0;
|
|
30097
|
+
return { hosted: match[1], labels: parsed };
|
|
30098
|
+
} catch {
|
|
30099
|
+
return void 0;
|
|
30100
|
+
}
|
|
30101
|
+
}
|
|
30102
|
+
|
|
30072
30103
|
// src/ci-audit.ts
|
|
30073
30104
|
var HUB_REPO4 = "mutmutco/MMI-Hub";
|
|
30074
30105
|
var HUB_GATE_CONTEXTS = ["cli", "infra", "docs"];
|
|
@@ -30128,6 +30159,29 @@ var NPM_PUBLISH_SAFE_FLOOR_SHA = "b5632c8b940d8025fdb5630e576502e1397c4552";
|
|
|
30128
30159
|
var SHARED_ACTION_PINS = [
|
|
30129
30160
|
{ path: "npm-publish", floor: NPM_PUBLISH_SAFE_FLOOR_SHA }
|
|
30130
30161
|
];
|
|
30162
|
+
function auditRunnerFallbacks(repo, files) {
|
|
30163
|
+
const checks = [];
|
|
30164
|
+
for (const { path: path2, body } of files) {
|
|
30165
|
+
const jobs = parseWorkflowJobs(body.split(/\r?\n/).filter((line) => !/^\s*#/.test(line)).join("\n"));
|
|
30166
|
+
for (const job of jobs) {
|
|
30167
|
+
const runsOn = /^ runs-on:\s*(.+)$/m.exec(job.body)?.[1] ?? "";
|
|
30168
|
+
const runnerSection = /^ runs-on:([^\n]*(?:\n {6,}[^\n]*)*)/m.exec(job.body)?.[1] ?? "";
|
|
30169
|
+
const fallback = parseRunnerFallbackExpression(runsOn);
|
|
30170
|
+
const selfHosted = !!fallback || /self-hosted/i.test(runnerSection) || /matrix\./.test(runsOn) && /self-hosted/i.test(job.body);
|
|
30171
|
+
if (!selfHosted) continue;
|
|
30172
|
+
const key = `${path2.split("/").pop()}:${job.id}`;
|
|
30173
|
+
const exemption = repo === HUB_REPO4 ? FLEET_ONLY_RUNNER_JOBS[key] : void 0;
|
|
30174
|
+
const safe = !!fallback;
|
|
30175
|
+
checks.push({
|
|
30176
|
+
ok: !!exemption || safe,
|
|
30177
|
+
label: `runner has hosted backup (#8671): ${path2}:${job.id}`,
|
|
30178
|
+
detail: exemption ? `fleet-only: ${exemption}` : safe ? void 0 : "self-hosted runner does not use the vars.MMI_RUNNER_FALLBACK backup expression",
|
|
30179
|
+
remediation: exemption || safe ? void 0 : `Use runs-on: \${{ vars.MMI_RUNNER_FALLBACK == 'hosted' && 'ubuntu-latest' || fromJSON('["self-hosted","linux","x64","mmi-live"]') }} (keeping the job's original self-hosted tuple). See gh-runner-runbook.md.`
|
|
30180
|
+
});
|
|
30181
|
+
}
|
|
30182
|
+
}
|
|
30183
|
+
return checks;
|
|
30184
|
+
}
|
|
30131
30185
|
function slugFromRepo(repo) {
|
|
30132
30186
|
return (repo.includes("/") ? repo.split("/")[1] : repo).toLowerCase();
|
|
30133
30187
|
}
|
|
@@ -30821,9 +30875,12 @@ async function auditRepoCi(repo, callerDeps) {
|
|
|
30821
30875
|
}
|
|
30822
30876
|
}
|
|
30823
30877
|
}
|
|
30824
|
-
|
|
30878
|
+
{
|
|
30825
30879
|
const workflows = await getBranchWorkflows();
|
|
30826
|
-
if (workflows)
|
|
30880
|
+
if (workflows) {
|
|
30881
|
+
checks.push(...auditRunnerFallbacks(repo, workflows));
|
|
30882
|
+
if (repoClass !== "hub") checks.push(...await auditSharedActionPins(workflows, deps));
|
|
30883
|
+
}
|
|
30827
30884
|
}
|
|
30828
30885
|
if (meta?.deployModel === "tenant-container" && deps.getDeployFacts) {
|
|
30829
30886
|
const fresh = await auditDevStageFreshness(repo, deps);
|
|
@@ -57943,9 +58000,10 @@ async function reportWrite(label, res) {
|
|
|
57943
58000
|
return failGraceful(`${label}: HTTP ${res.status}${detail ? ` \u2014 ${detail}` : ""}`);
|
|
57944
58001
|
}
|
|
57945
58002
|
var tenant = program2.command("tenant").description("tenant runtime control through Hub authority");
|
|
57946
|
-
tenant.command("control <owner/repo> <stage> <action>").description("bounded tenant control plus value-free vault/broker verification; dev/main retire needs master and exact data-loss confirmation").option("--watch", "block on the dispatched run and report its conclusion (status/retire/verify-secrets/verify-broker/logs watch by default)").option("--lines <n>", "logs only: trailing lines of the tenant service to return (1-2000, default 200)").option("--since <utc>", "logs only: read the journal from this UTC time, e.g. 2026-10-07T13:10Z (includes replaced containers)").option("--until <utc>", "logs only: read the journal up to this UTC time").option("--edge", "logs only: Caddy journal for this stage\u2019s hosts (default window: last 15 minutes)").option("--task <name>", "run-task only: registry-declared task name (never a command)").option("--artifact <id>", "run-task only: opaque id returned by tenant artifact put").option("--resume <run-id>", "run-task only: re-attach to an already-dispatched run (read-only, never re-dispatches) and print its receipt").option("--confirm-data-loss <owner/repo/stage>", "dev/main retire only: exact target to decommission; without it show the dry-run plan").option("--json", "machine-readable output").action(async (repo, stage, action, o) => {
|
|
58003
|
+
tenant.command("control <owner/repo> <stage> <action>").description("bounded tenant control plus value-free vault/broker verification; dev/main retire needs master and exact data-loss confirmation").option("--watch", "block on the dispatched run and report its conclusion (status/retire/verify-secrets/verify-broker/logs watch by default)").option("--lines <n>", "logs only: trailing lines of the tenant service to return (1-2000, default 200)").option("--since <utc>", "logs only: read the journal from this UTC time, e.g. 2026-10-07T13:10Z (includes replaced containers)").option("--until <utc>", "logs only: read the journal up to this UTC time").option("--edge", "logs only: Caddy journal for this stage\u2019s hosts (default window: last 15 minutes)").option("--task <name>", "run-task only: registry-declared task name (never a command)").option("--artifact <id>", "run-task only: opaque id returned by tenant artifact put").option("--input <file>", "run-task only: upload this file as the task artifact (same as tenant artifact put), then run with its id").option("--resume <run-id>", "run-task only: re-attach to an already-dispatched run (read-only, never re-dispatches) and print its receipt").option("--confirm-data-loss <owner/repo/stage>", "dev/main retire only: exact target to decommission; without it show the dry-run plan").option("--json", "machine-readable output").action(async (repo, stage, action, o) => {
|
|
57947
58004
|
try {
|
|
57948
|
-
if (o.resume !== void 0 && (action !== "run-task" || !/^\d+$/.test(o.resume) || o.task || o.artifact)) return fail("runtime tenant control: --resume <run-id> is run-task-only, takes a numeric run id, and cannot be combined with --task/--artifact");
|
|
58005
|
+
if (o.resume !== void 0 && (action !== "run-task" || !/^\d+$/.test(o.resume) || o.task || o.artifact || o.input)) return fail("runtime tenant control: --resume <run-id> is run-task-only, takes a numeric run id, and cannot be combined with --task/--artifact/--input");
|
|
58006
|
+
if (o.input !== void 0 && o.artifact !== void 0) return fail("runtime tenant control: pass --input <file> or --artifact <id>, not both");
|
|
57949
58007
|
if (o.confirmDataLoss !== void 0 && (action !== "retire" || stage === "rc")) return fail("runtime tenant control: --confirm-data-loss is dev/main retire-only");
|
|
57950
58008
|
if (action === "retire" && (stage === "dev" || stage === "main")) {
|
|
57951
58009
|
if (o.confirmDataLoss !== void 0 && o.confirmDataLoss !== `${repo}/${stage}`) return fail(`runtime tenant control: --confirm-data-loss must match ${repo}/${stage} exactly`);
|
|
@@ -57971,10 +58029,11 @@ Dry run: pass --confirm-data-loss ${repo}/${stage} to decommission.`);
|
|
|
57971
58029
|
}
|
|
57972
58030
|
if (action === "run-task") {
|
|
57973
58031
|
if (!o.task && o.resume === void 0) return fail("runtime tenant control: run-task requires --task <declared-name>");
|
|
57974
|
-
} else if (o.task || o.artifact) {
|
|
57975
|
-
return fail("runtime tenant control: --task/--artifact are valid only for run-task");
|
|
58032
|
+
} else if (o.task || o.artifact || o.input) {
|
|
58033
|
+
return fail("runtime tenant control: --task/--artifact/--input are valid only for run-task");
|
|
57976
58034
|
}
|
|
57977
|
-
const
|
|
58035
|
+
const artifact = o.input === void 0 ? o.artifact : (await putTenantArtifact(repo, stage, o.input, registryClientDeps(await loadConfig()))).artifactId;
|
|
58036
|
+
const result = await runTenantControl(trainApplyDeps(), { repo, stage, action, watch: o.watch, lines: lines2, edge: o.edge, logSince: o.since, logUntil: o.until, task: o.task, artifact, confirmDataLoss: o.confirmDataLoss, resume: o.resume === void 0 ? void 0 : Number(o.resume) });
|
|
57978
58037
|
if (!o.json && action === "verify-secrets" && result.secrets) {
|
|
57979
58038
|
const body = { ok: result.conclusion === "success", secrets: result.secrets, ssmStatus: result.conclusion === "success" ? "Success" : "Failed", raw: result.secretsRaw };
|
|
57980
58039
|
const { lines: lines3, failure } = renderVerifySecrets(body);
|
package/package.json
CHANGED