@mutmutco/cli 3.93.0 → 3.94.0
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 +68 -41
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -11706,6 +11706,29 @@ function parseAuthoritativeRuleset(raw, meta, repo) {
|
|
|
11706
11706
|
function sameContexts(left, right) {
|
|
11707
11707
|
return left.length === right.length && left.every((context, index) => context === right[index]);
|
|
11708
11708
|
}
|
|
11709
|
+
function resolveProductRulesetReconcilePlan(input) {
|
|
11710
|
+
const liveNeedsContextConvergence = !sameContexts(input.liveContexts, input.authorityContexts);
|
|
11711
|
+
const liveIsActive = input.liveEnforcement === "active";
|
|
11712
|
+
if (liveIsActive && !liveNeedsContextConvergence) {
|
|
11713
|
+
return { shouldActivate: false, targetEnforcement: "active" };
|
|
11714
|
+
}
|
|
11715
|
+
if (input.unsafeContexts.length > 0) {
|
|
11716
|
+
const unsafe = [...input.unsafeContexts];
|
|
11717
|
+
return {
|
|
11718
|
+
shouldActivate: false,
|
|
11719
|
+
targetEnforcement: "disabled",
|
|
11720
|
+
holdReason: `product ruleset left non-enforcing \u2014 [${unsafe.join(", ")}] ${unsafe.length === 1 ? "is" : "are"} emitted ONLY by path-filtered workflow(s), so the context is not reported on a PR outside those paths and requiring it would block that PR forever (#3836). Give the gate a companion job that runs unconditionally, then re-run this command`
|
|
11721
|
+
};
|
|
11722
|
+
}
|
|
11723
|
+
if (!input.gateProvenGreen) {
|
|
11724
|
+
return {
|
|
11725
|
+
shouldActivate: false,
|
|
11726
|
+
targetEnforcement: "disabled",
|
|
11727
|
+
holdReason: "product ruleset left non-enforcing \u2014 the gate is not green on development; activating it would block every PR (#3694)"
|
|
11728
|
+
};
|
|
11729
|
+
}
|
|
11730
|
+
return { shouldActivate: true, targetEnforcement: "active" };
|
|
11731
|
+
}
|
|
11709
11732
|
async function contentExists(deps, repo, branch, path2) {
|
|
11710
11733
|
try {
|
|
11711
11734
|
const encodedPath = path2.split("/").map(encodeURIComponent).join("/");
|
|
@@ -12409,46 +12432,39 @@ async function applyCiReconcileRepo(repo, deps) {
|
|
|
12409
12432
|
return finalizeCiReconcile(repo, deps, result, report);
|
|
12410
12433
|
}
|
|
12411
12434
|
const liveContexts = live == null ? [] : sortedUnique(rulesetRequiredContexts(live));
|
|
12412
|
-
const
|
|
12413
|
-
const
|
|
12414
|
-
|
|
12415
|
-
|
|
12416
|
-
|
|
12417
|
-
const
|
|
12418
|
-
|
|
12419
|
-
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
|
|
12429
|
-
|
|
12430
|
-
|
|
12431
|
-
if (
|
|
12432
|
-
|
|
12433
|
-
result.
|
|
12434
|
-
return finalizeCiReconcile(repo, deps, result, report, reason);
|
|
12435
|
-
}
|
|
12436
|
-
}
|
|
12437
|
-
if (liveNeedsConvergence) {
|
|
12438
|
-
try {
|
|
12439
|
-
const activation = await activateProductRuleset(repo, authority.apiPayload, deps.client, enforcement);
|
|
12440
|
-
if (activation.action === "skipped") result.skipped.push(activation.detail ?? "product ruleset");
|
|
12441
|
-
else result.applied.push(`product ruleset ${activation.action}${activation.detail ? `: ${activation.detail}` : ""}`);
|
|
12442
|
-
} catch (e) {
|
|
12443
|
-
result.errors.push(e.message);
|
|
12444
|
-
return finalizeCiReconcile(repo, deps, result, report);
|
|
12435
|
+
const prWorkflows = await prTriggeredWorkflowsOnRef(deps, repo, baseBranch, "deployable") ?? [];
|
|
12436
|
+
const gateFiles = gateWorkflowFiles(prWorkflows);
|
|
12437
|
+
const allPrWorkflows = await listWorkflowPaths(deps, repo, baseBranch) ?? [];
|
|
12438
|
+
const bodies = [];
|
|
12439
|
+
for (const path2 of allPrWorkflows) {
|
|
12440
|
+
const body = await fetchFileContent(deps, repo, baseBranch, path2);
|
|
12441
|
+
if (body) bodies.push({ path: path2, body });
|
|
12442
|
+
}
|
|
12443
|
+
const filteredPaths = new Set(pathFilteredPullRequestWorkflows(bodies).filter((p) => !p.endsWith("/agent-pr.yml")));
|
|
12444
|
+
const safeContexts = new Set(collectPullRequestWorkflowContexts(bodies.filter((b) => !filteredPaths.has(b.path))));
|
|
12445
|
+
const unsafe = collectPullRequestWorkflowContexts(bodies.filter((b) => filteredPaths.has(b.path))).filter((c) => authority.contexts.includes(c) && !safeContexts.has(c));
|
|
12446
|
+
const plan = resolveProductRulesetReconcilePlan({
|
|
12447
|
+
liveEnforcement: live?.enforcement,
|
|
12448
|
+
liveContexts,
|
|
12449
|
+
authorityContexts: authority.contexts,
|
|
12450
|
+
gateProvenGreen: await gateIsProvenGreen(repo, deps.client, baseBranch, gateFiles),
|
|
12451
|
+
unsafeContexts: unsafe
|
|
12452
|
+
});
|
|
12453
|
+
if (!plan.shouldActivate) {
|
|
12454
|
+
if (plan.holdReason) {
|
|
12455
|
+
result.skipped.push(plan.holdReason);
|
|
12456
|
+
return finalizeCiReconcile(repo, deps, result, report, plan.holdReason);
|
|
12445
12457
|
}
|
|
12446
|
-
} else {
|
|
12447
12458
|
result.skipped.push(`live ${PRODUCT_RULESET_NAME} contexts already match ${authority.source}`);
|
|
12459
|
+
return finalizeCiReconcile(repo, deps, result, report);
|
|
12448
12460
|
}
|
|
12449
|
-
|
|
12450
|
-
const
|
|
12451
|
-
|
|
12461
|
+
try {
|
|
12462
|
+
const activation = await activateProductRuleset(repo, authority.apiPayload, deps.client, plan.targetEnforcement);
|
|
12463
|
+
if (activation.action === "skipped") result.skipped.push(activation.detail ?? "product ruleset");
|
|
12464
|
+
else result.applied.push(`product ruleset ${activation.action}${activation.detail ? `: ${activation.detail}` : ""}`);
|
|
12465
|
+
} catch (e) {
|
|
12466
|
+
result.errors.push(e.message);
|
|
12467
|
+
return finalizeCiReconcile(repo, deps, result, report);
|
|
12452
12468
|
}
|
|
12453
12469
|
return finalizeCiReconcile(repo, deps, result, report);
|
|
12454
12470
|
}
|
|
@@ -23445,6 +23461,12 @@ async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
|
|
|
23445
23461
|
detail: optionDetail(missing)
|
|
23446
23462
|
});
|
|
23447
23463
|
} else if (repoClass === "deployable") {
|
|
23464
|
+
const productRuleset = rulesets.find((r) => r.name === PRODUCT_RULESET_NAME);
|
|
23465
|
+
checks.push({
|
|
23466
|
+
ok: productRuleset?.enforcement === "active",
|
|
23467
|
+
label: "product required-check ruleset enforcement active",
|
|
23468
|
+
detail: productRuleset?.enforcement !== "active" ? `${PRODUCT_RULESET_NAME} is ${productRuleset?.enforcement ?? "missing"} \u2014 run mmi-cli ci reconcile --apply --repo ${repo} once the gate is green` : void 0
|
|
23469
|
+
});
|
|
23448
23470
|
const statusChecks = rulesetStatusChecks2(rulesets.filter((r) => r.target === "branch" && r.enforcement === "active"));
|
|
23449
23471
|
const missing = requiredProductStatusChecks.filter((check) => !statusChecks.has(check));
|
|
23450
23472
|
checks.push({
|
|
@@ -28858,7 +28880,8 @@ function diagnoseSurface(evidence) {
|
|
|
28858
28880
|
return { ...base, state: "repair-failed", repairDetail: evidence.repair.detail };
|
|
28859
28881
|
}
|
|
28860
28882
|
if (evidence.repair?.attempted && evidence.repair.ok) {
|
|
28861
|
-
|
|
28883
|
+
const behindReleased = evidence.installedVersion && evidence.releasedVersion && compareVersions(evidence.installedVersion, evidence.releasedVersion) < 0;
|
|
28884
|
+
return { ...base, state: behindReleased ? "pending-reload" : "clean", repairDetail: evidence.repair.detail };
|
|
28862
28885
|
}
|
|
28863
28886
|
if (!evidence.installRecordPresent) return { ...base, state: "missing" };
|
|
28864
28887
|
if (!evidence.deliveryPresent || !evidence.payloadPresent || evidence.manifest === "missing") {
|
|
@@ -28876,7 +28899,7 @@ function reloadInstruction(descriptor) {
|
|
|
28876
28899
|
return `${verb} ${descriptor.displayName}`;
|
|
28877
28900
|
}
|
|
28878
28901
|
function planSurfaceRepair(diagnosis) {
|
|
28879
|
-
if (diagnosis.state === "clean" || diagnosis.state === "skipped" || diagnosis.state === "freshness-unknown") return null;
|
|
28902
|
+
if (diagnosis.state === "clean" || diagnosis.state === "pending-reload" || diagnosis.state === "skipped" || diagnosis.state === "freshness-unknown") return null;
|
|
28880
28903
|
const descriptor = diagnosis.descriptor;
|
|
28881
28904
|
if (descriptor.repairOwner !== "mmi-cli") {
|
|
28882
28905
|
return {
|
|
@@ -28901,6 +28924,7 @@ function buildSurfaceDoctorCheck(diagnosis) {
|
|
|
28901
28924
|
const detailByState = {
|
|
28902
28925
|
skipped: "skipped \u2014 host not active",
|
|
28903
28926
|
clean: diagnosis.repairDetail ? `clean \u2014 repaired and verified (${diagnosis.repairDetail})` : `clean${versions ? ` \u2014 ${versions}` : ""}`,
|
|
28927
|
+
"pending-reload": `pending ${descriptor.reload === "workspace" ? "reload" : "restart"} \u2014 repaired to ${diagnosis.releasedVersion}, but ${diagnosis.installedVersion} is still the version this host has loaded${diagnosis.repairDetail ? ` (${diagnosis.repairDetail})` : ""}`,
|
|
28904
28928
|
"freshness-unknown": `${diagnosis.installedVersion ?? "installed"} \u2014 freshness UNKNOWN, the published version could not be read`,
|
|
28905
28929
|
stale: `stale${versions ? ` \u2014 ${versions}` : ""}`,
|
|
28906
28930
|
missing: "missing \u2014 no install record",
|
|
@@ -28912,11 +28936,14 @@ function buildSurfaceDoctorCheck(diagnosis) {
|
|
|
28912
28936
|
id: `${descriptor.token}-plugin`,
|
|
28913
28937
|
surface: descriptor.token,
|
|
28914
28938
|
state,
|
|
28915
|
-
ok: state === "clean" || state === "skipped",
|
|
28939
|
+
ok: state === "clean" || state === "skipped" || state === "pending-reload",
|
|
28916
28940
|
...state === "freshness-unknown" ? { reportOnly: true } : {},
|
|
28941
|
+
// Nothing is broken, so this is not a ✗ — but it is the one OK row an operator must act on, and the
|
|
28942
|
+
// short default lane and the SessionStart banner both filter to failures unless a row says otherwise.
|
|
28943
|
+
...state === "pending-reload" ? { warn: true } : {},
|
|
28917
28944
|
label: `${descriptor.displayName} plugin`,
|
|
28918
28945
|
detail: detailByState[state],
|
|
28919
|
-
...plan ? { fix: plan.instruction } : state === "freshness-unknown" ? { fix: "check `mmi-cli --version` against `npm view @mutmutco/cli version`, then rerun doctor" } : {},
|
|
28946
|
+
...plan ? { fix: plan.instruction } : state === "pending-reload" ? { fix: reloadInstruction(descriptor) } : state === "freshness-unknown" ? { fix: "check `mmi-cli --version` against `npm view @mutmutco/cli version`, then rerun doctor" } : {},
|
|
28920
28947
|
verbose: [
|
|
28921
28948
|
// The three numbers the legacy builder used to print. They belong here now that this is the only
|
|
28922
28949
|
// plugin row, and a report that names a state without naming the versions behind it is not evidence.
|
package/package.json
CHANGED