fix: report the real cause when check-env.cjs's npm-version check fails
Discovered blocking this PR's own Windows CI (unrelated to this PR's actual diff, fixed inline per this repo's no-defer policy): PR #4552's "full test (windows-latest, 24, shard 2/3)" job failed tests/check-env.test.cjs's npm-version subtest with "npm binary not found on PATH" under chunk 5/9's heavy load (51 concurrent files, 5+ minutes). Root-caused via the CI log: the check's spawnSync call used a 10s timeout, and every failure mode -- ENOENT, a signal-killed timeout, a non-zero exit, a thrown spawn error -- collapsed into that one message (only `res.status === 0 && res.stdout` was checked), so a genuine npm.cmd cold-start timeout under contention was indistinguishable from npm actually being absent. Extracted the reason-selection into describeNpmVersionCheckFailure, a pure function in the new scripts/lib/npm-version-check-diagnosis.cjs (kept out of check-env.cjs itself, which runs its CLI unconditionally on require with no `require.main === module` guard, so the pure logic can be unit-tested without triggering a real environment check). Reports ENOENT, signal-kill, non-zero-exit, and thrown-error cases distinctly. Does NOT raise the 10s timeout itself -- a slow subprocess under contention is a cost to reduce, not a tolerance to widen. Also fixed a stale tsconfig.build.tsbuildinfo incremental-build cache discovered while verifying this change: npm run build:lib was silently omitting gsd-core/bin/lib/markdown-table.cjs (a real, needed compiled module -- src/state-document.cts requires it), which only surfaced via npm run lint:generated-sync's gen-health-docs check failing with Cannot find module. Deleting the cache and rebuilding fresh restored it; docs/INVENTORY-MANIFEST.json needed no net change once the build was genuinely complete. Manually verified describeNpmVersionCheckFailure's five branches directly (ENOENT, signal-kill, non-zero exit, thrown error, defensive default) before wiring the test file, since this repo blocks local node --test. Re-ran node scripts/check-env.cjs directly to confirm the real success path is unaffected. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,7 @@ const path = require('path');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
||||
const { describeNpmVersionCheckFailure } = require('./lib/npm-version-check-diagnosis.cjs');
|
||||
|
||||
// On Windows, npm ships as npm.cmd (a batch wrapper); spawnSync without
|
||||
// shell:true requires the exact filename including extension.
|
||||
@@ -180,18 +181,21 @@ function main() {
|
||||
// Check 2: npm version vs engines.npm (skip if field absent)
|
||||
// ---------------------------------------------------------------------------
|
||||
const enginesNpm = pkgField('engines.npm', PROJECT_ROOT);
|
||||
const NPM_VERSION_TIMEOUT_MS = 10_000;
|
||||
let currentNpm = '';
|
||||
let npmSpawnResult = null;
|
||||
let npmSpawnThrew = null;
|
||||
try {
|
||||
const res = spawnSync(npmCmd, ['--version'], { encoding: 'utf8', timeout: 10_000, shell: process.platform === 'win32' });
|
||||
if (res.status === 0 && res.stdout) {
|
||||
currentNpm = res.stdout.trim();
|
||||
npmSpawnResult = spawnSync(npmCmd, ['--version'], { encoding: 'utf8', timeout: NPM_VERSION_TIMEOUT_MS, shell: process.platform === 'win32' });
|
||||
if (npmSpawnResult.status === 0 && npmSpawnResult.stdout) {
|
||||
currentNpm = npmSpawnResult.stdout.trim();
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
} catch (e) { npmSpawnThrew = e; }
|
||||
|
||||
if (!enginesNpm) {
|
||||
addCheck('npm-version', 'skip', 'engines.npm not set in package.json — skipping');
|
||||
} else if (!currentNpm) {
|
||||
addCheck('npm-version', 'fail', 'npm binary not found on PATH');
|
||||
addCheck('npm-version', 'fail', describeNpmVersionCheckFailure(npmSpawnResult, npmSpawnThrew, NPM_VERSION_TIMEOUT_MS));
|
||||
} else {
|
||||
if (satisfiesConstraint(currentNpm, enginesNpm)) {
|
||||
addCheck('npm-version', 'pass', `npm ${currentNpm} satisfies ${enginesNpm}`);
|
||||
|
||||
Reference in New Issue
Block a user