-
Notifications
You must be signed in to change notification settings - Fork 33
IAEMOD-61187: Guard the ESLint warning baseline against regressions #638
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
fpigeonjr
merged 2 commits into
master
from
IAEMOD-61187/gh-581-guard-the-eslint-warning-baseline-against-regressi
Aug 27, 2026
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| { | ||
| "root": 1631, | ||
| "test-app": 4 | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| #!/usr/bin/env node | ||
| /** | ||
| * Guards against the ESLint warning baseline being raised directly in | ||
| * eslint-baseline.json rather than earned via --bump. | ||
| * | ||
| * The per-workspace gate (check-lint-baseline.mjs) only evaluates the | ||
| * baseline committed in the branch being checked, so a contributor could | ||
| * raise a ceiling (e.g. bumping "root" from 1631 to 5000) in the same PR | ||
| * that introduces new warnings, and both guard steps would pass. This script | ||
| * closes that hole by comparing the baseline on the PR branch against the | ||
| * baseline on the trusted base branch and failing if any shared workspace's | ||
| * ceiling increased. | ||
| * | ||
| * New workspace keys that don't exist on the base branch are allowed (they | ||
| * can't be "increased" if there's nothing to compare against). Any decrease | ||
| * or unchanged value is allowed, matching the ratchet-only semantics of | ||
| * --bump. | ||
| * | ||
| * Usage: | ||
| * node scripts/check-baseline-not-increased.mjs <base-baseline.json> <head-baseline.json> | ||
| */ | ||
| import { readFileSync } from "node:fs"; | ||
| import { resolve } from "node:path"; | ||
|
|
||
| const [baseArg, headArg] = process.argv.slice(2); | ||
|
|
||
| if (!baseArg || !headArg) { | ||
| console.error( | ||
| "Usage: node scripts/check-baseline-not-increased.mjs <base-baseline.json> <head-baseline.json>" | ||
| ); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| function loadBaselines(label, path) { | ||
| try { | ||
| return JSON.parse(readFileSync(resolve(path), "utf8")); | ||
| } catch (error) { | ||
| console.error(`✖ Could not read ${label} baselines at ${path}`); | ||
| console.error(` ${error.message}`); | ||
| process.exit(1); | ||
| } | ||
| } | ||
|
|
||
| const base = loadBaselines("base-branch", baseArg); | ||
| const head = loadBaselines("pull-request", headArg); | ||
|
|
||
| let hasIncrease = false; | ||
|
|
||
| for (const workspace of Object.keys(head)) { | ||
| if (!(workspace in base)) { | ||
| // New workspace entry — nothing to compare against, so it can't be an | ||
| // increase over a previously-trusted value. | ||
| continue; | ||
| } | ||
|
|
||
| const baseValue = base[workspace]; | ||
| const headValue = head[workspace]; | ||
|
|
||
| if (!Number.isFinite(baseValue) || !Number.isFinite(headValue)) { | ||
| console.error( | ||
| `✖ ${workspace}: invalid baseline value (base: ${baseValue}, head: ${headValue})` | ||
| ); | ||
| hasIncrease = true; | ||
| continue; | ||
| } | ||
|
|
||
| if (headValue > baseValue) { | ||
| console.error( | ||
| `✖ ${workspace}: baseline increased ${baseValue} → ${headValue}. ` + | ||
| "The accepted warning ceiling can only go down (via --bump), never up. " + | ||
| "Revert this change to eslint-baseline.json." | ||
| ); | ||
| hasIncrease = true; | ||
| } | ||
| } | ||
|
|
||
| if (hasIncrease) { | ||
| process.exit(1); | ||
| } | ||
|
|
||
| console.log( | ||
| "✓ eslint-baseline.json: no workspace's warning ceiling increased vs. the base branch." | ||
| ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| #!/usr/bin/env node | ||
| /** | ||
| * Tests for scripts/check-baseline-not-increased.mjs — the CI-only guard | ||
| * that compares eslint-baseline.json on a PR branch against the base | ||
| * branch's version and fails if any shared workspace's ceiling increased. | ||
| * | ||
| * Run: node --test scripts/check-baseline-not-increased.test.mjs | ||
| */ | ||
| import { test } from "node:test"; | ||
| import assert from "node:assert/strict"; | ||
| import { execFileSync } from "node:child_process"; | ||
| import { mkdtempSync, writeFileSync, rmSync } from "node:fs"; | ||
| import { tmpdir } from "node:os"; | ||
| import { join, dirname } from "node:path"; | ||
| import { fileURLToPath } from "node:url"; | ||
|
|
||
| const scriptDir = dirname(fileURLToPath(import.meta.url)); | ||
| const script = join(scriptDir, "check-baseline-not-increased.mjs"); | ||
|
|
||
| function run(args) { | ||
| try { | ||
| const stdout = execFileSync("node", [script, ...args], { | ||
| encoding: "utf8", | ||
| }); | ||
| return { status: 0, stdout, stderr: "" }; | ||
| } catch (error) { | ||
| return { | ||
| status: error.status ?? 1, | ||
| stdout: error.stdout?.toString() ?? "", | ||
| stderr: error.stderr?.toString() ?? "", | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| function withTempDir(fn) { | ||
| const dir = mkdtempSync(join(tmpdir(), "baseline-guard-")); | ||
| try { | ||
| return fn(dir); | ||
| } finally { | ||
| rmSync(dir, { recursive: true, force: true }); | ||
| } | ||
| } | ||
|
|
||
| function writeBaseline(dir, name, data) { | ||
| const path = join(dir, name); | ||
| writeFileSync(path, JSON.stringify(data)); | ||
| return path; | ||
| } | ||
|
|
||
| test("passes when the baseline is unchanged", () => { | ||
| withTempDir((dir) => { | ||
| const base = writeBaseline(dir, "base.json", { root: 10, "test-app": 4 }); | ||
| const head = writeBaseline(dir, "head.json", { root: 10, "test-app": 4 }); | ||
| const { status } = run([base, head]); | ||
| assert.equal(status, 0); | ||
| }); | ||
| }); | ||
|
|
||
| test("passes when a baseline is lowered", () => { | ||
| withTempDir((dir) => { | ||
| const base = writeBaseline(dir, "base.json", { root: 10, "test-app": 4 }); | ||
| const head = writeBaseline(dir, "head.json", { root: 6, "test-app": 4 }); | ||
| const { status } = run([base, head]); | ||
| assert.equal(status, 0); | ||
| }); | ||
| }); | ||
|
|
||
| test("fails when a baseline is raised", () => { | ||
| withTempDir((dir) => { | ||
| const base = writeBaseline(dir, "base.json", { root: 10, "test-app": 4 }); | ||
| const head = writeBaseline(dir, "head.json", { root: 20, "test-app": 4 }); | ||
| const { status, stderr } = run([base, head]); | ||
| assert.equal(status, 1); | ||
| assert.match(stderr, /baseline increased 10 → 20/); | ||
| }); | ||
| }); | ||
|
|
||
| test("fails when any of multiple workspaces is raised", () => { | ||
| withTempDir((dir) => { | ||
| const base = writeBaseline(dir, "base.json", { root: 10, "test-app": 4 }); | ||
| const head = writeBaseline(dir, "head.json", { | ||
| root: 10, | ||
| "test-app": 9, | ||
| }); | ||
| const { status, stderr } = run([base, head]); | ||
| assert.equal(status, 1); | ||
| assert.match(stderr, /test-app: baseline increased 4 → 9/); | ||
| }); | ||
| }); | ||
|
|
||
| test("allows a brand-new workspace entry not present on the base branch", () => { | ||
| withTempDir((dir) => { | ||
| const base = writeBaseline(dir, "base.json", { root: 10 }); | ||
| const head = writeBaseline(dir, "head.json", { root: 10, "new-pkg": 50 }); | ||
| const { status } = run([base, head]); | ||
| assert.equal(status, 0); | ||
| }); | ||
| }); | ||
|
|
||
| test("treats a missing base-branch baseline file as an empty baseline", () => { | ||
| withTempDir((dir) => { | ||
| const base = writeBaseline(dir, "base.json", {}); | ||
| const head = writeBaseline(dir, "head.json", { root: 10 }); | ||
| const { status } = run([base, head]); | ||
| assert.equal(status, 0); | ||
| }); | ||
| }); | ||
|
|
||
| test("fails on a non-numeric baseline value", () => { | ||
| withTempDir((dir) => { | ||
| const base = writeBaseline(dir, "base.json", { root: 10 }); | ||
| const head = writeBaseline(dir, "head.json", { root: "ten" }); | ||
| const { status, stderr } = run([base, head]); | ||
| assert.equal(status, 1); | ||
| assert.match(stderr, /invalid baseline value/); | ||
| }); | ||
| }); | ||
|
|
||
| test("exits non-zero with usage when arguments are missing", () => { | ||
| const { status, stderr } = run([]); | ||
| assert.equal(status, 1); | ||
| assert.match(stderr, /Usage:/); | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.