@mutmutco/cli 4.6.26 → 4.6.28
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 +78 -31
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -15621,7 +15621,9 @@ var notedVaultDenied = false;
|
|
|
15621
15621
|
var APP_SUBPROCESS_ROUTED_COMMANDS = /* @__PURE__ */ new Set([
|
|
15622
15622
|
"issue create",
|
|
15623
15623
|
"pr create",
|
|
15624
|
-
"pr merge"
|
|
15624
|
+
"pr merge",
|
|
15625
|
+
// #9871: queue enqueue + auto-merge fallback ride ghFilingAppEnv (#9490/#9528; receipt credential app_installation).
|
|
15626
|
+
"pr land"
|
|
15625
15627
|
]);
|
|
15626
15628
|
async function activateAppActor(commandPath4, env, mint, note = (line) => process.stderr.write(`${line}
|
|
15627
15629
|
`)) {
|
|
@@ -25908,7 +25910,7 @@ __export(gate_budget_exports, {
|
|
|
25908
25910
|
isGateWorkflowPath: () => isGateWorkflowPath
|
|
25909
25911
|
});
|
|
25910
25912
|
var BLESSED_RUN_WITH_BUDGET_SHA = "cb31a94c89729beac83500ad4d301132dcfe2e4c";
|
|
25911
|
-
var BLESSED_RUNNER_GATE_SHA = "
|
|
25913
|
+
var BLESSED_RUNNER_GATE_SHA = "7f9a25df2ce7198c67407b42d0c548f75ce3f430";
|
|
25912
25914
|
var BLESSED_RUNNER_CHECK_POLICY_SHA = "67bb2012710a96d21be825c29a5d5c2b4b8001e2";
|
|
25913
25915
|
var BLESSED_KIT_AGENT_FILES_SHA = "e976df208d891b215844386c1cac0f161946b6fa";
|
|
25914
25916
|
var BLESSED_JEV_FAILURES_SHA = "d5278c6133318b33727483ddfdfddc8f841e3ef6";
|
|
@@ -28179,10 +28181,10 @@ var rollout_plan_default = {
|
|
|
28179
28181
|
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)."
|
|
28180
28182
|
},
|
|
28181
28183
|
baseline: {
|
|
28182
|
-
version: "4.6.
|
|
28183
|
-
tag: "v4.6.
|
|
28184
|
-
commit: "
|
|
28185
|
-
npm: "@mutmutco/cli@4.6.
|
|
28184
|
+
version: "4.6.28",
|
|
28185
|
+
tag: "v4.6.28",
|
|
28186
|
+
commit: "e21db51c4641",
|
|
28187
|
+
npm: "@mutmutco/cli@4.6.28"
|
|
28186
28188
|
},
|
|
28187
28189
|
exitCriterion: "fleet-n-of-n",
|
|
28188
28190
|
hubOnlyShortcut: "forbidden",
|
|
@@ -28199,14 +28201,14 @@ var rollout_plan_default = {
|
|
|
28199
28201
|
repo: "mutmutco/mmi-hub",
|
|
28200
28202
|
role: "canary",
|
|
28201
28203
|
schedule: "train",
|
|
28202
|
-
v3Target: "v4.6.
|
|
28204
|
+
v3Target: "v4.6.28"
|
|
28203
28205
|
}
|
|
28204
28206
|
],
|
|
28205
28207
|
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.",
|
|
28206
28208
|
rollback: {
|
|
28207
28209
|
independent: true,
|
|
28208
|
-
mechanism: "npm dist-tag latest -> 4.6.
|
|
28209
|
-
v3Target: "v4.6.
|
|
28210
|
+
mechanism: "npm dist-tag latest -> 4.6.28 and redeploy the Hub Lambda from tag v4.6.28 (e21db51c4641); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
28211
|
+
v3Target: "v4.6.28 (@mutmutco/cli@4.6.28, tag commit e21db51c4641 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
28210
28212
|
}
|
|
28211
28213
|
},
|
|
28212
28214
|
{
|
|
@@ -43931,6 +43933,20 @@ ${list}
|
|
|
43931
43933
|
Co-authored-by: GitHub <noreply@github.com>
|
|
43932
43934
|
`;
|
|
43933
43935
|
}
|
|
43936
|
+
function checkQueueSquashOverride(title, pr, messages, root) {
|
|
43937
|
+
if (messages.length <= 1 || !messages.some((message2) => /^\s*Test-Policy-Override\s*:/im.test(`${message2.headline}
|
|
43938
|
+
${message2.body}`)) || readMessageOverride2(githubQueueSquashMessage(title, pr, messages), root)) return void 0;
|
|
43939
|
+
return {
|
|
43940
|
+
mergeStatus: "failed",
|
|
43941
|
+
preparation: {
|
|
43942
|
+
status: "preparation-needed",
|
|
43943
|
+
reason: "queue-message-override",
|
|
43944
|
+
issueTargets: [],
|
|
43945
|
+
nextAction: "PR author: this repository squashes with COMMIT_MESSAGES, and GitHub composes a multi-commit squash as a list of every commit message followed by its own co-author block, so this Test-Policy-Override trailer would not be in the final trailer block the queue check reads. First, add one commit on this same branch whose message ends with a trailer block carrying the Test-Policy-Override waiver, push (no force-push), then rerun pr land on the same PR. This makes the waiver the final trailer block of the LAST commit. As a fallback, put the change in ONE commit whose message ends with a trailer block carrying the waiver: push a new branch whose single commit has the same tree, parented on the PR base; open it as a new PR and close this one, then rerun pr land. Keep repository settings unchanged."
|
|
43946
|
+
},
|
|
43947
|
+
error: "queue preparation needed: the multi-commit COMMIT_MESSAGES squash would bury the Test-Policy-Override waiver mid-message, so the queue check would fail malformed-override-trailer"
|
|
43948
|
+
};
|
|
43949
|
+
}
|
|
43934
43950
|
async function inspectQueuePreparation(pr, repo, options) {
|
|
43935
43951
|
const read = options.read ?? readApi;
|
|
43936
43952
|
const queueRead = options.read ?? actorReadApi;
|
|
@@ -44044,21 +44060,9 @@ ${pull.body}`;
|
|
|
44044
44060
|
])];
|
|
44045
44061
|
return { kind: "refused", result: preparation("queue-message", targets, `GitHub's unchanged ${policy.body} message is unsafe for ${targets.map((number) => `${repo}#${number}`).join(", ")}. ${closingGuardDiagnostic(verdict.message)} See preparation.nextAction for the exact \`mmi-cli devops pr edit ${pr} --repo ${repo} --body-file .jerv/tmp/pr-${pr}-body.md\` remedy.`) };
|
|
44046
44062
|
}
|
|
44047
|
-
if (method === "SQUASH" && policy.body === "COMMIT_MESSAGES" &&
|
|
44048
|
-
|
|
44049
|
-
return {
|
|
44050
|
-
kind: "refused",
|
|
44051
|
-
result: {
|
|
44052
|
-
mergeStatus: "failed",
|
|
44053
|
-
preparation: {
|
|
44054
|
-
status: "preparation-needed",
|
|
44055
|
-
reason: "queue-message-override",
|
|
44056
|
-
issueTargets: [],
|
|
44057
|
-
nextAction: "PR author: this repository squashes with COMMIT_MESSAGES, and GitHub composes a multi-commit squash as a list of every commit message followed by its own co-author block, so this Test-Policy-Override trailer would not be in the final trailer block the queue check reads. Put the change in ONE commit whose message ends with a trailer block carrying the waiver (no force-push: push a new branch whose single commit has the same tree, parented on the PR base; open it as a new PR and close this one), or make the waiver the final trailer block of the LAST commit, then rerun pr land. Keep repository settings unchanged."
|
|
44058
|
-
},
|
|
44059
|
-
error: "queue preparation needed: the multi-commit COMMIT_MESSAGES squash would bury the Test-Policy-Override waiver mid-message, so the queue check would fail malformed-override-trailer"
|
|
44060
|
-
}
|
|
44061
|
-
};
|
|
44063
|
+
if (method === "SQUASH" && policy.body === "COMMIT_MESSAGES" && options.bodyText == null) {
|
|
44064
|
+
const refusal = checkQueueSquashOverride(pull.title, pr, messages, process.cwd());
|
|
44065
|
+
if (refusal) return { kind: "refused", result: refusal };
|
|
44062
44066
|
}
|
|
44063
44067
|
const requiredBody = options.bodyText ?? composedBody;
|
|
44064
44068
|
const prBodyPreservesOverrides = requiredBody != null && method === "SQUASH" && policy.body === "PR_BODY" && options.bodyText == null && prBodyPreservesCommitOverrides(messages, headline, pull.body, process.cwd());
|
|
@@ -47947,6 +47951,16 @@ function evaluateFilelessComposeGuard(input) {
|
|
|
47947
47951
|
}
|
|
47948
47952
|
return { ok: true };
|
|
47949
47953
|
}
|
|
47954
|
+
function positionalOrRepoFlagTarget(commandName, positional, repoFlag) {
|
|
47955
|
+
const pos = positional?.trim() || void 0;
|
|
47956
|
+
const flag = repoFlag?.trim() || void 0;
|
|
47957
|
+
if (!pos || !flag) return pos ?? flag;
|
|
47958
|
+
const [a, b] = [pos.toLowerCase(), flag.toLowerCase()];
|
|
47959
|
+
const tail = (s) => s.split("/").pop();
|
|
47960
|
+
const same2 = a === b || (!a.includes("/") || !b.includes("/")) && tail(a) === tail(b);
|
|
47961
|
+
if (!same2) throw new Error(`${commandName}: positional target ${pos} and --repo ${flag} name different repos; pass one of them`);
|
|
47962
|
+
return pos;
|
|
47963
|
+
}
|
|
47950
47964
|
function requireProjectTarget(commandName, explicitTarget, currentRepo) {
|
|
47951
47965
|
const target = explicitTarget?.trim() || currentRepo?.trim();
|
|
47952
47966
|
if (!target) {
|
|
@@ -56273,6 +56287,7 @@ function githubCustodyStore(repo, baseSha, api = defaultGitHubClient()) {
|
|
|
56273
56287
|
}
|
|
56274
56288
|
};
|
|
56275
56289
|
}
|
|
56290
|
+
var DISPATCH_ABSENT_AFTER_MS = 18e4;
|
|
56276
56291
|
var ReleaseCustodySession = class _ReleaseCustodySession {
|
|
56277
56292
|
constructor(store, sha, state) {
|
|
56278
56293
|
this.store = store;
|
|
@@ -56371,6 +56386,13 @@ var ReleaseCustodySession = class _ReleaseCustodySession {
|
|
|
56371
56386
|
async reconcile(observer) {
|
|
56372
56387
|
for (const effect of Object.values(this.state.effects).filter((e) => e.state === "intent")) {
|
|
56373
56388
|
const observation = await observer(effect);
|
|
56389
|
+
if (!observation.observed && observation.absent) {
|
|
56390
|
+
await this.change((state) => {
|
|
56391
|
+
const { [effect.id]: _dropped, ...effects } = state.effects;
|
|
56392
|
+
return { ...state, effects };
|
|
56393
|
+
});
|
|
56394
|
+
continue;
|
|
56395
|
+
}
|
|
56374
56396
|
if (!observation.observed) throw new ReleaseCustodyError(`effect ${effect.kind} (${effect.id}) has no proven outcome; observe it before retrying`);
|
|
56375
56397
|
await this.change((state) => ({ ...state, effects: {
|
|
56376
56398
|
...state.effects,
|
|
@@ -56541,7 +56563,8 @@ function effectObserver(api) {
|
|
|
56541
56563
|
const fields = i.fields;
|
|
56542
56564
|
const title = effect.kind === "tenant-deploy" ? `tenant-deploy ${i.slug} ${i.stage} @ ${i.ref}` : workflow === "get-publish.yml" ? `get-publish (${fields.product} ${fields.mode || "full"})` : workflow === "tenant-deploy.yml" ? `tenant-deploy ${fields.slug} ${fields.stage} @ ${fields.ref}` : null;
|
|
56543
56565
|
const matches = runs2.workflow_runs.filter((run) => title && run.display_title === title && run.event === "workflow_dispatch" && run.head_sha === i.sha && !i.beforeRunIds?.includes(run.id) && Date.parse(run.created_at) >= Date.parse(effect.startedAt) - 1e3 && Date.parse(run.created_at) <= Date.parse(effect.startedAt) + 12e4);
|
|
56544
|
-
|
|
56566
|
+
if (matches.length === 1) return { observed: true, result: effect.kind === "tenant-deploy" ? null : "" };
|
|
56567
|
+
return matches.length === 0 && Date.now() > Date.parse(effect.startedAt) + DISPATCH_ABSENT_AFTER_MS ? { observed: false, absent: true } : { observed: false };
|
|
56545
56568
|
}
|
|
56546
56569
|
return { observed: false };
|
|
56547
56570
|
};
|
|
@@ -60828,6 +60851,25 @@ function createPreparationDeps(input, io = {}) {
|
|
|
60828
60851
|
if (!await docs.refResolves(candidate.root, candidate.headSha) || !await docs.refResolves(candidate.root, candidate.baseSha)) {
|
|
60829
60852
|
return result("skipped", "exact remote candidate objects are not available locally; committed test policy was not checked");
|
|
60830
60853
|
}
|
|
60854
|
+
const log = (await exec3("git", ["-C", candidate.root, "log", "--reverse", "--format=%B%x00", `${candidate.baseSha}..${candidate.headSha}`], { timeout: GIT_TIMEOUT_MS })).stdout;
|
|
60855
|
+
const messages = log.split("\0").filter((message2) => message2.trim()).map((message2) => {
|
|
60856
|
+
const [headline, ...body] = message2.trim().split("\n");
|
|
60857
|
+
return { headline, body: body.join("\n") };
|
|
60858
|
+
});
|
|
60859
|
+
const refusal = checkQueueSquashOverride("Candidate PR", "0", messages, candidate.root);
|
|
60860
|
+
if (refusal) {
|
|
60861
|
+
const [owner, name] = candidate.repo.split("/");
|
|
60862
|
+
const metadata = await client.graphql(
|
|
60863
|
+
"query($owner:String!,$name:String!,$base:String!) { repository(owner:$owner,name:$name) { squashMergeCommitMessage mergeQueue(branch:$base) { configuration { mergeMethod } } } }",
|
|
60864
|
+
{ owner, name, base: candidate.base }
|
|
60865
|
+
);
|
|
60866
|
+
const rules = await client.restPaginate(`repos/${candidate.repo}/rules/branches/${encodeURIComponent(candidate.base)}?per_page=100`);
|
|
60867
|
+
const methods = [...new Set(rules.filter((rule) => rule.type === "merge_queue").map((rule) => rule.parameters?.merge_method))];
|
|
60868
|
+
const method = methods.length === 1 ? methods[0] : !methods.length ? metadata.repository.mergeQueue?.configuration?.mergeMethod : void 0;
|
|
60869
|
+
if (method === "SQUASH" && metadata.repository.squashMergeCommitMessage === "COMMIT_MESSAGES") {
|
|
60870
|
+
return result("failed", `${refusal.error} \u2014 ${refusal.preparation.nextAction}`);
|
|
60871
|
+
}
|
|
60872
|
+
}
|
|
60831
60873
|
const policy = runTestPolicy(candidate.root, { candidate: { head: candidate.headSha, base: candidate.baseSha } });
|
|
60832
60874
|
return result(policy.ok ? "passed" : "failed", policy.ok ? `committed test policy passed at ${candidate.headSha}` : policy.findings.map((finding) => finding.detail).join("\n"));
|
|
60833
60875
|
}
|
|
@@ -73549,13 +73591,13 @@ project.command("sync-info [owner/repo]").description("synchronize the owning Gi
|
|
|
73549
73591
|
}
|
|
73550
73592
|
});
|
|
73551
73593
|
async function runProjectDeployGet(repoOrSlug, o) {
|
|
73552
|
-
const cfg = await loadConfig();
|
|
73553
73594
|
let target;
|
|
73554
73595
|
try {
|
|
73555
|
-
target = await projectTarget("org project deploy get", repoOrSlug);
|
|
73596
|
+
target = await projectTarget("org project deploy get", positionalOrRepoFlagTarget("org project deploy get", repoOrSlug, o.repo));
|
|
73556
73597
|
} catch (e) {
|
|
73557
73598
|
return fail(e.message);
|
|
73558
73599
|
}
|
|
73600
|
+
const cfg = await loadConfig();
|
|
73559
73601
|
const out = await fetchDeployFactsBySlug(slugOf(target), registryClientDeps(cfg));
|
|
73560
73602
|
if (!out) return failGraceful(`org project deploy get: Hub deploy facts read failed for ${target}`);
|
|
73561
73603
|
const stage = o.stage?.trim();
|
|
@@ -73563,8 +73605,12 @@ async function runProjectDeployGet(repoOrSlug, o) {
|
|
|
73563
73605
|
const payload = stage ? { slug: out.slug, stage, deploy: out.stages[stage] ?? null } : out;
|
|
73564
73606
|
console.log(JSON.stringify(payload, null, 2));
|
|
73565
73607
|
}
|
|
73566
|
-
var projectDeploy = project.command("deploy").description("read nonsecret DEPLOY# facts (domain, port, deploy path, substrate, host presence)").argument("[ownerRepo]", "shorthand for `get <owner/repo>`").addOption(new Option("--stage <stage>", "dev | rc | main").choices(["dev", "rc", "main"])).option("--json", "machine-readable output").action(async (ownerRepo, o) => runProjectDeployGet(ownerRepo, o));
|
|
73567
|
-
projectDeploy.command("get [owner/repo]").description("read nonsecret DEPLOY# facts for one project; defaults to the current repo").addOption(new Option("--stage <stage>", "dev | rc | main").choices(["dev", "rc", "main"])).option("--json", "machine-readable output").action(async (repoOrSlug, o) => runProjectDeployGet(repoOrSlug, {
|
|
73608
|
+
var projectDeploy = project.command("deploy").description("read nonsecret DEPLOY# facts (domain, port, deploy path, substrate, host presence)").argument("[ownerRepo]", "shorthand for `get <owner/repo>`").addOption(new Option("--stage <stage>", "dev | rc | main").choices(["dev", "rc", "main"])).option("--repo <owner/repo>", "target repo (alias for the positional; defaults to the current repo)").option("--json", "machine-readable output").action(async (ownerRepo, o) => runProjectDeployGet(ownerRepo, o));
|
|
73609
|
+
projectDeploy.command("get [owner/repo]").description("read nonsecret DEPLOY# facts for one project; defaults to the current repo").addOption(new Option("--stage <stage>", "dev | rc | main").choices(["dev", "rc", "main"])).option("--repo <owner/repo>", "target repo (alias for the positional; defaults to the current repo)").option("--json", "machine-readable output").action(async (repoOrSlug, o) => runProjectDeployGet(repoOrSlug, {
|
|
73610
|
+
stage: projectDeploy.opts().stage,
|
|
73611
|
+
json: o.json,
|
|
73612
|
+
repo: projectDeploy.opts().repo ?? o.repo
|
|
73613
|
+
}));
|
|
73568
73614
|
projectDeploy.command("doctor").description("read-only estate port scan; --box lists its DEPLOY# owners and suggests free 8xxx ports (master-only)").option("--box <name>", "resolve box from the live Hetzner inventory and inspect its ports").option("--json", "machine-readable output").action(async (o) => {
|
|
73569
73615
|
const cfg = await loadConfig();
|
|
73570
73616
|
const report = await fetchDeployPortCollisions(registryClientDeps(cfg));
|
|
@@ -73593,13 +73639,14 @@ projectDeploy.command("doctor").description("read-only estate port scan; --box l
|
|
|
73593
73639
|
if (report.error && report.status !== 403) return failGraceful(`org project deploy doctor: ${report.error}`);
|
|
73594
73640
|
if (!report.ok && !report.error) process.exitCode = 1;
|
|
73595
73641
|
});
|
|
73596
|
-
projectDeploy.command("retire [owner/repo]").description("retire (tombstone) one stage's DEPLOY# row for a project that no longer deploys through it (#7095, master-only). --stage is required. DRY-RUN by default \u2014 pass --apply to actually write; defaults to the current repo. Refuses a tenant-container project's live main row.").addOption(new Option("--stage <stage>", "dev | rc | main (required)").choices(["dev", "rc", "main"])).option("--apply", "actually retire (default is a dry-run preview of the row that would be retired)").option("--decommissioned", "main only (#8148): retire a tenant-container main row after `runtime tenant control <repo> main retire`; the box must show main gone").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
73642
|
+
projectDeploy.command("retire [owner/repo]").description("retire (tombstone) one stage's DEPLOY# row for a project that no longer deploys through it (#7095, master-only). --stage is required. DRY-RUN by default \u2014 pass --apply to actually write; defaults to the current repo. Refuses a tenant-container project's live main row.").addOption(new Option("--stage <stage>", "dev | rc | main (required)").choices(["dev", "rc", "main"])).option("--apply", "actually retire (default is a dry-run preview of the row that would be retired)").option("--decommissioned", "main only (#8148): retire a tenant-container main row after `runtime tenant control <repo> main retire`; the box must show main gone").option("--repo <owner/repo>", "target repo (alias for the positional; defaults to the current repo)").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
73597
73643
|
const stage = projectDeploy.opts().stage;
|
|
73598
73644
|
if (!stage) return fail("org project deploy retire: --stage is required (dev | rc | main)");
|
|
73599
73645
|
const cfg = await loadConfig();
|
|
73600
73646
|
let target;
|
|
73601
73647
|
try {
|
|
73602
|
-
|
|
73648
|
+
const repoFlag = projectDeploy.opts().repo ?? o.repo;
|
|
73649
|
+
target = await projectTarget("org project deploy retire", positionalOrRepoFlagTarget("org project deploy retire", repoOrSlug, repoFlag));
|
|
73603
73650
|
} catch (e) {
|
|
73604
73651
|
return fail(e.message);
|
|
73605
73652
|
}
|
package/package.json
CHANGED