@mutmutco/cli 4.5.28 → 4.5.30

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 +92 -28
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -16818,10 +16818,10 @@ var rollout_plan_default = {
16818
16818
  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)."
16819
16819
  },
16820
16820
  baseline: {
16821
- version: "4.5.28",
16822
- tag: "v4.5.28",
16823
- commit: "7b61e100e4df",
16824
- npm: "@mutmutco/cli@4.5.28"
16821
+ version: "4.5.30",
16822
+ tag: "v4.5.30",
16823
+ commit: "058d754e0aae",
16824
+ npm: "@mutmutco/cli@4.5.30"
16825
16825
  },
16826
16826
  exitCriterion: "fleet-n-of-n",
16827
16827
  hubOnlyShortcut: "forbidden",
@@ -16838,14 +16838,14 @@ var rollout_plan_default = {
16838
16838
  repo: "mutmutco/mmi-hub",
16839
16839
  role: "canary",
16840
16840
  schedule: "train",
16841
- v3Target: "v4.5.28"
16841
+ v3Target: "v4.5.30"
16842
16842
  }
16843
16843
  ],
16844
16844
  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.",
16845
16845
  rollback: {
16846
16846
  independent: true,
16847
- mechanism: "npm dist-tag latest -> 4.5.28 and redeploy the Hub Lambda from tag v4.5.28 (7b61e100e4df); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16848
- v3Target: "v4.5.28 (@mutmutco/cli@4.5.28, tag commit 7b61e100e4df \u2014 last known-good release carrying the repo-index v4-only contract)"
16847
+ mechanism: "npm dist-tag latest -> 4.5.30 and redeploy the Hub Lambda from tag v4.5.30 (058d754e0aae); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16848
+ v3Target: "v4.5.30 (@mutmutco/cli@4.5.30, tag commit 058d754e0aae \u2014 last known-good release carrying the repo-index v4-only contract)"
16849
16849
  }
16850
16850
  },
16851
16851
  {
@@ -19158,7 +19158,7 @@ async function correlateRun(deps, args) {
19158
19158
  });
19159
19159
  const tokens2 = args.mode === "dispatch" ? args.titleIncludes : void 0;
19160
19160
  const titleMatch = tokens2 && tokens2.length ? candidates.find((c) => tokens2.every((t) => (c.row.displayTitle ?? "").includes(t))) : void 0;
19161
- const match = titleMatch ?? (args.mode === "workflow" || windowProvenWhole ? candidates[0] : void 0);
19161
+ const match = titleMatch ?? (!("requireTitleMatch" in args && args.requireTitleMatch) && (args.mode === "workflow" || windowProvenWhole) ? candidates[0] : void 0);
19162
19162
  if (match) return { runId: match.row.databaseId, runUrl: match.row.url, read: "found" };
19163
19163
  if (!windowProvenWhole) truncatedWindow = true;
19164
19164
  }
