Skip to content

Wire eslint-rules RuleTester suite into a runner, CI, and knip (#4409) - #4433

Merged
justin808 merged 1 commit into
mainfrom
jg/4409-eslint-rule-test-wire
Jul 3, 2026
Merged

Wire eslint-rules RuleTester suite into a runner, CI, and knip (#4409)#4433
justin808 merged 1 commit into
mainfrom
jg/4409-eslint-rule-test-wire

Conversation

@justin808

@justin808 justin808 commented Jul 2, 2026

Copy link
Copy Markdown
Member

Why

The custom ESLint rule eslint-rules/no-use-client-in-server-files.cjs guards a real hazard — a 'use client' directive landing in a server-only (.server.tsx/.server.ts) file, which forces webpack to bundle it as a client component and breaks React's react-server conditional exports. A RuleTester suite ships right next to it in eslint-rules/no-use-client-in-server-files.test.cjs, but nothing executed that suite: no package script, no workflow, no lefthook entry referenced it (grep -rn "no-use-client-in-server-files.test" → zero hits). The rule could regress silently while its "tests" rotted — a coverage illusion. knip couldn't flag the orphan either, because the root workspace's project globs only scanned top-level files (no eslint-rules/, no .cjs).

Fixes #4409

What changed

Smallest-footprint wiring, matching the existing precedent in this same workflow — the Test GitHub Action helpers step runs a standalone .cjs test via plain node. ESLint's RuleTester runs standalone and throws (exits non-zero) on failure, so no test framework is needed.

  • package.json: add "test:eslint-rules": "node eslint-rules/no-use-client-in-server-files.test.cjs" and chain it into the aggregate "check" script (pnpm run lint && pnpm run test:eslint-rules && pnpm -r run check).
  • .github/workflows/lint-js-and-ruby.yml: add a Test custom ESLint rules step immediately after Test GitHub Action helpers in the build job, so the suite runs on every PR.
  • knip.ts (root workspace): add eslint-rules/**/*.cjs to project (so the directory is analyzed) and eslint-rules/**/*.test.cjs to entry (the test file has no importer, so knip needs it declared as an entry point). The rule .cjs is seen as used via its import in eslint.config.ts.

No new dependencies. The test file itself needed no changes to be runnable.

Validation (real results, local; Node 22.12.0 pinned in .tool-versions = CI Node)

Core proof — the test runs and passes via the new script:

$ pnpm run test:eslint-rules
> node eslint-rules/no-use-client-in-server-files.test.cjs
(exit 0 — RuleTester is silent on success)

Fail-when-broken proof — the test genuinely exercises the rule (not a no-op): temporarily changed a valid fixture's filename from Component.tsx to Component.server.tsx (so a 'use client' server file was asserted valid). The run failed with exit code 1:

AssertionError [ERR_ASSERTION]:
  actual: 1, expected: 0, operator: 'strictEqual'
 ELIFECYCLE  Command failed with exit code 1.

The rule correctly flagged 1 error where the (broken) test asserted 0. Reverted; git diff on the test file is empty and it passes again.

Other gates (all green):

Check Result
pnpm exec knip exit 0 — no new findings; no eslint-rules/ unused/unlisted lines
pnpm exec knip --production exit 1 on this branch AND on clean origin/main — pre-existing prop-types finding in react_on_rails/spec/dummy (generated-packs artifact), byte-identical behavior with/without my change. My globs add zero new findings.
pnpm run lint (eslint) exit 0
pnpm start format.listDifferent (prettier) "All matched files use Prettier code style!"
pnpm run type-check all 9 workspaces pass
pnpm run test (main suite) unchanged script (byte-identical to main); orthogonal to test:eslint-rules. Sanity-ran create-react-on-rails-app: 104/104 pass
actionlint .github/workflows/lint-js-and-ruby.yml NO ISSUES (exit 0)
yamllint repo has no yamllint config and uses actionlint as the workflow linter; my 2 added lines produce zero yamllint findings (only pre-existing default-config line-length noise on other lines)
pre-commit hooks trailing-newlines, prettier, eslint all pass

QA Evidence

  • The exact command CI will run (pnpm run test:eslint-rules) was executed locally on the pinned CI Node (22.12.0) and passes (exit 0).
  • Proved the test is real, not a no-op: deliberately broke a fixture → the command failed (exit 1, ERR_ASSERTION actual:1 expected:0) → reverted → passes again (empty git diff on the test file). This is the "deliberately break an assertion, confirm the step fails, restore" evidence the issue's acceptance criteria requires.
  • After the PR opens I will confirm the new Test custom ESLint rules CI step goes green.

Codex Decision Log

codex review --base origin/main ran and returned: "No actionable correctness, CI wiring, or maintainability issues were found in the diff. The new ESLint rule test script is wired into both the lint workflow and the root check script, and the Knip entries account for the standalone test files." No findings to address.

New-gate stale-base race control

Swept open PRs (gh pr list --state open, 24 open). Only #4390 (Tighten hosted CI workflow safeguards) also touches lint-js-and-ruby.yml, but it edits the detect-changes job's if: condition (~line 26); my change adds a step in the build job (~line 210) — no line-level collision, merges cleanly in either order. No PR touches eslint-rules/, knip.ts, or package.json scripts. Coordinator should re-sweep before landing.

Labels: ready-for-hosted-ci, hosted-ci-no-benchmarks — CI/tooling-only change (test wiring, knip globs, one CI step); cannot affect runtime performance, so benchmarks add no signal.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • Added automated checks for custom lint rules to the validation workflow and main project check command.
    • Included standalone rule tests in workspace coverage so they run consistently.
  • Chores

    • Updated repository configuration so the new lint-rule tests are recognized by maintenance tools and CI.

The custom ESLint rule eslint-rules/no-use-client-in-server-files.cjs
guards a real hazard (a 'use client' directive landing in a server-only
file) and ships a RuleTester suite next to it, but nothing executed that
suite: no package script, workflow, or lefthook entry referenced it, so
the rule could regress silently while its "tests" rotted. knip could not
flag the orphan either, because the root workspace project globs only
scanned top-level files.

This wires the suite in with the smallest footprint, matching the
existing "Test GitHub Action helpers" precedent that runs a standalone
.cjs test via plain node (ESLint's RuleTester runs standalone and exits
non-zero on failure, so no test framework is needed):

- package.json: add "test:eslint-rules" (plain node invocation) and chain
  it into the aggregate "check" script.
- .github/workflows/lint-js-and-ruby.yml: add a "Test custom ESLint
  rules" step next to "Test GitHub Action helpers" so it runs on every PR.
- knip.ts: add eslint-rules/**/*.cjs to the root project globs and
  eslint-rules/**/*.test.cjs to entry (the test has no importer), so the
  directory is analyzed and stays clean.

No new dependencies. The test file itself needed no changes to be
runnable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@justin808

Copy link
Copy Markdown
Member Author

Workflow Change Audit

File: .github/workflows/lint-js-and-ruby.yml

Classification: SEMANTIC — adds a new CI step (test gate) to the existing build job.

Diff (the entire workflow change):

       - name: Test GitHub Action helpers
         run: node .github/actions/ensure-main-docs-safety/check-previous-main.test.cjs
+      - name: Test custom ESLint rules
+        run: pnpm run test:eslint-rules
       - name: Check formatting
         run: pnpm start format.listDifferent

Before → After audit of security-relevant surfaces:

Surface Before After
Secret refs (secrets.*) none in this region unchanged — none added
permissions: contents: read (job/workflow) unchanged
on: triggers push/pull_request/merge_group/workflow_dispatch unchanged
Third-party actions none added unchanged — the new step is a plain run: shell command
Untrusted input interpolation n/a nonerun: pnpm run test:eslint-rules is a static string with no ${{ github.event.* }} interpolation, so no command-injection surface
Network / new binaries n/a none — reuses pnpm + node already set up earlier in the job

Command-injection review: the added run: uses no ${{ ... }} expressions and no untrusted event fields. No injection risk. It runs an in-repo .cjs test via pnpm, identical in shape to the adjacent pre-existing Test GitHub Action helpers step.

Net effect: one additional lint-job step that executes the repo's own custom-ESLint-rule RuleTester suite. It runs in the existing build job under the same contents: read permission and adds no new capabilities, secrets, actions, or triggers.

A follow-up issue for post-merge Actions verification is linked below.

@justin808

Copy link
Copy Markdown
Member Author

Follow-up issue for post-merge GitHub Actions verification: #4434 (umbrella #4346).

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

Failed to post review comments.

GitHub was unavailable or timed out while CodeRabbit was posting the review. Please request a new review later if the pull request still needs one. Use @coderabbitai full review to retry the review.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1596dc3d-e76d-4871-a830-0e11c50926c3

📥 Commits

Reviewing files that changed from the base of the PR and between 75b89f5 and 60cd09c.

📒 Files selected for processing (3)
  • .github/workflows/lint-js-and-ruby.yml
  • knip.ts
  • package.json
🧰 Additional context used
📓 Path-based instructions (2)
.github/workflows/**

📄 CodeRabbit inference engine (AGENTS.md)

For CI workflow edits, inspect secret exposure, permission changes, trigger changes, and third-party action execution; run actionlint, yamllint .github/, and script/ci-changes-detector origin/main; and add a Workflow Change Audit: PR comment summarizing the before/after diff for those areas.

Files:

  • .github/workflows/lint-js-and-ruby.yml
**/package.json

📄 CodeRabbit inference engine (AGENTS.md)

When updating package scripts, dependencies, or workspace/package-manager settings, use pnpm-based workflows and keep the monorepo package configuration consistent with the declared package manager.

Files:

  • package.json
🧠 Learnings (3)
📚 Learning: 2026-06-18T22:53:09.355Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 4106
File: .github/workflows/pro-integration-tests.yml:895-899
Timestamp: 2026-06-18T22:53:09.355Z
Learning: In GitHub Actions workflows, `defaults.run.working-directory` applies only to inline `run:` steps that do not specify their own `working-directory:` override. It does NOT apply to `uses:` action inputs such as `with: working-directory: ...`—those require a path relative to the repository root. Therefore, when `defaults.run.working-directory` is set, do not flag `run:` commands that use shorter relative paths (e.g., `defaults.run.working-directory: react_on_rails_pro` with `run: cd spec/foo` should resolve to `react_on_rails_pro/spec/foo`). But if an action input `with: working-directory:` is used, require the full repo-root-prefixed path and do not treat a shorter path as a missing prefix.

Applied to files:

  • .github/workflows/lint-js-and-ruby.yml
📚 Learning: 2026-06-18T22:53:16.750Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 4106
File: .github/workflows/pro-integration-tests.yml:927-928
Timestamp: 2026-06-18T22:53:16.750Z
Learning: When a GitHub Actions workflow sets `defaults.run.working-directory` at the workflow level, all `run:` steps that do not override `working-directory` inherit it and execute from that directory. In such cases, use paths relative to the inherited working directory (do not prefix paths with the working-directory folder again); otherwise you’ll double-prefix the directory and break references (e.g., manifests, spec paths, artifacts).

Applied to files:

  • .github/workflows/lint-js-and-ruby.yml
📚 Learning: 2026-06-01T08:02:28.428Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 3516
File: docs/pro/react-server-components/rspack-compatibility.md:58-60
Timestamp: 2026-06-01T08:02:28.428Z
Learning: In shakacode/react_on_rails, use the canonical RSC manifest filename pair: `react-client-manifest.json` and `react-server-client-manifest.json`. Do not introduce or reference the legacy/incorrect variant `react-ssr-manifest.json`; if a change uses the wrong name, update it to the canonical filenames.

Applied to files:

  • knip.ts

Walkthrough

This PR wires a previously orphaned ESLint rule test file into the project's tooling: a new test:eslint-rules npm script executes it, the check script chains it, CI runs it as a step, and knip's project globs are extended to scan eslint-rules/.

Changes

Test wiring for custom ESLint rules

Layer / File(s) Summary
Add test script and chain into check
package.json
Adds test:eslint-rules script running the RuleTester file via node, and updates check to run it between lint and pnpm -r run check.
Run test in CI and update knip scanning
.github/workflows/lint-js-and-ruby.yml, knip.ts
Adds a "Test custom ESLint rules" CI step, and extends knip's root entry/project globs to include eslint-rules/**/*.test.cjs and eslint-rules/**/*.cjs.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Related issues: #4409 — wires the orphaned eslint-rules/no-use-client-in-server-files.test.cjs into a test runner, CI, and knip's project globs.

Suggested labels: tooling, ci, testing

Suggested reviewers: justin808

🐰 A rule once tested, silently, alone,
Now runs in CI, its status known,
Knip peeks into eslint-rules' door,
No orphaned test shall lurk anymore.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes wiring the ESLint rule tests into a runner, CI, and knip.
Linked Issues check ✅ Passed The PR adds the test script, CI step, and knip globs required to run the standalone RuleTester suite for [#4409].
Out of Scope Changes check ✅ Passed The changes are limited to the requested test wiring and knip coverage, with no unrelated edits.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg/4409-eslint-rule-test-wire

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: dependency version conflict. Check your lock file or package.json.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@justin808 justin808 added ready-for-hosted-ci Run optimized hosted GitHub CI for this PR hosted-ci-no-benchmarks Suppress benchmark suites for hosted CI/tooling PRs that cannot affect runtime performance labels Jul 2, 2026
@greptile-apps

greptile-apps Bot commented Jul 2, 2026

Copy link
Copy Markdown

Greptile Summary

This PR wires up an existing but previously unexecuted RuleTester suite for the custom no-use-client-in-server-files ESLint rule into CI, a new package script, and knip's dead-code analysis — no new dependencies and no changes to the rule or test logic itself.

  • package.json: adds test:eslint-rules (runs the .cjs test via plain node) and chains it into the aggregate check script, matching the existing precedent set by the GitHub Actions helpers test step.
  • .github/workflows/lint-js-and-ruby.yml: inserts Test custom ESLint rules immediately after Test GitHub Action helpers in the build job so the suite runs on every PR.
  • knip.ts: adds eslint-rules/**/*.cjs to project and eslint-rules/**/*.test.cjs to entry so knip can track the directory and correctly treat the standalone test file as an entry point.

Confidence Score: 5/5

Safe to merge — all three files make minimal, targeted additions with no logic changes to the rule or test file itself.

The change is purely additive CI/tooling wiring. The new workflow step follows the exact same pattern as the adjacent Test GitHub Action helpers step. The knip entries correctly separate entry (no-importer test file) from project (rule file imported by eslint.config.ts). The test:eslint-rules script explicitly targets a .cjs extension, which is evaluated as CommonJS by Node regardless of the workspace-level type:module. No runtime code is affected.

No files require special attention.

Important Files Changed

Filename Overview
package.json Adds test:eslint-rules script and chains it into check; straightforward and correct.
.github/workflows/lint-js-and-ruby.yml Inserts one new CI step using pnpm run test:eslint-rules; placement and syntax are correct.
knip.ts Adds eslint-rules/**/*.cjs to project and eslint-rules/**/*.test.cjs to entry; correctly distinguishes entry-point-only test files from imported rule files.

Reviews (1): Last reviewed commit: "Wire eslint-rules RuleTester suite into ..." | Re-trigger Greptile

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size
react-on-rails/client bundled (gzip) 63.51 KB (0%)
react-on-rails/client bundled (gzip) (time) 63.51 KB (0%)
react-on-rails/client bundled (brotli) 54.51 KB (0%)
react-on-rails/client bundled (brotli) (time) 54.51 KB (0%)
react-on-rails-pro/client bundled (gzip) 64.54 KB (0%)
react-on-rails-pro/client bundled (gzip) (time) 64.54 KB (0%)
react-on-rails-pro/client bundled (brotli) 55.53 KB (0%)
react-on-rails-pro/client bundled (brotli) (time) 55.53 KB (0%)
registerServerComponent/client bundled (gzip) 75.89 KB (0%)
registerServerComponent/client bundled (gzip) (time) 75.89 KB (0%)
registerServerComponent/client bundled (brotli) 65.42 KB (0%)
registerServerComponent/client bundled (brotli) (time) 65.42 KB (+0.11% 🔺)
wrapServerComponentRenderer/client bundled (gzip) 68.37 KB (0%)
wrapServerComponentRenderer/client bundled (gzip) (time) 68.37 KB (0%)
wrapServerComponentRenderer/client bundled (brotli) 58.76 KB (0%)
wrapServerComponentRenderer/client bundled (brotli) (time) 58.76 KB (0%)

Merged via the queue into main with commit 29725eb Jul 3, 2026
92 of 105 checks passed
@justin808
justin808 deleted the jg/4409-eslint-rule-test-wire branch July 3, 2026 00:56
justin808 added a commit that referenced this pull request Jul 3, 2026
…cache-4317

* origin/main:
  Preserve streaming LoadError during dependency failures (#4388)
  Wire eslint-rules RuleTester suite into a runner, CI, and knip (#4409) (#4433)
  Handle fire-and-forget RSCRoute retry failures (#4378)
  Fix visible hydration cleanup for detached roots (#4374)
  Avoid full locale default obsolete scans (#4398)
  Document RSC public-page validation and sidecar patterns (#4387)

# Conflicts:
#	CHANGELOG.md
justin808 added a commit that referenced this pull request Jul 3, 2026
…derer-shutdown-restart

* origin/main:
  Preserve streaming LoadError during dependency failures (#4388)
  Wire eslint-rules RuleTester suite into a runner, CI, and knip (#4409) (#4433)
  Handle fire-and-forget RSCRoute retry failures (#4378)
  Fix visible hydration cleanup for detached roots (#4374)
  Avoid full locale default obsolete scans (#4398)
justin808 added a commit that referenced this pull request Jul 3, 2026
* origin/main:
  Preserve streaming LoadError during dependency failures (#4388)
  Wire eslint-rules RuleTester suite into a runner, CI, and knip (#4409) (#4433)
  Handle fire-and-forget RSCRoute retry failures (#4378)
  Fix visible hydration cleanup for detached roots (#4374)
  Avoid full locale default obsolete scans (#4398)
  Document RSC public-page validation and sidecar patterns (#4387)
  Remove dead methods; prune always-false Rails<5.0 spec branches (#4418) (#4431)
justin808 added a commit that referenced this pull request Jul 3, 2026
…nsport

* origin/main:
  Preserve streaming LoadError during dependency failures (#4388)
  Wire eslint-rules RuleTester suite into a runner, CI, and knip (#4409) (#4433)
  Handle fire-and-forget RSCRoute retry failures (#4378)

# Conflicts:
#	CHANGELOG.md
justin808 added a commit that referenced this pull request Jul 3, 2026
…-4364

* origin/main: (24 commits)
  Preserve streaming LoadError during dependency failures (#4388)
  Wire eslint-rules RuleTester suite into a runner, CI, and knip (#4409) (#4433)
  Handle fire-and-forget RSCRoute retry failures (#4378)
  Fix visible hydration cleanup for detached roots (#4374)
  Avoid full locale default obsolete scans (#4398)
  Document RSC public-page validation and sidecar patterns (#4387)
  Remove dead methods; prune always-false Rails<5.0 spec branches (#4418) (#4431)
  Drop deprecation-tombstone config options for 17.0.0 (#4419) (#4432)
  Add mechanical parity guards for Ruby↔TS protocol constants (#4412) (#4427)
  Move Node tsconfigs from @tsconfig/node14 to @tsconfig/node18 (#4410) (#4429)
  [Pro] Remove unused addressable and rainbow runtime deps from gemspec (#4416) (#4422)
  Delete finished #3313 Prism Gemfile-rewriter spike (#4421)
  Extract generator scan/tracking helpers (#4405) (#4430)
  Extract install_dependency_group helper in JsDependencyManager (#4403) (#4424)
  Remove obsolete Ruby<2.6 YAML-aliases capability shim (#4417) (#4428)
  Remove inert config.server_render_method option (#4415) (#4423)
  Prune stale knip ignores and enforce binaries in CI (#4408) (#4425)
  Extract shared redux_store kwargs validator (#4402) (#4420)
  Avoid caching async props prerender streams (#4376)
  Release incremental render context on setup failure (#4383)
  ...

# Conflicts:
#	CHANGELOG.md
justin808 added a commit that referenced this pull request Jul 3, 2026
…370' into codex/batch-e-loadable-stats-retry-4371

* origin/codex/batch-e-rsc-parser-flush-4370:
  Add cached static RSC helper and diagnostics (#4386)
  Fix Pro tag revalidation retry after delete failures (#4375)
  Fix node renderer graceful shutdown restarts (#4400)
  Improve release-finish dry-run fetch handling (#4441)
  Flush RSC payloads before incomplete HTML tails (#4379)
  Handle sync RSC route failures as fetch errors (#4393)
  Delete never-wired RenderRequest/JsCodeBuilder/RenderingStrategy layer (#4414) (#4437)
  Delegate deprecated base/ shims to capabilities/ instead of cloning (#4413) (#4436)
  Remove pre-monorepo node-renderer devDep baggage; consolidate test multipart builders (#4435)
  Preserve streaming LoadError during dependency failures (#4388)
  Wire eslint-rules RuleTester suite into a runner, CI, and knip (#4409) (#4433)
  Handle fire-and-forget RSCRoute retry failures (#4378)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hosted-ci-no-benchmarks Suppress benchmark suites for hosted CI/tooling PRs that cannot affect runtime performance ready-for-hosted-ci Run optimized hosted GitHub CI for this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant