@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.
Files changed (2) hide show
  1. package/dist/main.cjs +80 -8
  2. 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.12",
16002
- tag: "v4.5.12",
16003
- commit: "b5632c8b940d",
16004
- npm: "@mutmutco/cli@4.5.12"
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.12"
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.12 and redeploy the Hub Lambda from tag v4.5.12 (b5632c8b940d); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16028
- v3Target: "v4.5.12 (@mutmutco/cli@4.5.12, tag commit b5632c8b940d \u2014 last known-good release carrying the repo-index v4-only contract)"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.5.12",
3
+ "version": "4.5.14",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",