Skip to content

verify:typecheck-coverage cannot run on Windows — execFileSync('npx', …) is ENOENT, so validate/ci fail at step one #1939

Description

@olaservo

Version: v2 (main)
Affected client: repo tooling (scripts/verify-typecheck-coverage.mjs)

Symptom

On Windows, npm run validate (and therefore the mandatory npm run ci pre-push gate) fails at its first step. Every project harvest prints:

verify:typecheck-coverage — `tsc -p tsconfig.json` (in clients/cli) exited non-zero:
(no diagnostic captured)

…for every client/project, followed by 905 tracked source file(s) get no tsc pass — every TS file in the repo listed as uncovered.

Root cause

rawProjectFiles() (and projectDisablesChecking()) call execFileSync("npx", ["--no-install", "tsc", …]). On Windows there is no npx executable — only npx.cmd — and Node cannot spawn a .cmd without a shell (and refuses shell-less .cmd spawns since the CVE-2024-27980 hardening). The spawn throws ENOENT, the catch treats it as a tsc config error with empty stdout, every project's file set comes back empty, and the guard reports the whole repo as unclaimed.

Verified on Windows 11 / Node 22:

> node -e "require('child_process').execFileSync('npx',['--no-install','tsc','--version'])"
Error: spawnSync npx ENOENT

GitHub CI never sees this because its runners are Linux, so the gate is green there while being unrunnable for any Windows contributor.

Suggested fix

Resolve the real tsc entry instead of shelling through npx: spawn process.execPath with the client-local node_modules/typescript/lib/tsc.js (falling back up the tree), which is cross-platform, faster (no npx resolution), and keeps execFileSync shell-free. Alternatively spawn npx.cmd with shell: true on win32 and quote args — the approach scripts/pack-and-verify.mjs needs for the same reason — but the direct-node form avoids the shell-quoting hazards entirely. scripts/verify-typecheck-coverage.test.mjs should pin whichever contract is chosen with a Windows-shaped regression test (a spawner that throws ENOENT must surface as a hard "cannot measure" error, not as an empty file set silently reported as 905 uncovered files).

Context

Found while working #1852 on a Windows machine — the gate could not be run to validate that fix. Note the failure mode is doubly silent: the ENOENT is reported as "(no diagnostic captured)" and then converted into a misleading coverage report instead of an environment error.

Metadata

Metadata

Assignees

Labels

v2Issues and PRs for v2

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions