@mutmutco/cli 4.5.17 → 4.5.19
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 +88 -32
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -9544,11 +9544,16 @@ function resolveUseEnvNames(keys, name) {
|
|
|
9544
9544
|
function referencesDollarEnv(arg, name) {
|
|
9545
9545
|
return arg.includes(`\${${name}}`) || new RegExp(`\\$${name}(?![A-Za-z0-9_])`).test(arg);
|
|
9546
9546
|
}
|
|
9547
|
+
function isShellScriptCommand(command) {
|
|
9548
|
+
const exe = (command[0] ?? "").split(/[/\\]/).pop()?.toLowerCase().replace(/\.exe$/, "") ?? "";
|
|
9549
|
+
return /^(?:ba|z|da|k)?sh$/.test(exe) && command.slice(1).some((a) => /^-[a-z]*c[a-z]*$/i.test(a));
|
|
9550
|
+
}
|
|
9547
9551
|
function secretsUseDollarHint(command, envNames) {
|
|
9552
|
+
if (isShellScriptCommand(command)) return null;
|
|
9548
9553
|
for (const arg of command) {
|
|
9549
9554
|
for (const name of envNames) {
|
|
9550
9555
|
if (!referencesDollarEnv(arg, name)) continue;
|
|
9551
|
-
return `secrets use: $${name} is injected into the CHILD's env,
|
|
9556
|
+
return `secrets use: ${command[0]} receives the literal text $${name} \u2014 the value is injected into the CHILD's env, and ${command[0]} does not expand variables in its arguments. Wrap the consumer in a shell so expansion happens inside: bash -c 'your-command "$${name}"'`;
|
|
9552
9557
|
}
|
|
9553
9558
|
}
|
|
9554
9559
|
return null;
|
|
@@ -10865,6 +10870,9 @@ function readSessionId(env) {
|
|
|
10865
10870
|
}
|
|
10866
10871
|
return void 0;
|
|
10867
10872
|
}
|
|
10873
|
+
function readSessionIdSource(env) {
|
|
10874
|
+
return SESSION_ID_ENV_VARS.find((key) => Boolean(env[key]?.trim()));
|
|
10875
|
+
}
|
|
10868
10876
|
var synthesizedSessionId;
|
|
10869
10877
|
function fallbackSessionId(surface) {
|
|
10870
10878
|
if (!synthesizedSessionId) {
|
|
@@ -12581,15 +12589,36 @@ async function moveBoardItem(options, deps = {}) {
|
|
|
12581
12589
|
({ credential } = await updateItemSingleSelect(client, cfg.projectId, item.itemId, cfg.statusFieldId, optionId));
|
|
12582
12590
|
} catch (e) {
|
|
12583
12591
|
if (options.status === "Done" && isArchivedItemRefusal(ghError(e))) {
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
|
|
12592
|
+
if (item.status === "Done") {
|
|
12593
|
+
return {
|
|
12594
|
+
item,
|
|
12595
|
+
viewer: lookup.viewer,
|
|
12596
|
+
repo: currentRepo,
|
|
12597
|
+
status: "Done",
|
|
12598
|
+
partial: false,
|
|
12599
|
+
archived: true,
|
|
12600
|
+
warning: `${item.ref} is archived and already Done; no status write was needed`
|
|
12601
|
+
};
|
|
12602
|
+
}
|
|
12603
|
+
try {
|
|
12604
|
+
await client.graphql(
|
|
12605
|
+
`mutation($projectId: ID!, $itemId: ID!) { unarchiveProjectV2Item(input: { projectId: $projectId, itemId: $itemId }) { item { id } } }`,
|
|
12606
|
+
{ projectId: cfg.projectId, itemId: item.itemId }
|
|
12607
|
+
);
|
|
12608
|
+
({ credential } = await updateItemSingleSelect(client, cfg.projectId, item.itemId, cfg.statusFieldId, optionId));
|
|
12609
|
+
return {
|
|
12610
|
+
item: { ...item, status: "Done", statusOptionId: optionId },
|
|
12611
|
+
viewer: lookup.viewer,
|
|
12612
|
+
repo: currentRepo,
|
|
12613
|
+
status: "Done",
|
|
12614
|
+
partial: false,
|
|
12615
|
+
credential
|
|
12616
|
+
};
|
|
12617
|
+
} catch (recoveryError) {
|
|
12618
|
+
const warning2 = `partial move: ${item.ref} was archived at '${item.status}' and could not be unarchived and moved to Done (${ghError(recoveryError)})`;
|
|
12619
|
+
if (!options.allowPartial) throw new Error(warning2);
|
|
12620
|
+
return { item, viewer: lookup.viewer, repo: currentRepo, status: item.status, partial: true, warning: warning2 };
|
|
12621
|
+
}
|
|
12593
12622
|
}
|
|
12594
12623
|
const warning = `partial move: ${item.ref} status was not changed to ${options.status} (${ghError(e)})`;
|
|
12595
12624
|
if (!options.allowPartial) throw new Error(warning);
|
|
@@ -12663,9 +12692,26 @@ async function prepareClaimContext(options, selectors, deps, collected, snapshot
|
|
|
12663
12692
|
items: collected.items,
|
|
12664
12693
|
writable: writableOrUnknown(writable),
|
|
12665
12694
|
report,
|
|
12666
|
-
session: deps.session ?? describeSessionIdentity()
|
|
12695
|
+
session: deps.session ?? describeSessionIdentity(),
|
|
12696
|
+
...deps.snapshot ? {
|
|
12697
|
+
freshAppItem: async (selector) => {
|
|
12698
|
+
const fresh = await collectBoardPreferringSnapshot(cfg, { repo: options.repo }, deps);
|
|
12699
|
+
if (!fresh.snapshot || fresh.collected.partial) return void 0;
|
|
12700
|
+
return fresh.collected.items.find((candidate) => candidate.repository.toLowerCase() === selector.repo.toLowerCase() && candidate.number === selector.number);
|
|
12701
|
+
}
|
|
12702
|
+
} : {}
|
|
12667
12703
|
};
|
|
12668
12704
|
}
|
|
12705
|
+
async function freshClaimItem(ctx, selector) {
|
|
12706
|
+
try {
|
|
12707
|
+
return (await fetchIssueProjectItem(ctx.client, ctx.cfg, selector)).item;
|
|
12708
|
+
} catch (e) {
|
|
12709
|
+
if (!isRateLimitRefusal(e) || !ctx.freshAppItem) throw e;
|
|
12710
|
+
const item = await ctx.freshAppItem(selector).catch(() => void 0);
|
|
12711
|
+
if (!item) throw e;
|
|
12712
|
+
return item;
|
|
12713
|
+
}
|
|
12714
|
+
}
|
|
12669
12715
|
async function claimOneBoardItem(ctx, selector, options) {
|
|
12670
12716
|
const { cfg, client, report } = ctx;
|
|
12671
12717
|
const flatItem = findBoardItem(ctx.items, selector, { owner: cfg.projectOwner, number: cfg.projectNumber });
|
|
@@ -12721,7 +12767,7 @@ async function claimOneBoardItem(ctx, selector, options) {
|
|
|
12721
12767
|
const pre = evaluateClaim(flatItem, assignedLogin);
|
|
12722
12768
|
if (!pre.ok) throw new NotClaimableError(pre.reason, { ref: flatItem.ref, status: flatItem.status });
|
|
12723
12769
|
let item = flatItem;
|
|
12724
|
-
const fresh =
|
|
12770
|
+
const fresh = await freshClaimItem(ctx, { repo: item.repository, number: item.number });
|
|
12725
12771
|
if (!fresh) throw new Error(`${item.ref} is not on this project board`);
|
|
12726
12772
|
const verdict = evaluateClaim(fresh, assignedLogin);
|
|
12727
12773
|
if (!verdict.ok) throw new NotClaimableError(verdict.reason, { ref: fresh.ref, status: fresh.status });
|
|
@@ -14838,9 +14884,10 @@ function buildRegisterPayload(repo, cls, vars, options = {}) {
|
|
|
14838
14884
|
deployModel,
|
|
14839
14885
|
// Persist the canonical stage hosts at the same write-time boundary as deployModel. Read paths never
|
|
14840
14886
|
// infer from an absent edgeDomains field.
|
|
14841
|
-
edgeDomains: inferEdgeDomainsForWrite(slug),
|
|
14887
|
+
edgeDomains: options.existing?.edgeDomains ?? inferEdgeDomainsForWrite(slug),
|
|
14842
14888
|
// #1359: always persist an explicit track so release tooling never guesses from absence alone.
|
|
14843
|
-
|
|
14889
|
+
// #7134: an explicit flag wins, then the registered track, then the class default (first bootstrap).
|
|
14890
|
+
releaseTrack: options.releaseTrack ?? (isReleaseTrack(options.existing?.releaseTrack) ? options.existing.releaseTrack : void 0) ?? resolveBootstrapReleaseTrack(cls, void 0),
|
|
14844
14891
|
// Board coords (from GraphQL at bootstrap, passed as --var by the skill).
|
|
14845
14892
|
projectOwner: vars.PROJECT_OWNER || void 0,
|
|
14846
14893
|
projectNumber: num(vars.PROJECT_NUMBER),
|
|
@@ -16115,10 +16162,10 @@ var rollout_plan_default = {
|
|
|
16115
16162
|
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)."
|
|
16116
16163
|
},
|
|
16117
16164
|
baseline: {
|
|
16118
|
-
version: "4.5.
|
|
16119
|
-
tag: "v4.5.
|
|
16120
|
-
commit: "
|
|
16121
|
-
npm: "@mutmutco/cli@4.5.
|
|
16165
|
+
version: "4.5.19",
|
|
16166
|
+
tag: "v4.5.19",
|
|
16167
|
+
commit: "513a4686091e",
|
|
16168
|
+
npm: "@mutmutco/cli@4.5.19"
|
|
16122
16169
|
},
|
|
16123
16170
|
exitCriterion: "fleet-n-of-n",
|
|
16124
16171
|
hubOnlyShortcut: "forbidden",
|
|
@@ -16135,14 +16182,14 @@ var rollout_plan_default = {
|
|
|
16135
16182
|
repo: "mutmutco/mmi-hub",
|
|
16136
16183
|
role: "canary",
|
|
16137
16184
|
schedule: "train",
|
|
16138
|
-
v3Target: "v4.5.
|
|
16185
|
+
v3Target: "v4.5.19"
|
|
16139
16186
|
}
|
|
16140
16187
|
],
|
|
16141
16188
|
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.",
|
|
16142
16189
|
rollback: {
|
|
16143
16190
|
independent: true,
|
|
16144
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
16145
|
-
v3Target: "v4.5.
|
|
16191
|
+
mechanism: "npm dist-tag latest -> 4.5.19 and redeploy the Hub Lambda from tag v4.5.19 (513a4686091e); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
16192
|
+
v3Target: "v4.5.19 (@mutmutco/cli@4.5.19, tag commit 513a4686091e \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
16146
16193
|
}
|
|
16147
16194
|
},
|
|
16148
16195
|
{
|
|
@@ -18473,14 +18520,14 @@ async function watchTenantRun(deps, runId, repo = HUB_REPO2) {
|
|
|
18473
18520
|
confirmError = e;
|
|
18474
18521
|
}
|
|
18475
18522
|
}
|
|
18476
|
-
if (!watchSaidSuccess) return "failure";
|
|
18477
18523
|
if (confirmError) {
|
|
18478
18524
|
deps.warn?.(
|
|
18479
|
-
`run ${runId} (${repo}): the confirming \`gh run view --json status,conclusion\` read FAILED (${describeReadError(confirmError)}) \u2014 \`gh run watch\` exited green, but an unconfirmed
|
|
18525
|
+
`run ${runId} (${repo}): the confirming \`gh run view --json status,conclusion\` read FAILED (${describeReadError(confirmError)}) \u2014 \`gh run watch\` exited ${watchSaidSuccess ? "green" : "non-zero"}, but an unconfirmed verdict is not a result (#2561, #7136); reporting the run as PENDING. Re-check: gh run view ${runId} --repo ${repo} (#4713).`
|
|
18480
18526
|
);
|
|
18481
18527
|
return "pending";
|
|
18482
18528
|
}
|
|
18483
|
-
|
|
18529
|
+
deps.warn?.(`run ${runId} (${repo}): \`gh run watch\` exited ${watchSaidSuccess ? "green" : "non-zero"} while the run is still in progress, reporting PENDING (#7136)`);
|
|
18530
|
+
return "pending";
|
|
18484
18531
|
}
|
|
18485
18532
|
async function fetchControlRunLog(deps, runId) {
|
|
18486
18533
|
try {
|
|
@@ -30218,14 +30265,18 @@ function registerBootstrapCommands(program3) {
|
|
|
30218
30265
|
}
|
|
30219
30266
|
let registryRequiredCheckBranches;
|
|
30220
30267
|
let registryMeta = null;
|
|
30221
|
-
|
|
30222
|
-
|
|
30268
|
+
const metaRead = await fetchProjectBySlugChecked(slug, registryClientDeps(await loadConfig()));
|
|
30269
|
+
if (metaRead.ok) {
|
|
30270
|
+
const meta = metaRead.project;
|
|
30223
30271
|
registryMeta = meta;
|
|
30224
30272
|
registryRequiredCheckBranches = meta?.requiredCheckBranches;
|
|
30225
30273
|
for (const [k, v] of Object.entries(gateConfigToVars(meta?.gate))) if (rawVars[k] == null) rawVars[k] = v;
|
|
30226
|
-
}
|
|
30274
|
+
} else if (o.execute && !onlyTarget) {
|
|
30275
|
+
return failGraceful(`bootstrap apply: registry META unreadable (${metaRead.error}); refusing the live registry write, which would reset a registered project's releaseTrack and edgeDomains to class defaults (#7138)`);
|
|
30276
|
+
} else {
|
|
30277
|
+
console.error(`bootstrap apply: registry META unreadable (${metaRead.error}); this plan assumes an unregistered project and --execute will refuse (#7138)`);
|
|
30227
30278
|
}
|
|
30228
|
-
const vars = withDerivedRepoVars(rawVars, parsedRepo, o.class,
|
|
30279
|
+
const vars = withDerivedRepoVars(rawVars, parsedRepo, o.class, effectiveTrack, registryRequiredCheckBranches, registryMeta?.requiredChecks);
|
|
30229
30280
|
if (!vars.PROJECT_ID) {
|
|
30230
30281
|
try {
|
|
30231
30282
|
const r = await gh(linkedProjectsQueryArgs(parsedRepo.owner, parsedRepo.name));
|
|
@@ -30591,7 +30642,9 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
|
|
|
30591
30642
|
projectType: o.projectType || registryMeta?.projectType || void 0,
|
|
30592
30643
|
// #6090: META is authoritative on a rerun
|
|
30593
30644
|
deployModel: o.deployModel || registryMeta?.deployModel || void 0,
|
|
30594
|
-
|
|
30645
|
+
// #7134: explicit flag only; the registered track and edge domains win on a re-run.
|
|
30646
|
+
releaseTrack: o.releaseTrack || void 0,
|
|
30647
|
+
existing: registryMeta
|
|
30595
30648
|
});
|
|
30596
30649
|
} catch (e) {
|
|
30597
30650
|
return fail(`bootstrap apply: ${e.message}`);
|
|
@@ -33115,7 +33168,8 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
33115
33168
|
let board = { ok: false, detail: "no config" };
|
|
33116
33169
|
try {
|
|
33117
33170
|
if (cfg.sagaApiUrl) {
|
|
33118
|
-
const
|
|
33171
|
+
const boardCfg = await loadConfigOrDiscover();
|
|
33172
|
+
const report = await readBoard({ config: boardCfg }, { snapshot: registryClientDeps(boardCfg) });
|
|
33119
33173
|
const total = report.primary.claimable.length + report.primary.userOwned.length + report.primary.taken.length;
|
|
33120
33174
|
board = { ok: true, detail: `board has ${total} active items (${report.primary.claimable.length} claimable, ${report.primary.userOwned.length} yours)` };
|
|
33121
33175
|
} else {
|
|
@@ -41149,6 +41203,8 @@ async function prCreateClaimRefusal(body, repoOption, deps = {}) {
|
|
|
41149
41203
|
const repo = await requireRepo(repoOption);
|
|
41150
41204
|
const { client, throttledPaths } = withRateLimitRetry(deps.client ?? defaultGitHubClient(), deps);
|
|
41151
41205
|
const actor = deps.actor ?? describeSessionIdentity();
|
|
41206
|
+
const source = deps.actorSource ?? (deps.actor ? "the caller-supplied identity" : readSessionIdSource(process.env) ?? "a synthesized fallback");
|
|
41207
|
+
const requester = `requesting lane: ${actor.surface}/${actor.session}@${actor.host} (session id from ${source})`;
|
|
41152
41208
|
const checkContest = deps.checkContest ?? checkLaneContest;
|
|
41153
41209
|
for (const number of issues) {
|
|
41154
41210
|
const state = await client.rest("GET", `repos/${repo}/issues/${number}`).then((issue) => issue?.state, () => void 0);
|
|
@@ -41166,10 +41222,10 @@ async function prCreateClaimRefusal(body, repoOption, deps = {}) {
|
|
|
41166
41222
|
contest.unverifiable.length ? `liveness could not be fully verified (failed to read: ${contest.unverifiable.join(", ")})` : ""
|
|
41167
41223
|
].filter(Boolean).join("; ");
|
|
41168
41224
|
if (!contest.marker && !contest.unverifiable.includes("comments")) {
|
|
41169
|
-
return `pr create: REFUSED \u2014 ${ref} carries no claim marker (its claim-marker comments were read from ${commentsPath} and name no holder) and shows ${kinds} work evidence (${why}); if this work is yours, \`mmi-cli oracle board claim ${ref} --force\` re-stamps your lane's marker and clears the refusal (move it to In Progress first if it sits In Review), or drop the closing line before creating this PR`;
|
|
41225
|
+
return `pr create: REFUSED \u2014 ${ref} carries no claim marker (its claim-marker comments were read from ${commentsPath} and name no holder) and shows ${kinds} work evidence (${why}); if this work is yours, \`mmi-cli oracle board claim ${ref} --force\` re-stamps your lane's marker and clears the refusal (move it to In Progress first if it sits In Review), or drop the closing line before creating this PR; ${requester}`;
|
|
41170
41226
|
}
|
|
41171
41227
|
const holder = contest.marker ? `lane ${describeClaimMarker(contest.marker)} (claim marker read from ${commentsPath}${posted})` : `an unreadable lane (its claim-marker comments could not be read${throttledPaths.has(commentsPath) ? " \u2014 GitHub rate-limited the read (HTTP 403) and the bounded reset-aware retry could not clear it" : ""}; the holder may be this very lane)`;
|
|
41172
|
-
return `pr create: REFUSED \u2014 ${ref} is held by ${holder} with ${kinds} work evidence (${why}); run \`mmi-cli oracle board claim ${ref} --force\` (move it to In Progress first if it sits In Review) or drop the closing line before creating this PR`;
|
|
41228
|
+
return `pr create: REFUSED \u2014 ${ref} is held by ${holder} with ${kinds} work evidence (${why}); run \`mmi-cli oracle board claim ${ref} --force\` (move it to In Progress first if it sits In Review) or drop the closing line before creating this PR; ${requester}`;
|
|
41173
41229
|
}
|
|
41174
41230
|
return void 0;
|
|
41175
41231
|
}
|
package/package.json
CHANGED