@mutmutco/cli 4.4.10 → 4.4.11

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 +41 -162
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -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.10",
15942
- tag: "v4.4.10",
15943
- commit: "187ab08827c9",
15944
- npm: "@mutmutco/cli@4.4.10"
15941
+ version: "4.4.11",
15942
+ tag: "v4.4.11",
15943
+ commit: "47113a5df32d",
15944
+ npm: "@mutmutco/cli@4.4.11"
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.10"
15961
+ v3Target: "v4.4.11"
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.10 and redeploy the Hub Lambda from tag v4.4.10 (187ab08827c9); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15968
- v3Target: "v4.4.10 (@mutmutco/cli@4.4.10, tag commit 187ab08827c9 \u2014 last known-good release carrying the repo-index v4-only contract)"
15967
+ mechanism: "npm dist-tag latest -> 4.4.11 and redeploy the Hub Lambda from tag v4.4.11 (47113a5df32d); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15968
+ v3Target: "v4.4.11 (@mutmutco/cli@4.4.11, tag commit 47113a5df32d \u2014 last known-good release carrying the repo-index v4-only contract)"
15969
15969
  }
15970
15970
  },
15971
15971
  {
@@ -18235,7 +18235,7 @@ async function correlateRun(deps, args) {
18235
18235
  });
18236
18236
  const tokens2 = args.mode === "dispatch" ? args.titleIncludes : void 0;
18237
18237
  const titleMatch = tokens2 && tokens2.length ? candidates.find((c) => tokens2.every((t) => (c.row.displayTitle ?? "").includes(t))) : void 0;
18238
- const match = titleMatch ?? (windowProvenWhole ? candidates[0] : void 0);
18238
+ const match = titleMatch ?? (args.mode === "workflow" || windowProvenWhole ? candidates[0] : void 0);
18239
18239
  if (match) return { runId: match.row.databaseId, runUrl: match.row.url, read: "found" };
18240
18240
  if (!windowProvenWhole) truncatedWindow = true;
18241
18241
  }
@@ -18256,9 +18256,6 @@ async function correlateRun(deps, args) {
18256
18256
  function correlateTenantRun(deps, since, titleIncludes) {
18257
18257
  return correlateRun(deps, { workflow: "tenant-deploy.yml", since, mode: "dispatch", titleIncludes });
18258
18258
  }
18259
- function correlatePublishRun(deps, since, titleIncludes) {
18260
- return correlateRun(deps, { workflow: "tenant-publish.yml", since, mode: "dispatch", titleIncludes });
18261
- }
18262
18259
  function correlateGetPublishRun(deps, since, product) {
18263
18260
  return correlateRun(deps, { workflow: "get-publish.yml", since, mode: "dispatch", titleIncludes: [product] });
18264
18261
  }
@@ -18877,134 +18874,19 @@ async function dispatchTenantDeployWithRetry(deps, input) {
18877
18874
  }
18878
18875
  }
18879
18876
  }
