@mutmutco/codex-plugin 4.6.15 → 4.6.17
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/cli/dist/main.cjs
CHANGED
|
@@ -25523,6 +25523,7 @@ __export(gate_budget_exports, {
|
|
|
25523
25523
|
BLESSED_JEV_FAILURES_SHA: () => BLESSED_JEV_FAILURES_SHA,
|
|
25524
25524
|
BLESSED_KIT_AGENT_FILES_SHA: () => BLESSED_KIT_AGENT_FILES_SHA,
|
|
25525
25525
|
BLESSED_RUNBOOK_GUARD_SHA: () => BLESSED_RUNBOOK_GUARD_SHA,
|
|
25526
|
+
BLESSED_RUNNER_CHECK_POLICY_SHA: () => BLESSED_RUNNER_CHECK_POLICY_SHA,
|
|
25526
25527
|
BLESSED_RUNNER_CHOICE_SHA: () => BLESSED_RUNNER_CHOICE_SHA,
|
|
25527
25528
|
BLESSED_RUNNER_GATE_SHA: () => BLESSED_RUNNER_GATE_SHA,
|
|
25528
25529
|
BLESSED_RUN_WITH_BUDGET_SHA: () => BLESSED_RUN_WITH_BUDGET_SHA,
|
|
@@ -25533,6 +25534,7 @@ __export(gate_budget_exports, {
|
|
|
25533
25534
|
});
|
|
25534
25535
|
var BLESSED_RUN_WITH_BUDGET_SHA = "cb31a94c89729beac83500ad4d301132dcfe2e4c";
|
|
25535
25536
|
var BLESSED_RUNNER_GATE_SHA = "a19e9392e5ad9b0675f02e4304d68dd933fad60f";
|
|
25537
|
+
var BLESSED_RUNNER_CHECK_POLICY_SHA = "67bb2012710a96d21be825c29a5d5c2b4b8001e2";
|
|
25536
25538
|
var BLESSED_KIT_AGENT_FILES_SHA = "e976df208d891b215844386c1cac0f161946b6fa";
|
|
25537
25539
|
var BLESSED_JEV_FAILURES_SHA = "d5278c6133318b33727483ddfdfddc8f841e3ef6";
|
|
25538
25540
|
var BLESSED_VERDICT_RECEIPT_SHA = "";
|
|
@@ -25912,6 +25914,11 @@ function gateSeedVars(cls, releaseTrack, runtime = "node", requiredCheckBranches
|
|
|
25912
25914
|
// The pin is the CLI's blessed SHA, not an operator knob — one central place to bump (#3178).
|
|
25913
25915
|
GATE_BUDGET_SHA: BLESSED_RUN_WITH_BUDGET_SHA,
|
|
25914
25916
|
GATE_RUNNER_SHA: BLESSED_RUNNER_GATE_SHA,
|
|
25917
|
+
GATE_CHECK_POLICY_SHA: BLESSED_RUNNER_CHECK_POLICY_SHA,
|
|
25918
|
+
// No shared threshold: only a caller's matched complete-check experiment may enable concurrency.
|
|
25919
|
+
GATE_CHECK_PARALLEL_MIN_FREE_MB: "0",
|
|
25920
|
+
GATE_CHECK_POLICY_STEP_YAML: "",
|
|
25921
|
+
GATE_CHECK_POLICY_ENV_YAML: "",
|
|
25915
25922
|
// #9161: the suite-only kit-file drift action arrived after the shared runner pin, so it carries its own
|
|
25916
25923
|
// blessed revision — bumping GATE_RUNNER_SHA for it would move every generic repo's toolchain too.
|
|
25917
25924
|
KIT_AGENT_FILES_SHA: BLESSED_KIT_AGENT_FILES_SHA,
|
|
@@ -25979,6 +25986,7 @@ function withDerivedRepoVars(vars, parsed, cls, releaseTrack, requiredCheckBranc
|
|
|
25979
25986
|
for (const [key, value] of Object.entries(gateSeedVars(cls, track, runtime, requiredCheckBranches, requiredChecks))) {
|
|
25980
25987
|
out[key] ??= value;
|
|
25981
25988
|
}
|
|
25989
|
+
Object.assign(out, gateCheckPolicyYaml(out.GATE_CHECK_POLICY_SHA, out.GATE_CHECK_PARALLEL_MIN_FREE_MB, runtime));
|
|
25982
25990
|
if (!out.GATE_AFFECTED_CMD.trim()) out.GATE_AFFECTED_CMD = out.GATE_CMD;
|
|
25983
25991
|
return out;
|
|
25984
25992
|
}
|
|
@@ -26147,6 +26155,26 @@ function gateJevStepsYaml(sha, runtime = "node") {
|
|
|
26147
26155
|
GATE_JEV_REPORT_YAML: step("Report test outcomes to Jev", "always() && ", "outcomes")
|
|
26148
26156
|
};
|
|
26149
26157
|
}
|
|
26158
|
+
function gateCheckPolicyYaml(sha, minFreeMb, runtime) {
|
|
26159
|
+
if (runtime !== "node" || !/^[0-9a-f]{40}$/.test(sha) || !/^0*[1-9][0-9]*$/.test(minFreeMb)) {
|
|
26160
|
+
return { GATE_CHECK_POLICY_STEP_YAML: "", GATE_CHECK_POLICY_ENV_YAML: "" };
|
|
26161
|
+
}
|
|
26162
|
+
return {
|
|
26163
|
+
GATE_CHECK_POLICY_STEP_YAML: [
|
|
26164
|
+
"\n - name: Choose check scheduling",
|
|
26165
|
+
"\n id: check-policy",
|
|
26166
|
+
"\n if: ${{ steps.development-gate.outputs.reuse != 'true' }}",
|
|
26167
|
+
"\n continue-on-error: true",
|
|
26168
|
+
"\n timeout-minutes: 1",
|
|
26169
|
+
`
|
|
26170
|
+
uses: mutmutco/MMI-Hub/.github/actions/runner-check-policy@${sha}`,
|
|
26171
|
+
"\n with:",
|
|
26172
|
+
`
|
|
26173
|
+
min-free-mb: ${yamlScalar(minFreeMb)}`
|
|
26174
|
+
].join(""),
|
|
26175
|
+
GATE_CHECK_POLICY_ENV_YAML: "\n RUN_CHECK_SERIAL: ${{ steps.check-policy.outputs.serial || '1' }}"
|
|
26176
|
+
};
|
|
26177
|
+
}
|
|
26150
26178
|
function gateExtraStepsYaml(steps) {
|
|
26151
26179
|
return steps.map((step) => [
|
|
26152
26180
|
`
|
|
@@ -27186,7 +27214,16 @@ async function auditRepoCollaborators(repo, owners, deps, projectAdmins = /* @__
|
|
|
27186
27214
|
}
|
|
27187
27215
|
return findings;
|
|
27188
27216
|
}
|
|
27189
|
-
async function
|
|
27217
|
+
async function directWriters(deps, repo) {
|
|
27218
|
+
const collabs = await restPagedJson(deps, `repos/${repo}/collaborators?affiliation=direct`, []);
|
|
27219
|
+
const writers = /* @__PURE__ */ new Set();
|
|
27220
|
+
for (const c of collabs) {
|
|
27221
|
+
const role = collaboratorRole(c);
|
|
27222
|
+
if (role !== null && POWERED_ROLES.has(role)) writers.add(c.login.toLowerCase());
|
|
27223
|
+
}
|
|
27224
|
+
return writers;
|
|
27225
|
+
}
|
|
27226
|
+
async function auditTrainBranch(repo, branch, owners, deps, projectAdmins = /* @__PURE__ */ new Set(), writers = /* @__PURE__ */ new Set()) {
|
|
27190
27227
|
let restrictions = null;
|
|
27191
27228
|
try {
|
|
27192
27229
|
restrictions = await deps.client.rest("GET", `repos/${repo}/branches/${branch}/protection/restrictions`) ?? null;
|
|
@@ -27221,15 +27258,16 @@ async function auditTrainBranch(repo, branch, owners, deps, projectAdmins = /* @
|
|
|
27221
27258
|
}
|
|
27222
27259
|
const findings = [];
|
|
27223
27260
|
const users = (restrictions.users ?? []).map((u) => u.login);
|
|
27261
|
+
const listed = (logins, login) => [...logins].some((entry) => entry.toLowerCase() === login.toLowerCase());
|
|
27224
27262
|
for (const login of users) {
|
|
27225
|
-
if (!owners
|
|
27263
|
+
if (!listed(owners, login) && !listed(projectAdmins, login) && !listed(writers, login)) {
|
|
27226
27264
|
findings.push({
|
|
27227
27265
|
repo,
|
|
27228
27266
|
branch,
|
|
27229
27267
|
kind: "train-allowlist-extra",
|
|
27230
27268
|
severity: "medium",
|
|
27231
27269
|
actor: login,
|
|
27232
|
-
detail: `@${login} is on the ${branch} push allowlist \u2014 legitimate only if an intended full-write project-admin; confirm`,
|
|
27270
|
+
detail: `@${login} is on the ${branch} push allowlist \u2014 legitimate only if an intended full-write member (project-admin or direct repo writer); confirm`,
|
|
27233
27271
|
remediation: `# if NOT an intended full-write member: gh api -X DELETE repos/${repo}/branches/${branch}/protection/restrictions/users -f users[]="${login}"`
|
|
27234
27272
|
});
|
|
27235
27273
|
}
|
|
@@ -27296,8 +27334,9 @@ async function auditRepoAccess(repo, repoClass, owners, deps, projectAdmins = /*
|
|
|
27296
27334
|
findings.push(...await auditRepoCollaborators(repo, owners, deps, projectAdmins, sanctionedAdmins));
|
|
27297
27335
|
if (dataAccess) findings.push(...auditDataAccessContracts(repo, dataAccess));
|
|
27298
27336
|
const track = releaseTrack ?? (repoClass === "content" ? "trunk" : void 0);
|
|
27337
|
+
const writers = repo.toLowerCase() === PLUGIN_READ_REPO2.toLowerCase() ? /* @__PURE__ */ new Set() : await directWriters(deps, repo);
|
|
27299
27338
|
for (const branch of lockedBranches(repoClass, track)) {
|
|
27300
|
-
findings.push(...await auditTrainBranch(repo, branch, owners, deps, projectAdmins));
|
|
27339
|
+
findings.push(...await auditTrainBranch(repo, branch, owners, deps, projectAdmins, writers));
|
|
27301
27340
|
}
|
|
27302
27341
|
return { repo, class: repoClass, releaseTrack: track, ok: !findings.some((f) => f.severity === "high"), findings };
|
|
27303
27342
|
}
|
|
@@ -27766,10 +27805,10 @@ var rollout_plan_default = {
|
|
|
27766
27805
|
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)."
|
|
27767
27806
|
},
|
|
27768
27807
|
baseline: {
|
|
27769
|
-
version: "4.6.
|
|
27770
|
-
tag: "v4.6.
|
|
27771
|
-
commit: "
|
|
27772
|
-
npm: "@mutmutco/cli@4.6.
|
|
27808
|
+
version: "4.6.17",
|
|
27809
|
+
tag: "v4.6.17",
|
|
27810
|
+
commit: "efe71da889a6",
|
|
27811
|
+
npm: "@mutmutco/cli@4.6.17"
|
|
27773
27812
|
},
|
|
27774
27813
|
exitCriterion: "fleet-n-of-n",
|
|
27775
27814
|
hubOnlyShortcut: "forbidden",
|
|
@@ -27786,14 +27825,14 @@ var rollout_plan_default = {
|
|
|
27786
27825
|
repo: "mutmutco/mmi-hub",
|
|
27787
27826
|
role: "canary",
|
|
27788
27827
|
schedule: "train",
|
|
27789
|
-
v3Target: "v4.6.
|
|
27828
|
+
v3Target: "v4.6.17"
|
|
27790
27829
|
}
|
|
27791
27830
|
],
|
|
27792
27831
|
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.",
|
|
27793
27832
|
rollback: {
|
|
27794
27833
|
independent: true,
|
|
27795
|
-
mechanism: "npm dist-tag latest -> 4.6.
|
|
27796
|
-
v3Target: "v4.6.
|
|
27834
|
+
mechanism: "npm dist-tag latest -> 4.6.17 and redeploy the Hub Lambda from tag v4.6.17 (efe71da889a6); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
27835
|
+
v3Target: "v4.6.17 (@mutmutco/cli@4.6.17, tag commit efe71da889a6 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
27797
27836
|
}
|
|
27798
27837
|
},
|
|
27799
27838
|
{
|
|
@@ -47126,7 +47165,7 @@ function sshRemoteCommand(remoteCommand) {
|
|
|
47126
47165
|
function shellSingleQuote(value) {
|
|
47127
47166
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
47128
47167
|
}
|
|
47129
|
-
var SSH_RECIPE_AGENT_NOTE =
|
|
47168
|
+
var SSH_RECIPE_AGENT_NOTE = '# agent callers: this is multi-line, so running it inline is denied (#1473/#2125).\n# Use `mmi-cli devops runtime box get <name> --ssh --script <path>` to write it to a file, then `bash <path>`.\n# For a multi-line remote body add --stdin (or --body <file>): the recipe then runs `sh -s` on the box,\n# and you stream the body over stdin \u2014 `bash "<path>" < "<body-file>"` \u2014 with no remote re-quoting (#9750).';
|
|
47130
47169
|
function sshRecipeScript(box, remoteCommand) {
|
|
47131
47170
|
return `#!/usr/bin/env bash
|
|
47132
47171
|
${formatSshRecipe(box, remoteCommand)}
|
|
@@ -47210,8 +47249,16 @@ function registerBoxCommands(program3) {
|
|
|
47210
47249
|
await failGraceful(e.message);
|
|
47211
47250
|
}
|
|
47212
47251
|
});
|
|
47213
|
-
box.command("get <name> [command...]").description("one box by name \u2014 its address and ssh key path; --ssh prints a ready-to-run connect recipe").option("--json", "machine-readable output").option("--ssh", "print a copy-paste ssh recipe (runs under the keyless vault wrapper and loads the key into an ssh-agent; the key is never written to a file or echoed)").option("--script <path>", "with --ssh: write the recipe to <path> and print the one line to run \u2014 the runnable form for agent callers, whose transport denies a multi-line inline body (#3385)").action(async (name, command, o) => {
|
|
47252
|
+
box.command("get <name> [command...]").description("one box by name \u2014 its address and ssh key path; --ssh prints a ready-to-run connect recipe").option("--json", "machine-readable output").option("--ssh", "print a copy-paste ssh recipe (runs under the keyless vault wrapper and loads the key into an ssh-agent; the key is never written to a file or echoed)").option("--script <path>", "with --ssh: write the recipe to <path> and print the one line to run \u2014 the runnable form for agent callers, whose transport denies a multi-line inline body (#3385)").option("--stdin", "with --ssh --script: write the recipe with remote command sh -s so a multi-line script can be piped or redirected on stdin without remote shell re-quoting (#9750)").option("--body <file>", 'with --ssh --script: same as --stdin, and name <file> in the printed run line: bash "<script>" < "<file>" (#9750)').action(async (name, command, o) => {
|
|
47214
47253
|
try {
|
|
47254
|
+
if ((o.stdin || o.body) && (!o.ssh || !o.script)) {
|
|
47255
|
+
await failGraceful("runtime box get: --stdin/--body requires --ssh --script <path> (it writes an sh -s recipe that reads the remote body from stdin)");
|
|
47256
|
+
return;
|
|
47257
|
+
}
|
|
47258
|
+
if ((o.stdin || o.body) && command && command.length) {
|
|
47259
|
+
await failGraceful("runtime box get: cannot combine --stdin/--body with trailing [command...] arguments \u2014 put the commands in the stdin body file instead");
|
|
47260
|
+
return;
|
|
47261
|
+
}
|
|
47215
47262
|
const { boxes, incomplete } = await fetchInventory();
|
|
47216
47263
|
const result = lookupBox(name, boxes, incomplete);
|
|
47217
47264
|
if (result.kind === "unknown") {
|
|
@@ -47230,16 +47277,19 @@ function registerBoxCommands(program3) {
|
|
|
47230
47277
|
await failGraceful("runtime box get: --script requires --ssh (it writes the ssh connect recipe)");
|
|
47231
47278
|
return;
|
|
47232
47279
|
}
|
|
47233
|
-
const
|
|
47280
|
+
const useStdin = Boolean(o.stdin || o.body);
|
|
47281
|
+
const remote = useStdin ? "sh -s" : command && command.length ? command.join(" ") : void 0;
|
|
47282
|
+
const stdinSource = o.body ? `"${o.body}"` : "<body.sh>";
|
|
47234
47283
|
const wroteScript = o.ssh && o.script ? o.script : null;
|
|
47284
|
+
const runLine = wroteScript ? useStdin ? `bash "${wroteScript}" < ${stdinSource}` : `bash "${wroteScript}"` : void 0;
|
|
47235
47285
|
if (wroteScript) (0, import_node_fs37.writeFileSync)(wroteScript, sshRecipeScript(found, remote), "utf8");
|
|
47236
47286
|
if (o.json) {
|
|
47237
47287
|
console.log(JSON.stringify({
|
|
47238
47288
|
box: found,
|
|
47239
47289
|
incomplete,
|
|
47240
|
-
...wroteScript ? { script: { path: wroteScript, run:
|
|
47290
|
+
...wroteScript ? { script: { path: wroteScript, run: runLine, ...useStdin ? { stdin: o.body ?? true } : {} } } : {}
|
|
47241
47291
|
}, null, 2));
|
|
47242
|
-
} else if (wroteScript) console.log(`wrote ${wroteScript} \u2014 run:
|
|
47292
|
+
} else if (wroteScript) console.log(`wrote ${wroteScript} \u2014 run: ${runLine}`);
|
|
47243
47293
|
else if (o.ssh) console.log(`${formatSshRecipe(found, remote)}
|
|
47244
47294
|
${SSH_RECIPE_AGENT_NOTE}`);
|
|
47245
47295
|
else console.log(formatBoxTable([found]));
|
|
@@ -68336,6 +68386,7 @@ function registerSchedulesCommands(program3) {
|
|
|
68336
68386
|
var company_emails_default = {
|
|
68337
68387
|
_comment: "Org-level, NON-secret GitHub login -> company email map (#7678). `mmi-cli oracle org access sync <repo>` maps a repo's current developer/admin collaborators through it to rebuild the stage allowlist a project opts into with the registry field `stageAllowlist` (e.g. MM-Work's MMW_STAGE_ALLOWLIST). Bundled into mmi-cli at build time (cli/src/stage-allowlist.ts) \u2014 one source of truth, edited here in a PR. Keys are lowercase GitHub logins; a collaborator missing here is reported loudly by the sync, never silently dropped.",
|
|
68338
68388
|
emails: {
|
|
68389
|
+
guzelt: "guzel@mutatismutandis.co",
|
|
68339
68390
|
jervaise: "suphi@mutatismutandis.co",
|
|
68340
68391
|
sezenlerinadil: "adil@mutatismutandis.co"
|
|
68341
68392
|
}
|
|
@@ -68360,6 +68411,22 @@ function stageAllowlistConfig(project2) {
|
|
|
68360
68411
|
const stages = cfg.stages.filter((s) => ALLOWLIST_STAGES.includes(s));
|
|
68361
68412
|
return stages.length > 0 ? { key: cfg.key, stages } : null;
|
|
68362
68413
|
}
|
|
68414
|
+
function resolveStageAllowlistTarget(repo, projects) {
|
|
68415
|
+
if (!projects) return { kind: "registry-unreadable", exitCode: 1, message: "org access sync: could not read the org project registry \u2014 nothing written" };
|
|
68416
|
+
const project2 = projects.find((p) => (p.repos ?? []).some((r) => r.toLowerCase() === repo.toLowerCase())) ?? null;
|
|
68417
|
+
if (!project2) return { kind: "unregistered", exitCode: 1, message: `org access sync: ${repo} is not in the org project registry \u2014 nothing written` };
|
|
68418
|
+
if (project2.stageAllowlist == null) {
|
|
68419
|
+
return {
|
|
68420
|
+
kind: "no-opt-in",
|
|
68421
|
+
exitCode: 0,
|
|
68422
|
+
json: { repo, optIn: null, stages: [], skipped: "no-stageAllowlist" },
|
|
68423
|
+
message: `org access sync: ${repo} has no stageAllowlist opt-in \u2014 nothing to rebuild (set it with \`mmi-cli oracle org project set ${repo} --var 'stageAllowlist={"key":"<KEY>","stages":["dev"]}'\`)`
|
|
68424
|
+
};
|
|
68425
|
+
}
|
|
68426
|
+
const config = stageAllowlistConfig(project2);
|
|
68427
|
+
if (!config) return { kind: "malformed", exitCode: 1, message: `org access sync: ${repo} has a malformed stageAllowlist in the registry \u2014 set it with \`mmi-cli oracle org project set ${repo} --var 'stageAllowlist={"key":"<KEY>","stages":["dev"]}'\`` };
|
|
68428
|
+
return { kind: "sync", exitCode: 0, config };
|
|
68429
|
+
}
|
|
68363
68430
|
function developerAdminLogins(collaborators) {
|
|
68364
68431
|
const logins = /* @__PURE__ */ new Set();
|
|
68365
68432
|
for (const c of collaborators) {
|
|
@@ -72175,11 +72242,13 @@ access.command("sync [repo]").description("rebuild the stage allowlist (e.g. MM-
|
|
|
72175
72242
|
if (!repo) return fail("org access sync: pass <owner/repo> or run inside a repo checkout");
|
|
72176
72243
|
const cfg = await loadConfig();
|
|
72177
72244
|
if (!cfg.sagaApiUrl) return fail("org access sync: Hub API URL not configured");
|
|
72178
|
-
const
|
|
72179
|
-
if (
|
|
72180
|
-
|
|
72181
|
-
|
|
72182
|
-
|
|
72245
|
+
const target = resolveStageAllowlistTarget(repo, await fetchProjectsList(registryClientDeps(cfg)));
|
|
72246
|
+
if (target.kind === "no-opt-in") {
|
|
72247
|
+
console.log(o.json ? JSON.stringify(target.json, null, 2) : target.message);
|
|
72248
|
+
return;
|
|
72249
|
+
}
|
|
72250
|
+
if (target.kind !== "sync") return failGraceful(target.message);
|
|
72251
|
+
const config = target.config;
|
|
72183
72252
|
const secretsDeps = makeSecretsDeps(cfg);
|
|
72184
72253
|
const client = defaultGitHubClient();
|
|
72185
72254
|
const result = await syncStageAllowlist({
|
package/cli/package.json
CHANGED
package/package.json
CHANGED
|
@@ -309,7 +309,15 @@ collaborator list + the per-branch allowlist are the record — no separate rost
|
|
|
309
309
|
with `--var GATE_MAX_SECONDS=` (300s onboarding default — tighten once the gate is measured, via
|
|
310
310
|
`--var` or `org project set <repo> --var gate={"maxSeconds":N}`); `ci audit` and the release train
|
|
311
311
|
both enforce the step, so do not remove it. The tenant-container product image build (#7542)
|
|
312
|
-
shares that ceiling — no separate knob.
|
|
312
|
+
shares that ceiling — no separate knob. Node gates can opt into the shared memory policy (#9710), with
|
|
313
|
+
`--var GATE_CHECK_PARALLEL_MIN_FREE_MB=0` by default: no policy step or scheduling variable is rendered,
|
|
314
|
+
so existing scheduler behavior stays unchanged. A positive caller-measured minimum free MiB opts in;
|
|
315
|
+
the step then exports `RUN_CHECK_SERIAL=1` unless that minimum is met, and missing output keeps `1`.
|
|
316
|
+
A check script may consume that switch without changing its command or coverage. Opt in only after a
|
|
317
|
+
matched full-check serial/parallel experiment records duration, effective memory, whole-check peak,
|
|
318
|
+
OOM-event deltas and exit status. Python renders no policy step or variable. Shared pin propagation preserves
|
|
319
|
+
consumer thresholds; new seed delivery and live adoption remain separate verification.
|
|
320
|
+
Every trigger runs the full `GATE_CMD`. A repo whose checks
|
|
313
321
|
need a service container or an extra budgeted step declares them in its registry gate config, never by
|
|
314
322
|
hand-editing the rendered gate (#7407): `gate.services` is keyed by service name with `image`, `env`,
|
|
315
323
|
`ports`, one-line `options` and `tmpfs` paths; `gate.steps` is a list of `{name, command, env,
|
|
@@ -32,6 +32,8 @@ name: gate
|
|
|
32
32
|
# GATE_PY_VERSION — Python version for setup-python ({{GATE_PY_VERSION}})
|
|
33
33
|
# GATE_MAX_SECONDS — run-with-budget wall-clock ceiling in seconds ({{GATE_MAX_SECONDS}}); 300 default,
|
|
34
34
|
# tighten per repo (org project set --var gate={"maxSeconds":N}) once measured (#3178)
|
|
35
|
+
# GATE_CHECK_PARALLEL_MIN_FREE_MB — measured minimum free MiB for parallel Node checks; 0 leaves scheduling unchanged.
|
|
36
|
+
# Override only after a matched complete-check experiment (#9710).
|
|
35
37
|
# GATE_PUSH_BRANCHES_YAML — YAML list of push branches that trigger the workflow file (rendered by
|
|
36
38
|
# bootstrap from the release track; not usually hand-passed)
|
|
37
39
|
# GATE_FULL_RUN_BRANCH — train branch whose PUSHES run the full gate job (development on full/direct;
|
|
@@ -39,6 +41,7 @@ name: gate
|
|
|
39
41
|
# checks ruleset on main cannot be silently skipped by a future bootstrap (#4113).
|
|
40
42
|
# GATE_BUDGET_SHA is rendered by the CLI from its blessed run-with-budget pin — not an operator knob.
|
|
41
43
|
# GATE_RUNNER_SHA pins the org runner-checkout / runner-node-toolchain actions (#5610).
|
|
44
|
+
# GATE_CHECK_POLICY_SHA pins the opt-in memory policy; once configured, missing output selects serial (#9710).
|
|
42
45
|
# GATE_VERDICT_STEP_YAML (org CI H1) renders the first step from the blessed verdict-receipt pin: a train push
|
|
43
46
|
# reuses the merge queue's exact-SHA verdict, an rc tag the train push verdict (#8206). merge_group is the full
|
|
44
47
|
# set here (only pull_request fails fast or skips), so `queue-is-full-verdict: 'true'` holds. Empty pin: the legacy #8206 rc-tag step.
|
|
@@ -185,7 +188,7 @@ jobs:
|
|
|
185
188
|
run: |
|
|
186
189
|
python3 --version || { echo "::error title=CI runner toolchain::RUNNER NOT PROVISIONED — python3 not on PATH after setup-python. This is the runner, not your diff; no install or test ran. Re-run the job; if it persists the mmi-runner python toolchain needs attention (docs/Guides/gh-runner-runbook.md in MMI-Hub)."; exit 1; }
|
|
187
190
|
- run: {{GATE_INSTALL_CMD}}
|
|
188
|
-
if: ${{ steps.development-gate.outputs.reuse != 'true' }}{{GATE_JEV_FETCH_YAML}}
|
|
191
|
+
if: ${{ steps.development-gate.outputs.reuse != 'true' }}{{GATE_JEV_FETCH_YAML}}{{GATE_CHECK_POLICY_STEP_YAML}}
|
|
189
192
|
# #3178: the check runs under the org wall-clock budget — a hung suite dies at the ceiling instead
|
|
190
193
|
# of squatting a runner lane for GitHub's 6h default. ci-audit + the release train enforce this step.
|
|
191
194
|
# #9293: PRs fail fast, merge_group and tags run everything. pytest reads PYTEST_ADDOPTS natively; a
|
|
@@ -198,7 +201,7 @@ jobs:
|
|
|
198
201
|
# outcomes step runs under always() (pass or fail).
|
|
199
202
|
- uses: mutmutco/MMI-Hub/.github/actions/run-with-budget@{{GATE_BUDGET_SHA}}
|
|
200
203
|
if: ${{ steps.development-gate.outputs.reuse != 'true' }}
|
|
201
|
-
env:
|
|
204
|
+
env:{{GATE_CHECK_POLICY_ENV_YAML}}
|
|
202
205
|
MMI_FAIL_FAST: ${{ github.event_name == 'pull_request' && '1' || '' }}
|
|
203
206
|
PYTEST_ADDOPTS: ${{ github.event_name == 'pull_request' && format('--maxfail=1 {0}', env.JEV_PYTEST_ARGS) || '' }}
|
|
204
207
|
MMI_JEV_SKIP_ARGS: ${{ github.event_name == 'pull_request' && env.JEV_VITEST_ARGS || '' }}
|
|
@@ -5,9 +5,9 @@ registry enrollment. Registry-listed admins with repository write access remain
|
|
|
5
5
|
Use `mmi-cli oracle org access role <owner/repo> --json` to verify authority for release,
|
|
6
6
|
candidate, hotfix, and runtime operations. Organization membership role alone is not this check.
|
|
7
7
|
Verified repository writers (GitHub write, maintain or admin) may land PRs targeting `development`
|
|
8
|
-
on non-Hub repositories
|
|
9
|
-
|
|
10
|
-
merges remain master-only.
|
|
8
|
+
and hold rc/main merge authority (`train: true`) on non-Hub product repositories (#9748) — keep them
|
|
9
|
+
on those repos' branch-lock push allowlists. They keep the `developer` role: no `/secrets` or
|
|
10
|
+
`/tenant-control` authority. The Hub's development merges and its distribution train remain master-only.
|
|
11
11
|
The Hub distribution train remains organization-master-only. It runs in the `hub-release` workflow on
|
|
12
12
|
our self-hosted runners, dispatched by an org owner, never on a workstation (#9137).
|
|
13
13
|
A 503 `github-rate-limited` answer means the org App's hourly GitHub allowance is used up. It is
|