@mutmutco/cli 4.1.11 → 4.1.13
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 +141 -17
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -13152,10 +13152,10 @@ var rollout_plan_default = {
|
|
|
13152
13152
|
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)."
|
|
13153
13153
|
},
|
|
13154
13154
|
baseline: {
|
|
13155
|
-
version: "4.1.
|
|
13156
|
-
tag: "v4.1.
|
|
13157
|
-
commit: "
|
|
13158
|
-
npm: "@mutmutco/cli@4.1.
|
|
13155
|
+
version: "4.1.13",
|
|
13156
|
+
tag: "v4.1.13",
|
|
13157
|
+
commit: "91a8a172eda6",
|
|
13158
|
+
npm: "@mutmutco/cli@4.1.13"
|
|
13159
13159
|
},
|
|
13160
13160
|
exitCriterion: "fleet-n-of-n",
|
|
13161
13161
|
hubOnlyShortcut: "forbidden",
|
|
@@ -13172,14 +13172,14 @@ var rollout_plan_default = {
|
|
|
13172
13172
|
repo: "mutmutco/mmi-hub",
|
|
13173
13173
|
role: "canary",
|
|
13174
13174
|
schedule: "train",
|
|
13175
|
-
v3Target: "v4.1.
|
|
13175
|
+
v3Target: "v4.1.13"
|
|
13176
13176
|
}
|
|
13177
13177
|
],
|
|
13178
13178
|
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.",
|
|
13179
13179
|
rollback: {
|
|
13180
13180
|
independent: true,
|
|
13181
|
-
mechanism: "npm dist-tag latest -> 4.1.
|
|
13182
|
-
v3Target: "v4.1.
|
|
13181
|
+
mechanism: "npm dist-tag latest -> 4.1.13 and redeploy the Hub Lambda from tag v4.1.13 (91a8a172eda6); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
13182
|
+
v3Target: "v4.1.13 (@mutmutco/cli@4.1.13, tag commit 91a8a172eda6 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
13183
13183
|
}
|
|
13184
13184
|
},
|
|
13185
13185
|
{
|
|
@@ -15938,9 +15938,13 @@ async function withCheckoutRestoredAfterRelease(deps, result, startBranch) {
|
|
|
15938
15938
|
return { ...result, checkout, ...localSync ? { localSync } : {} };
|
|
15939
15939
|
}
|
|
15940
15940
|
var HUB_REPO3 = "mutmutco/MMI-Hub";
|
|
15941
|
+
var JERV_HUB_REPO = "mutmutco/Jerv-Hub";
|
|
15941
15942
|
function isHubControlRepo(repo) {
|
|
15942
15943
|
return repo.toLowerCase() === HUB_REPO3.toLowerCase();
|
|
15943
15944
|
}
|
|
15945
|
+
function isJervHubRepo(repo) {
|
|
15946
|
+
return repo.toLowerCase() === JERV_HUB_REPO.toLowerCase();
|
|
15947
|
+
}
|
|
15944
15948
|
function supportsReleaseAbort(_repo, meta, _branchHints) {
|
|
15945
15949
|
return DEPLOY_MODELS.includes(meta.deployModel);
|
|
15946
15950
|
}
|
|
@@ -16653,6 +16657,42 @@ function appendPublishDispatch(deploy, publish) {
|
|
|
16653
16657
|
deployStatus: deploy.deployStatus === "failure" || publish.deployStatus === "failure" ? "failure" : deploy.deployStatus === "pending" || publish.deployStatus === "pending" ? "pending" : "success"
|
|
16654
16658
|
};
|
|
16655
16659
|
}
|
|
16660
|
+
async function appendJervGatewayReleaseDeploy(deps, repo, tag, tagSha, dispatch) {
|
|
16661
|
+
if (!isJervHubRepo(repo)) return dispatch;
|
|
16662
|
+
if (dispatch.deployStatus !== "success") {
|
|
16663
|
+
return {
|
|
16664
|
+
...dispatch,
|
|
16665
|
+
note: `${dispatch.note}; Jerv Gateway deploy deferred until package publication is proven green`
|
|
16666
|
+
};
|
|
16667
|
+
}
|
|
16668
|
+
try {
|
|
16669
|
+
const output = await deps.run("bash", ["scripts/jerv-gateway-release-deploy.sh", "--tag", tag]);
|
|
16670
|
+
const receipt = output.trim().split(/\r?\n/).reverse().map((line) => {
|
|
16671
|
+
try {
|
|
16672
|
+
return JSON.parse(line);
|
|
16673
|
+
} catch {
|
|
16674
|
+
return null;
|
|
16675
|
+
}
|
|
16676
|
+
}).find((value) => value?.schema === "jerv-gateway/release-deploy-v1");
|
|
16677
|
+
if (!receipt || receipt.tag !== tag || receipt.commit !== tagSha || receipt.status !== "healthy") {
|
|
16678
|
+
throw new Error("helper receipt did not match the released tag and commit");
|
|
16679
|
+
}
|
|
16680
|
+
return {
|
|
16681
|
+
...dispatch,
|
|
16682
|
+
note: `${dispatch.note}; Jerv Gateway deployed ${tagSha.slice(0, 12)} and passed health checks`,
|
|
16683
|
+
workflowRuns: [...dispatch.workflowRuns ?? [], { workflow: "jerv-gateway", conclusion: "success" }],
|
|
16684
|
+
deployStatus: "success"
|
|
16685
|
+
};
|
|
16686
|
+
} catch (error) {
|
|
16687
|
+
const detail = (error instanceof Error ? error.message : String(error)).split(/\r?\n/, 1)[0].slice(0, 240);
|
|
16688
|
+
return {
|
|
16689
|
+
...dispatch,
|
|
16690
|
+
note: `${dispatch.note}; Jerv Gateway deploy FAILED (${detail})`,
|
|
16691
|
+
workflowRuns: [...dispatch.workflowRuns ?? [], { workflow: "jerv-gateway", conclusion: "failure" }],
|
|
16692
|
+
deployStatus: "failure"
|
|
16693
|
+
};
|
|
16694
|
+
}
|
|
16695
|
+
}
|
|
16656
16696
|
async function watchOwnWorkflowRuns(deps, repo, targets, since, headSha, enumerateSha = false) {
|
|
16657
16697
|
const workflowRuns = [];
|
|
16658
16698
|
for (const target of targets) {
|
|
@@ -17338,6 +17378,9 @@ async function completeMainRelease(deps, ctx, meta, deployModel, watch, options,
|
|
|
17338
17378
|
let tagPush;
|
|
17339
17379
|
try {
|
|
17340
17380
|
await assertComputedReleaseTagStillNext(deps, tag);
|
|
17381
|
+
if (deployModel === "hub-serverless") {
|
|
17382
|
+
await deps.run("node", ["scripts/release-distribution.mjs", "assert-release-lineage", tag.replace(/^v/, ""), "--release-commit", releaseSha]);
|
|
17383
|
+
}
|
|
17341
17384
|
tagPush = await ensureTagPushed(deps, tag, releaseSha, tagProbe, ctx.repo);
|
|
17342
17385
|
} catch (e) {
|
|
17343
17386
|
throw await recoverFailedFold(deps, e, startBranch, preFold.mainSha, resumeCommand);
|
|
@@ -17363,6 +17406,7 @@ async function completeMainRelease(deps, ctx, meta, deployModel, watch, options,
|
|
|
17363
17406
|
const deployDispatch = await dispatchDeploy(deps, ctx, "main", "main", deployModel, watch, autoRunSince, releaseSha, "report", meta.publishDir);
|
|
17364
17407
|
const publishDispatch = deployDispatch.deployStatus === "success" ? await dispatchPublishIfRequired(deps, ctx, meta, deployModel, "main", tag, watch, "report") : null;
|
|
17365
17408
|
let dispatch = appendPublishDispatch(deployDispatch, publishDispatch);
|
|
17409
|
+
dispatch = await appendJervGatewayReleaseDeploy(deps, ctx.repo, tag, releaseSha, dispatch);
|
|
17366
17410
|
if (!publishDispatch && deployDispatch.deployStatus !== "success" && meta.publishRequired && (deployModel === "tenant-container" || deployModel === "solo-container")) {
|
|
17367
17411
|
const reason = deployDispatch.deployStatus === "failure" ? "box deploy failed \u2014 redeploy the box before publishing" : "box deploy not confirmed (run with --watch) \u2014 publish after the box deploy lands";
|
|
17368
17412
|
dispatch = {
|
|
@@ -17501,9 +17545,14 @@ async function runReleaseResume(deps, options = {}) {
|
|
|
17501
17545
|
const rcAlignment2 = !directTrack && branchHints.hasRcBranch ? await alignRcForward(deps, ctx, tag) : void 0;
|
|
17502
17546
|
if (rcAlignment2) steps2.push(`rc alignment: ${rcAlignment2.status}`);
|
|
17503
17547
|
const historicalDeploy = await deriveHistoricalDeployStatus(deps, ctx, deployModel, tagSha);
|
|
17548
|
+
const dispatch2 = await appendJervGatewayReleaseDeploy(deps, ctx.repo, tag, tagSha, {
|
|
17549
|
+
note: `historical ${deployModel} deploy/publish status: ${historicalDeploy}`,
|
|
17550
|
+
deployStatus: historicalDeploy
|
|
17551
|
+
});
|
|
17552
|
+
if (isJervHubRepo(ctx.repo)) steps2.push(dispatch2.note);
|
|
17504
17553
|
const state2 = resumeStateOf(deriveTrainFollowUpStatus({
|
|
17505
17554
|
projectInfo: "ok",
|
|
17506
|
-
deploy: deployFollowUpOutcome(
|
|
17555
|
+
deploy: deployFollowUpOutcome(dispatch2.deployStatus),
|
|
17507
17556
|
rcRetirement: "ok",
|
|
17508
17557
|
alignment: alignmentFollowUpOutcome(devRollForward2, rcAlignment2),
|
|
17509
17558
|
foldPort: "ok"
|
|
@@ -17516,6 +17565,7 @@ async function runReleaseResume(deps, options = {}) {
|
|
|
17516
17565
|
steps: steps2,
|
|
17517
17566
|
devRollForward: devRollForward2,
|
|
17518
17567
|
rcAlignment: rcAlignment2,
|
|
17568
|
+
dispatch: dispatch2,
|
|
17519
17569
|
note: state2 === "failed" ? `release ${tag} was already published, but its deploy/publish on the release SHA FAILED \u2014 re-run the failed job (do NOT re-tag); the protected alignment PRs were reused` : state2 === "pending" ? `release ${tag} was already published; reused its protected alignment PRs, but a deploy/publish or alignment follow-up is unresolved \u2014 watch it to completion` : `release ${tag} was already published; alignment is complete and the deploy on the release SHA is proven green`
|
|
17520
17570
|
};
|
|
17521
17571
|
}
|
|
@@ -17548,7 +17598,8 @@ async function runReleaseResume(deps, options = {}) {
|
|
|
17548
17598
|
const autoRunSince = (deps.now ?? Date.now)();
|
|
17549
17599
|
const deployDispatch = await dispatchDeploy(deps, ctx, "main", "main", deployModel, watch, autoRunSince, tagSha, "report", meta.publishDir);
|
|
17550
17600
|
const publishDispatch = deployDispatch.deployStatus === "success" ? await dispatchPublishIfRequired(deps, ctx, meta, deployModel, "main", tag, watch, "report") : null;
|
|
17551
|
-
|
|
17601
|
+
let dispatch = appendPublishDispatch(deployDispatch, publishDispatch);
|
|
17602
|
+
dispatch = await appendJervGatewayReleaseDeploy(deps, ctx.repo, tag, tagSha, dispatch);
|
|
17552
17603
|
steps.push(`dispatched the ${deployModel} deploy path`);
|
|
17553
17604
|
const devRollForward = await rollDevelopmentForward(deps, ctx, tag);
|
|
17554
17605
|
steps.push(`development roll-forward: ${devRollForward.status}`);
|
|
@@ -24450,6 +24501,9 @@ async function runHotfixRelease(deps, versionInput, options = {}) {
|
|
|
24450
24501
|
}
|
|
24451
24502
|
const required = await discoverRequiredCheckContexts(deps, ctx, "main");
|
|
24452
24503
|
assertTagAddressableRequiredContexts(deps, required, ctx.repo);
|
|
24504
|
+
if (deployModel === "hub-serverless") {
|
|
24505
|
+
await deps.run("node", ["scripts/release-distribution.mjs", "assert-release-lineage", version, "--release-commit", mergedSha]);
|
|
24506
|
+
}
|
|
24453
24507
|
const tagPush = await ensureTagPushed(deps, tag, mergedSha);
|
|
24454
24508
|
const tagPushSince = Date.now();
|
|
24455
24509
|
const tagNote = tagPush.note;
|
|
@@ -33658,6 +33712,27 @@ function selectSafeWorktreeCwd(worktrees, targetPath, options) {
|
|
|
33658
33712
|
const exists = options?.pathExists ?? (() => true);
|
|
33659
33713
|
return worktrees.find((w) => !samePath(w.path, targetPath) && exists(w.path))?.path;
|
|
33660
33714
|
}
|
|
33715
|
+
function formatGitCommandError(e) {
|
|
33716
|
+
if (!(e instanceof Error)) return String(e);
|
|
33717
|
+
const err = e;
|
|
33718
|
+
const stderr = typeof err.stderr === "string" ? err.stderr.trim() : "";
|
|
33719
|
+
const stdout = typeof err.stdout === "string" ? err.stdout.trim() : "";
|
|
33720
|
+
const message = err.message.trim();
|
|
33721
|
+
if (stderr) return stderr;
|
|
33722
|
+
if (message && !/^Command failed:/i.test(message)) return message;
|
|
33723
|
+
if (stdout) return stdout;
|
|
33724
|
+
return message || "git command failed";
|
|
33725
|
+
}
|
|
33726
|
+
function isWorktreePathRegistered(worktrees, wtPath) {
|
|
33727
|
+
return worktrees.some((w) => samePath(w.path, wtPath));
|
|
33728
|
+
}
|
|
33729
|
+
function isNotAWorkingTreeMessage(error) {
|
|
33730
|
+
return /is not a working tree/i.test(error);
|
|
33731
|
+
}
|
|
33732
|
+
function formatWorktreeRemovalFailureDetail(options) {
|
|
33733
|
+
const detail = options.removeError.trim() || "git worktree remove exited nonzero";
|
|
33734
|
+
return `${detail}; registered=${options.registered ? "yes" : "no"}; pathExists=${options.pathExists ? "yes" : "no"}; path=${options.wtPath}`;
|
|
33735
|
+
}
|
|
33661
33736
|
async function resolveMergeBaseTimestampMs(git3, baseRef, branch) {
|
|
33662
33737
|
try {
|
|
33663
33738
|
const mergeBase = (await git3(["merge-base", baseRef, branch])).trim();
|
|
@@ -33685,14 +33760,52 @@ async function teardownWorktreeStage(worktreePath) {
|
|
|
33685
33760
|
return { status: "failed", error: e instanceof Error ? e.message : String(e) };
|
|
33686
33761
|
}
|
|
33687
33762
|
}
|
|
33688
|
-
async function
|
|
33689
|
-
|
|
33690
|
-
|
|
33763
|
+
async function removeWorktreeWithReconcile(wtPath, git3, listWorktrees, pathExists) {
|
|
33764
|
+
const attemptRemove = async () => {
|
|
33765
|
+
try {
|
|
33766
|
+
await git3(["worktree", "remove", "--force", wtPath]);
|
|
33767
|
+
return void 0;
|
|
33768
|
+
} catch (e) {
|
|
33769
|
+
return formatGitCommandError(e);
|
|
33770
|
+
}
|
|
33771
|
+
};
|
|
33772
|
+
const firstError = await attemptRemove();
|
|
33773
|
+
if (!firstError) {
|
|
33691
33774
|
await git3(["worktree", "prune"]).catch(() => "");
|
|
33692
33775
|
return { status: "removed" };
|
|
33693
|
-
} catch (e) {
|
|
33694
|
-
return { status: "failed", error: e instanceof Error ? e.message : String(e) };
|
|
33695
33776
|
}
|
|
33777
|
+
let worktrees = await listWorktrees().catch(() => []);
|
|
33778
|
+
if (!isWorktreePathRegistered(worktrees, wtPath)) {
|
|
33779
|
+
await git3(["worktree", "prune"]).catch(() => "");
|
|
33780
|
+
return { status: "removed", reason: "reconciled-unregistered" };
|
|
33781
|
+
}
|
|
33782
|
+
const retryError = await attemptRemove();
|
|
33783
|
+
if (!retryError) {
|
|
33784
|
+
await git3(["worktree", "prune"]).catch(() => "");
|
|
33785
|
+
return { status: "removed", reason: "reconciled-after-retry" };
|
|
33786
|
+
}
|
|
33787
|
+
if (isNotAWorkingTreeMessage(retryError) || isNotAWorkingTreeMessage(firstError)) {
|
|
33788
|
+
worktrees = await listWorktrees().catch(() => worktrees);
|
|
33789
|
+
if (!isWorktreePathRegistered(worktrees, wtPath)) {
|
|
33790
|
+
await git3(["worktree", "prune"]).catch(() => "");
|
|
33791
|
+
return { status: "removed", reason: "reconciled-not-a-working-tree" };
|
|
33792
|
+
}
|
|
33793
|
+
}
|
|
33794
|
+
worktrees = await listWorktrees().catch(() => worktrees);
|
|
33795
|
+
const registered = isWorktreePathRegistered(worktrees, wtPath);
|
|
33796
|
+
if (!registered) {
|
|
33797
|
+
await git3(["worktree", "prune"]).catch(() => "");
|
|
33798
|
+
return { status: "removed", reason: "reconciled-unregistered" };
|
|
33799
|
+
}
|
|
33800
|
+
return {
|
|
33801
|
+
status: "failed",
|
|
33802
|
+
error: formatWorktreeRemovalFailureDetail({
|
|
33803
|
+
removeError: retryError || firstError,
|
|
33804
|
+
registered,
|
|
33805
|
+
pathExists: pathExists(wtPath),
|
|
33806
|
+
wtPath
|
|
33807
|
+
})
|
|
33808
|
+
};
|
|
33696
33809
|
}
|
|
33697
33810
|
async function cleanupPrMergeLocalBranch(branch, options) {
|
|
33698
33811
|
const report = { branch };
|
|
@@ -33797,7 +33910,12 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
33797
33910
|
report.localBranch = { name: branch, status: "not-attempted", reason: "archive-failed" };
|
|
33798
33911
|
return report;
|
|
33799
33912
|
}
|
|
33800
|
-
const removal = await
|
|
33913
|
+
const removal = await removeWorktreeWithReconcile(
|
|
33914
|
+
wtPath,
|
|
33915
|
+
git3,
|
|
33916
|
+
async () => parseGitWorktreePorcelain(await execGit(["worktree", "list", "--porcelain"])),
|
|
33917
|
+
pathExists
|
|
33918
|
+
);
|
|
33801
33919
|
if (removal.status === "failed") {
|
|
33802
33920
|
report.worktree = {
|
|
33803
33921
|
path: wtPath,
|
|
@@ -33813,6 +33931,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
33813
33931
|
report.worktree = {
|
|
33814
33932
|
path: wtPath,
|
|
33815
33933
|
status: "removed",
|
|
33934
|
+
...removal.reason ? { reason: removal.reason } : {},
|
|
33816
33935
|
artifactsArchive,
|
|
33817
33936
|
stageTeardown,
|
|
33818
33937
|
tmpEvidenceCount: tmpEvidence.length
|
|
@@ -33831,7 +33950,11 @@ function renderPrMergeCleanupLines(cleanup) {
|
|
|
33831
33950
|
if (!wt?.path) return [];
|
|
33832
33951
|
const lines = [];
|
|
33833
33952
|
if (wt.status === "removed") {
|
|
33834
|
-
|
|
33953
|
+
if (wt.reason?.startsWith("reconciled")) {
|
|
33954
|
+
lines.push(`pr merge: worktree ${wt.path} remove reported failure but git no longer registers it \u2014 deleted local branch`);
|
|
33955
|
+
} else {
|
|
33956
|
+
lines.push(`pr merge: removed worktree ${wt.path} \u2014 that directory is gone; cd elsewhere before your next command`);
|
|
33957
|
+
}
|
|
33835
33958
|
} else if (wt.status === "refused") {
|
|
33836
33959
|
lines.push(`pr merge: worktree ${wt.path} kept \u2014 ${wt.reason ?? "refused"}${wt.error ? `: ${wt.error}` : ""}`);
|
|
33837
33960
|
if (wt.tmpEvidenceCount) lines.push(`pr merge: ${wt.tmpEvidenceCount} tmp path(s) newer than branch base would have been destroyed`);
|
|
@@ -43087,10 +43210,11 @@ var GH_RUN_WATCH_TIMEOUT_MS = 20 * 6e4;
|
|
|
43087
43210
|
var NODE_PREPARE_TIMEOUT_MS = 10 * 6e4;
|
|
43088
43211
|
var NODE_VERIFY_TIMEOUT_MS = 5 * 6e4;
|
|
43089
43212
|
var NPM_TRAIN_TIMEOUT_MS = 6e4;
|
|
43213
|
+
var JERV_GATEWAY_DEPLOY_TIMEOUT_MS = 20 * 6e4;
|
|
43090
43214
|
function trainApplyDeps() {
|
|
43091
43215
|
return {
|
|
43092
43216
|
run: async (file, args) => {
|
|
43093
|
-
const timeout = file === "node" && args[1] === "prepare" ? NODE_PREPARE_TIMEOUT_MS : file === "node" && args[1] === "verify" ? NODE_VERIFY_TIMEOUT_MS : file === "npm" ? NPM_TRAIN_TIMEOUT_MS : file !== "gh" ? GIT_TIMEOUT_MS : args[0] === "run" && args[1] === "watch" ? GH_RUN_WATCH_TIMEOUT_MS : GH_TRAIN_TIMEOUT_MS;
|
|
43217
|
+
const timeout = file === "node" && args[1] === "prepare" ? NODE_PREPARE_TIMEOUT_MS : file === "node" && args[1] === "verify" ? NODE_VERIFY_TIMEOUT_MS : file === "npm" ? NPM_TRAIN_TIMEOUT_MS : file === "bash" && args[0] === "scripts/jerv-gateway-release-deploy.sh" ? JERV_GATEWAY_DEPLOY_TIMEOUT_MS : file !== "gh" ? GIT_TIMEOUT_MS : args[0] === "run" && args[1] === "watch" ? GH_RUN_WATCH_TIMEOUT_MS : GH_TRAIN_TIMEOUT_MS;
|
|
43094
43218
|
try {
|
|
43095
43219
|
return isWin2 && file === "npm" ? (await execFileP2("cmd.exe", ["/c", "npm", ...args], { timeout })).stdout : (await execFileP2(file, args, { timeout })).stdout;
|
|
43096
43220
|
} catch (e) {
|
package/package.json
CHANGED