Problem
eslint-plugin-diff catches violations on changed lines, but can't detect regressions introduced at a distance (e.g., renaming an export breaks lint in untouched files). A cross-ref diff of lint results can catch these.
Implementation
Formatter (CLI package)
- Custom ESLint formatter that writes slim JSON to
dist/eslint-results.json (files with violations only, fields: filePath, ruleId, line, column, endLine, endColumn, severity, message)
- Returns stylish formatter output for console display
- Used by
lint:eslint via --format flag
Compare command (CLI package, not a turbo task)
gtb lint:eslint:compare --base-sha <sha>
- Reads
dist/eslint-results-base.json and dist/eslint-results.json per package
- Computes
git diff <base-sha> HEAD to build line offset mappings
- Adjusts base violation positions using the mappings
- Outputs new violations (violations in HEAD not present in adjusted base)
- Exits non-zero if any new violations found
Workflow (new or added to existing CI)
- Triggers on push to non-main branches + pull requests
- Computes merge base against main
- Checks out base SHA, runs
turbo run lint:eslint (remote cache hit)
- Copies each package's
dist/eslint-results.json to dist/eslint-results-base.json
- Checks out HEAD, runs
turbo run lint:eslint (from existing build, or reruns affected)
- Runs
gtb lint:eslint:compare --base-sha <merge-base-sha>
- Posts/updates a single PR comment with grouped violations (hidden HTML marker for idempotent updates)
Gating
- Blocking for all PRs (human and bot)
- PR label override to accept known regressions (e.g., dependency upgrades introducing new rules)
- On push without PR: commit status. Failure triggers Renovate PR creation for bot branches
Prerequisites
- Add
eslint-plugin-diff to pre-commit/prek ESLint hook (diff-only local linting)
- Add
dist/eslint-results.json to turbo outputs for lint:eslint
- Wire
--format into lint:eslint CLI command
Problem
eslint-plugin-diffcatches violations on changed lines, but can't detect regressions introduced at a distance (e.g., renaming an export breaks lint in untouched files). A cross-ref diff of lint results can catch these.Implementation
Formatter (CLI package)
dist/eslint-results.json(files with violations only, fields:filePath,ruleId,line,column,endLine,endColumn,severity,message)lint:eslintvia--formatflagCompare command (CLI package, not a turbo task)
gtb lint:eslint:compare --base-sha <sha>dist/eslint-results-base.jsonanddist/eslint-results.jsonper packagegit diff <base-sha> HEADto build line offset mappingsWorkflow (new or added to existing CI)
turbo run lint:eslint(remote cache hit)dist/eslint-results.jsontodist/eslint-results-base.jsonturbo run lint:eslint(from existing build, or reruns affected)gtb lint:eslint:compare --base-sha <merge-base-sha>Gating
Prerequisites
eslint-plugin-diffto pre-commit/prek ESLint hook (diff-only local linting)dist/eslint-results.jsonto turbooutputsforlint:eslint--formatintolint:eslintCLI command