@@ -19179,8 +19179,8 @@ async function correlateRun(deps, args) {
19179
19179
  function correlateTenantRun(deps, since, titleIncludes) {
19180
19180
  return correlateRun(deps, { workflow: "tenant-deploy.yml", since, mode: "dispatch", titleIncludes });
19181
19181
  }
19182
- function correlateGetPublishRun(deps, since, product) {
19183
- return correlateRun(deps, { workflow: "get-publish.yml", since, mode: "dispatch", titleIncludes: [product] });
19182
+ function correlateGetPublishRun(deps, since, product, mode) {
19183
+ return correlateRun(deps, { workflow: "get-publish.yml", since, mode: "dispatch", titleIncludes: [product, ...mode ? [mode] : []], ...mode ? { requireTitleMatch: true } : {} });
19184
19184
  }
19185
19185
  function correlateControlRun(deps, since, titleIncludes) {
19186
19186
  return correlateRun(deps, { workflow: "tenant-control.yml", since, mode: "dispatch", titleIncludes });
@@ -20063,10 +20063,50 @@ async function appendMmiGetRedeploy(deps, input, dispatch) {
20063
20063
  workflowRuns: [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: "success" }]
20064
20064
  };
20065
20065
  }
20066
+ async function appendServedChannelStage(deps, input, dispatch) {
20067
+ if (!isHubControlRepo(input.repo)) return dispatch;
20068
+ const remedy = `${getPublishDispatchCommand(input.repo)} -f mode=stage`;
20069
+ const prior = await findPriorGreenDispatch(deps, input.repo, "get-publish.yml", input.releaseSha, [HUB_SERVED_PRODUCT, "stage"]);
20070
+ let row;
20071
+ if (prior) {
20072
+ row = { workflow: "get-publish.yml", runId: prior.runId, ...prior.runUrl ? { runUrl: prior.runUrl } : {}, conclusion: "success" };
20073
+ } else {
20074
+ const since = (deps.now ?? Date.now)();
20075
+ try {
20076
+ await deps.run("gh", ["workflow", "run", "get-publish.yml", "--repo", input.repo, "--ref", "main", "-f", `product=${HUB_SERVED_PRODUCT}`, "-f", "mode=stage"]);
20077
+ } catch (e) {
20078
+ const why = e instanceof Error ? e.message : String(e);
20079
+ return {
20080
+ ...dispatch,
20081
+ note: `${dispatch.note}; get-publish stage dispatch FAILED: ${why} \u2014 recover with \`${remedy}\``,
20082
+ deployStatus: "failure",
20083
+ workflowRuns: [...dispatch.workflowRuns ?? [], planeFailureRow("get-publish.yml", `stage dispatch refused: ${why}`)]
20084
+ };
20085
+ }
20086
+ const correlated = await correlateGetPublishRun(deps, since, HUB_SERVED_PRODUCT, "stage");
20087
+ if (correlated.read !== "found") {
20088
+ return {
20089
+ ...dispatch,
20090
+ note: `${dispatch.note}; get-publish stage dispatched but its run could not be READ \u2014 check Actions; recover with \`${remedy}\``,
20091
+ deployStatus: "failure",
20092
+ workflowRuns: [...dispatch.workflowRuns ?? [], planeFailureRow("get-publish.yml", "stage dispatched, but its run could not be read")]
20093
+ };
20094
+ }
20095
+ row = { workflow: "get-publish.yml", runId: correlated.runId, ...correlated.runUrl ? { runUrl: correlated.runUrl } : {}, conclusion: "pending" };
20096
+ if (input.watch) row.conclusion = await watchTenantRun(deps, correlated.runId, input.repo);
20097
+ }
20098
+ return {
20099
+ ...dispatch,
20100
+ note: `${dispatch.note}; get-publish stage ${row.conclusion}${row.conclusion === "failure" ? ` \u2014 recover with \`${remedy}\`` : ""}`,
20101
+ workflowRuns: [...dispatch.workflowRuns ?? [], row],
20102
+ ...row.conclusion === "failure" ? { deployStatus: "failure" } : {}
20103
+ };
20104
+ }
20066
20105
  async function appendServedChannelPublish(deps, input, dispatch, redeployed) {
20067
20106
  if (!isHubControlRepo(input.repo)) return dispatch;
20068
20107
  const channelUrl = input.channelUrl ?? readServedChannelUrl();
20069
20108
  const remedy = getPublishDispatchCommand(input.repo);
20109
+ const pointerRemedy = input.tag ? `${remedy} -f mode=pointer -f version=${input.tag.replace(/^v/, "")}` : remedy;
20070
20110
  const withNote = (note, deployStatus, row) => ({
20071
20111
  ...dispatch,
20072
20112
  note: `${dispatch.note}; ${note}`,
@@ -20076,23 +20116,25 @@ async function appendServedChannelPublish(deps, input, dispatch, redeployed) {
20076
20116
  if (input.publishStatus !== "success") {
20077
20117
  return withNote(`served channel NOT moved (publish did not succeed) \u2014 after the publish is green, run \`${remedy}\``);
20078
20118
  }
20079
- const prior = input.releaseSha ? await findPriorGreenDispatch(deps, input.repo, "get-publish.yml", input.releaseSha, [HUB_SERVED_PRODUCT]) : void 0;
20119
+ const prior = input.releaseSha ? await findPriorGreenDispatch(deps, input.repo, "get-publish.yml", input.releaseSha, [HUB_SERVED_PRODUCT, "pointer"]) : void 0;
20080
20120
  let runRow;
20081
20121
  let runStatus;
