fix: align Node runtime guard with package engines - #305
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughThe Node.js runtime guard now enforces the package engine windows, including minor-version thresholds and excluded major versions. Startup and doctor diagnostics use the shared supported range. Tests validate boundaries and synchronization with ChangesNode engine validation
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
@ruili-testsprite @zeshi-du — this is ready for maintainer review: current head Could you please review the PR and confirm whether a merged solution for the assigned CLI task qualifies for the contribution bonus? I’ll address any requested change immediately and can provide the payout/onboarding details through your preferred private route after acceptance. |
zeshi-du
left a comment
There was a problem hiding this comment.
Thanks for taking #254 on — the diagnosis is right and the direction matches what the triage asked for. Two things block the merge, and the first one needs a correction to the record.
I've also approved the pending CI / Test Coverage runs on this PR so the suite result is public rather than something we argue about in prose.
Blocking
1. This diff breaks 11 tests in src/commands/doctor.test.ts — they are not environment-sensitive
doctor.test.ts builds its healthy fixture with nodeVersion: '22.9.0' (L53, L302). Under the new guard 22.9.0 is rejected (22 < 22.13), so the Node.js check flips to fail, runDoctor throws CLIError: doctor: 1 check(s) failed, and every "healthy environment" test dies with it.
Control run, npm ci in a clean worktree, macOS / Node 26:
| commit | version-guard.test.ts + doctor.test.ts |
|---|---|
d1e697c~1 (base, v0.5.0) |
26 passed, 0 failed |
d1e697c (this PR) |
16 passed, 11 failed |
All 11 are in doctor.test.ts. version-guard.test.ts itself is 8/8 green, so the new guard tests are fine — it is the existing doctor fixtures that this change invalidates.
The PR body records these as "16 unrelated environment-sensitive failures in doctor/subprocess tests". The doctor ones are neither: the version is injected by the fixture, so they reproduce on every platform and every Node version. Please don't file known-failing output under "unrelated" — the PR template asks for npm test to pass, and the Validation section here lists only npx vitest run src/version-guard.test.ts.
Fix: move the fixture to a supported release (22.13.0+) and add a doctor-level regression asserting that a now-excluded in-range major such as 22.9.0 fails the check.
2. doctor still prints the old, now-false range
SUPPORTED_NODE_RANGE landed and is wired into src/index.ts, but src/commands/doctor.ts:168-169 is still on MIN_SUPPORTED_NODE_MAJOR:
- rejected path →
v22.9.0 is below the required Node 20; upgrade Node.js— literally false - ok path →
v24.1.0 (>=20 required)— understates the real requirement
Issue #254 names doctor explicitly ("causes doctor to report them as supported"), so the fix isn't complete until both consumers of the guard speak the same range. While you're in that function, the comment above checkNodeVersion is also stale: it claims the precise floor "is enforced by npm at install time via .npmrc engine-strict", but that repo-local .npmrc governs contributors, not consumers installing from npm — which is exactly why the runtime guard has to carry the range itself.
Non-blocking
-
The guard got weaker for major-only strings.
parseMajorMinor('18')→Number(undefined)isNaN→null→ not rejected, where the oldparseMajorVersionrejected it. Realprocess.versions.nodeis alwaysx.y.z, butdoctor's injectablenodeVersiondep is not. Falling back to minor0when only a major is present keeps the old behavior. -
Three parallel encodings of one range.
SUPPORTED_NODE_ENGINE(semver),SUPPORTED_NODE_RANGE(prose), and the branch ladder (MIN_NODE_20_MINOR,MIN_NODE_22_MINOR, the 21/23 rejections, the implicit>=24). The new test pins only the string topackage.json, so the logic can still drift from it silently. A table-driven test derived from the engine string — or at minimum a comment tying each branch to its clause — would close that.MIN_SUPPORTED_NODE_MAJORis also bypassed by bare literals insideshouldRejectNodeVersionnow. -
User-visible break, no CHANGELOG entry. Users on 20.0–20.18, 21.x, 22.0–22.12 and 23.x run fine today (engine-strict is not enforced on consumers) and get hard-blocked after this ships. That is the intended outcome of #254, but it belongs under
## [Unreleased].
Credit where it's due
The boundary coverage is genuinely good — every window edge (20.18.99 / 20.19.0, 22.12.99 / 22.13.0, 21.x, 23.x, 24, 25) is asserted, no new dependency was pulled in, and the diff stays scoped. Items 1 and 2 are one small follow-up commit away; push it and I'll re-review.
On the contribution-bonus question from your comment above: that's coordinated outside this thread, and I'll leave it to the folks running the program to follow up. Reviews here stay on the code.
|
CI has now run (I approved the pending fork workflows) and it settles the "environment-sensitive" question:
Same 11 failures, same file, on ubuntu/Node 20 as on my macOS/Node 26 control run — so they track the diff, not the machine. Everything else in the pipeline is green, which is a good sign for the rest of the change: fix the doctor fixture and the doctor message and this should go green in one push. Run: https://github.com/TestSprite/testsprite-cli/actions/runs/31323659046 |
04b76e7 to
4c27180
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/commands/doctor.ts`:
- Around line 114-121: Update the doctor test fixtures to use a supported Node
version such as 22.13.0 for both cases, and revise the outdated-runtime
assertion to expect “outside the supported Node range 20.19+, 22.13+, or 24+”.
Keep the checkNodeVersion behavior unchanged.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 8b429f34-4602-4e30-91c4-0a2394fa1e30
📒 Files selected for processing (1)
src/commands/doctor.ts
| function checkNodeVersion(nodeVersion: string): DoctorCheck { | ||
| // Reuse the CLI's own runtime guard so the verdict matches exactly what the | ||
| // entrypoint enforces at startup, rather than a divergent hardcoded check. | ||
| // The precise engines floor (20.19+/22.13+/24+) is enforced by npm at install | ||
| // time via .npmrc engine-strict. sourceRef: src/version-guard.ts. | ||
| const rejected = shouldRejectNodeVersion(nodeVersion); | ||
| return { | ||
| name: 'Node.js', | ||
| status: rejected ? 'fail' : 'ok', | ||
| detail: rejected | ||
| ? `v${nodeVersion} is below the required Node ${MIN_SUPPORTED_NODE_MAJOR}; upgrade Node.js` | ||
| : `v${nodeVersion} (>=${MIN_SUPPORTED_NODE_MAJOR} required)`, | ||
| ? `v${nodeVersion} is outside the supported Node range ${SUPPORTED_NODE_RANGE}; upgrade Node.js` | ||
| : `v${nodeVersion} (${SUPPORTED_NODE_RANGE} supported)`, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ast-grep outline src/commands/doctor.test.ts --items all
rg -n -C 4 'nodeVersion|Node\.js|20\.19|22\.13|24\+' src/commands/doctor.test.tsRepository: TestSprite/testsprite-cli
Length of output: 2420
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- doctor source outline ---'
ast-grep outline src/commands/doctor.ts --items all
printf '%s\n' '--- relevant doctor implementation ---'
rg -n -C 8 'SUPPORTED_NODE_RANGE|shouldRejectNodeVersion|checkNodeVersion|nodeVersion' src/commands/doctor.ts
printf '%s\n' '--- complete test fixture and assertions ---'
cat -n src/commands/doctor.test.ts
printf '%s\n' '--- documented runtime range ---'
rg -n -C 3 '20\.19|22\.13|Node\.js 24|supported runtime|runtime' DOCUMENTATION.mdRepository: TestSprite/testsprite-cli
Length of output: 23314
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- version guard ---'
fd -i 'version-guard' .
for file in $(fd -i 'version-guard' .); do
cat -n "$file"
done
printf '%s\n' '--- test references to the unsupported baseline ---'
rg -n -C 2 "22\.9\.0|below the required Node 20|outside the supported Node range|20\.19\+|22\.13\+" src/commands/doctor.test.ts src
printf '%s\n' '--- count affected test declarations ---'
python3 - <<'PY'
from pathlib import Path
p = Path("src/commands/doctor.test.ts")
lines = p.read_text().splitlines()
for i, line in enumerate(lines):
if "22.9.0" in line:
start = max(0, i - 30)
tests = [(j + 1, x.strip()) for j, x in enumerate(lines[start:i + 1], start) if x.strip().startswith("it(")]
print(f"fixture line {i + 1}: nearest test declaration: {tests[-1] if tests else 'none'}")
print("22.9.0 occurrences:", sum("22.9.0" in line for line in lines))
PYRepository: TestSprite/testsprite-cli
Length of output: 9594
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
lines = Path("src/commands/doctor.test.ts").read_text().splitlines()
# Associate each test body with its nearest preceding `it(...)` declaration.
tests = []
current = None
for number, line in enumerate(lines, 1):
match = re.search(r"\bit\((['\"])(.*?)\1", line)
if match:
current = {"line": number, "name": match.group(2), "lines": []}
tests.append(current)
if current is not None:
current["lines"].append(line)
affected = []
for test in tests:
body = "\n".join(test["lines"])
# The helper default is 22.9.0 unless a test overrides nodeVersion.
uses_unsupported_default = "healthyDeps(credentialsPath" in body and "nodeVersion:" not in body
uses_unsupported_dry_run_fixture = (
"nodeVersion: '22.9.0'" in body
and "dry-run" in test["name"]
)
if uses_unsupported_default or uses_unsupported_dry_run_fixture:
affected.append((test["line"], test["name"]))
print("Tests affected by the unsupported default/explicit fixture:")
for line, name in affected:
print(f"{line}: {name}")
print("Affected test count:", len(affected))
# Reproduce the relevant guard result without importing or running repository code.
def rejects(version):
major, minor = map(int, version.split(".")[:2])
if major < 20:
return True
if major == 20:
return minor < 19
if major == 21:
return True
if major == 22:
return minor < 13
if major == 23:
return True
return False
print("shouldRejectNodeVersion('22.9.0'):", rejects("22.9.0"))
print("shouldRejectNodeVersion('22.13.0'):", rejects("22.13.0"))
print(
"Expected outdated detail:",
"v18.0.0 is outside the supported Node range "
"20.19+, 22.13+, or 24+; upgrade Node.js",
)
PYRepository: TestSprite/testsprite-cli
Length of output: 1448
Update the stale doctor test fixtures and assertion.
Set both Node fixtures to a supported version such as 22.13.0. Replace the outdated-runtime assertion with outside the supported Node range 20.19+, 22.13+, or 24+. The current 22.9.0 fixture is rejected and causes 11 passing-path tests to fail.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/commands/doctor.ts` around lines 114 - 121, Update the doctor test
fixtures to use a supported Node version such as 22.13.0 for both cases, and
revise the outdated-runtime assertion to expect “outside the supported Node
range 20.19+, 22.13+, or 24+”. Keep the checkNodeVersion behavior unchanged.
a256ff0 to
887a324
Compare
What changed
package.jsonpackage.json#engines.nodeWhy
The runtime guard only checked
major >= 20, so it admitted releases excluded by the package engine contract and could fail later without the intended actionable message.Closes #254
Validation
npx vitest run src/version-guard.test.ts— 8/8 passednpm run lint— passednpm run typecheck— passednpm run build— passedgit diff --check— passedThe full Windows/Node 24 suite also exposed 16 unrelated environment-sensitive failures in doctor/subprocess tests; no affected guard test failed.
Summary by CodeRabbit
Bug Fixes
Tests