18880
- function tenantPublishRecoveryCommand(slug, repo, ref, stage, publishDir) {
18881
- const parts = [
18882
- `gh workflow run tenant-publish.yml --repo ${HUB_REPO2}`,
18883
- `-f slug=${slug}`,
18884
- `-f repo=${repo}`,
18885
- `-f ref=${ref}`,
18886
- `-f stage=${stage}`
18887
- ];
18888
- if (publishDir && publishDir !== ".") parts.push(`-f publishDir=${publishDir}`);
18889
- return parts.join(" ");
18890
- }
18891
- var PUBLISH_CONFLICT_SIGNAL = /E409|cannot publish over|previously published|\b(?:HTTP|status|code)\s*[:=]?\s*409\b|\b409\s+Conflict\b/i;
18892
- var PUBLISH_LOG_RETRY_ATTEMPTS = 4;
18893
- var PUBLISH_LOG_RETRY_DELAY_MS = 6e3;
18894
- function publishRegistryTarget(meta, repo, publishRef) {
18895
- const version = publishRef.replace(/^v/, "");
18896
- if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) return void 0;
18897
- try {
18898
- return { name: resolveReleaseAbortNpmPackage(meta, repo), version };
18899
- } catch {
18900
- return void 0;
18901
- }
18902
- }
18903
- async function provePublishedVersion(deps, target) {
18904
- try {
18905
- return clean2(await deps.run("npm", ["view", `${target.name}@${target.version}`, "version"])) === target.version;
18906
- } catch {
18907
- return false;
18908
- }
18909
- }
18910
- function publishReconcileFailureNote(input) {
18911
- if (input.signal && input.target) {
18912
- return `run FAILED and its log shows a publish conflict (${input.signal}) but the registry does not report ${input.target.name}@${input.target.version} live \u2014 the failure stands (verify with an authenticated registry read, never a bare npm view \u2014 see docs/Guides/train-troubleshooting.md#publish-private-package)`;
18913
- }
18914
- if (input.signal) {
18915
- return `run FAILED and its log shows a publish conflict (${input.signal}) but no npm package name could be resolved to prove the exact version live \u2014 the failure stands`;
18916
- }
18917
- if (input.readError) {
18918
- return `run FAILED and its log could not be read (${input.readError}) \u2014 the failure stands`;
18919
- }
18920
- return 'run FAILED and its log shows no publish-conflict signal (E409, "cannot publish over", "previously published", or a 409 status) \u2014 the failure stands';
18921
- }
18922
- async function reconcilePublishFailure(deps, runId, target) {
18923
- if (runId == null) {
18924
- return { status: "failure", note: "the failed publish run had no run id to re-read \u2014 the failure stands" };
18925
- }
18926
- const sleep2 = resolveSleep(deps);
18927
- let signal;
18928
- let readError;
18929
- for (let attempt = 0; attempt < PUBLISH_LOG_RETRY_ATTEMPTS; attempt++) {
18930
- if (attempt > 0) await sleep2(PUBLISH_LOG_RETRY_DELAY_MS);
18931
- const log = await fetchControlRunLog(deps, runId);
18932
- if (!log.ok) {
18933
- readError = log.error;
18934
- continue;
18935
- }
18936
- const match = log.log.match(PUBLISH_CONFLICT_SIGNAL);
18937
- if (!match) continue;
18938
- signal = match[0];
18939
- if (target && await provePublishedVersion(deps, target)) {
18940
- return {
18941
- status: "success",
18942
- note: `registry reports ${target.name}@${target.version} live; run failed with a publish conflict (${signal}) \u2014 #2428/#6777`
18943
- };
18944
- }
18945
- }
18946
- return { status: "failure", note: publishReconcileFailureNote({ signal, readError, target }) };
18947
- }
18948
- async function dispatchTenantPublish(deps, ctx, stage, ref, watch, dispatchFailure = "throw", publishDir, publishTarget) {
18949
- deps.warn?.("Release: Starting package publishing");
18950
- const since = (deps.now ?? Date.now)();
18951
- const dispatchArgs = [
18952
- "workflow",
18953
- "run",
18954
- "tenant-publish.yml",
18955
- "--repo",
18956
- HUB_REPO2,
18957
- "-f",
18958
- `slug=${ctx.slug}`,
18959
- "-f",
18960
- `repo=${ctx.repo}`,
18961
- "-f",
18962
- `ref=${ref}`,
18963
- "-f",
18964
- `stage=${stage}`
18965
- ];
18966
- if (publishDir && publishDir !== ".") dispatchArgs.push("-f", `publishDir=${publishDir}`);
18967
- try {
18968
- await deps.run("gh", dispatchArgs);
18969
- } catch (e) {
18970
- if (dispatchFailure === "throw") throw e;
18971
- const msg = e instanceof Error ? e.message : String(e);
18972
- const recovery = tenantPublishRecoveryCommand(ctx.slug, ctx.repo, ref, stage, publishDir);
18973
- return {
18974
- note: `tenant-publish dispatch FAILED: ${msg}. The promotion itself landed \u2014 recover with \`${recovery}\``,
18975
- deployStatus: "failure"
18976
- };
18977
- }
18978
- const correlated = await correlatePublishRun(deps, since, [ctx.slug, stage]);
18979
- if (correlated.read === "failed") {
18980
- return {
18981
- note: `tenant-publish dispatched (slug=${ctx.slug}, ref=${ref}, stage=${stage}) but its run could not be READ: ${correlated.error}. The promotion itself landed \u2014 check the Actions tab, and if the publish never ran, recover with \`${tenantPublishRecoveryCommand(ctx.slug, ctx.repo, ref, stage, publishDir)}\``,
18982
- deployStatus: "failure"
18983
- };
18984
- }
18985
- const { runId, runUrl } = correlated;
18986
- let deployStatus = watch ? await watchTenantRun(deps, runId) : "pending";
18987
- let note = `dispatched tenant-publish.yml (slug=${ctx.slug}, ref=${ref}, stage=${stage})`;
18988
- if (deployStatus === "failure") {
18989
- const reconciled = await reconcilePublishFailure(deps, runId, publishTarget);
18990
- deployStatus = reconciled.status;
18991
- note = `${note}; ${reconciled.note}`;
18992
- }
18993
- return { note, runId, runUrl, deployStatus };
18994
- }
18995
- async function dispatchPublishIfRequired(deps, ctx, meta, model, stage, publishRef, watch, dispatchFailure) {
18877
+ async function observePublishIfRequired(deps, ctx, meta, model, stage, releaseSha, watch) {
18996
18878
  if (!meta.publishRequired || stage !== "main") return null;
18997
18879
  if (model !== "tenant-container" && model !== "solo-container") return null;
18998
- return dispatchTenantPublish(
18999
- deps,
19000
- ctx,
19001
- stage,
19002
- publishRef,
19003
- watch,
19004
- dispatchFailure,
19005
- meta.publishDir,
19006
- publishRegistryTarget(meta, ctx.repo, publishRef)
19007
- );
18880
+ const target = { workflow: "publish.yml", event: "release" };
18881
+ const workflowRuns = watch ? await watchOwnWorkflowRuns(deps, ctx.repo, [target], 0, releaseSha) : [await listTargetRun(deps, ctx.repo, target, releaseSha)];
18882
+ const primary = workflowRuns[0];
18883
+ return {
18884
+ note: `observed ${ctx.repo} release-triggered publish.yml for ${releaseSha}`,
18885
+ runId: primary?.runId,
18886
+ runUrl: primary?.runUrl,
18887
+ workflowRuns,
18888
+ deployStatus: aggregateWorkflowRuns(workflowRuns)
18889
+ };
19008
18890
  }
19009
18891
  function appendPublishDispatch(deploy, publish) {
19010
18892
  if (!publish) return deploy;
@@ -19012,7 +18894,7 @@ function appendPublishDispatch(deploy, publish) {
19012
18894
  note: `${deploy.note}; ${publish.note}`,
19013
18895
  runId: deploy.runId,
19014
18896
  runUrl: deploy.runUrl,
19015
- workflowRuns: [...deploy.workflowRuns ?? [], ...publish.workflowRuns ?? [{ workflow: "tenant-publish.yml", runId: publish.runId, runUrl: publish.runUrl, conclusion: publish.deployStatus }]],
18897
+ workflowRuns: [...deploy.workflowRuns ?? [], ...publish.workflowRuns ?? [{ workflow: "publish.yml", runId: publish.runId, runUrl: publish.runUrl, conclusion: publish.deployStatus }]],
19016
18898
  deployStatus: deploy.deployStatus === "failure" || publish.deployStatus === "failure" ? "failure" : deploy.deployStatus === "pending" || publish.deployStatus === "pending" ? "pending" : "success"
19017
18899
  };
19018
18900
  }
@@ -21580,7 +21462,8 @@ function publishPhaseInput(model, dispatch, publishDispatch, publishRequired, op
21580
21462
  const sha = opts.releaseSha;
21581
21463
  const publishRow = publishDispatch ? {
21582
21464
  state: publishDispatch.deployStatus === "success" ? "complete" : publishDispatch.deployStatus === "failure" ? "failed" : "pending",
21583
- ...runMetadata({ runId: publishDispatch.runId, runUrl: publishDispatch.runUrl, workflow: "tenant-publish.yml", event: "workflow_dispatch" }, HUB_REPO2, "dispatch"),
21465
+ ...sha ? { sha } : {},
21466
+ ...runMetadata({ runId: publishDispatch.runId, runUrl: publishDispatch.runUrl, workflow: "publish.yml", event: "release" }, opts.repo ?? "", "sha"),
21584
21467
  note: publishDispatch.note
21585
21468
  } : void 0;
21586
21469
  switch (model) {
@@ -23720,29 +23603,23 @@ ${recovery.note}`), recoveryInput);
23720
23603
  await recordPhase(ledger, deps, ledgerAnchors, "deploy", deployLeg, {
23721
23604
  landed: "the immutable tag, the green required-check wall, the origin/main fast-forward, and the verified GitHub Release"
23722
23605
  });
23723
- const publishDispatch0 = deployDispatch.deployStatus === "success" ? await dispatchPublishIfRequired(deps, ctx, meta, deployModel, "main", tag, watch, "report") : null;
23606
+ const publishDispatch0 = await observePublishIfRequired(deps, ctx, meta, deployModel, "main", releaseSha, watch);
23724
23607
  const publishDispatch = publishDispatch0 ? await recoverFailedDispatchPhase(
23725
23608
  deps,
23726
23609
  ledger,
23727
23610
  ledgerAnchors,
23728
23611
  "publish",
23729
23612
  publishDispatch0,
23730
- { repo: releaseRunRepoFor(deployModel, ctx.repo), sha: releaseSha, watched: isCentralDispatchModel(deployModel), workflow: isCentralDispatchModel(deployModel) ? "tenant-publish.yml" : "publish.yml" }
23613
+ { repo: ctx.repo, sha: releaseSha, watched: false, workflow: "publish.yml" }
23731
23614
  ) : null;
23732
- let publishSkipNote;
23733
- if (!publishDispatch && deployDispatch.deployStatus !== "success" && meta.publishRequired && (deployModel === "tenant-container" || deployModel === "solo-container")) {
23734
- publishSkipNote = deployDispatch.deployStatus === "failure" ? "box deploy failed - redeploy the box before publishing" : "box deploy not confirmed (run with --watch) - publish after the box deploy lands";
23735
- }
23736
23615
  const publishLeg = phaseEntry(publishPhaseInput(deployModel, deployDispatch, publishDispatch, meta.publishRequired === true, {
23737
23616
  releaseSha,
23738
- repo: ctx.repo,
23739
- ...publishSkipNote !== void 0 ? { skipPublishNote: publishSkipNote } : {}
23617
+ repo: ctx.repo
23740
23618
  }));
23741
23619
  await recordPhase(ledger, deps, ledgerAnchors, "publish", publishLeg, {
23742
23620
  landed: "the immutable tag, the green required-check wall, origin/main, the verified GitHub Release, and the dispatched deploy path"
23743
23621
  });
23744
23622
  let dispatch = appendPublishDispatch(deployDispatch, publishDispatch);
23745
- if (publishSkipNote) dispatch = { ...dispatch, note: `${dispatch.note}; tenant-publish.yml skipped (${publishSkipNote})` };
23746
23623
  dispatch = await appendMmiGetRedeploy(
23747
23624
  deps,
23748
23625
  { repo: ctx.repo, publishStatus: publishLeg.state === "complete" ? "success" : "failure", watch },
@@ -24072,6 +23949,15 @@ Nothing was written. Inspect ${ledger.path} (or clear it only after proving the
24072
23949
  workflowRuns: historicalRows
24073
23950
  });
24074
23951
  const phaseInputs = phaseInputsFromRunRows(deployModel, dispatch2.workflowRuns ?? historicalRows, tagSha, { repo: ctx.repo });
23952
+ const companion = await observePublishIfRequired(deps, ctx, meta, deployModel, "main", tagSha, watch);
23953
+ if (companion) {
23954
+ if (persisted) phaseInputs.deploy = persisted.phases.deploy;
23955
+ phaseInputs.publish = publishPhaseInput(deployModel, dispatch2, companion, true, { releaseSha: tagSha, repo: ctx.repo });
23956
+ historicalRows.push(...companion.workflowRuns ?? []);
23957
+ historicalDeploy = phaseInputs.deploy.state === "failed" || companion.deployStatus === "failure" ? "failure" : phaseInputs.deploy.state === "complete" && companion.deployStatus === "success" ? "success" : "pending";
23958
+ dispatch2.deployStatus = historicalDeploy;
23959
+ dispatch2.note = `historical ${deployModel} deploy/publish status: ${historicalDeploy}; ${companion.note}`;
23960
+ }
24075
23961
  if (persisted) {
24076
23962
  if (persisted.phases.deploy.superseded !== true) {
24077
23963
  await recordPhase(ledger, deps, anchors, "deploy", phaseEntry(phaseInputs.deploy), { strict: true, landed: "the verified GitHub Release and origin/main at the immutable tag SHA" });
@@ -24083,7 +23969,7 @@ Nothing was written. Inspect ${ledger.path} (or clear it only after proving the
24083
23969
  await recordPhase(ledger, deps, anchors, "promotion", phaseEntry({ state: "complete", sha: tagSha, note: "re-verified live: origin/main contains the immutable tag SHA" }), { strict: true, landed: "nothing beyond the already-public release" });
24084
23970
  }
24085
23971
  if (isJervHubRepo(ctx.repo)) steps2.push(dispatch2.note);
24086
- const announceNote2 = persisted && !shippedBefore && persisted.phases.deploy.superseded !== true && prodReleaseTargets(deployModel).length > 0 ? await announceShippedRelease(
23972
+ const announceNote2 = persisted && !shippedBefore && persisted.phases.deploy.superseded !== true && (prodReleaseTargets(deployModel).length > 0 || companion !== null) ? await announceShippedRelease(
24087
23973
  deps,
24088
23974
  { repo: ctx.repo, tag, summaryFile: options.announceSummaryFile },
24089
23975
  deployModel,
@@ -24194,14 +24080,14 @@ ${recovery.note}`), recoveryInput);
24194
24080
  strict: ledgerMode === "strict",
24195
24081
  landed: "the immutable tag, the re-proven wall, the origin/main push, and the verified GitHub Release"
24196
24082
  });