20082
20122
  if (prior) {
20083
20123
  runRow = { workflow: "get-publish.yml", runId: prior.runId, ...prior.runUrl ? { runUrl: prior.runUrl } : {} };
20084
20124
  runStatus = "success";
20085
20125
  } else {
20126
+ const version = input.tag?.replace(/^v/, "") ?? (deps.readLauncherVersion ?? readLocalLauncherVersion)();
20127
+ if (!version) return withNote("the local launcher version could not be read \u2014 nothing was compared, so the move is unverified");
20086
20128
  const since = (deps.now ?? Date.now)();
20087
20129
  try {
20088
- await deps.run("gh", ["workflow", "run", "get-publish.yml", "--repo", input.repo, "--ref", "main", "-f", `product=${HUB_SERVED_PRODUCT}`]);
20130
+ await deps.run("gh", ["workflow", "run", "get-publish.yml", "--repo", input.repo, "--ref", "main", "-f", `product=${HUB_SERVED_PRODUCT}`, "-f", "mode=pointer", "-f", `version=${version}`]);
20089
20131
  } catch (e) {
20090
20132
  const msg = e instanceof Error ? e.message : String(e);
20091
- return withNote(`get-publish dispatch FAILED: ${msg}. The release itself landed, but the SERVED CHANNEL IS UNCHANGED \u2014 recover with \`${remedy}\``, "failure", planeFailureRow("get-publish.yml", `dispatch refused: ${msg}`));
20133
+ return withNote(`get-publish dispatch FAILED: ${msg}. The release itself landed, but the SERVED CHANNEL IS UNCHANGED \u2014 recover with \`${pointerRemedy}\``, "failure", planeFailureRow("get-publish.yml", `dispatch refused: ${msg}`));
20092
20134
  }
20093
- const correlated = await correlateGetPublishRun(deps, since, HUB_SERVED_PRODUCT);
20094
- if (correlated.read === "failed") {
20095
- return withNote(`get-publish dispatched but its run could not be READ: ${correlated.error}. Check the Actions tab; if it never ran, recover with \`${remedy}\``, "failure", planeFailureRow("get-publish.yml", `dispatched, but its run could not be read`));
20135
+ const correlated = await correlateGetPublishRun(deps, since, HUB_SERVED_PRODUCT, "pointer");
20136
+ if (correlated.read !== "found") {
20137
+ return withNote(`get-publish dispatched but its run could not be READ: ${correlated.error ?? "no matching run appeared"}. Check the Actions tab; if it never ran, recover with \`${pointerRemedy}\``, "failure", planeFailureRow("get-publish.yml", `dispatched, but its run could not be read`));
20096
20138
  }
20097
20139
  const { runId, runUrl } = correlated;
20098
20140
  runRow = { workflow: "get-publish.yml", ...runId ? { runId } : {}, ...runUrl ? { runUrl } : {} };
@@ -20108,7 +20150,7 @@ async function appendServedChannelPublish(deps, input, dispatch, redeployed) {
20108
20150
  if (runStatus !== "success") {
20109
20151
  return {
20110
20152
  ...dispatch,
20111
- note: `${dispatch.note}; get-publish.yml ${runStatus} \u2014 the SERVED CHANNEL IS UNCHANGED; recover with \`${remedy}\``,
20153
+ note: `${dispatch.note}; get-publish.yml ${runStatus} \u2014 the SERVED CHANNEL IS UNCHANGED; recover with \`${pointerRemedy}\``,
20112
20154
  workflowRuns: [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: runStatus }],
20113
20155
  deployStatus: "failure"
20114
20156
  };
@@ -20145,7 +20187,7 @@ async function appendServedChannelPublish(deps, input, dispatch, redeployed) {
20145
20187
  if (expected && probe.version !== expected) {
20146
20188
  return {
20147
20189
  ...dispatch,
20148
- note: `${dispatch.note}; get-publish.yml succeeded but the served channel still reports ${probe.version} (expected launcher ${expected}) \u2014 THE CHANNEL DID NOT MOVE; recover with \`${remedy}\``,
20190
+ note: `${dispatch.note}; get-publish.yml succeeded but the served channel still reports ${probe.version} (expected launcher ${expected}) \u2014 THE CHANNEL DID NOT MOVE; recover with \`${pointerRemedy}\``,
20149
20191
  workflowRuns: evidence("failure", `${channelUrl} reports ${probe.version}, expected ${expected}`),
20150
20192
  deployStatus: "failure"
20151
20193
  };
@@ -20286,14 +20328,33 @@ function mergeServedLegs(base, first, second) {
20286
20328
  ...first.deployStatus === "failure" || second.deployStatus === "failure" ? { deployStatus: "failure" } : {}
20287
20329
  };
20288
20330
  }
20331
+ function prepareServedChannel(deps, input) {
20332
+ const base = { note: "", deployStatus: "success", workflowRuns: [] };
20333
+ const prepared = Promise.all([
20334
+ appendMmiGetRedeploy(deps, { ...input, publishStatus: "success" }, base),
20335
+ appendServedChannelStage(deps, input, base)
20336
+ ]).then(([box, stage]) => mergeServedLegs(base, box, stage));
20337
+ prepared.catch(() => void 0);
20338
+ return prepared;
20339
+ }
20289
20340
  async function appendServedChannelLegs(deps, input, dispatch) {
20290
20341
  const publishStatus = input.publish?.state === "complete" ? "success" : "failure";
20291
- const redeployed = appendMmiGetRedeploy(deps, { repo: input.repo, publishStatus, watch: input.watch, releaseSha: input.releaseSha }, dispatch);
20292
- const [box, channel] = await Promise.all([
20293
- redeployed,
20294
- appendServedChannelPublish(deps, { repo: input.repo, publishStatus, watch: input.watch, releaseSha: input.releaseSha, tag: input.tag }, dispatch, redeployed)
20295
- ]);
20296
- const out = mergeServedLegs(dispatch, box, channel);
20342
+ const prepared = await (input.prepared ?? prepareServedChannel(deps, input));
20343
+ if (!isHubControlRepo(input.repo)) {
20344
+ const [home2, proof2] = await Promise.all([
20345
+ appendInstallHomePublish(deps, { repo: input.repo, installer: input.installer, publishStatus, watch: input.watch }, dispatch),
20346
+ appendServedChannelProof(deps, { repo: input.repo, tag: input.tag, shipped: shipLegsGreen(input.deploy, input.publish), installer: input.installer }, dispatch)
20347
+ ]);
20348
+ return mergeServedLegs(dispatch, home2, proof2);
20349
+ }
20350
+ const stageGreen = (prepared.workflowRuns ?? []).filter((r) => r.workflow === "get-publish.yml").every((r) => r.conclusion === "success");
20351
+ const channel = stageGreen || !isHubControlRepo(input.repo) || publishStatus !== "success" ? await appendServedChannelPublish(deps, { repo: input.repo, publishStatus, watch: input.watch, releaseSha: input.releaseSha, tag: input.tag }, dispatch, Promise.resolve(prepared)) : { ...dispatch, note: `${dispatch.note}; served channel NOT moved (stage not green) \u2014 recover with \`${getPublishDispatchCommand(input.repo)} -f mode=stage\`, then \`${getPublishDispatchCommand(input.repo)} -f mode=pointer -f version=${input.tag.replace(/^v/, "")}\`` };
20352
+ const out = mergeServedLegs(dispatch, {
20353
+ ...dispatch,
20354
+ note: dispatch.note + prepared.note,
20355
+ workflowRuns: [...dispatch.workflowRuns ?? [], ...prepared.workflowRuns ?? []],
20356
+ ...prepared.deployStatus === "failure" ? { deployStatus: "failure" } : {}
20357
+ }, channel);
20297
20358
  const [home, proof] = await Promise.all([
20298
20359
  appendInstallHomePublish(deps, { repo: input.repo, installer: input.installer, publishStatus, watch: input.watch }, out),
20299
20360
  appendServedChannelProof(deps, { repo: input.repo, tag: input.tag, shipped: shipLegsGreen(input.deploy, input.publish), installer: input.installer }, out)
@@ -25317,6 +25378,7 @@ ${recovery.note}`), recoveryInput);
25317
25378
  await verifyPublishedRelease(deps, ctx.repo, tag, "main", releaseSha);
25318
25379
  await recordPhase(ledger, deps, ledgerAnchors, "githubRelease", phaseEntry({ state: "complete", sha: releaseSha, runUrl: releaseUrl, note: `verified GitHub Release ${tag} against ${releaseSha.slice(0, 12)}` }), { landed: "the immutable tag, the green required-check wall, and the origin/main fast-forward" });
25319
25380
  let alignment = alignEarly ? await alignEarly() : void 0;
25381
+ const prepared = prepareServedChannel(deps, { repo: ctx.repo, releaseSha, watch });
25320
25382
  const [deployDispatch0, publishDispatch0] = await joinDeployAndPublish(
25321
25383
  dispatchDeploy(deps, ctx, "main", "main", deployModel, watch, autoRunSince, releaseSha, "report", meta.publishDir),
25322
25384
  observePublishIfRequired(deps, ctx, meta, deployModel, "main", releaseSha, watch)
@@ -25367,7 +25429,7 @@ ${recovery.note}`), recoveryInput);
25367
25429
  const aligning = watch && alignment ? awaitAlignmentsLanded(deps, ctx, alignment) : Promise.resolve(alignment);
25368
25430
  const dispatch = await appendServedChannelLegs(
25369
25431
  deps,
25370
- { repo: ctx.repo, tag, releaseSha, watch, deploy: deployLeg, publish: publishLeg, installer: meta.installer },
25432
+ { repo: ctx.repo, tag, releaseSha, watch, deploy: deployLeg, publish: publishLeg, installer: meta.installer, prepared },
25371
25433
  appendPublishDispatch(deployDispatch, publishDispatch)
25372
25434
  );
25373
25435
  alignment = await aligning;
@@ -25859,6 +25921,7 @@ ${recovery.note}`), recoveryInput);
25859
25921
  await recordPhase(ledger, deps, anchors, "githubRelease", phaseEntry({ state: "complete", sha: tagSha, runUrl: releaseUrl, note: "verified the Release published against the tagged commit" }), { strict: ledgerMode === "strict", landed: "the immutable tag, the re-proven wall, and the origin/main push" });
25860
25922
  let devRollForward = await rollDevelopmentForward(deps, ctx, tag);
25861
25923
  let rcAlignment = !directTrack && branchHints.hasRcBranch ? await alignRcForward(deps, ctx, tag) : void 0;
25924
+ const channelPreparation = prepareServedChannel(deps, { repo: ctx.repo, releaseSha: tagSha, watch });
25862
25925
  const [deployDispatch0, publishDispatch0] = await joinDeployAndPublish(
25863
25926
  dispatchDeploy(deps, ctx, "main", "main", deployModel, watch, autoRunSince, tagSha, "report", meta.publishDir),
25864
25927
  observePublishIfRequired(deps, ctx, meta, deployModel, "main", tagSha, watch)
@@ -25902,7 +25965,7 @@ ${recovery.note}`), recoveryInput);
25902
25965
  }
25903
25966
  const dispatch = await appendServedChannelLegs(
25904
25967
  deps,
25905
- { repo: ctx.repo, tag, releaseSha: tagSha, watch, deploy: deployLeg, publish: publishLeg, installer: meta.installer },
25968
+ { repo: ctx.repo, tag, releaseSha: tagSha, watch, deploy: deployLeg, publish: publishLeg, installer: meta.installer, prepared: channelPreparation },
25906
25969
  appendPublishDispatch(deployDispatch, publishDispatch)
25907
25970
  );
25908
25971
  const announcing = announceShippedRelease(
@@ -46640,7 +46703,7 @@ async function appendHotfixServedChannel(deps, input, runs, note) {
46640
46703
  const before = hotfixDispatchFromRuns(runs, note);
46641
46704
  const after = await appendServedChannelLegs(
46642
46705
  deps,
46643
- { repo: input.repo, tag: input.tag, releaseSha: input.sha, watch: true, deploy: input.deploy, publish: input.publish, installer: input.installer },
46706
+ { repo: input.repo, tag: input.tag, releaseSha: input.sha, watch: true, deploy: input.deploy, publish: input.publish, installer: input.installer, prepared: input.prepared },
46644
46707
  before
46645
46708
  );
46646
46709
  for (const row of servedChannelRowsAfter(before, after)) {
@@ -46873,6 +46936,7 @@ ${decision.note}`);
46873
46936
  releaseNote = `Release ${tag} created (target main)`;
46874
46937
  }
46875
46938
  await recordHotfixPhase(ledger, anchors, "githubRelease", { state: "complete", sha: mergedSha, note: releaseNote }, "the immutable tag, the green wall, and origin/main");
46939
+ const prepared = prepareServedChannel(deps, { repo: ctx.repo, releaseSha: mergedSha, watch: true });
46876
46940
  const runs = [];
46877
46941
  let deployNote;
46878
46942
  let tenantPublishDispatch = null;
@@ -46931,7 +46995,7 @@ ${decision.note}`);
46931
46995
  );
46932
46996
  await recordHotfixPhase(ledger, anchors, "deploy", ledgerInputs.deploy, "the immutable tag, the green wall, origin/main, and the GitHub Release");
46933
46997
  await recordHotfixPhase(ledger, anchors, "publish", ledgerInputs.publish, "the immutable tag, the green wall, origin/main, the GitHub Release, and the deploy path");
46934
- deployNote = await appendHotfixServedChannel(deps, { repo: ctx.repo, tag, sha: mergedSha, deploy: ledgerInputs.deploy, publish: ledgerInputs.publish, installer: meta.installer }, runs, deployNote);
46998
+ deployNote = await appendHotfixServedChannel(deps, { repo: ctx.repo, tag, sha: mergedSha, deploy: ledgerInputs.deploy, publish: ledgerInputs.publish, installer: meta.installer, prepared }, runs, deployNote);
46935
46999
  const announceNote = releaseExists ? void 0 : await announceShippedRelease(
46936
47000
  deps,
46937
47001
  { repo: ctx.repo, tag, summaryFile: options.announceSummaryFile },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.5.28",
3
+ "version": "4.5.30",
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",