@mutmutco/cli 4.3.33 → 4.3.34
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 +114 -30
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3679,6 +3679,8 @@ function buildErrorEnvelope(message2, payload) {
|
|
|
3679
3679
|
if (payload.did_you_mean !== void 0) env.did_you_mean = payload.did_you_mean;
|
|
3680
3680
|
if (payload.corrected_command !== void 0) env.corrected_command = payload.corrected_command;
|
|
3681
3681
|
if (payload.current_parent !== void 0) env.current_parent = payload.current_parent;
|
|
3682
|
+
if (payload.issue_ref !== void 0) env.issue_ref = payload.issue_ref;
|
|
3683
|
+
if (payload.board_status !== void 0) env.board_status = payload.board_status;
|
|
3682
3684
|
return env;
|
|
3683
3685
|
}
|
|
3684
3686
|
function formatErrorEnvelope(message2, payload) {
|
|
@@ -10965,6 +10967,21 @@ function boardNotFoundError(ref, board, opts = {}) {
|
|
|
10965
10967
|
const remedy = opts.remedy ?? "if it lives on a different board, pass --repo <owner/repo> for the repo that owns it";
|
|
10966
10968
|
return new Error(`${ref} ${verb} the ${board.owner} #${board.number} board; ${remedy}`);
|
|
10967
10969
|
}
|
|
10970
|
+
var NotClaimableError = class extends Error {
|
|
10971
|
+
/** The board item's status at refusal time, when the refusal was a status verdict. Absent for the
|
|
10972
|
+
* write-access and dependency refusals, whose cause is not the status. */
|
|
10973
|
+
boardStatus;
|
|
10974
|
+
issueRef;
|
|
10975
|
+
constructor(message2, detail) {
|
|
10976
|
+
super(message2);
|
|
10977
|
+
this.name = "NotClaimableError";
|
|
10978
|
+
this.issueRef = detail.ref;
|
|
10979
|
+
if (detail.status !== void 0) this.boardStatus = detail.status;
|
|
10980
|
+
}
|
|
10981
|
+
};
|
|
10982
|
+
function isNotClaimable(e) {
|
|
10983
|
+
return e instanceof NotClaimableError;
|
|
10984
|
+
}
|
|
10968
10985
|
function evaluateClaim(item, login) {
|
|
10969
10986
|
const others = item.assignees.filter((a) => a.toLowerCase() !== login.toLowerCase());
|
|
10970
10987
|
const mine = item.assignees.some((a) => a.toLowerCase() === login.toLowerCase());
|
|
@@ -12217,8 +12234,9 @@ async function claimOneBoardItem(ctx, selector, options) {
|
|
|
12217
12234
|
const flatItem = findBoardItem(ctx.items, selector, { owner: cfg.projectOwner, number: cfg.projectNumber });
|
|
12218
12235
|
const wouldWrite = flatItem.assignees.length === 0 && (flatItem.status === "Todo" || flatItem.status === "In Progress" || flatItem.status === "In Review");
|
|
12219
12236
|
if (wouldWrite && !ctx.writable.has(flatItem.repository.toLowerCase())) {
|
|
12220
|
-
throw new
|
|
12221
|
-
`${flatItem.ref} is not claimable: the token from ${describeTokenSource()} reports no write access to ${flatItem.repository}
|
|
12237
|
+
throw new NotClaimableError(
|
|
12238
|
+
`${flatItem.ref} is not claimable: the token from ${describeTokenSource()} reports no write access to ${flatItem.repository}`,
|
|
12239
|
+
{ ref: flatItem.ref }
|
|
12222
12240
|
);
|
|
12223
12241
|
}
|
|
12224
12242
|
if (flatItem.contentType === "Issue") {
|
|
@@ -12241,7 +12259,10 @@ async function claimOneBoardItem(ctx, selector, options) {
|
|
|
12241
12259
|
retryCommand: `mmi-cli oracle board claim ${flatItem.number}${options.check ? " --check" : ""}`
|
|
12242
12260
|
});
|
|
12243
12261
|
if (gate.blocked) {
|
|
12244
|
-
throw new
|
|
12262
|
+
throw new NotClaimableError(
|
|
12263
|
+
`${flatItem.ref} is not claimable: blocked on open ${gate.openDependencies.join(", ")}`,
|
|
12264
|
+
{ ref: flatItem.ref }
|
|
12265
|
+
);
|
|
12245
12266
|
}
|
|
12246
12267
|
}
|
|
12247
12268
|
const assignee = options.assignee ?? "@me";
|
|
@@ -12261,12 +12282,12 @@ async function claimOneBoardItem(ctx, selector, options) {
|
|
|
12261
12282
|
const heldReceipt = () => previousHolder ? resumeEvidence ? { outcome: "resumed", holder, previousHolder, resumeEvidence } : { outcome: "took-over", holder, previousHolder } : { outcome: "held", holder };
|
|
12262
12283
|
if (flatItem.contentType !== "Issue") throw new Error(`${flatItem.ref} is not an issue`);
|
|
12263
12284
|
const pre = evaluateClaim(flatItem, assignedLogin);
|
|
12264
|
-
if (!pre.ok) throw new
|
|
12285
|
+
if (!pre.ok) throw new NotClaimableError(pre.reason, { ref: flatItem.ref, status: flatItem.status });
|
|
12265
12286
|
let item = flatItem;
|
|
12266
12287
|
const fresh = (await fetchIssueProjectItem(client, cfg, { repo: item.repository, number: item.number })).item;
|
|
12267
12288
|
if (!fresh) throw new Error(`${item.ref} is not on this project board`);
|
|
12268
12289
|
const verdict = evaluateClaim(fresh, assignedLogin);
|
|
12269
|
-
if (!verdict.ok) throw new
|
|
12290
|
+
if (!verdict.ok) throw new NotClaimableError(verdict.reason, { ref: fresh.ref, status: fresh.status });
|
|
12270
12291
|
item = fresh;
|
|
12271
12292
|
const refuseIfContested = async () => {
|
|
12272
12293
|
const contest = await checkLaneContest(client, item, ctx.session, report.viewer);
|
|
@@ -12951,6 +12972,7 @@ async function waitForPrChecks(deps) {
|
|
|
12951
12972
|
|
|
12952
12973
|
// src/bootstrap-ruleset.ts
|
|
12953
12974
|
var PRODUCT_RULESET_NAME = "mmi-product-required-checks";
|
|
12975
|
+
var PRODUCT_GATE_CONTEXT = "gate";
|
|
12954
12976
|
var PRODUCT_RULESET_PATH = ".github/rulesets/mmi-product-required-checks.json";
|
|
12955
12977
|
function reseedProductRulesetStrictness(current, seed) {
|
|
12956
12978
|
const parse = (raw) => {
|
|
@@ -12993,6 +13015,15 @@ function rulesetStrictPolicy(ruleset) {
|
|
|
12993
13015
|
const rules = (ruleset.rules ?? []).filter((rule) => rule.type === "required_status_checks");
|
|
12994
13016
|
return rules.length > 0 && rules.every((rule) => rule.parameters?.strict_required_status_checks_policy === true);
|
|
12995
13017
|
}
|
|
13018
|
+
function committedRequiredContexts(raw) {
|
|
13019
|
+
if (raw == null) return [PRODUCT_GATE_CONTEXT];
|
|
13020
|
+
try {
|
|
13021
|
+
const contexts = rulesetRequiredContexts(stripRulesetComment(raw));
|
|
13022
|
+
return contexts.length ? [...new Set(contexts)] : [PRODUCT_GATE_CONTEXT];
|
|
13023
|
+
} catch {
|
|
13024
|
+
return [PRODUCT_GATE_CONTEXT];
|
|
13025
|
+
}
|
|
13026
|
+
}
|
|
12996
13027
|
function rulesetBranchIncludes(ruleset) {
|
|
12997
13028
|
const raw = ruleset.conditions?.ref_name?.include;
|
|
12998
13029
|
return Array.isArray(raw) ? [...new Set(raw.filter((ref) => typeof ref === "string" && ref.length > 0))].sort((a, b) => a.localeCompare(b)) : [];
|
|
@@ -15586,10 +15617,10 @@ var rollout_plan_default = {
|
|
|
15586
15617
|
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)."
|
|
15587
15618
|
},
|
|
15588
15619
|
baseline: {
|
|
15589
|
-
version: "4.3.
|
|
15590
|
-
tag: "v4.3.
|
|
15591
|
-
commit: "
|
|
15592
|
-
npm: "@mutmutco/cli@4.3.
|
|
15620
|
+
version: "4.3.34",
|
|
15621
|
+
tag: "v4.3.34",
|
|
15622
|
+
commit: "09818c6f3de7",
|
|
15623
|
+
npm: "@mutmutco/cli@4.3.34"
|
|
15593
15624
|
},
|
|
15594
15625
|
exitCriterion: "fleet-n-of-n",
|
|
15595
15626
|
hubOnlyShortcut: "forbidden",
|
|
@@ -15606,14 +15637,14 @@ var rollout_plan_default = {
|
|
|
15606
15637
|
repo: "mutmutco/mmi-hub",
|
|
15607
15638
|
role: "canary",
|
|
15608
15639
|
schedule: "train",
|
|
15609
|
-
v3Target: "v4.3.
|
|
15640
|
+
v3Target: "v4.3.34"
|
|
15610
15641
|
}
|
|
15611
15642
|
],
|
|
15612
15643
|
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.",
|
|
15613
15644
|
rollback: {
|
|
15614
15645
|
independent: true,
|
|
15615
|
-
mechanism: "npm dist-tag latest -> 4.3.
|
|
15616
|
-
v3Target: "v4.3.
|
|
15646
|
+
mechanism: "npm dist-tag latest -> 4.3.34 and redeploy the Hub Lambda from tag v4.3.34 (09818c6f3de7); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
15647
|
+
v3Target: "v4.3.34 (@mutmutco/cli@4.3.34, tag commit 09818c6f3de7 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
15617
15648
|
}
|
|
15618
15649
|
},
|
|
15619
15650
|
{
|
|
@@ -25657,6 +25688,20 @@ function refuseRateLimited(e, json) {
|
|
|
25657
25688
|
process.exitCode = 1;
|
|
25658
25689
|
return true;
|
|
25659
25690
|
}
|
|
25691
|
+
function refuseNotClaimable(e, json) {
|
|
25692
|
+
if (!isNotClaimable(e)) return false;
|
|
25693
|
+
const message2 = `board claim failed: ${e.message}`;
|
|
25694
|
+
if (json) {
|
|
25695
|
+
console.log(formatErrorEnvelope(message2, {
|
|
25696
|
+
code: ERROR_CODES.ERR_STATE_CONFLICT,
|
|
25697
|
+
issue_ref: e.issueRef,
|
|
25698
|
+
...e.boardStatus === void 0 ? {} : { board_status: e.boardStatus }
|
|
25699
|
+
}));
|
|
25700
|
+
}
|
|
25701
|
+
console.error(`mmi-cli ${message2}`);
|
|
25702
|
+
process.exitCode = 1;
|
|
25703
|
+
return true;
|
|
25704
|
+
}
|
|
25660
25705
|
function registerBoardCommands(program3) {
|
|
25661
25706
|
function withDiscoverMissDetail(message2) {
|
|
25662
25707
|
const miss = lastBoardDiscoverMiss();
|
|
@@ -25741,6 +25786,7 @@ function registerBoardCommands(program3) {
|
|
|
25741
25786
|
printClaimWarnings(result.warnings);
|
|
25742
25787
|
} catch (e) {
|
|
25743
25788
|
if (refuseRateLimited(e, o.json)) return;
|
|
25789
|
+
if (refuseNotClaimable(e, o.json)) return;
|
|
25744
25790
|
return failGraceful(`board claim failed: ${e.message}`);
|
|
25745
25791
|
}
|
|
25746
25792
|
return;
|
|
@@ -26939,7 +26985,6 @@ var requiredBoardSwimlaneField = "Repository";
|
|
|
26939
26985
|
var requiredBoardCardFields = ["Title", "Assignees", "Status", "Labels", "Linked pull requests", "Parent issue", "Sub-issues progress", "Priority"];
|
|
26940
26986
|
var requiredOrgRulesetTypes = ["pull_request", "non_fast_forward", "deletion"];
|
|
26941
26987
|
var requiredHubStatusChecks = ["cli", "infra", "docs"];
|
|
26942
|
-
var requiredProductStatusChecks = ["gate"];
|
|
26943
26988
|
function expectedBranches(repoClass, releaseTrack) {
|
|
26944
26989
|
if (isReleaseTrack(releaseTrack)) return branchesForTrack(releaseTrack);
|
|
26945
26990
|
return repoClass === "content" ? ["main"] : ["development", "rc", "main"];
|
|
@@ -27080,6 +27125,18 @@ function filledDocCheck(label, text, path2) {
|
|
|
27080
27125
|
const unfilled = unfilledDocPlaceholders(text);
|
|
27081
27126
|
return { ok: unfilled.length === 0, label, detail: unfilled.length ? `unfilled: ${unfilled.join(", ")}` : void 0 };
|
|
27082
27127
|
}
|
|
27128
|
+
var NPMRC_PATHS = [".npmrc", "web/.npmrc"];
|
|
27129
|
+
var SCOPED_GITHUB_PACKAGES_RE = /^\s*@[A-Za-z0-9-]+:registry\s*=\s*https:\/\/npm\.pkg\.github\.com/m;
|
|
27130
|
+
var GITHUB_PACKAGES_AUTH_RE = /^\s*\/\/npm\.pkg\.github\.com\/:_authToken\s*=/m;
|
|
27131
|
+
function npmrcGitHubPackagesAuthCheck(path2, text) {
|
|
27132
|
+
if (text === null || !SCOPED_GITHUB_PACKAGES_RE.test(text)) return null;
|
|
27133
|
+
const ok = GITHUB_PACKAGES_AUTH_RE.test(text);
|
|
27134
|
+
return {
|
|
27135
|
+
ok,
|
|
27136
|
+
label: `${path2} authenticates GitHub Packages`,
|
|
27137
|
+
detail: ok ? void 0 : `${path2} points a scope at npm.pkg.github.com with no token line \u2014 add \`//npm.pkg.github.com/:_authToken=\${NODE_AUTH_TOKEN}\` (the value stays in the environment, never in the file) or npm ci 401s outside a setup-node CI job`
|
|
27138
|
+
};
|
|
27139
|
+
}
|
|
27083
27140
|
function isCentralContainerDeployModel(model) {
|
|
27084
27141
|
return model === "tenant-container" || model === "solo-container";
|
|
27085
27142
|
}
|
|
@@ -27188,12 +27245,25 @@ async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
|
|
|
27188
27245
|
for (const path2 of requiredIssueTemplates) {
|
|
27189
27246
|
checks.push({ ok: await contentExists2(deps, repo, baseBranch, path2), label: `issue template exists: ${path2}` });
|
|
27190
27247
|
}
|
|
27248
|
+
let productContexts = [PRODUCT_GATE_CONTEXT];
|
|
27191
27249
|
if (repo !== HUB_REPO5 && repoClass === "deployable") {
|
|
27192
|
-
|
|
27193
|
-
|
|
27250
|
+
const rulesetRaw = await contentText(deps, repo, baseBranch, requiredProductRulesetRef);
|
|
27251
|
+
productContexts = committedRequiredContexts(rulesetRaw);
|
|
27252
|
+
const workflowCandidates = [.../* @__PURE__ */ new Set([
|
|
27253
|
+
...productContexts.map((context) => `.github/workflows/${context}.yml`),
|
|
27254
|
+
...requiredProductWorkflows
|
|
27255
|
+
])];
|
|
27256
|
+
const presentWorkflows = [];
|
|
27257
|
+
for (const path2 of workflowCandidates) {
|
|
27258
|
+
if (await contentExists2(deps, repo, baseBranch, path2)) presentWorkflows.push(path2);
|
|
27194
27259
|
}
|
|
27195
27260
|
checks.push({
|
|
27196
|
-
ok:
|
|
27261
|
+
ok: presentWorkflows.length > 0,
|
|
27262
|
+
label: "gate workflow exists",
|
|
27263
|
+
detail: presentWorkflows.length ? presentDetail(presentWorkflows) : `none of: ${workflowCandidates.join(", ")}`
|
|
27264
|
+
});
|
|
27265
|
+
checks.push({
|
|
27266
|
+
ok: rulesetRaw !== null,
|
|
27197
27267
|
label: "product required-check ruleset reference exists",
|
|
27198
27268
|
detail: `expected: ${requiredProductRulesetRef} (apply as an active repo ruleset after bootstrap)`
|
|
27199
27269
|
});
|
|
@@ -27211,6 +27281,10 @@ async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
|
|
|
27211
27281
|
});
|
|
27212
27282
|
}
|
|
27213
27283
|
}
|
|
27284
|
+
for (const path2 of NPMRC_PATHS) {
|
|
27285
|
+
const check = npmrcGitHubPackagesAuthCheck(path2, await contentText(deps, repo, baseBranch, path2));
|
|
27286
|
+
if (check) checks.push(check);
|
|
27287
|
+
}
|
|
27214
27288
|
const portRangeCheck = centralContainerPortRangeCheck(deps.deployModel, deps.projectMeta?.portRange, repo);
|
|
27215
27289
|
if (portRangeCheck) checks.push(portRangeCheck);
|
|
27216
27290
|
checks.push(...deployRowChecks(
|
|
@@ -27424,7 +27498,7 @@ async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
|
|
|
27424
27498
|
detail: productRuleset?.enforcement !== "active" ? `${PRODUCT_RULESET_NAME} is ${productRuleset?.enforcement ?? "missing"} \u2014 run mmi-cli devops ci reconcile --apply --repo ${repo} once the gate is green` : void 0
|
|
27425
27499
|
});
|
|
27426
27500
|
const statusChecks = rulesetStatusChecks2(rulesets.filter((r) => r.target === "branch" && r.enforcement === "active"));
|
|
27427
|
-
const missing =
|
|
27501
|
+
const missing = productContexts.filter((check) => !statusChecks.has(check));
|
|
27428
27502
|
checks.push({
|
|
27429
27503
|
ok: missing.length === 0,
|
|
27430
27504
|
label: "product required status checks configured",
|
|
@@ -45488,6 +45562,19 @@ function recoveryPointerFor(path2) {
|
|
|
45488
45562
|
if (runnable === path2) return `run: mmi-cli ${canonicalPathFor(runnable)} \u2026`;
|
|
45489
45563
|
return `'${path2}' is not a registered command \u2014 run \`mmi-cli ${canonicalPathFor(runnable)} --help\` for what lives under it`;
|
|
45490
45564
|
}
|
|
45565
|
+
function refuseHouseShim(typedPath, message2) {
|
|
45566
|
+
if (argvWantsJson3()) {
|
|
45567
|
+
consoleIo.log(
|
|
45568
|
+
formatErrorEnvelope(message2, {
|
|
45569
|
+
code: ERROR_CODES.ERR_UNKNOWN_FLAG,
|
|
45570
|
+
did_you_mean: canonicalPathFor(registeredPathPrefix(typedPath))
|
|
45571
|
+
})
|
|
45572
|
+
);
|
|
45573
|
+
}
|
|
45574
|
+
process.stderr.write(`${message2}
|
|
45575
|
+
`);
|
|
45576
|
+
hardExit(2);
|
|
45577
|
+
}
|
|
45491
45578
|
function resolveHouseShim(argv) {
|
|
45492
45579
|
const houseToken = argv[2];
|
|
45493
45580
|
if (!houseToken) return;
|
|
@@ -45496,11 +45583,10 @@ function resolveHouseShim(argv) {
|
|
|
45496
45583
|
const flatPath = flatTokens.join(" ");
|
|
45497
45584
|
const flatHouse = houseForPath(flatPath);
|
|
45498
45585
|
if (!flatHouse || flatHouse === "core") return;
|
|
45499
|
-
|
|
45500
|
-
|
|
45501
|
-
`
|
|
45586
|
+
refuseHouseShim(
|
|
45587
|
+
flatPath,
|
|
45588
|
+
`mmi-cli: the flat '${flatPath}' alias was removed in ${COMPAT_SHIM_DEATH_WAVE} (#4316) \u2014 ${recoveryPointerFor(flatPath)}`
|
|
45502
45589
|
);
|
|
45503
|
-
hardExit(2);
|
|
45504
45590
|
}
|
|
45505
45591
|
const remainder = argv.slice(3);
|
|
45506
45592
|
const first = remainder[0];
|
|
@@ -45520,17 +45606,15 @@ function resolveHouseShim(argv) {
|
|
|
45520
45606
|
return;
|
|
45521
45607
|
}
|
|
45522
45608
|
if (actual) {
|
|
45523
|
-
|
|
45524
|
-
|
|
45525
|
-
`
|
|
45526
|
-
);
|
|
45527
|
-
} else {
|
|
45528
|
-
process.stderr.write(
|
|
45529
|
-
`mmi-cli ${houseToken}: '${first}' is not a command of house '${houseToken}' \u2014 run \`mmi-cli ${houseToken} --help\` for its commands
|
|
45530
|
-
`
|
|
45609
|
+
refuseHouseShim(
|
|
45610
|
+
lookupPath,
|
|
45611
|
+
`mmi-cli ${houseToken}: '${lookupPath}' lives in house '${actual}' \u2014 ${recoveryPointerFor(lookupPath)}`
|
|
45531
45612
|
);
|
|
45532
45613
|
}
|
|
45533
|
-
|
|
45614
|
+
refuseHouseShim(
|
|
45615
|
+
lookupPath,
|
|
45616
|
+
`mmi-cli ${houseToken}: '${first}' is not a command of house '${houseToken}' \u2014 run \`mmi-cli ${houseToken} --help\` for its commands`
|
|
45617
|
+
);
|
|
45534
45618
|
}
|
|
45535
45619
|
function printHouseRootHelp(house) {
|
|
45536
45620
|
const manifest = buildCommandManifest(program2);
|
package/package.json
CHANGED