@mutmutco/cli 4.3.35 → 4.3.37
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 +117 -20
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -7648,7 +7648,7 @@ function whereLabel(v) {
|
|
|
7648
7648
|
function formatFindHits(intent, hits) {
|
|
7649
7649
|
if (!hits.length) return `no catalog match for "${intent}". Browse with \`mmi-cli vault secrets catalog\`, or run \`mmi-cli vault secrets doctor\` for drift.`;
|
|
7650
7650
|
const blocks = hits.map((h) => [`${h.key} (${whereLabel(h)}) \u2014 ${h.purpose}`, ` ${h.command}`].join("\n"));
|
|
7651
|
-
return [`${hits.length} match(es) for "${intent}" (names + meaning only \u2014 consume keyless with \`secrets use\`):`, "", ...blocks].join("\n");
|
|
7651
|
+
return [`${hits.length} match(es) for "${intent}" (names + meaning only \u2014 consume keyless with \`mmi-cli vault secrets use\`):`, "", ...blocks].join("\n");
|
|
7652
7652
|
}
|
|
7653
7653
|
function formatCatalog(r) {
|
|
7654
7654
|
const lines2 = [`catalog \u2014 ${r.count} secret(s) across ${r.groups.length} group(s):`, ""];
|
|
@@ -15617,10 +15617,10 @@ var rollout_plan_default = {
|
|
|
15617
15617
|
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)."
|
|
15618
15618
|
},
|
|
15619
15619
|
baseline: {
|
|
15620
|
-
version: "4.3.
|
|
15621
|
-
tag: "v4.3.
|
|
15622
|
-
commit: "
|
|
15623
|
-
npm: "@mutmutco/cli@4.3.
|
|
15620
|
+
version: "4.3.37",
|
|
15621
|
+
tag: "v4.3.37",
|
|
15622
|
+
commit: "37821457da10",
|
|
15623
|
+
npm: "@mutmutco/cli@4.3.37"
|
|
15624
15624
|
},
|
|
15625
15625
|
exitCriterion: "fleet-n-of-n",
|
|
15626
15626
|
hubOnlyShortcut: "forbidden",
|
|
@@ -15637,14 +15637,14 @@ var rollout_plan_default = {
|
|
|
15637
15637
|
repo: "mutmutco/mmi-hub",
|
|
15638
15638
|
role: "canary",
|
|
15639
15639
|
schedule: "train",
|
|
15640
|
-
v3Target: "v4.3.
|
|
15640
|
+
v3Target: "v4.3.37"
|
|
15641
15641
|
}
|
|
15642
15642
|
],
|
|
15643
15643
|
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.",
|
|
15644
15644
|
rollback: {
|
|
15645
15645
|
independent: true,
|
|
15646
|
-
mechanism: "npm dist-tag latest -> 4.3.
|
|
15647
|
-
v3Target: "v4.3.
|
|
15646
|
+
mechanism: "npm dist-tag latest -> 4.3.37 and redeploy the Hub Lambda from tag v4.3.37 (37821457da10); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
15647
|
+
v3Target: "v4.3.37 (@mutmutco/cli@4.3.37, tag commit 37821457da10 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
15648
15648
|
}
|
|
15649
15649
|
},
|
|
15650
15650
|
{
|
|
@@ -18112,6 +18112,50 @@ async function enqueueAlignmentAutoMerge(deps, ctx, prNumber, prUrl, openedNote)
|
|
|
18112
18112
|
}
|
|
18113
18113
|
return { ...base, note: manual };
|
|
18114
18114
|
}
|
|
18115
|
+
var ALIGNMENT_LAND_POLL_MS = 15e3;
|
|
18116
|
+
var ALIGNMENT_LAND_TIMEOUT_MS = 20 * 6e4;
|
|
18117
|
+
async function awaitAlignmentLanded(deps, ctx, leg, base, opts = {}) {
|
|
18118
|
+
if (leg.status !== "pr-pending" || !leg.autoMergeEnqueued || leg.prNumber === void 0) return leg;
|
|
18119
|
+
const sleep2 = resolveSleep(deps);
|
|
18120
|
+
const now = deps.now ?? Date.now;
|
|
18121
|
+
const startedAt = now();
|
|
18122
|
+
const timeoutMs = opts.timeoutMs ?? ALIGNMENT_LAND_TIMEOUT_MS;
|
|
18123
|
+
const n = leg.prNumber;
|
|
18124
|
+
deps.warn?.(`watching alignment PR #${n} (${ctx.repo}) until it lands on ${base}`);
|
|
18125
|
+
for (; ; ) {
|
|
18126
|
+
let view;
|
|
18127
|
+
try {
|
|
18128
|
+
view = JSON.parse(await deps.run("gh", ["pr", "view", String(n), "--repo", ctx.repo, "--json", "state,mergeCommit"]));
|
|
18129
|
+
} catch (e) {
|
|
18130
|
+
deps.warn?.(`alignment PR #${n}: read failed (${describeReadError(e)}) \u2014 retrying`);
|
|
18131
|
+
}
|
|
18132
|
+
if (view?.state === "MERGED") {
|
|
18133
|
+
const oid = typeof view.mergeCommit?.oid === "string" ? view.mergeCommit.oid : void 0;
|
|
18134
|
+
let parents;
|
|
18135
|
+
if (oid) {
|
|
18136
|
+
try {
|
|
18137
|
+
parents = Number(clean2(await deps.run("gh", ["api", `repos/${ctx.repo}/commits/${oid}`, "--jq", ".parents | length"])));
|
|
18138
|
+
} catch {
|
|
18139
|
+
parents = void 0;
|
|
18140
|
+
}
|
|
18141
|
+
}
|
|
18142
|
+
if (parents === 1) {
|
|
18143
|
+
const squashNote = `alignment PR #${n} landed as a SQUASH (${oid?.slice(0, 12)} has one parent), not the true merge the Merge floor requires \u2014 re-align ${base} by true merge before the next train`;
|
|
18144
|
+
deps.warn?.(squashNote);
|
|
18145
|
+
return { ...leg, note: `${leg.note} \u2014 ${squashNote}` };
|
|
18146
|
+
}
|
|
18147
|
+
return { status: "aligned", alignment: "already-merged", prNumber: n, ...leg.prUrl ? { prUrl: leg.prUrl } : {}, note: `alignment PR #${n} landed on ${base} by true merge (watched)` };
|
|
18148
|
+
}
|
|
18149
|
+
if (view?.state === "CLOSED") {
|
|
18150
|
+
return { ...leg, note: `${leg.note} \u2014 alignment PR #${n} was CLOSED without merging; rerun the roll-forward` };
|
|
18151
|
+
}
|
|
18152
|
+
if (now() - startedAt >= timeoutMs) {
|
|
18153
|
+
deps.warn?.(`alignment PR #${n} still open after ${Math.round(timeoutMs / 6e4)}m \u2014 leaving the alignment leg pending; fold it later with mmi-cli devops release --resume --watch`);
|
|
18154
|
+
return leg;
|
|
18155
|
+
}
|
|
18156
|
+
await sleep2(ALIGNMENT_LAND_POLL_MS);
|
|
18157
|
+
}
|
|
18158
|
+
}
|
|
18115
18159
|
async function alignRcForward(deps, ctx, tag) {
|
|
18116
18160
|
await runGitRemoteRead(deps, ["fetch", "origin", "main", "rc"]);
|
|
18117
18161
|
const required = await discoverRequiredCheckContexts(deps, ctx, "rc");
|
|
@@ -21625,6 +21669,17 @@ async function runTrainDoctor(input) {
|
|
|
21625
21669
|
} catch (e) {
|
|
21626
21670
|
add({ code: "contexts-not-tag-addressable", severity: "blocker", source: "local", title: `required ${stage} check(s) cannot materialize on a tag push (#5428)`, remedy: message(e) });
|
|
21627
21671
|
}
|
|
21672
|
+
if (startBranch) {
|
|
21673
|
+
const tip = await git3(["rev-parse", "--verify", "--quiet", `refs/remotes/origin/${startBranch}`]);
|
|
21674
|
+
const checkRuns = JSON.parse(await train.run("gh", ["api", `repos/${repo}/commits/${tip}/check-runs`, "--jq", TRAIN_CHECK_RUNS_JQ]));
|
|
21675
|
+
const statuses = JSON.parse(await train.run("gh", ["api", `repos/${repo}/commits/${tip}/status`, "--jq", TRAIN_COMMIT_STATUS_JQ]));
|
|
21676
|
+
const observed = required.filter((c) => checkRuns.some((r) => r.name === c) || statuses.some((s) => s.context === c));
|
|
21677
|
+
const red = observed.filter((c) => resolveContextState(c, checkRuns, statuses) === "failed");
|
|
21678
|
+
const inFlight = observed.filter((c) => resolveContextState(c, checkRuns, statuses) === "pending");
|
|
21679
|
+
const at = `${startBranch}@${tip.slice(0, 12)}`;
|
|
21680
|
+
if (red.length) add({ code: "required-check-red", severity: "blocker", source: "origin", title: `required ${stage} check(s) failed on the lane's start branch tip: ${red.join(", ")} at ${at}`, remedy: `--heal never clears this one \u2014 a red trunk needs a fix, not a heal: land the failure's fix on ${startBranch} through a CI-gated PR, then rerun once its run is green (#6461)` });
|
|
21681
|
+
else if (inFlight.length) add({ code: "required-check-red", severity: "warning", source: "origin", title: `required ${stage} check(s) still in flight on the lane's start branch tip: ${inFlight.join(", ")} at ${at}`, remedy: `wait for the run to conclude and rerun \u2014 the tip's verdict is not known yet, and the train would tag before it is` });
|
|
21682
|
+
}
|
|
21628
21683
|
}
|
|
21629
21684
|
} catch (e) {
|
|
21630
21685
|
unverified(`required status checks on ${stage}`, e);
|
|
@@ -22473,7 +22528,7 @@ async function recordRcandDeployLegs(ledger, deps, anchors, deployModel, d, opts
|
|
|
22473
22528
|
await recordPhase(ledger, deps, anchors, "alignment", phaseEntry({ state: "skipped", sha, note: "rcand aligns no branch \u2014 alignment rides the release" }), { ...opts, landed });
|
|
22474
22529
|
return dispatch;
|
|
22475
22530
|
}
|
|
22476
|
-
async function completeMainRelease(deps, ctx, meta, deployModel, watch, options, tag, releaseSha, startBranch, preFold, tagProbe, branchHints, ledger) {
|
|
22531
|
+
async function completeMainRelease(deps, ctx, meta, deployModel, watch, options, tag, releaseSha, startBranch, preFold, tagProbe, branchHints, ledger, alignEarly) {
|
|
22477
22532
|
const resumeCommand = options.dev ? "mmi-cli devops release --dev --apply" : "mmi-cli devops release --apply";
|
|
22478
22533
|
const ledgerAnchors = { repo: ctx.repo, tag, tagSha: releaseSha };
|
|
22479
22534
|
try {
|
|
@@ -22578,6 +22633,7 @@ ${recovery.note}`), recoveryInput);
|
|
|
22578
22633
|
await verifyPublishedRelease(deps, ctx.repo, tag, "main", releaseSha);
|
|
22579
22634
|
await recordPhase(ledger, deps, ledgerAnchors, "githubRelease", phaseEntry({ state: "complete", sha: releaseSha, runUrl: releaseUrl, note: `verified GitHub Release ${tag} against ${releaseSha.slice(0, 12)}` }), { landed: "the immutable tag, the green required-check wall, and the origin/main fast-forward" });
|
|
22580
22635
|
const announceNote = deps.announce ? (await deps.announce({ repo: ctx.repo, tag, summaryFile: options.announceSummaryFile })).note : void 0;
|
|
22636
|
+
let alignment = alignEarly ? await alignEarly() : void 0;
|
|
22581
22637
|
const autoRunSince = (deps.now ?? Date.now)();
|
|
22582
22638
|
const deployDispatch0 = await dispatchDeploy(deps, ctx, "main", "main", deployModel, watch, autoRunSince, releaseSha, "report", meta.publishDir);
|
|
22583
22639
|
const deployRunRepo = releaseRunRepoFor(deployModel, ctx.repo);
|
|
@@ -22622,7 +22678,13 @@ ${recovery.note}`), recoveryInput);
|
|
|
22622
22678
|
});
|
|
22623
22679
|
let dispatch = appendPublishDispatch(deployDispatch, publishDispatch);
|
|
22624
22680
|
if (publishSkipNote) dispatch = { ...dispatch, note: `${dispatch.note}; tenant-publish.yml skipped (${publishSkipNote})` };
|
|
22625
|
-
|
|
22681
|
+
if (watch && alignment) {
|
|
22682
|
+
alignment = {
|
|
22683
|
+
dev: await awaitAlignmentLanded(deps, ctx, alignment.dev, "development"),
|
|
22684
|
+
...alignment.rc ? { rc: await awaitAlignmentLanded(deps, ctx, alignment.rc, "rc") } : {}
|
|
22685
|
+
};
|
|
22686
|
+
}
|
|
22687
|
+
return { checks, releaseUrl, announceNote, dispatch, ledgerAnchors, ...alignment ? { alignment } : {} };
|
|
22626
22688
|
}
|
|
22627
22689
|
function resumeStateOf(status) {
|
|
22628
22690
|
return status === "complete" ? "aligned" : status;
|
|
@@ -23010,6 +23072,8 @@ ${recovery.note}`), recoveryInput);
|
|
|
23010
23072
|
steps.push("verified the Release published against the tagged commit");
|
|
23011
23073
|
await recordPhase(ledger, deps, anchors, "githubRelease", phaseEntry({ state: "complete", sha: tagSha, runUrl: releaseUrl, note: "verified the Release published against the tagged commit" }), { strict: ledgerMode === "strict", landed: "the immutable tag, the re-proven wall, and the origin/main push" });
|
|
23012
23074
|
const announceNote = deps.announce ? (await deps.announce({ repo: ctx.repo, tag, summaryFile: options.announceSummaryFile })).note : void 0;
|
|
23075
|
+
let devRollForward = await rollDevelopmentForward(deps, ctx, tag);
|
|
23076
|
+
let rcAlignment = !directTrack && branchHints.hasRcBranch ? await alignRcForward(deps, ctx, tag) : void 0;
|
|
23013
23077
|
const autoRunSince = (deps.now ?? Date.now)();
|
|
23014
23078
|
const deployDispatch0 = await dispatchDeploy(deps, ctx, "main", "main", deployModel, watch, autoRunSince, tagSha, "report", meta.publishDir);
|
|
23015
23079
|
const recoveredDeployDispatch = await recoverFailedDispatchPhase(
|
|
@@ -23045,9 +23109,11 @@ ${recovery.note}`), recoveryInput);
|
|
|
23045
23109
|
landed: "the immutable tag, the re-proven wall, origin/main, the verified GitHub Release, and the dispatched deploy path"
|
|
23046
23110
|
});
|
|
23047
23111
|
const dispatch = appendPublishDispatch(deployDispatch, publishDispatch);
|
|
23048
|
-
|
|
23112
|
+
if (watch) {
|
|
23113
|
+
devRollForward = await awaitAlignmentLanded(deps, ctx, devRollForward, "development");
|
|
23114
|
+
if (rcAlignment) rcAlignment = await awaitAlignmentLanded(deps, ctx, rcAlignment, "rc");
|
|
23115
|
+
}
|
|
23049
23116
|
steps.push(`development roll-forward: ${devRollForward.status}`);
|
|
23050
|
-
const rcAlignment = !directTrack && branchHints.hasRcBranch ? await alignRcForward(deps, ctx, tag) : void 0;
|
|
23051
23117
|
if (rcAlignment) steps.push(`rc alignment: ${rcAlignment.status}`);
|
|
23052
23118
|
const followUp = deriveTrainFollowUpStatus({
|
|
23053
23119
|
projectInfo: "ok",
|
|
@@ -23388,9 +23454,25 @@ ${spent.note}`), rcandRecovery(tag2));
|
|
|
23388
23454
|
const releaseSha3 = requireValue(clean2(await deps.run("git", ["rev-parse", "main"])), "release sha");
|
|
23389
23455
|
return { versionFold: versionFold3, releaseSha: releaseSha3 };
|
|
23390
23456
|
});
|
|
23391
|
-
const { checks: checks2, releaseUrl: releaseUrl2, announceNote: announceNote2, dispatch: d2, ledgerAnchors: ledgerAnchors2 } = await completeMainRelease(
|
|
23392
|
-
|
|
23393
|
-
|
|
23457
|
+
const { checks: checks2, releaseUrl: releaseUrl2, announceNote: announceNote2, dispatch: d2, ledgerAnchors: ledgerAnchors2, alignment: alignment2 } = await completeMainRelease(
|
|
23458
|
+
deps,
|
|
23459
|
+
ctx,
|
|
23460
|
+
meta,
|
|
23461
|
+
deployModel2,
|
|
23462
|
+
watch,
|
|
23463
|
+
options,
|
|
23464
|
+
tag2,
|
|
23465
|
+
releaseSha2,
|
|
23466
|
+
"development",
|
|
23467
|
+
preFold2,
|
|
23468
|
+
tagProbe2,
|
|
23469
|
+
branchHints,
|
|
23470
|
+
ledger,
|
|
23471
|
+
// #6468: alignment PRs open before the publish wait; rc aligns only on the --dev lane of a repo that has one.
|
|
23472
|
+
async () => ({ dev: await rollDevelopmentForward(deps, ctx, tag2), ...!directTrack && hasRcBranch ? { rc: await alignRcForward(deps, ctx, tag2) } : {} })
|
|
23473
|
+
);
|
|
23474
|
+
const devRollForward2 = alignment2?.dev ?? await rollDevelopmentForward(deps, ctx, tag2);
|
|
23475
|
+
await recordPhase(ledger, deps, ledgerAnchors2, "alignment", phaseEntry(alignmentPhaseEntry(devRollForward2, alignment2?.rc)), { landed: "the immutable tag, the green wall, origin/main, the verified Release, and the deploy/publish path" });
|
|
23394
23476
|
const ledgerReport2 = releaseLedgerReport(await ledger?.load(ledgerAnchors2).catch(() => void 0), ledger?.path ?? "(no ledger)");
|
|
23395
23477
|
if (directTrack) {
|
|
23396
23478
|
return {
|
|
@@ -23419,8 +23501,7 @@ ${spent.note}`), rcandRecovery(tag2));
|
|
|
23419
23501
|
};
|
|
23420
23502
|
}
|
|
23421
23503
|
const retirement2 = hasRcBranch ? await retireRcRuntime(deps, ctx, deployModel2, d2.deployStatus, rcShaAtRelease) : { status: "not-applicable", note: "no origin/rc branch \u2014 rc runtime retirement skipped" };
|
|
23422
|
-
const rcAlignment2 =
|
|
23423
|
-
await recordPhase(ledger, deps, ledgerAnchors2, "alignment", phaseEntry(alignmentPhaseEntry(devRollForward2, rcAlignment2)), { landed: "the immutable tag, the green wall, origin/main, the verified Release, and the deploy/publish path" });
|
|
23504
|
+
const rcAlignment2 = alignment2?.rc;
|
|
23424
23505
|
const fullLedgerReport = releaseLedgerReport(await ledger?.load(ledgerAnchors2).catch(() => void 0), ledger?.path ?? "(no ledger)");
|
|
23425
23506
|
const environments2 = await buildEnvironments(deps, ctx, deployModel2, d2.deployStatus, retirement2);
|
|
23426
23507
|
return {
|
|
@@ -23482,10 +23563,26 @@ ${spent.note}`), rcandRecovery(tag2));
|
|
|
23482
23563
|
const releaseSha2 = requireValue(clean2(await deps.run("git", ["rev-parse", "main"])), "release sha");
|
|
23483
23564
|
return { versionFold: versionFold2, releaseSha: releaseSha2 };
|
|
23484
23565
|
});
|
|
23485
|
-
const { checks, releaseUrl, announceNote, dispatch: d, ledgerAnchors } = await completeMainRelease(
|
|
23566
|
+
const { checks, releaseUrl, announceNote, dispatch: d, ledgerAnchors, alignment } = await completeMainRelease(
|
|
23567
|
+
deps,
|
|
23568
|
+
ctx,
|
|
23569
|
+
meta,
|
|
23570
|
+
deployModel,
|
|
23571
|
+
watch,
|
|
23572
|
+
options,
|
|
23573
|
+
tag,
|
|
23574
|
+
releaseSha,
|
|
23575
|
+
"rc",
|
|
23576
|
+
preFold,
|
|
23577
|
+
tagProbe,
|
|
23578
|
+
branchHints,
|
|
23579
|
+
ledger,
|
|
23580
|
+
// #6468: both alignment PRs open before the publish wait.
|
|
23581
|
+
async () => ({ dev: await rollDevelopmentForward(deps, ctx, tag), rc: await alignRcForward(deps, ctx, tag) })
|
|
23582
|
+
);
|
|
23486
23583
|
const retirement = await retireRcRuntime(deps, ctx, deployModel, d.deployStatus, releasedRcSha);
|
|
23487
|
-
const devRollForward = await rollDevelopmentForward(deps, ctx, tag);
|
|
23488
|
-
const rcAlignment = await alignRcForward(deps, ctx, tag);
|
|
23584
|
+
const devRollForward = alignment?.dev ?? await rollDevelopmentForward(deps, ctx, tag);
|
|
23585
|
+
const rcAlignment = alignment?.rc ?? await alignRcForward(deps, ctx, tag);
|
|
23489
23586
|
await recordPhase(ledger, deps, ledgerAnchors, "alignment", phaseEntry(alignmentPhaseEntry(devRollForward, rcAlignment)), { landed: "the immutable tag, the green wall, origin/main, the verified Release, and the deploy/publish path" });
|
|
23490
23587
|
const ledgerReport = releaseLedgerReport(await ledger?.load(ledgerAnchors).catch(() => void 0), ledger?.path ?? "(no ledger)");
|
|
23491
23588
|
const environments = await buildEnvironments(deps, ctx, deployModel, d.deployStatus, retirement);
|
package/package.json
CHANGED