24197
- const publishDispatch0 = deployDispatch.deployStatus === "success" ? await dispatchPublishIfRequired(deps, ctx, meta, deployModel, "main", tag, watch, "report") : null;
24083
+ const publishDispatch0 = await observePublishIfRequired(deps, ctx, meta, deployModel, "main", tagSha, watch);
24198
24084
  const publishDispatch = publishDispatch0 ? await recoverFailedDispatchPhase(
24199
24085
  deps,
24200
24086
  ledger,
24201
24087
  anchors,
24202
24088
  "publish",
24203
24089
  publishDispatch0,
24204
- { repo: releaseRunRepoFor(deployModel, ctx.repo), sha: tagSha, watched: isCentralDispatchModel(deployModel), workflow: isCentralDispatchModel(deployModel) ? "tenant-publish.yml" : "publish.yml" }
24090
+ { repo: ctx.repo, sha: tagSha, watched: false, workflow: "publish.yml" }
24205
24091
  ) : null;
24206
24092
  const publishLeg = phaseEntry(publishPhaseInput(deployModel, deployDispatch, publishDispatch, meta.publishRequired === true, { releaseSha: tagSha, repo: ctx.repo }));
24207
24093
  await recordPhase(ledger, deps, anchors, "publish", publishLeg, {
@@ -44561,27 +44447,20 @@ ${decision.note}`);
44561
44447
  deploy0,
44562
44448
  { repo: releaseRunRepoFor(deployModel, ctx.repo), sha: mergedSha, watched: true, workflow: "tenant-deploy.yml" }
44563
44449
  );
44564
- const publish0 = deploy.deployStatus === "success" ? await dispatchPublishIfRequired(deps, ctx, meta, deployModel, "main", tag, true, "report") : null;
44450
+ const publish0 = await observePublishIfRequired(deps, ctx, meta, deployModel, "main", mergedSha, true);
44565
44451
  const publish = publish0 ? await recoverFailedDispatchPhase(
44566
44452
  deps,
44567
44453
  ledger,
44568
44454
  anchors,
44569
44455
  "publish",
44570
44456
  publish0,
44571
- { repo: releaseRunRepoFor(deployModel, ctx.repo), sha: mergedSha, watched: true, workflow: "tenant-publish.yml" }
44457
+ { repo: ctx.repo, sha: mergedSha, watched: false, workflow: "publish.yml" }
44572
44458
  ) : null;
44573
44459
  tenantPublishDispatch = publish;
44574
- let dispatch = appendPublishDispatch(deploy, publish);
44575
- if (!publish && deploy.deployStatus !== "success" && meta.publishRequired) {
44576
- const reason = deploy.deployStatus === "failure" ? "box deploy failed \u2014 redeploy the box before publishing" : "box deploy not confirmed (run with --watch) \u2014 publish after the box deploy lands";
44577
- dispatch = {
44578
- ...dispatch,
44579
- note: `${dispatch.note}; tenant-publish.yml skipped (${reason})`
44580
- };
44581
- }
44460
+ const dispatch = appendPublishDispatch(deploy, publish);
44582
44461
  deployNote = dispatch.note;
44583
44462
  runs.push(hotfixWorkflowRunFromDispatch("tenant-deploy.yml", deploy));
44584
- if (publish) runs.push(hotfixWorkflowRunFromDispatch("tenant-publish.yml", publish));
44463
+ if (publish) runs.push(hotfixWorkflowRunFromDispatch("publish.yml", publish));
44585
44464
  } else {
44586
44465
  deployNote = `no hotfix deploy dispatch for deployModel=${deployModel} \u2014 prod deploy is repo-specific`;
44587
44466
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.4.10",
3
+ "version": "4.4.11",
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",