feat(check): support knip 6 JSON reporter output and bump knip to 6.17.1 - #2826
feat(check): support knip 6 JSON reporter output and bump knip to 6.17.1#2826marschattha wants to merge 6 commits into
Conversation
Node 21 is an odd-numbered, EOL release line whose engine ranges are excluded by current eslint, knip, oxlint, and prisma releases. npm silently skips optional dependencies (including native bindings) whose engines exclude the running node, breaking installs. Closes #2814 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
eslint 10 no longer bundles @eslint/js, so the basic_9 fixture config now enables the three exercised rules directly instead of extending js.configs.recommended. The config works unchanged across eslint 9 and 10, keeping all historical snapshots valid. 10.5.0 is the newest release passing the 30-day stability gate (published 2026-06-12) with no known security advisories. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Uses the version-gated >=1.0.0 driver block added previously for the oxlint 1.x --format github output change. The new snapshot records proper rule keys (e.g. eslint(no-cond-assign)) where the 0.11.1 format offered none. 1.70.0 is the newest release passing the 30-day stability gate (published 2026-06-15) with no known security advisories. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
prisma 7 no longer allows a url property in schema-file datasource blocks, while prisma 5/6 require it, so a separate basic_7 fixture covers 7.x and the original fixture keeps covering the 5.x snapshots. 7.8.0 is the newest release passing the 30-day stability gate (published 2026-04-22) with no known security advisories. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
knip 6 restructured its JSON reporter: unused files moved from a top-level files array into per-issue rows, and enumMembers / classMembers changed from maps keyed by parent symbol to flat lists with the parent on each identifier's namespace field. The parser now accepts both shapes, so knip 5 and 6 outputs produce identical issues. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verified that knip 6.17.1 produces findings identical to 5.88.1 across all three fixtures with the dual-format parser. 6.17.1 is the newest release passing the 30-day stability gate (published 2026-06-16) with no known security advisories. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Coverage Impact - ubuntu-latest This PR will not change total coverage. Modified Files with Diff Coverage (2)
🤖 Increase coverage with AI coding...🚦 See full report on Qlty Cloud » 🛟 Help
|
|
Coverage Impact - macos-15 This PR will not change total coverage. Modified Files with Diff Coverage (2)
🤖 Increase coverage with AI coding...🚦 See full report on Qlty Cloud » 🛟 Help
|
## Summary Bumps the managed Node runtime from 21.7.3 (odd-numbered, EOL, non-LTS) to 22.23.1 (latest 22 LTS "Jod"). Closes #2814. Node 21 is a short-lived non-LTS line that the npm ecosystem deliberately excludes: engine ranges target even LTS lines (`^20.19 || ^22.12 || >=24`), and npm silently skips optional dependencies — including native bindings — whose `engines` exclude the running node. This blocked eslint 10, knip 6, oxlint 1.x, and prisma 7. The download URLs in `qlty-check/src/tool/node.rs` are version-templated, so this is a one-line change. The plugin promotions this unblocks follow in stacked PRs: #2825 (eslint, oxc, prisma) and #2826 (knip, which also needs a parser change). ## Test results - Full node-linter jest matrix on node 22.23.1: 11/11 suites, 24 tests, 24 snapshots passed — every fixture at every historical snapshot version (back to eslint 8.10.0, prettier 2.6.2) runs unmodified on the new runtime - `cargo test`: 1087 passed, 0 failed ## Test plan - [ ] All linter tests pass in CI (including windows-2022) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1c1ebd3 to
f631774
Compare
## Summary Promotes the three plugins unblocked by the node 22 runtime bump that need no code changes. The runtime bump itself landed separately in #2827 (closing #2814), so this PR is now plugin promotions only. npm silently skips optional dependencies (including native bindings) whose `engines` exclude the running node, and some tools crash outright — node 21 was excluded by current eslint, knip, oxlint, and prisma engine ranges. The knip 6 bump requires a parser change and follows in a separate stacked PR (#2826). ## Updates | Plugin | Previous | New | Published | Age | Notes | |--------|----------|-----|-----------|-----|-------| | eslint | 9.39.4 | 10.5.0 | 2026-06-12 | 39d | fixture config no longer imports `@eslint/js` (not bundled in eslint 10) | | oxc | 0.11.1 | 1.70.0 | 2026-06-15 | 36d | uses the pre-existing version-gated `>=1.0.0` driver block | | prisma | 6.15.0 | 7.8.0 | 2026-04-22 | 90d | new `basic_7` fixture — prisma 7 forbids datasource `url`, prisma 5/6 require it | ## Stability policy All promoted versions have been published for at least 30 days and checked against GitHub Security Advisories (repo-level and the npm ecosystem database). No unwithdrawn advisories affect any promoted version. Newer releases (eslint 10.7.0, oxlint 1.74.0, prisma 7.9.0) were rejected by the 30-day gate; `latest_version` fields record them. ## Test results - Plain-mode jest for touched linters (every fixture at every snapshot version, old and new): all passed - eslint's rewritten `basic_9` fixture config verified against the existing 9.6.0 / 9.7.0 / 9.39.4 snapshots - oxc's new snapshot records proper rule keys (`eslint(no-cond-assign)`) where 0.11.1's output format offered none - `cargo test`: 1087 passed, 0 failed ## Test plan - [ ] All linter tests pass in CI (including windows-2022) - [ ] No security advisories affect updated versions 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Summary Update linter plugin versions with stability and security vetting. Covers the gaps the weekly `updateLinterVersions.ts` script leaves behind. After this PR, **knip is the only linter with `latest_version` ahead of `known_good_version`** (blocked on #2826), and every script-unreachable linter is either current or has a filed reason below. ## Updates | Linter | Previous | New | Published | Age | Notes | |--------|----------|-----|-----------|-----|-------| | redocly | 2.32.2 | **2.41.0** | 2026-07-27 | 30d |⚠️ security — see below | | shfmt | 3.8.0 | **3.13.1** | 2026-04-06 | 142d |⚠️ also fixes a silent-pass bug — see below | | checkstyle | 13.5.0 | 13.9.0 | 2026-07-27 | 30d | | | pmd | 7.25.0 | 7.26.0 | 2026-06-29 | 58d | | | trufflehog | 3.95.5 | 3.96.0 | 2026-07-24 | 33d | security-sensitive linter | | swiftformat | 0.61.1 | 0.62.1 | 2026-07-07 | 50d | custom-download | | swiftlint | 0.63.3 | 0.65.0 | 2026-06-27 | 60d | custom-download | | terraform | 1.15.6 | 1.15.8 | 2026-07-08 | 49d | custom-download | | eslint | 10.5.0 | 10.8.0 | 2026-07-24 | 33d | within the major from #2825 | | oxc (oxlint) | 1.70.0 | 1.76.0 | 2026-07-27 | 30d | within the major from #2825 | | prisma | 7.8.0 | 7.9.1 | 2026-07-27 | 30d | within the major from #2825 | ## Security **redocly 2.32.2 was vulnerable.** The pinned version falls inside the range of two advisories published 2026-07-27: - **CVE-2026-63325** (high) — arbitrary code execution via Arazzo `$faker` expression using `respect`; patched in 2.33.0. - **CVE-2026-63225** (medium) — path traversal in the `split` command; patched in 2.33.2. 2.41.0 clears both. All other candidates were checked against `/repos/{owner}/{repo}/security-advisories` and the GitHub Advisory Database, filtering withdrawn advisories. The only hits were against versions far older than both current and target (checkstyle < 8.29, pmd <= 7.21.0, trufflehog < 3.81.9, terraform < 0.12.17, prisma < 2.20.0). **trufflehog** is a security-sensitive linter (secret detection, elevated trust). Beyond the advisory check, its 3.96.0 release assets were verified for expected content types and its release notes reviewed — nothing suspicious. ## shfmt was reporting unparseable files as clean The version bump surfaced a live correctness bug, fixed here. The format driver declared `success_codes = [0, 1]`, but with `-w` shfmt exits 0 whether or not it rewrites the file — **exit 1 means only a parse or I/O error**. Accepting 1 as success meant any shell file shfmt could not parse produced no rewrite, no error, and zero issues. Verified across both versions: | input | shfmt 3.8.0 | shfmt 3.13.1 | |-------|-------------|--------------| | unterminated quote | 1 | 1 | | unfinished `case` | 1 | 1 | | unclosed `{` | 1 | 1 | | `if true; then` / `fi` (empty body) | 0 | 1 | | valid but misformatted | 0 | 0 | The exit-code contract is identical across versions, so this is **not** version-gated — 3.8.0 silently passes an unterminated quote today, and gating would preserve that for anyone pinning an old shfmt. shfmt 3.9 only made the parser stricter (an `if` with an empty body is now correctly rejected), which is what exposed the bug: the old fixture was invalid shell, so 3.13.1 refused it, the rewrite never ran, and the test went green with zero findings. The fixture now has a body, so old and new shfmt both parse it and both still find it misformatted. Its snapshot is unchanged — `output = "rewrite"` records only a generic formatting issue, not file contents. An invalid fixture cannot be kept as coverage once exit 1 is honored, since the harness asserts `success: true` per fixture. **Behavior change:** shell files that cannot be parsed now surface a plugin error instead of silently reporting clean. Worth a follow-up audit: biome, ktlint, markdownlint, rubocop, ruby-stree, and standardrb also declare `[0, 1]` on rewrite-mode drivers. For several of them exit 1 legitimately means "issues found" and the config is correct — but each should be checked against its tool's actual contract. ## Coverage of script-unreachable linters The script can only update a linter that declares `releases` or a `runtime` in {java, php, ruby, python, node}; anything else hits its `Unknown runtime` throw. Because it also cannot write their `latest_version`, these linters never appear in the usual "latest != known_good" gap scan either. Full enumeration: | Linter | Why unreachable | Disposition | |--------|-----------------|-------------| | kube-linter | no releases, no runtime | already latest (0.8.3) | | stringslint | no releases, no runtime | already latest (0.1.10) | | swiftformat, swiftlint, terraform | no releases, no runtime | **updated here** | | shfmt | runtime `go` | **updated here** | | clippy, rustfmt | runtime `rust` | see below | | gofmt | runtime `go` | see below | | hadolint | skip-list | too new (2.15.1 is 26d) | | tsc | skip-list | no version fields (`hidden = true`) | **clippy, rustfmt, and gofmt are runtime-coupled, not ordinary plugins.** `gofmt` 1.22.0 *is* the managed Go pin (`tool_builder.rs:148`), and clippy/rustfmt track the Rust toolchain (pinned 1.77.2). Upstream is far ahead — Rust 1.98.0, Go 1.27.0 — but bumping them means moving the managed runtimes, the same class of work as the node 21→22 bump in #2814/#2825, with much wider blast radius. Deliberately out of scope here; worth its own issue. ## Stability policy Every version here has been published for at least 30 days, no overrides. Newer releases rejected by the gate, to pick up next run: redocly 2.48.0, checkstyle 14.0.0, eslint 10.9.1, oxlint 1.80.0, prisma 7.10.0, swiftlint 0.65.1, terraform 1.15.9, hadolint 2.15.1 (26d). Node linters were pre-checked with `npm view {pkg}@{ver} engines` against the managed runtime (22.23.1) — all satisfied. ## Also noticed - The stale comments in `knip/plugin.toml` and `oxc/plugin.toml` still blame node 21.7.3. knip's real blocker is #2826; oxc's is simply obsolete. Left untouched here. - **#2813 (biome parser) looks closeable** — #2821 landed an rdjson reporter and biome is already at 2.5.6 on main. ## Test results All 11 updated linters pass in **plain mode**, which runs every fixture at every historical snapshot version — what CI effectively exercises. Each target was additionally run in compare-latest-snapshot mode. Apart from shfmt, output was byte-identical everywhere, so no snapshot changes were needed. Snapshots were inspected for non-zero findings to rule out vacuous passes (this is what caught shfmt). Two fixtures fail *in compare mode only* — eslint `basic_8` (eslintrc, ESLint 8 only) and prisma `basic` (prisma 5 schema). Compare mode forces the target version onto every fixture regardless of what it supports. Both were confirmed to fail identically at the **current** promoted versions (10.5.0 / 7.8.0), so this is pre-existing harness behaviour, not a regression. Custom-download URL templates were HEAD-checked at the target version across every platform variant (all 200) — no asset-name drift this round. ## Test plan - [ ] All linter tests pass in CI - [x] Download URLs verified for custom-download linters (all platforms, 200) - [x] No security advisories affect updated versions - [x] Node engines checked against the managed runtime - [x] Every script-unreachable linter enumerated and dispositioned - [ ] Watch the windows-2022 job specifically — local runs only cover macOS --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

