@mutmutco/cli 4.5.11 → 4.5.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.
Files changed (2) hide show
  1. package/dist/main.cjs +81 -10
  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.11",
16002
- tag: "v4.5.11",
16003
- commit: "3e598c9aa265",
16004
- npm: "@mutmutco/cli@4.5.11"
16004
+ version: "4.5.13",
16005
+ tag: "v4.5.13",
16006
+ commit: "33eeee1ae7f6",
16007
+ npm: "@mutmutco/cli@4.5.13"
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.11"
16024
+ v3Target: "v4.5.13"
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.11 and redeploy the Hub Lambda from tag v4.5.11 (3e598c9aa265); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16028
- v3Target: "v4.5.11 (@mutmutco/cli@4.5.11, tag commit 3e598c9aa265 \u2014 last known-good release carrying the repo-index v4-only contract)"
16030
+ mechanism: "npm dist-tag latest -> 4.5.13 and redeploy the Hub Lambda from tag v4.5.13 (33eeee1ae7f6); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16031
+ v3Target: "v4.5.13 (@mutmutco/cli@4.5.13, tag commit 33eeee1ae7f6 \u2014 last known-good release carrying the repo-index v4-only contract)"
16029
16032
  }
16030
16033
  },
16031
16034
  {
@@ -24796,7 +24799,6 @@ async function runReleaseResumePipeline(deps, options, prepared) {
24796
24799
  Live origin/tag/Release proof stays authoritative; nothing was written. Inspect ${ledger.path} before retrying.`
24797
24800
  );
24798
24801
  }
24799
- const shippedBefore = shipLegsGreen(persisted?.phases.deploy, persisted?.phases.publish);
24800
24802
  if (persisted?.lane && persisted.lane !== "release") {
24801
24803
  throw new Error(`release --resume refused: the ${tag} ledger belongs to the ${persisted.lane} lane \u2014 finish it with ${ledgerLaneResumeCommand(persisted)} (#6068). Nothing was written.`);
24802
24804
  }
@@ -24884,7 +24886,7 @@ Nothing was written. Inspect ${ledger.path} (or clear it only after proving the
24884
24886
  Object.assign(dispatch2, shippedDispatch);
24885
24887
  if (isJervHubRepo(ctx.repo)) steps2.push(dispatch2.note);
24886
24888
  const settledBySupersede = persisted?.phases.deploy.superseded === true || persisted?.phases.publish.superseded === true;
24887
- const announceNote2 = persisted && !shippedBefore && !settledBySupersede && (prodReleaseTargets(deployModel).length > 0 || deployModel === "vercel" || companion !== null) ? await announceShippedRelease(
24889
+ const announceNote2 = persisted && !settledBySupersede && (prodReleaseTargets(deployModel).length > 0 || deployModel === "vercel" || companion !== null) ? await announceShippedRelease(
24888
24890
  deps,
24889
24891
  { repo: ctx.repo, tag, summaryFile: options.announceSummaryFile },
24890
24892
  deployModel,
@@ -37330,6 +37332,41 @@ function writeError(res) {
37330
37332
  return `HTTP ${res.status}${detail ? ` \u2014 ${detail}` : ""}`;
37331
37333
  }
37332
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
+
37333
37370
  // src/schedules-commands.ts
37334
37371
  var import_promises5 = require("node:fs/promises");
37335
37372
  init_clean_exit();
@@ -47958,7 +47995,7 @@ async function runProjectDeployGet(repoOrSlug, o) {
47958
47995
  console.log(JSON.stringify(payload));
47959
47996
  }
47960
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));
47961
- 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 }));
47962
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) => {
47963
48000
  const cfg = await loadConfig();
47964
48001
  const report = await fetchDeployPortCollisions(registryClientDeps(cfg));
@@ -47970,6 +48007,40 @@ projectDeploy.command("doctor").description("read-only estate scan for duplicate
47970
48007
  if (report.error && report.status !== 403) return failGraceful(`org project deploy doctor: ${report.error}`);
47971
48008
  if (!report.ok && !report.error) process.exitCode = 1;
47972
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
+ });
47973
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) => {
47974
48045
  const cfg = await loadConfig();
47975
48046
  let target;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.5.11",
3
+ "version": "4.5.13",
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",