@mutmutco/cli 4.4.16 → 4.4.17
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 +335 -55
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -5436,7 +5436,7 @@ function resolveVerbAliasShim(argv) {
|
|
|
5436
5436
|
}
|
|
5437
5437
|
|
|
5438
5438
|
// src/command-composition.ts
|
|
5439
|
-
var
|
|
5439
|
+
var import_node_os22 = require("node:os");
|
|
5440
5440
|
var import_node_path47 = require("node:path");
|
|
5441
5441
|
|
|
5442
5442
|
// src/board-read.ts
|
|
@@ -15938,10 +15938,10 @@ var rollout_plan_default = {
|
|
|
15938
15938
|
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)."
|
|
15939
15939
|
},
|
|
15940
15940
|
baseline: {
|
|
15941
|
-
version: "4.4.
|
|
15942
|
-
tag: "v4.4.
|
|
15943
|
-
commit: "
|
|
15944
|
-
npm: "@mutmutco/cli@4.4.
|
|
15941
|
+
version: "4.4.17",
|
|
15942
|
+
tag: "v4.4.17",
|
|
15943
|
+
commit: "748489250635",
|
|
15944
|
+
npm: "@mutmutco/cli@4.4.17"
|
|
15945
15945
|
},
|
|
15946
15946
|
exitCriterion: "fleet-n-of-n",
|
|
15947
15947
|
hubOnlyShortcut: "forbidden",
|
|
@@ -15958,14 +15958,14 @@ var rollout_plan_default = {
|
|
|
15958
15958
|
repo: "mutmutco/mmi-hub",
|
|
15959
15959
|
role: "canary",
|
|
15960
15960
|
schedule: "train",
|
|
15961
|
-
v3Target: "v4.4.
|
|
15961
|
+
v3Target: "v4.4.17"
|
|
15962
15962
|
}
|
|
15963
15963
|
],
|
|
15964
15964
|
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.",
|
|
15965
15965
|
rollback: {
|
|
15966
15966
|
independent: true,
|
|
15967
|
-
mechanism: "npm dist-tag latest -> 4.4.
|
|
15968
|
-
v3Target: "v4.4.
|
|
15967
|
+
mechanism: "npm dist-tag latest -> 4.4.17 and redeploy the Hub Lambda from tag v4.4.17 (748489250635); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
15968
|
+
v3Target: "v4.4.17 (@mutmutco/cli@4.4.17, tag commit 748489250635 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
15969
15969
|
}
|
|
15970
15970
|
},
|
|
15971
15971
|
{
|
|
@@ -18803,6 +18803,7 @@ function isReleaseShaDeployEnumerationCandidate(row, nonDeployEvents) {
|
|
|
18803
18803
|
|
|
18804
18804
|
// src/train-apply-deploy.ts
|
|
18805
18805
|
var import_node_fs19 = require("node:fs");
|
|
18806
|
+
var import_node_os10 = require("node:os");
|
|
18806
18807
|
var import_node_path17 = require("node:path");
|
|
18807
18808
|
|
|
18808
18809
|
// src/vercel-deployments.ts
|
|
@@ -19082,6 +19083,24 @@ async function appendServedChannelPublish(deps, input, dispatch) {
|
|
|
19082
19083
|
};
|
|
19083
19084
|
}
|
|
19084
19085
|
var JERV_GATEWAY_RUN_URL_NOTE = "no run URL emitted \u2014 host health leg runs outside GitHub Actions";
|
|
19086
|
+
var JERV_GATEWAY_RELEASE_DEPLOY_SCRIPT = "scripts/jerv-gateway-release-deploy.sh";
|
|
19087
|
+
async function runTaggedJervGatewayLocalWrapper(deps, tag, tagSha) {
|
|
19088
|
+
let script;
|
|
19089
|
+
try {
|
|
19090
|
+
script = await deps.run("git", ["show", `${tagSha}:${JERV_GATEWAY_RELEASE_DEPLOY_SCRIPT}`]);
|
|
19091
|
+
} catch (error) {
|
|
19092
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
19093
|
+
throw new Error(`could not extract ${JERV_GATEWAY_RELEASE_DEPLOY_SCRIPT} from tagged commit ${tagSha.slice(0, 12)}: ${reason}`);
|
|
19094
|
+
}
|
|
19095
|
+
const dir = (0, import_node_fs19.mkdtempSync)((0, import_node_path17.join)((0, import_node_os10.tmpdir)(), "jerv-gateway-release-deploy-"));
|
|
19096
|
+
const extracted = (0, import_node_path17.join)(dir, "jerv-gateway-release-deploy.sh");
|
|
19097
|
+
try {
|
|
19098
|
+
(0, import_node_fs19.writeFileSync)(extracted, script, "utf8");
|
|
19099
|
+
return await deps.run("bash", [extracted, "--tag", tag]);
|
|
19100
|
+
} finally {
|
|
19101
|
+
(0, import_node_fs19.rmSync)(dir, { recursive: true, force: true });
|
|
19102
|
+
}
|
|
19103
|
+
}
|
|
19085
19104
|
async function appendJervGatewayReleaseDeploy(deps, repo, tag, tagSha, dispatch) {
|
|
19086
19105
|
if (!isJervHubRepo(repo)) return dispatch;
|
|
19087
19106
|
if (dispatch.deployStatus !== "success") {
|
|
@@ -19092,7 +19111,7 @@ async function appendJervGatewayReleaseDeploy(deps, repo, tag, tagSha, dispatch)
|
|
|
19092
19111
|
};
|
|
19093
19112
|
}
|
|
19094
19113
|
try {
|
|
19095
|
-
const output = await deps
|
|
19114
|
+
const output = await runTaggedJervGatewayLocalWrapper(deps, tag, tagSha);
|
|
19096
19115
|
const receipt = output.trim().split(/\r?\n/).reverse().map((line) => {
|
|
19097
19116
|
try {
|
|
19098
19117
|
return JSON.parse(line);
|
|
@@ -19110,11 +19129,12 @@ async function appendJervGatewayReleaseDeploy(deps, repo, tag, tagSha, dispatch)
|
|
|
19110
19129
|
deployStatus: "success"
|
|
19111
19130
|
};
|
|
19112
19131
|
} catch (error) {
|
|
19113
|
-
const
|
|
19132
|
+
const full = error instanceof Error ? error.message : String(error);
|
|
19114
19133
|
return {
|
|
19115
19134
|
...dispatch,
|
|
19116
19135
|
// #6095: the helper's receipt, health or rollback failed after a proven publish — the release resumes, never re-tags.
|
|
19117
|
-
|
|
19136
|
+
// #6985: keep the FULL failing output in the note so the ledger can persist its tail (the first-line 240-char clip hid the real cause).
|
|
19137
|
+
note: `${dispatch.note}; Jerv Gateway deploy FAILED (${full}) \u2014 see docs/Guides/train-troubleshooting.md#jerv-gateway-deploy`,
|
|
19118
19138
|
workflowRuns: [...dispatch.workflowRuns ?? [], { workflow: "jerv-gateway", runUrlNote: JERV_GATEWAY_RUN_URL_NOTE, conclusion: "failure" }],
|
|
19119
19139
|
deployStatus: "failure"
|
|
19120
19140
|
};
|
|
@@ -19851,12 +19871,12 @@ Resume it: ${resumeCommand}
|
|
|
19851
19871
|
// src/train-doctor.ts
|
|
19852
19872
|
var import_node_child_process9 = require("node:child_process");
|
|
19853
19873
|
var import_node_fs23 = require("node:fs");
|
|
19854
|
-
var
|
|
19874
|
+
var import_node_os14 = require("node:os");
|
|
19855
19875
|
var import_node_path23 = require("node:path");
|
|
19856
19876
|
|
|
19857
19877
|
// src/doctor-io.ts
|
|
19858
19878
|
var import_node_fs20 = require("node:fs");
|
|
19859
|
-
var
|
|
19879
|
+
var import_node_os11 = require("node:os");
|
|
19860
19880
|
var import_node_path18 = require("node:path");
|
|
19861
19881
|
var import_node_child_process7 = require("node:child_process");
|
|
19862
19882
|
var import_node_util4 = require("node:util");
|
|
@@ -19886,7 +19906,7 @@ function execFileCapture(file, args, opts = {}) {
|
|
|
19886
19906
|
var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
19887
19907
|
function stagedClaudePluginVersion() {
|
|
19888
19908
|
try {
|
|
19889
|
-
const stagingRoot = (0, import_node_path18.join)((0,
|
|
19909
|
+
const stagingRoot = (0, import_node_path18.join)((0, import_node_os11.homedir)(), ".claude", "plugins", "staging");
|
|
19890
19910
|
const record = JSON.parse((0, import_node_fs20.readFileSync)((0, import_node_path18.join)(stagingRoot, "launch-target.json"), "utf8"));
|
|
19891
19911
|
if (typeof record.target !== "string" || typeof record.incoming !== "string") return void 0;
|
|
19892
19912
|
const manifest = JSON.parse((0, import_node_fs20.readFileSync)((0, import_node_path18.join)(record.incoming, ".claude-plugin", "plugin.json"), "utf8"));
|
|
@@ -19899,7 +19919,7 @@ function stagedClaudePluginVersion() {
|
|
|
19899
19919
|
function installedClaudePluginVersion() {
|
|
19900
19920
|
try {
|
|
19901
19921
|
const file = JSON.parse(
|
|
19902
|
-
(0, import_node_fs20.readFileSync)((0, import_node_path18.join)((0,
|
|
19922
|
+
(0, import_node_fs20.readFileSync)((0, import_node_path18.join)((0, import_node_os11.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
19903
19923
|
);
|
|
19904
19924
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
19905
19925
|
if (versions.length === 0) return void 0;
|
|
@@ -20071,11 +20091,11 @@ function compatHolds(range, version) {
|
|
|
20071
20091
|
|
|
20072
20092
|
// src/claude-binary-doctor.ts
|
|
20073
20093
|
var import_node_fs21 = require("node:fs");
|
|
20074
|
-
var
|
|
20094
|
+
var import_node_os13 = require("node:os");
|
|
20075
20095
|
var import_node_path20 = require("node:path");
|
|
20076
20096
|
|
|
20077
20097
|
// src/jerv-cli-spawn.ts
|
|
20078
|
-
var
|
|
20098
|
+
var import_node_os12 = require("node:os");
|
|
20079
20099
|
var import_node_path19 = require("node:path");
|
|
20080
20100
|
init_cli_shared();
|
|
20081
20101
|
var JERV_CLI_ENTRY = (0, import_node_path19.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
|
|
@@ -20100,7 +20120,7 @@ function normalizeSpawnPathEntry(entry, platform2 = process.platform) {
|
|
|
20100
20120
|
if (msys) return `${msys[1].toUpperCase()}:\\${msys[2].replace(/\//g, "\\")}`;
|
|
20101
20121
|
return trimmed;
|
|
20102
20122
|
}
|
|
20103
|
-
function jervCliCandidateDirs(env = process.env, home = (0,
|
|
20123
|
+
function jervCliCandidateDirs(env = process.env, home = (0, import_node_os12.homedir)(), platform2 = process.platform) {
|
|
20104
20124
|
const seen = /* @__PURE__ */ new Set();
|
|
20105
20125
|
const out = [];
|
|
20106
20126
|
const push = (dir) => {
|
|
@@ -20171,7 +20191,7 @@ function globalNodeModulesRoots(host) {
|
|
|
20171
20191
|
};
|
|
20172
20192
|
const prefix = env.npm_config_prefix?.trim();
|
|
20173
20193
|
if (prefix) push(platform2 === "win32" ? (0, import_node_path20.join)(prefix, "node_modules") : (0, import_node_path20.join)(prefix, "lib", "node_modules"));
|
|
20174
|
-
for (const dir of jervCliCandidateDirs(env, host.home ?? (0,
|
|
20194
|
+
for (const dir of jervCliCandidateDirs(env, host.home ?? (0, import_node_os13.homedir)(), platform2)) {
|
|
20175
20195
|
push((0, import_node_path20.join)(dir, "node_modules"));
|
|
20176
20196
|
push((0, import_node_path20.join)((0, import_node_path20.dirname)(dir), "lib", "node_modules"));
|
|
20177
20197
|
}
|
|
@@ -20818,6 +20838,53 @@ var MAX_TEXT = 500;
|
|
|
20818
20838
|
function truncate(text, max) {
|
|
20819
20839
|
return text.length <= max ? text : `${text.slice(0, max - 1)}\u2026`;
|
|
20820
20840
|
}
|
|
20841
|
+
var FAILING_OUTPUT_LEGS = /* @__PURE__ */ new Set(["publish", "deploy", "alignment"]);
|
|
20842
|
+
var LEDGER_OUTPUT_FILE_LINES = 50;
|
|
20843
|
+
var LEDGER_OUTPUT_NOTE_LINES = 5;
|
|
20844
|
+
function lastNonEmptyLines(text, n) {
|
|
20845
|
+
const lines2 = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
20846
|
+
while (lines2.length > 0 && lines2[lines2.length - 1] === "") lines2.pop();
|
|
20847
|
+
return lines2.slice(-n);
|
|
20848
|
+
}
|
|
20849
|
+
function attachOutputNote(note, suffix) {
|
|
20850
|
+
const room = Math.max(0, MAX_TEXT - suffix.length - 3);
|
|
20851
|
+
const head = note && room > 20 ? truncate(note, room) : "";
|
|
20852
|
+
return truncate(head ? `${head} \u2014 ${suffix}` : suffix, MAX_TEXT);
|
|
20853
|
+
}
|
|
20854
|
+
function writeFailingLegOutput(artifactPath, output) {
|
|
20855
|
+
const fileLines = lastNonEmptyLines(output, LEDGER_OUTPUT_FILE_LINES);
|
|
20856
|
+
(0, import_node_fs22.mkdirSync)((0, import_node_path22.dirname)(artifactPath), { recursive: true });
|
|
20857
|
+
(0, import_node_fs22.writeFileSync)(artifactPath, `${fileLines.join("\n")}
|
|
20858
|
+
`, "utf8");
|
|
20859
|
+
const tail = fileLines.slice(-LEDGER_OUTPUT_NOTE_LINES).join("\n");
|
|
20860
|
+
const noteSuffix = truncate(`failing-leg output: ${artifactPath}
|
|
20861
|
+
${tail}`, MAX_TEXT);
|
|
20862
|
+
return { artifactPath, noteSuffix };
|
|
20863
|
+
}
|
|
20864
|
+
function stampPhaseRecord(ledgerPath, tag, phase, entry, previousRetries, now) {
|
|
20865
|
+
const { output: failingOutput, ...fields } = entry;
|
|
20866
|
+
let note = fields.note;
|
|
20867
|
+
if (fields.state === "failed" && FAILING_OUTPUT_LEGS.has(phase)) {
|
|
20868
|
+
const raw = failingOutput ?? fields.error ?? fields.note;
|
|
20869
|
+
if (raw) {
|
|
20870
|
+
try {
|
|
20871
|
+
const persisted = writeFailingLegOutput(
|
|
20872
|
+
(0, import_node_path22.join)((0, import_node_path22.dirname)((0, import_node_path22.dirname)(ledgerPath)), "ledger-output", `${tag}-${phase}.txt`),
|
|
20873
|
+
raw
|
|
20874
|
+
);
|
|
20875
|
+
note = attachOutputNote(note, persisted.noteSuffix);
|
|
20876
|
+
} catch {
|
|
20877
|
+
}
|
|
20878
|
+
}
|
|
20879
|
+
}
|
|
20880
|
+
return {
|
|
20881
|
+
...fields,
|
|
20882
|
+
note: note !== void 0 ? truncate(note, MAX_TEXT) : void 0,
|
|
20883
|
+
error: fields.error !== void 0 ? truncate(fields.error, MAX_TEXT * 2) : void 0,
|
|
20884
|
+
retries: fields.retries ?? previousRetries,
|
|
20885
|
+
updatedAt: now().toISOString()
|
|
20886
|
+
};
|
|
20887
|
+
}
|
|
20821
20888
|
function normalizeLedgerAnchors(anchors) {
|
|
20822
20889
|
const repo = anchors.repo?.trim();
|
|
20823
20890
|
const tag = anchors.tag?.trim();
|
|
@@ -21029,13 +21096,7 @@ function openReleaseLedgerWriter(cwd, opts = {}) {
|
|
|
21029
21096
|
updatedAt: now().toISOString()
|
|
21030
21097
|
};
|
|
21031
21098
|
const previousRetries = base.phases[phase].retries ?? 0;
|
|
21032
|
-
const record =
|
|
21033
|
-
...entry,
|
|
21034
|
-
note: entry.note !== void 0 ? truncate(entry.note, MAX_TEXT) : void 0,
|
|
21035
|
-
error: entry.error !== void 0 ? truncate(entry.error, MAX_TEXT * 2) : void 0,
|
|
21036
|
-
retries: entry.retries ?? previousRetries,
|
|
21037
|
-
updatedAt: now().toISOString()
|
|
21038
|
-
};
|
|
21099
|
+
const record = stampPhaseRecord(path2, normalized.tag, phase, entry, previousRetries, now);
|
|
21039
21100
|
const updated = {
|
|
21040
21101
|
...base,
|
|
21041
21102
|
phases: { ...base.phases, [phase]: record },
|
|
@@ -21707,6 +21768,48 @@ function ledgerLaneResumeCommand(prior) {
|
|
|
21707
21768
|
if (prior.lane === "hotfix") return `mmi-cli devops hotfix release ${prior.tag}`;
|
|
21708
21769
|
return "mmi-cli devops release --resume (or, only when proven unpublished, mmi-cli devops release --abort --apply)";
|
|
21709
21770
|
}
|
|
21771
|
+
var RETRY_DEPLOY_LEGS = /* @__PURE__ */ new Set(["deploy", "alignment"]);
|
|
21772
|
+
function joinLegNames(legs) {
|
|
21773
|
+
if (legs.length <= 1) return legs[0] ?? "";
|
|
21774
|
+
if (legs.length === 2) return `${legs[0]} and ${legs[1]}`;
|
|
21775
|
+
return `${legs.slice(0, -1).join(", ")} and ${legs[legs.length - 1]}`;
|
|
21776
|
+
}
|
|
21777
|
+
function formatFailedLegTails(ledger) {
|
|
21778
|
+
const chunks = [];
|
|
21779
|
+
for (const phase of ["publish", "deploy", "alignment"]) {
|
|
21780
|
+
const rec = ledger.phases[phase];
|
|
21781
|
+
if (rec.state !== "failed") continue;
|
|
21782
|
+
const text = rec.note ?? rec.error ?? "";
|
|
21783
|
+
const marker = "failing-leg output: ";
|
|
21784
|
+
const idx = text.indexOf(marker);
|
|
21785
|
+
if (idx < 0) continue;
|
|
21786
|
+
chunks.push(`${phase} ${text.slice(idx)}`);
|
|
21787
|
+
}
|
|
21788
|
+
return chunks.length ? `; ${chunks.join("; ")}` : "";
|
|
21789
|
+
}
|
|
21790
|
+
function hotfixRetryDeployShape(ledger) {
|
|
21791
|
+
if (ledger.lane !== "hotfix") return false;
|
|
21792
|
+
if (ledger.phases.githubRelease.state !== "complete") return false;
|
|
21793
|
+
const open2 = RELEASE_LEDGER_PHASES.filter((phase) => {
|
|
21794
|
+
const state = ledger.phases[phase].state;
|
|
21795
|
+
return state === "pending" || state === "failed";
|
|
21796
|
+
});
|
|
21797
|
+
if (open2.length === 0) return false;
|
|
21798
|
+
if (!open2.every((phase) => RETRY_DEPLOY_LEGS.has(phase))) return false;
|
|
21799
|
+
return open2.some((phase) => ledger.phases[phase].state === "failed");
|
|
21800
|
+
}
|
|
21801
|
+
function ledgerFailedRemedy(ledger) {
|
|
21802
|
+
const derived = deriveReleasePhaseStatus(ledger);
|
|
21803
|
+
const openLegs = derived.legs.filter((l) => l.state === "pending" || l.state === "failed");
|
|
21804
|
+
const tails = formatFailedLegTails(ledger);
|
|
21805
|
+
if (hotfixRetryDeployShape(ledger)) {
|
|
21806
|
+
const failed = openLegs.filter((l) => l.state === "failed").map((l) => l.phase);
|
|
21807
|
+
return `the published ${ledger.tag} hotfix can retry the failed ${joinLegNames(failed)} of the same tag: \`mmi-cli devops hotfix release ${ledger.tag} --retry-deploy\` (the deploy helper is idempotent; alignment re-attempts the fold-port PR) (#6985)${tails}`;
|
|
21808
|
+
}
|
|
21809
|
+
const deployOnlyDeadlock = openLegs.length > 0 && openLegs.every((l) => l.phase === "deploy" && supersedeableDeployLegShape(l));
|
|
21810
|
+
const supersession = deployOnlyDeadlock ? `; when the deploy failure is content-caused on the already-published ${ledger.tag} (the immutable tag can never deploy green), the sanctioned exit is \`mmi-cli devops release --supersede-deploy --apply\` after explicit master approval \u2014 the next patch train carries the cause fix (#6553)` : "";
|
|
21811
|
+
return `diagnose the failed leg, then ${ledgerLaneResumeCommand(ledger)} once its cause is fixed (an abort only with fresh approval)${supersession}${tails}`;
|
|
21812
|
+
}
|
|
21710
21813
|
function alignmentPrNumbers(note) {
|
|
21711
21814
|
return [...new Set([...(note ?? "").matchAll(/\/pull\/(\d+)/g)].map((m) => Number.parseInt(m[1], 10)))];
|
|
21712
21815
|
}
|
|
@@ -22098,9 +22201,10 @@ async function beginReleaseLedger(deps, cwd, targetTag) {
|
|
|
22098
22201
|
}
|
|
22099
22202
|
if (derived.phaseStatus !== "phases-green") {
|
|
22100
22203
|
const open2 = derived.legs.filter((l) => l.state === "pending" || l.state === "failed");
|
|
22204
|
+
const next = derived.phaseStatus === "phases-failed" ? ledgerFailedRemedy(prior) : `Finish it first: ${ledgerLaneResumeCommand(prior)} (#5987/#6068)`;
|
|
22101
22205
|
return {
|
|
22102
22206
|
ok: false,
|
|
22103
|
-
error: `a prior ${prior.lane ?? "release"} ledger for ${prior.tag} is ${derived.phaseStatus} (${open2.map((l) => `${l.phase}=${l.state}`).join(", ")}) \u2014 a new train run must not stack on an unresolved one.
|
|
22207
|
+
error: `a prior ${prior.lane ?? "release"} ledger for ${prior.tag} is ${derived.phaseStatus} (${open2.map((l) => `${l.phase}=${l.state}`).join(", ")}) \u2014 a new train run must not stack on an unresolved one. ${next}`
|
|
22104
22208
|
};
|
|
22105
22209
|
}
|
|
22106
22210
|
let liveSha = "";
|
|
@@ -22124,6 +22228,124 @@ async function beginReleaseLedger(deps, cwd, targetTag) {
|
|
|
22124
22228
|
return { ok: true, note: `archived the completed release ledger ${prior.tag} to ${archivePath} (live-proven at ${prior.tagSha.slice(0, 12)})` };
|
|
22125
22229
|
});
|
|
22126
22230
|
}
|
|
22231
|
+
async function defaultRetryDeploy(deps, prior) {
|
|
22232
|
+
const input = {
|
|
22233
|
+
note: `hotfix --retry-deploy ${prior.tag}`,
|
|
22234
|
+
deployStatus: "success",
|
|
22235
|
+
workflowRuns: []
|
|
22236
|
+
};
|
|
22237
|
+
const dispatch = await appendJervGatewayReleaseDeploy(deps, prior.repo, prior.tag, prior.tagSha, input);
|
|
22238
|
+
const host = (dispatch.workflowRuns ?? []).find((r) => r.workflow === "jerv-gateway");
|
|
22239
|
+
if (!host) {
|
|
22240
|
+
throw new Error(
|
|
22241
|
+
`hotfix --retry-deploy: the ${prior.tag} deploy helper was not invoked (not the operator-host Gateway) \u2014 nothing was written (#6985)`
|
|
22242
|
+
);
|
|
22243
|
+
}
|
|
22244
|
+
const entry = deployPhaseInput("registry-publish", dispatch, { releaseSha: prior.tagSha });
|
|
22245
|
+
if (entry.state === "failed") {
|
|
22246
|
+
return { ...entry, error: entry.note ?? dispatch.note, output: dispatch.note };
|
|
22247
|
+
}
|
|
22248
|
+
return entry;
|
|
22249
|
+
}
|
|
22250
|
+
async function retryHotfixDeployLegs(deps, cwd, opts = {}) {
|
|
22251
|
+
const path2 = releaseLedgerPath(cwd);
|
|
22252
|
+
return withFileLock(`${path2}.lock`, { label: "release ledger" }, async () => {
|
|
22253
|
+
let prior;
|
|
22254
|
+
try {
|
|
22255
|
+
prior = parseReleaseLedger((0, import_node_fs22.readFileSync)(path2, "utf8"));
|
|
22256
|
+
} catch (e) {
|
|
22257
|
+
throw new ReleaseLedgerError(
|
|
22258
|
+
"unreadable",
|
|
22259
|
+
`hotfix --retry-deploy: the active release ledger at ${path2} could not be read (${e instanceof Error ? e.message : String(e)}) \u2014 resolve it by hand; nothing was written (#6985)`
|
|
22260
|
+
);
|
|
22261
|
+
}
|
|
22262
|
+
if (prior.lane !== "hotfix") {
|
|
22263
|
+
throw new Error(
|
|
22264
|
+
`hotfix --retry-deploy: the active ledger is a ${prior.lane ?? "release"} lane ledger for ${prior.tag} \u2014 --retry-deploy is the hotfix lane's recovery for a published hotfix; nothing was written (#6985)`
|
|
22265
|
+
);
|
|
22266
|
+
}
|
|
22267
|
+
if (prior.phases.githubRelease.state !== "complete") {
|
|
22268
|
+
throw new Error(
|
|
22269
|
+
`hotfix --retry-deploy: ${prior.tag} is not a published hotfix (githubRelease=${prior.phases.githubRelease.state}) \u2014 --retry-deploy re-runs deploy/alignment of a PUBLISHED tag; nothing was written (#6985)`
|
|
22270
|
+
);
|
|
22271
|
+
}
|
|
22272
|
+
if (!hotfixRetryDeployShape(prior)) {
|
|
22273
|
+
const derived = deriveReleasePhaseStatus(prior);
|
|
22274
|
+
const open2 = derived.legs.filter((l) => l.state === "pending" || l.state === "failed");
|
|
22275
|
+
const extra = open2.filter((l) => l.phase !== "deploy" && l.phase !== "alignment");
|
|
22276
|
+
if (extra.length > 0) {
|
|
22277
|
+
throw new Error(
|
|
22278
|
+
`hotfix --retry-deploy: ${prior.tag} has unresolved legs besides deploy/alignment (${extra.map((l) => `${l.phase}=${l.state}`).join(", ")}) \u2014 it re-runs only the failed deploy and/or fold-port alignment of a published hotfix of the same tag; nothing was written (#6985)`
|
|
22279
|
+
);
|
|
22280
|
+
}
|
|
22281
|
+
throw new Error(
|
|
22282
|
+
`hotfix --retry-deploy: ${prior.tag} has no failed deploy or alignment leg to retry (${open2.map((l) => `${l.phase}=${l.state}`).join(", ") || "phases-green"}) \u2014 nothing was written (#6985)`
|
|
22283
|
+
);
|
|
22284
|
+
}
|
|
22285
|
+
if (prior.phases.alignment.state === "failed" && !opts.retryAlignment) {
|
|
22286
|
+
throw new Error(
|
|
22287
|
+
`hotfix --retry-deploy: the ${prior.tag} alignment (fold-port) leg is failed \u2014 nothing was written (#6985)`
|
|
22288
|
+
);
|
|
22289
|
+
}
|
|
22290
|
+
if (prior.phases.deploy.state === "failed" && !isJervHubRepo(prior.repo) && prior.phases.deploy.workflow !== "jerv-gateway") {
|
|
22291
|
+
throw new Error(
|
|
22292
|
+
`hotfix --retry-deploy: the ${prior.tag} deploy leg is ${prior.phases.deploy.workflow ?? "not the operator-host helper"} \u2014 diagnose that run; nothing was written (#6985)`
|
|
22293
|
+
);
|
|
22294
|
+
}
|
|
22295
|
+
let liveSha = "";
|
|
22296
|
+
try {
|
|
22297
|
+
liveSha = (await deps.run("git", ["ls-remote", "origin", `refs/tags/${prior.tag}`])).trim().split(/\s+/)[0] ?? "";
|
|
22298
|
+
} catch (e) {
|
|
22299
|
+
throw new Error(
|
|
22300
|
+
`hotfix --retry-deploy: could not prove ${prior.tag} live on origin (${e instanceof Error ? e.message : String(e)}) \u2014 refusing to retry on an unproven state; retry when origin answers (#6985)`
|
|
22301
|
+
);
|
|
22302
|
+
}
|
|
22303
|
+
if (liveSha.toLowerCase() !== prior.tagSha.toLowerCase()) {
|
|
22304
|
+
throw new Error(
|
|
22305
|
+
`hotfix --retry-deploy: origin reports ${prior.tag} at ${liveSha.slice(0, 12) || "no tag"} but the ledger anchors ${prior.tagSha.slice(0, 12)} \u2014 an absent or divergent tag is not retryable; nothing was written (#6985)`
|
|
22306
|
+
);
|
|
22307
|
+
}
|
|
22308
|
+
const retried = [];
|
|
22309
|
+
const phases = { ...prior.phases };
|
|
22310
|
+
const now = () => /* @__PURE__ */ new Date();
|
|
22311
|
+
if (phases.deploy.state === "failed") {
|
|
22312
|
+
retried.push("deploy");
|
|
22313
|
+
const entry = opts.retryDeploy ? await opts.retryDeploy(prior) : await defaultRetryDeploy(deps, prior);
|
|
22314
|
+
phases.deploy = stampPhaseRecord(path2, prior.tag, "deploy", entry, phases.deploy.retries ?? 0, now);
|
|
22315
|
+
}
|
|
22316
|
+
if (phases.alignment.state === "failed") {
|
|
22317
|
+
retried.push("alignment");
|
|
22318
|
+
try {
|
|
22319
|
+
const entry = await opts.retryAlignment(prior);
|
|
22320
|
+
phases.alignment = stampPhaseRecord(path2, prior.tag, "alignment", entry, phases.alignment.retries ?? 0, now);
|
|
22321
|
+
} catch (e) {
|
|
22322
|
+
const message2 = e instanceof Error ? e.message : String(e);
|
|
22323
|
+
phases.alignment = stampPhaseRecord(
|
|
22324
|
+
path2,
|
|
22325
|
+
prior.tag,
|
|
22326
|
+
"alignment",
|
|
22327
|
+
{ state: "failed", sha: prior.tagSha, error: message2, output: message2 },
|
|
22328
|
+
phases.alignment.retries ?? 0,
|
|
22329
|
+
now
|
|
22330
|
+
);
|
|
22331
|
+
}
|
|
22332
|
+
}
|
|
22333
|
+
atomicWriteJson(path2, { ...prior, phases, updatedAt: now().toISOString() });
|
|
22334
|
+
const stillFailed = retried.filter((phase) => phases[phase].state === "failed");
|
|
22335
|
+
const status = stillFailed.length === 0 ? "success" : "failed";
|
|
22336
|
+
return {
|
|
22337
|
+
command: "hotfix-retry-deploy",
|
|
22338
|
+
repo: prior.repo,
|
|
22339
|
+
tag: prior.tag,
|
|
22340
|
+
tagSha: prior.tagSha,
|
|
22341
|
+
retried,
|
|
22342
|
+
status,
|
|
22343
|
+
note: status === "success" ? `retried the failed ${joinLegNames(retried)} of published ${prior.tag} \u2014 the ledger is phases-green (#6985)` : `retried the failed ${joinLegNames(retried)} of published ${prior.tag} \u2014 ${joinLegNames(stillFailed)} still failed (#6985)`,
|
|
22344
|
+
...retried.includes("deploy") ? { deploy: phases.deploy } : {},
|
|
22345
|
+
...retried.includes("alignment") ? { alignment: phases.alignment } : {}
|
|
22346
|
+
};
|
|
22347
|
+
});
|
|
22348
|
+
}
|
|
22127
22349
|
|
|
22128
22350
|
// src/train-doctor.ts
|
|
22129
22351
|
init_client_version();
|
|
@@ -22221,7 +22443,7 @@ function readOriginWorkflowsDefault(root, ref) {
|
|
|
22221
22443
|
}
|
|
22222
22444
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
22223
22445
|
async function probeRegistryWhoami(train) {
|
|
22224
|
-
const dir = (0, import_node_fs23.mkdtempSync)((0, import_node_path23.join)((0,
|
|
22446
|
+
const dir = (0, import_node_fs23.mkdtempSync)((0, import_node_path23.join)((0, import_node_os14.tmpdir)(), "mmi-npmrc-"));
|
|
22225
22447
|
const rc = (0, import_node_path23.join)(dir, "npmrc");
|
|
22226
22448
|
try {
|
|
22227
22449
|
(0, import_node_fs23.writeFileSync)(rc, "//registry.npmjs.org/:_authToken=${NPM_TOKEN}\n");
|
|
@@ -22644,7 +22866,7 @@ async function runTrainDoctor(input) {
|
|
|
22644
22866
|
// #6429: each lane finishes its own run (#6068) — an rcand-lane ledger named as a release
|
|
22645
22867
|
// ledger, with `release --resume` as its remedy, sends the operator to a verb that refuses it.
|
|
22646
22868
|
title: `a prior ${ledger.lane ?? "release"} ledger for ${ledger.tag} is ${derived.phaseStatus} (${open2}) \u2014 a new train must not stack on it (#5987)`,
|
|
22647
|
-
remedy: failed ?
|
|
22869
|
+
remedy: failed ? ledgerFailedRemedy(ledger) : `finish it first: ${ledgerLaneResumeCommand(ledger)} (alignment-only pending legs close themselves with \`train doctor --heal\` once the PR merges)${supersession}`
|
|
22648
22870
|
});
|
|
22649
22871
|
} else if (newestRemoteReleaseTag && newestRemoteReleaseTag !== ledger.tag) {
|
|
22650
22872
|
add({
|
|
@@ -27122,7 +27344,7 @@ function registerBoardCommands(program3) {
|
|
|
27122
27344
|
|
|
27123
27345
|
// src/bootstrap-commands.ts
|
|
27124
27346
|
var import_node_fs26 = require("node:fs");
|
|
27125
|
-
var
|
|
27347
|
+
var import_node_os15 = require("node:os");
|
|
27126
27348
|
var import_node_path26 = require("node:path");
|
|
27127
27349
|
init_cli_shared();
|
|
27128
27350
|
init_clean_exit();
|
|
@@ -29130,7 +29352,7 @@ function registerBootstrapCommands(program3) {
|
|
|
29130
29352
|
const readFile9 = (p) => (0, import_node_fs26.existsSync)(p) ? (0, import_node_fs26.readFileSync)(p, "utf8") : null;
|
|
29131
29353
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
29132
29354
|
const putSeed = async (target, content, ref, sha) => {
|
|
29133
|
-
const tmp = (0, import_node_path26.join)((0,
|
|
29355
|
+
const tmp = (0, import_node_path26.join)((0, import_node_os15.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
29134
29356
|
(0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
29135
29357
|
try {
|
|
29136
29358
|
await gh(contentPutInputArgs(repo, target, tmp));
|
|
@@ -29771,7 +29993,7 @@ LIVE apply to ${repo}:
|
|
|
29771
29993
|
} catch {
|
|
29772
29994
|
existingSha = void 0;
|
|
29773
29995
|
}
|
|
29774
|
-
const tmp = (0, import_node_path26.join)((0,
|
|
29996
|
+
const tmp = (0, import_node_path26.join)((0, import_node_os15.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
29775
29997
|
const desiredContent = desiredByRepo.get(rec.repo);
|
|
29776
29998
|
if (desiredContent == null) return fail(`bootstrap propagate: no resolved content for ${rec.repo} ${seed.target} \u2014 refusing to write`);
|
|
29777
29999
|
(0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha)), "utf8");
|
|
@@ -29946,7 +30168,7 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
29946
30168
|
} catch {
|
|
29947
30169
|
existingSha = void 0;
|
|
29948
30170
|
}
|
|
29949
|
-
const tmp = (0, import_node_path26.join)((0,
|
|
30171
|
+
const tmp = (0, import_node_path26.join)((0, import_node_os15.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
29950
30172
|
(0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
|
|
29951
30173
|
try {
|
|
29952
30174
|
await gh(contentPutInputArgs(repo, seed.target, tmp));
|
|
@@ -31897,7 +32119,7 @@ async function collectWaveStatus(deps) {
|
|
|
31897
32119
|
init_github_client();
|
|
31898
32120
|
init_cli_shared();
|
|
31899
32121
|
var import_node_fs30 = require("node:fs");
|
|
31900
|
-
var
|
|
32122
|
+
var import_node_os16 = require("node:os");
|
|
31901
32123
|
var import_node_path29 = require("node:path");
|
|
31902
32124
|
var GC_GH_TIMEOUT_MS = 2e4;
|
|
31903
32125
|
async function collectStatus() {
|
|
@@ -32086,7 +32308,7 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
32086
32308
|
else if (top) nextCommand = `mmi-cli oracle board claim ${top.number} # ${top.title}`;
|
|
32087
32309
|
else nextCommand = "mmi-cli oracle board read \u2014 no claimable items found";
|
|
32088
32310
|
}
|
|
32089
|
-
const home = (0,
|
|
32311
|
+
const home = (0, import_node_os16.homedir)();
|
|
32090
32312
|
const plugin = onboardPluginGate({
|
|
32091
32313
|
readKnown: () => readFileSyncSafe((0, import_node_path29.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs30.readFileSync),
|
|
32092
32314
|
readSettings: () => readFileSyncSafe((0, import_node_path29.join)(home, ".claude", "settings.json"), import_node_fs30.readFileSync)
|
|
@@ -34114,7 +34336,7 @@ init_error_codes();
|
|
|
34114
34336
|
init_github_client();
|
|
34115
34337
|
|
|
34116
34338
|
// src/issue-body.ts
|
|
34117
|
-
var
|
|
34339
|
+
var import_node_os17 = require("node:os");
|
|
34118
34340
|
init_error_codes();
|
|
34119
34341
|
var TextArgError = class extends Error {
|
|
34120
34342
|
constructor(message2, code, offendingFlag) {
|
|
@@ -34127,7 +34349,7 @@ var TextArgError = class extends Error {
|
|
|
34127
34349
|
offendingFlag;
|
|
34128
34350
|
};
|
|
34129
34351
|
function emptyStdinMessage(fileFlag) {
|
|
34130
|
-
if ((0,
|
|
34352
|
+
if ((0, import_node_os17.platform)() === "win32") {
|
|
34131
34353
|
return `${fileFlag} - read empty stdin (on Windows, ${fileFlag} - is unreliable through the npm .cmd shim \u2014 use ${fileFlag} <path>, or pipe to \`node cli/dist/index.cjs\` directly)`;
|
|
34132
34354
|
}
|
|
34133
34355
|
return `${fileFlag} - read empty stdin (nothing piped \u2014 pass a heredoc/pipe, or ${fileFlag} <path>)`;
|
|
@@ -36642,7 +36864,7 @@ function registerSchedulesCommands(program3) {
|
|
|
36642
36864
|
// src/secrets-commands.ts
|
|
36643
36865
|
var import_node_fs33 = require("node:fs");
|
|
36644
36866
|
var import_node_path31 = require("node:path");
|
|
36645
|
-
var
|
|
36867
|
+
var import_node_os18 = require("node:os");
|
|
36646
36868
|
init_cli_shared();
|
|
36647
36869
|
init_hub_auth();
|
|
36648
36870
|
init_github_client();
|
|
@@ -36768,7 +36990,7 @@ async function decryptRailsCredentials(input) {
|
|
|
36768
36990
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
36769
36991
|
"puts JSON.generate(config.config)"
|
|
36770
36992
|
].join("\n");
|
|
36771
|
-
const scriptDir = (0, import_node_fs33.mkdtempSync)((0, import_node_path31.join)((0,
|
|
36993
|
+
const scriptDir = (0, import_node_fs33.mkdtempSync)((0, import_node_path31.join)((0, import_node_os18.tmpdir)(), "mmi-rails-decrypt-"));
|
|
36772
36994
|
const scriptPath = (0, import_node_path31.join)(scriptDir, "decrypt.rb");
|
|
36773
36995
|
(0, import_node_fs33.writeFileSync)(scriptPath, script, "utf8");
|
|
36774
36996
|
try {
|
|
@@ -38164,7 +38386,7 @@ init_github_client();
|
|
|
38164
38386
|
// src/merge-cleanup.ts
|
|
38165
38387
|
var import_node_fs37 = require("node:fs");
|
|
38166
38388
|
var import_node_path35 = require("node:path");
|
|
38167
|
-
var
|
|
38389
|
+
var import_node_os19 = require("node:os");
|
|
38168
38390
|
init_cli_shared();
|
|
38169
38391
|
init_github_client();
|
|
38170
38392
|
|
|
@@ -39234,7 +39456,7 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
|
|
|
39234
39456
|
return rewritten === base ? null : rewritten;
|
|
39235
39457
|
}
|
|
39236
39458
|
function writeSquashBodyFile(body) {
|
|
39237
|
-
const dir = (0, import_node_fs37.mkdtempSync)((0, import_node_path35.join)((0,
|
|
39459
|
+
const dir = (0, import_node_fs37.mkdtempSync)((0, import_node_path35.join)((0, import_node_os19.tmpdir)(), "mmi-squash-body-"));
|
|
39238
39460
|
const path2 = (0, import_node_path35.join)(dir, "body.txt");
|
|
39239
39461
|
(0, import_node_fs37.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
|
|
39240
39462
|
`, "utf8");
|
|
@@ -39657,7 +39879,7 @@ function postMergeReconWarnings(input) {
|
|
|
39657
39879
|
// src/review-verdict.ts
|
|
39658
39880
|
var import_node_child_process13 = require("node:child_process");
|
|
39659
39881
|
var import_node_fs39 = require("node:fs");
|
|
39660
|
-
var
|
|
39882
|
+
var import_node_os20 = require("node:os");
|
|
39661
39883
|
var import_node_path37 = require("node:path");
|
|
39662
39884
|
init_cli_shared();
|
|
39663
39885
|
var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
|
|
@@ -39823,7 +40045,7 @@ async function readPrIssueComments(number, repo) {
|
|
|
39823
40045
|
return comments;
|
|
39824
40046
|
}
|
|
39825
40047
|
async function postPrCommentFromFile(number, repo, body) {
|
|
39826
|
-
const dir = (0, import_node_fs39.mkdtempSync)((0, import_node_path37.join)((0,
|
|
40048
|
+
const dir = (0, import_node_fs39.mkdtempSync)((0, import_node_path37.join)((0, import_node_os20.tmpdir)(), "mmi-review-verdict-"));
|
|
39827
40049
|
const path2 = (0, import_node_path37.join)(dir, "body.md");
|
|
39828
40050
|
try {
|
|
39829
40051
|
(0, import_node_fs39.writeFileSync)(path2, body, "utf8");
|
|
@@ -42808,7 +43030,7 @@ ${SSH_RECIPE_AGENT_NOTE}`);
|
|
|
42808
43030
|
var import_node_child_process16 = require("node:child_process");
|
|
42809
43031
|
var import_node_crypto13 = require("node:crypto");
|
|
42810
43032
|
var import_node_fs45 = require("node:fs");
|
|
42811
|
-
var
|
|
43033
|
+
var import_node_os21 = require("node:os");
|
|
42812
43034
|
var import_node_path42 = require("node:path");
|
|
42813
43035
|
|
|
42814
43036
|
// ../scripts/distribution-digest.mjs
|
|
@@ -42959,7 +43181,7 @@ function rebuildTo(packageRoot, outDir) {
|
|
|
42959
43181
|
});
|
|
42960
43182
|
}
|
|
42961
43183
|
function runDistStatus(root) {
|
|
42962
|
-
const stage = (0, import_node_fs45.mkdtempSync)((0, import_node_path42.join)((0,
|
|
43184
|
+
const stage = (0, import_node_fs45.mkdtempSync)((0, import_node_path42.join)((0, import_node_os21.tmpdir)(), "mmi-dist-drift-"));
|
|
42963
43185
|
let overlayCount = 0;
|
|
42964
43186
|
try {
|
|
42965
43187
|
const cliOut = (0, import_node_path42.join)(stage, "cli-dist");
|
|
@@ -44542,6 +44764,48 @@ ${decision.note}`);
|
|
|
44542
44764
|
const localSync = await syncLocalTrainBranches(deps.run, { fetch: false, warn: deps.warn }).catch(() => void 0);
|
|
44543
44765
|
return { ...result, ...localSync ? { localSync } : {} };
|
|
44544
44766
|
}
|
|
44767
|
+
async function runHotfixRetryDeploy(deps, versionInput) {
|
|
44768
|
+
const ctx = await buildTrainApplyContext(deps);
|
|
44769
|
+
const { tag } = normalizeHotfixVersion(versionInput);
|
|
44770
|
+
const status = await deps.run("git", ["status", "--porcelain"]);
|
|
44771
|
+
if (porcelainHasBlockingChanges(status)) {
|
|
44772
|
+
throw new Error(`working tree must be clean before hotfix --retry-deploy${describeBlockingPaths(status)}`);
|
|
44773
|
+
}
|
|
44774
|
+
const root = await hotfixCheckoutRoot(deps);
|
|
44775
|
+
await deps.run("git", ["fetch", "origin", "--tags"]);
|
|
44776
|
+
const meta = requireProjectMetaForTrain(await loadProjectMeta(deps, ctx), ctx.repo);
|
|
44777
|
+
const deployModel = requireDeployModel(meta, ctx.repo);
|
|
44778
|
+
const result = await retryHotfixDeployLegs(deps, root, {
|
|
44779
|
+
retryAlignment: async (prior) => {
|
|
44780
|
+
let foldNote2;
|
|
44781
|
+
let foldStatus2 = "ok";
|
|
44782
|
+
try {
|
|
44783
|
+
foldNote2 = await portFoldToDevelopment(deps, ctx, deployModel, prior.tag);
|
|
44784
|
+
} catch (e) {
|
|
44785
|
+
foldStatus2 = "failure";
|
|
44786
|
+
foldNote2 = `development fold port FAILED: ${e.message ?? e} \u2014 the release stands; port it by hand on a fresh branch: git checkout -B ${devFoldBranch(prior.tag)}-manual origin/development && ${foldPortManualCommand(deployModel, prior.tag.replace(/^v/, ""))}, then push it as the canonical head (git push origin ${devFoldBranch(prior.tag)}-manual:refs/heads/${devFoldBranch(prior.tag)}) and open a development-base PR \u2014 see docs/Guides/train-troubleshooting.md#hotfix-fold-port`;
|
|
44787
|
+
}
|
|
44788
|
+
return hotfixFoldAlignmentEntry(foldStatus2, foldNote2, prior.tagSha);
|
|
44789
|
+
}
|
|
44790
|
+
});
|
|
44791
|
+
const ledger = openReleaseLedgerWriter(root);
|
|
44792
|
+
const report = releaseLedgerReport(
|
|
44793
|
+
await ledger.load({ repo: result.repo, tag: result.tag, tagSha: result.tagSha }).catch(() => void 0),
|
|
44794
|
+
ledger.path
|
|
44795
|
+
);
|
|
44796
|
+
const deployNote = result.deploy?.note ?? result.deploy?.error;
|
|
44797
|
+
const foldNote = result.alignment?.note ?? result.alignment?.error;
|
|
44798
|
+
const foldStatus = result.alignment ? result.alignment.state === "failed" ? "failure" : result.alignment.state === "pending" ? "unresolved" : "ok" : void 0;
|
|
44799
|
+
const runs = result.deploy ? [{ workflow: result.deploy.workflow ?? "jerv-gateway", conclusion: result.deploy.state === "complete" ? "success" : result.deploy.state === "failed" ? "failure" : "pending" }] : void 0;
|
|
44800
|
+
return {
|
|
44801
|
+
...result,
|
|
44802
|
+
...deployNote ? { deployNote } : {},
|
|
44803
|
+
...foldNote ? { foldNote } : {},
|
|
44804
|
+
...foldStatus ? { foldStatus } : {},
|
|
44805
|
+
...runs ? { runs } : {},
|
|
44806
|
+
...report ? { ledger: report } : {}
|
|
44807
|
+
};
|
|
44808
|
+
}
|
|
44545
44809
|
function deriveHotfixState(f) {
|
|
44546
44810
|
if (!f.branchExists && !f.pr && !f.tagPushed && !f.releaseExists) {
|
|
44547
44811
|
return { state: "not-started", next: `mmi-cli devops hotfix start --from <pr#|sha> (would mint ${f.tag})` };
|
|
@@ -45117,7 +45381,7 @@ async function announceSummaryRefusal(path2) {
|
|
|
45117
45381
|
function trainApplyDeps() {
|
|
45118
45382
|
return {
|
|
45119
45383
|
run: async (file, args) => {
|
|
45120
|
-
const timeout = file === "node" && args[1] === "prepare" ? NODE_PREPARE_TIMEOUT_MS : file === "node" && args[1] === "verify" ? NODE_VERIFY_TIMEOUT_MS : file === "npm" && (args[0] === "version" || args[0] === "ci" || args[0] === "install") ? NPM_FOLD_TIMEOUT_MS : file === "npm" ? NPM_TRAIN_TIMEOUT_MS : file === "bash" &&
|
|
45384
|
+
const timeout = file === "node" && args[1] === "prepare" ? NODE_PREPARE_TIMEOUT_MS : file === "node" && args[1] === "verify" ? NODE_VERIFY_TIMEOUT_MS : file === "npm" && (args[0] === "version" || args[0] === "ci" || args[0] === "install") ? NPM_FOLD_TIMEOUT_MS : file === "npm" ? NPM_TRAIN_TIMEOUT_MS : file === "bash" && /jerv-gateway-release-deploy\.sh$/.test(args[0] ?? "") ? 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;
|
|
45121
45385
|
try {
|
|
45122
45386
|
return isWin3 && file === "npm" ? (await execFileP("cmd.exe", ["/c", "npm", ...args], { timeout })).stdout : (await execFileP(file, args, { timeout })).stdout;
|
|
45123
45387
|
} catch (e) {
|
|
@@ -45792,6 +46056,14 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
|
|
|
45792
46056
|
const evidenced = hotfixRunWithEvidence(run);
|
|
45793
46057
|
return ` - ${evidenced.workflow}: ${evidenced.conclusion}${evidenced.conclusion === "failure" ? " \u2014 error: workflow reported failure" : ""} (${evidenced.url ?? evidenced.runUrlNote})`;
|
|
45794
46058
|
}
|
|
46059
|
+
function renderHotfixRetryDeploy(r) {
|
|
46060
|
+
return [
|
|
46061
|
+
`mmi-cli devops hotfix release --retry-deploy: ${r.repo} ${r.tag} (${r.tagSha.slice(0, 12)}) \u2014 ${r.note}`,
|
|
46062
|
+
...r.deployNote ? [` - deploy: ${r.deployNote}`] : [],
|
|
46063
|
+
...r.foldNote ? [` - fold: ${r.foldNote}`] : [],
|
|
46064
|
+
...r.ledger ? formatReleaseLedgerReport(r.ledger).map((line) => ` ${line}`) : []
|
|
46065
|
+
].join("\n");
|
|
46066
|
+
}
|
|
45795
46067
|
function renderHotfixRelease(r) {
|
|
45796
46068
|
return [
|
|
45797
46069
|
`mmi-cli devops hotfix release: ${r.tag} at ${r.mergedSha.slice(0, 7)} on ${r.repo} (deployModel=${r.deployModel})`,
|
|
@@ -45865,11 +46137,19 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
|
|
|
45865
46137
|
console.log(o.json ? JSON.stringify({ command: "hotfix", steps }, null, 2) : renderSteps("mmi-cli devops hotfix: dry-run plan", steps));
|
|
45866
46138
|
});
|
|
45867
46139
|
hotfixCmd.command("start").description("cherry-pick one or more merged development PRs (or SHAs) onto hotfix/vX.Y.Z from origin/main, bump the distribution, open the main-base PR").requiredOption("--from <pr#|sha[,pr#|sha...]>", "merged development PR number(s) or commit SHA(s) to cherry-pick, in pick order \u2014 one hotfix cycle carries as many fixes as you name (#4411)").option("--json", "machine-readable output").option("--out <path>", "write the result to this file as UTF-8 (no BOM) instead of stdout \u2014 the shell-free receipt path (#5983/#6068)").action(async (o) => runHotfixSub("start", () => runHotfixStart(trainApplyDeps(), { from: o.from }), o, renderHotfixStart));
|
|
45868
|
-
hotfixCmd.command("release <version>").description("after the hotfix PR is merged + checks green: tag, GitHub Release, watch deploy/publish, verify distribution (idempotent)").option("--json", "machine-readable output").option("--announce-summary-file <path>", "agent-curated 3-6 line release Slack summary; required for a NEW MMI-Hub hotfix Release (#883/#3901/#6068), optional for a product repo announcing to its project release channel (#6517)").option("--carries <pr#|sha[,pr#|sha...]>", "declared fix target(s) this hotfix must carry; each must be proven present before tagging (#3056)").option("--out <path>", "write the result to this file as UTF-8 (no BOM) instead of stdout \u2014 the shell-free receipt path (#5983/#6068)").action(async (version, o) =>
|
|
45869
|
-
|
|
45870
|
-
|
|
45871
|
-
|
|
45872
|
-
|
|
46140
|
+
hotfixCmd.command("release <version>").description("after the hotfix PR is merged + checks green: tag, GitHub Release, watch deploy/publish, verify distribution (idempotent)").option("--json", "machine-readable output").option("--announce-summary-file <path>", "agent-curated 3-6 line release Slack summary; required for a NEW MMI-Hub hotfix Release (#883/#3901/#6068), optional for a product repo announcing to its project release channel (#6517)").option("--carries <pr#|sha[,pr#|sha...]>", "declared fix target(s) this hotfix must carry; each must be proven present before tagging (#3056)").option("--retry-deploy", "re-run only the failed deploy and/or fold-port alignment legs of a published hotfix of this same tag (#6985)").option("--out <path>", "write the result to this file as UTF-8 (no BOM) instead of stdout \u2014 the shell-free receipt path (#5983/#6068)").action(async (version, o) => {
|
|
46141
|
+
if (o.retryDeploy) {
|
|
46142
|
+
if (o.announceSummaryFile || o.carries) {
|
|
46143
|
+
return fail("hotfix release: --retry-deploy accepts only --json and --out; it re-runs failed deploy/alignment legs of the published tag and does not recut");
|
|
46144
|
+
}
|
|
46145
|
+
return runHotfixSub("retry-deploy", () => runHotfixRetryDeploy(trainApplyDeps(), version), o, renderHotfixRetryDeploy);
|
|
46146
|
+
}
|
|
46147
|
+
return runHotfixSub("release", async () => {
|
|
46148
|
+
const result = await runHotfixRelease(trainApplyDeps(), version, { announceSummaryFile: o.announceSummaryFile, carries: o.carries ? [o.carries] : [] });
|
|
46149
|
+
const projectInfoSync = await runProjectInfoSyncLeg(runProjectInfoSyncCallback, result.repo);
|
|
46150
|
+
return { ...result, projectInfoSync };
|
|
46151
|
+
}, o, renderHotfixRelease);
|
|
46152
|
+
});
|
|
45873
46153
|
function hotfixStatusDeps() {
|
|
45874
46154
|
const STATUS_GH_MS = 8e3;
|
|
45875
46155
|
const STATUS_GIT_MS = 1e4;
|
|
@@ -45911,7 +46191,7 @@ function envHealLockPath(home) {
|
|
|
45911
46191
|
async function withEnvHealLock(what, run) {
|
|
45912
46192
|
try {
|
|
45913
46193
|
return await withFileLock(
|
|
45914
|
-
envHealLockPath((0,
|
|
46194
|
+
envHealLockPath((0, import_node_os22.homedir)()),
|
|
45915
46195
|
{ staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
|
|
45916
46196
|
run
|
|
45917
46197
|
);
|
|
@@ -46008,7 +46288,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
46008
46288
|
const configRoot = surfaceConfigRoot(surface);
|
|
46009
46289
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
46010
46290
|
const plan = buildPluginCachePlan(
|
|
46011
|
-
(0,
|
|
46291
|
+
(0, import_node_os22.homedir)(),
|
|
46012
46292
|
running,
|
|
46013
46293
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
46014
46294
|
{ configRoot, includeStaging: surface !== "codex" }
|
|
@@ -46036,7 +46316,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
46036
46316
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
46037
46317
|
const installed = installedActivePluginVersion(surface);
|
|
46038
46318
|
const plan = buildPluginCachePlan(
|
|
46039
|
-
(0,
|
|
46319
|
+
(0, import_node_os22.homedir)(),
|
|
46040
46320
|
running,
|
|
46041
46321
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
46042
46322
|
{ configRoot, includeStaging: surface !== "codex", installedVersion: installed }
|
|
@@ -47261,7 +47541,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
47261
47541
|
return;
|
|
47262
47542
|
}
|
|
47263
47543
|
const plan = buildPluginCachePlan(
|
|
47264
|
-
(0,
|
|
47544
|
+
(0, import_node_os22.homedir)(),
|
|
47265
47545
|
running,
|
|
47266
47546
|
pluginCacheFsDeps(configRoot, directoryBytes),
|
|
47267
47547
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
package/package.json
CHANGED