Summary
Stacked on #2825 (knip 6 requires the node 22 runtime). Updates the knip output parser for knip 6's restructured JSON reporter and promotes knip to 6.17.1.
knip 6 changed its JSON reporter output:
filesarray into per-issue rows with afilesentryenumMembers/classMemberschanged from maps keyed by parent symbol to flat lists carrying the parent on each identifier'snamespacefieldThe parser now accepts both shapes via an untagged
KnipMembersenum, so knip 5 and 6 outputs produce identical issues — rule keys, messages, and categories are unchanged, preserving issue identity for existing users' ignores and triage records.Updates
Stability policy
6.17.1 is the newest release passing the 30-day stability gate; everything from 6.18.0 (29 days) through 6.27.0 is inside the window. No unwithdrawn security advisories affect knip (repo-level and npm ecosystem checked).
latest_versionrecords 6.27.0.Test results
parse_v6unit test alongside the existing knip 5 parser testcargo test: 1087 passed, 0 failedFollow-up
knip also ships a
codeclimatereporter. qlty has no Code Climate output-format parser today; adding one and migrating knip to it would end the JSON-shape chasing but changes issue identity (rule keys/categories), so it's deliberately out of scope here.Test plan
mainafter feat(plugins): bump eslint to 10, oxlint to 1.x, and prisma to 7 #2825 merges🤖 Generated with Claude Code