@mutmutco/cli 4.5.12 → 4.5.14
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 +80 -8
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -6985,6 +6985,9 @@ async function retireProject(slug, deps) {
|
|
|
6985
6985
|
async function setDeployCoords(slug, payload, deps) {
|
|
6986
6986
|
return postJson(`/projects/${encodeURIComponent(slug)}/deploy`, payload, deps);
|
|
6987
6987
|
}
|
|
6988
|
+
async function retireDeployCoords(slug, stage, deps) {
|
|
6989
|
+
return postJson(`/projects/${encodeURIComponent(slug)}/deploy/${encodeURIComponent(stage)}`, {}, deps, "DELETE", { noRetry: true });
|
|
6990
|
+
}
|
|
6988
6991
|
async function postSchedulesLift(repo, schedules, deps) {
|
|
6989
6992
|
const res = await postJson("/schedules/lift", { repo, schedules }, deps);
|
|
6990
6993
|
if (!res.ok && res.status === 404) return { ...res, unreachable: "route-absent" };
|
|
@@ -15998,10 +16001,10 @@ var rollout_plan_default = {
|
|
|
15998
16001
|
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)."
|
|
15999
16002
|
},
|
|
16000
16003
|
baseline: {
|
|
16001
|
-
version: "4.5.
|
|
16002
|
-
tag: "v4.5.
|
|
16003
|
-
commit: "
|
|
16004
|
-
npm: "@mutmutco/cli@4.5.
|
|
16004
|
+
version: "4.5.14",
|
|
16005
|
+
tag: "v4.5.14",
|
|
16006
|
+
commit: "632a94fc230f",
|
|
16007
|
+
npm: "@mutmutco/cli@4.5.14"
|
|
16005
16008
|
},
|
|
16006
16009
|
exitCriterion: "fleet-n-of-n",
|
|
16007
16010
|
hubOnlyShortcut: "forbidden",
|
|
@@ -16018,14 +16021,14 @@ var rollout_plan_default = {
|
|
|
16018
16021
|
repo: "mutmutco/mmi-hub",
|
|
16019
16022
|
role: "canary",
|
|
16020
16023
|
schedule: "train",
|
|
16021
|
-
v3Target: "v4.5.
|
|
16024
|
+
v3Target: "v4.5.14"
|
|
16022
16025
|
}
|
|
16023
16026
|
],
|
|
16024
16027
|
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.",
|
|
16025
16028
|
rollback: {
|
|
16026
16029
|
independent: true,
|
|
16027
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
16028
|
-
v3Target: "v4.5.
|
|
16030
|
+
mechanism: "npm dist-tag latest -> 4.5.14 and redeploy the Hub Lambda from tag v4.5.14 (632a94fc230f); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
16031
|
+
v3Target: "v4.5.14 (@mutmutco/cli@4.5.14, tag commit 632a94fc230f \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
16029
16032
|
}
|
|
16030
16033
|
},
|
|
16031
16034
|
{
|
|
@@ -37329,6 +37332,41 @@ function writeError(res) {
|
|
|
37329
37332
|
return `HTTP ${res.status}${detail ? ` \u2014 ${detail}` : ""}`;
|
|
37330
37333
|
}
|
|
37331
37334
|
|
|
37335
|
+
// src/deploy-retire.ts
|
|
37336
|
+
function buildDeployRetirePlan(input) {
|
|
37337
|
+
const target = input.target?.trim();
|
|
37338
|
+
if (!target) {
|
|
37339
|
+
throw new Error("org project deploy retire: pass <owner/repo> or a slug");
|
|
37340
|
+
}
|
|
37341
|
+
const slug = (target.includes("/") ? target.split("/").pop() : target).toLowerCase();
|
|
37342
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(slug)) {
|
|
37343
|
+
throw new Error(`org project deploy retire: "${target}" does not resolve to a valid slug`);
|
|
37344
|
+
}
|
|
37345
|
+
if (!["dev", "rc", "main"].includes(input.stage)) {
|
|
37346
|
+
throw new Error("org project deploy retire: --stage must be dev, rc, or main");
|
|
37347
|
+
}
|
|
37348
|
+
return { slug, stage: input.stage, apply: Boolean(input.apply) };
|
|
37349
|
+
}
|
|
37350
|
+
async function runDeployRetire(input, deps) {
|
|
37351
|
+
const plan = buildDeployRetirePlan(input);
|
|
37352
|
+
const facts = await fetchDeployFactsBySlug(plan.slug, deps.registry);
|
|
37353
|
+
const existing = facts ? facts.stages[plan.stage] ?? null : void 0;
|
|
37354
|
+
if (!plan.apply) {
|
|
37355
|
+
return { slug: plan.slug, stage: plan.stage, applied: false, ok: facts !== null, existing };
|
|
37356
|
+
}
|
|
37357
|
+
const res = await retireDeployCoords(plan.slug, plan.stage, deps.registry);
|
|
37358
|
+
if (!res.ok) {
|
|
37359
|
+
return { slug: plan.slug, stage: plan.stage, applied: true, ok: false, existing, error: writeError2(res) };
|
|
37360
|
+
}
|
|
37361
|
+
const removed = res.body?.removed;
|
|
37362
|
+
return { slug: plan.slug, stage: plan.stage, applied: true, ok: true, existing, removed };
|
|
37363
|
+
}
|
|
37364
|
+
function writeError2(res) {
|
|
37365
|
+
if (res.error) return res.error;
|
|
37366
|
+
const detail = res.body?.error ?? "";
|
|
37367
|
+
return `HTTP ${res.status}${detail ? ` \u2014 ${detail}` : ""}`;
|
|
37368
|
+
}
|
|
37369
|
+
|
|
37332
37370
|
// src/schedules-commands.ts
|
|
37333
37371
|
var import_promises5 = require("node:fs/promises");
|
|
37334
37372
|
init_clean_exit();
|
|
@@ -47957,7 +47995,7 @@ async function runProjectDeployGet(repoOrSlug, o) {
|
|
|
47957
47995
|
console.log(JSON.stringify(payload));
|
|
47958
47996
|
}
|
|
47959
47997
|
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));
|
|
47960
|
-
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, o));
|
|
47998
|
+
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, { stage: projectDeploy.opts().stage, json: o.json }));
|
|
47961
47999
|
projectDeploy.command("doctor").description("read-only estate scan for duplicate Hetzner (sshHost, port) DEPLOY# coordinates (#5431); master-only; never auto-reassigns ports").option("--json", "machine-readable output").action(async (o) => {
|
|
47962
48000
|
const cfg = await loadConfig();
|
|
47963
48001
|
const report = await fetchDeployPortCollisions(registryClientDeps(cfg));
|
|
@@ -47969,6 +48007,40 @@ projectDeploy.command("doctor").description("read-only estate scan for duplicate
|
|
|
47969
48007
|
if (report.error && report.status !== 403) return failGraceful(`org project deploy doctor: ${report.error}`);
|
|
47970
48008
|
if (!report.ok && !report.error) process.exitCode = 1;
|
|
47971
48009
|
});
|
|
48010
|
+
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("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
48011
|
+
const stage = projectDeploy.opts().stage;
|
|
48012
|
+
if (!stage) return fail("org project deploy retire: --stage is required (dev | rc | main)");
|
|
48013
|
+
const cfg = await loadConfig();
|
|
48014
|
+
let target;
|
|
48015
|
+
try {
|
|
48016
|
+
target = await projectTarget("org project deploy retire", repoOrSlug);
|
|
48017
|
+
} catch (e) {
|
|
48018
|
+
return fail(e.message);
|
|
48019
|
+
}
|
|
48020
|
+
let result;
|
|
48021
|
+
try {
|
|
48022
|
+
result = await runDeployRetire(
|
|
48023
|
+
{ target, stage, apply: Boolean(o.apply) },
|
|
48024
|
+
{ registry: registryClientDeps(cfg) }
|
|
48025
|
+
);
|
|
48026
|
+
} catch (e) {
|
|
48027
|
+
return fail(e.message);
|
|
48028
|
+
}
|
|
48029
|
+
if (o.json) {
|
|
48030
|
+
printLine(JSON.stringify(result));
|
|
48031
|
+
} else {
|
|
48032
|
+
const verb = result.applied ? "retired" : "WOULD retire (dry-run; pass --apply to write)";
|
|
48033
|
+
printLine(`org project deploy retire: ${verb} DEPLOY#${result.stage} for ${result.slug}`);
|
|
48034
|
+
if (!result.ok) {
|
|
48035
|
+
printLine(` FAILED \u2014 ${result.error}`);
|
|
48036
|
+
} else if (result.applied) {
|
|
48037
|
+
printLine(` removed=${result.removed ?? "unknown"}`);
|
|
48038
|
+
} else {
|
|
48039
|
+
printLine(` ${result.existing ? "live row present \u2014 would be tombstoned" : "no live row (already absent/tombstoned)"}`);
|
|
48040
|
+
}
|
|
48041
|
+
}
|
|
48042
|
+
if (!result.ok) return failGraceful(`org project deploy retire: ${result.error ?? "read failed"}`);
|
|
48043
|
+
});
|
|
47972
48044
|
project.command("set [owner/repo]").description("upsert project META (idempotent merge; master-gated)").addOption(new Option("--class <class>", "deployable | content").choices(["deployable", "content"])).addOption(new Option("--project-type <type>", `${PROJECT_TYPES.join(" | ")} (capability shape)`).choices([...PROJECT_TYPES])).addOption(new Option("--deploy-model <model>", `${DEPLOY_MODELS.join(" | ")} (release/deploy path; none means no Hub deploy registration)`).choices([...DEPLOY_MODELS])).addOption(new Option("--release-track <track>", `${RELEASE_TRACKS.join(" | ")} (branch topology; direct skips rc)`).choices([...RELEASE_TRACKS])).option("--var <KEY=VALUE...>", settableVarHelp()).option("--set <KEY=VALUE...>", "alias of --var (one KEY=VALUE per flag; repeat the flag to set several)").option("--secrets-file <path>", 'read the #2244 secrets catalog map (JSON) from a file and merge it per entry into the existing catalog (drop ONE entry with --unset-secret KEY; clear ALL with --unset secrets --confirm-clear-secrets). SHAPE: a JSON object keyed by env name, each entry {"key":"UPPER_SNAKE" (repeat the env name), "purpose":"<non-empty>", "group":"<e.g. auth|database>", "owner":"<github login>", "stages":[] (empty = the one stageless shared value; else any of dev/rc/main), "consumers":["runtime"|"box"|?], "provider":"<optional>"}').option("--unset-secret <KEY...>", "remove exactly these #2244 catalog entries (repeatable; KEY or KEY@dev|rc|main). Refuses while a stage still requires the key \u2014 drop it from requiredRuntimeSecrets/requiredBuildSecrets first").option("--confirm-clear-secrets", "required to let --unset secrets wipe the WHOLE catalog (the refusal names how many entries that removes)").option("--unset <KEY...>", "META field to remove (repeatable): oauth, requiredRuntimeSecrets, requiredBuildSecrets, secrets (the WHOLE catalog \u2014 needs --confirm-clear-secrets), edgeDomains, requiredGcpApis, publishRequired, releaseChannel, releaseLanguage").option("--clear-web-profile", "remove web-only registry fields (oauth, edgeDomains) for non-web/content projects").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
47973
48045
|
const cfg = await loadConfig();
|
|
47974
48046
|
let target;
|
package/package.json
CHANGED