Actually run eslint in CI - #295
Merged
Merged
Conversation
`eslint.config.js` was added specifically because `npm run lint` had been silently erroring with "couldn't find a config" and the lint command was a no-op. The config fixed that — and then nothing ever ran it. The frontend CI job does `npm ci`, `npm audit`, `npm test` and the build, and never the linter. So the no-op survived the fix. Thirteen errors accumulated in master unnoticed, and the eslint 9 -> 10 major in #294 had to be reviewed by running the linter by hand, because CI could say nothing about it. Two changes: - deploy.yml gains a `Lint (eslint)` step in the frontend job. - The two React Compiler advisories that were holding lint at a non-zero exit join the four already demoted to `warn` by the policy written into this config. `react-hooks/immutability` (12) fires on `useEffect(() => { loadThings() }, [...])` where `loadThings` is a `const` declared further down — correct at runtime, since effects run after render, so it is the compiler reporting it cannot track the value rather than a live bug. `react-hooks/use-memo` (1) fires on a non-inline first argument. Hoisting the six files' loaders is a real change that belongs with the next edit to each, not bundled here. Warnings stay visible in the CI log; only errors fail the build. That split is the point. Verified the gate is not vacuous: lint exits 0 on the current tree (47 warnings, 0 errors), and exits 1 with a deliberate `no-undef` planted in src/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
The gap
frontend/eslint.config.jsexists becausenpm run lintused to fail with"couldn't find a config" — its own header comment says the lint command
"had been a no-op for however long. This file fixes that."
It didn't, quite. No CI job has ever run the linter.
Frontend audit + buildrunsnpm ci,npm audit,npm testand the production build, andstops there. So the config landed and nothing invoked it.
Consequences, both concrete:
masterwith nothing reporting them.by hand locally, because CI had no opinion to offer about a linter bump.
Changes
1.
deploy.ymlgains aLint (eslint)step in the frontend job, beforethe tests.
2. Two rules join the existing
warnlist ineslint.config.js. Theywere the only thing keeping lint at a non-zero exit, and they are the same
family as the four React Compiler advisories this config already demotes,
for the same documented reason:
react-hooks/immutabilityuseEffect(() => { loadThings() }, [...])whereloadThingsis aconstdeclared further down the componentreact-hooks/use-memoThe
immutabilityone is correct at runtime — effects run after render,by which point the binding is initialized. It is the React Compiler saying
it cannot track the value, not a live bug. The fix (hoist each loader, or
useCallback) touches six files and belongs with the next edit to each,not bundled into a CI change.
eslint exits non-zero on errors only, so the 47 deliberate warnings stay
visible in the log without blocking deploys.
Verification
The gate is not vacuous:
🤖 Generated with Claude Code