fix: resolve node bins via package.json instead of spawning npx .cmd shims so the verify/smoke scripts run on Windows
On Windows npx/npm are .cmd shims a shell-free execFileSync/spawnSync cannot start (ENOENT since the CVE-2024-27980 hardening), so verify:typecheck-coverage died at validate's second step — doubly silently, echoing "(no diagnostic captured)" per project and then reporting all 918 tracked files as getting no tsc pass — and verify:build-gate, the smoke test-server bootstraps, and pack:verify's npm pack failed the same way. Add scripts/lib/resolve-node-bin.mjs: resolves the JS entry behind a package's bin from <pkg>/package.json (deep resolution is blocked by Vite 8's exports map) and spawns it via process.execPath — the same walk npx --no-install did, cross-platform and shell-free. Switch the six npx call sites to it; the npm pack call gets the existing shell-on-win32 idiom instead (npm has no in-tree package to resolve). An unresolvable tsc is now a hard "cannot measure" error with actionable stderr, pinned by a new main() regression test. Closes #1939 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
57d6e0da2e
commit
e3a026802b
1 changed files with 309 additions and 27 deletions
|
|
@ -32,6 +32,7 @@ import { mkdtempSync, readFileSync, writeFileSync } from "node:fs";
|
|||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { resolveNodeBin } from "./lib/resolve-node-bin.mjs";
|
||||
|
||||
const repoRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
|
|
@ -82,6 +83,20 @@ function escapeRegExp(literal) {
|
|||
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
// The repo-pinned Vite's JS entry, resolved from clients/web — the old `npx
|
||||
// --no-install` guarantee (never a registry fetch) — and spawned via this Node,
|
||||
// since `npx` is a `.cmd` shim on Windows that a shell-free spawnSync can't
|
||||
// start (ENOENT — #1939). Resolved up front, BEFORE the probe is injected, so a
|
||||
// missing install fails actionably without ever touching src/main.tsx.
|
||||
let viteEntry;
|
||||
try {
|
||||
viteEntry = resolveNodeBin("vite", "vite", webDir);
|
||||
} catch (err) {
|
||||
fail(
|
||||
`cannot resolve \`vite\` from clients/web (${err.message}) — run \`npm install\` at the repo root first`,
|
||||
);
|
||||
}
|
||||
|
||||
// Write the captured original to a backup and fail — the honest remedy when the
|
||||
// in-place restore can't be trusted, since it preserves any uncommitted edits
|
||||
// the developer had (unlike `git checkout --`). The backup goes in a fresh
|
||||
|
|
@ -229,15 +244,14 @@ try {
|
|||
console.log(
|
||||
"verify:build-gate: running a real `vite build` with a node:fs probe (takes a minute)…",
|
||||
);
|
||||
// `--no-install` pins to the locally installed (repo-pinned) Vite: the whole
|
||||
// point is proving the message-keyed gate fires against THIS Vite, so `npx`
|
||||
// must never silently fetch a different version from the registry when
|
||||
// clients/web/node_modules is missing/partial. A missing local bin then
|
||||
// surfaces via the `result.error` check below. `timeout` bounds a hung build:
|
||||
// spawnSync sets `result.error` (ETIMEDOUT) on timeout, so the same branch
|
||||
// reports it — otherwise a hang would burn to the GitHub job's 360-min default
|
||||
// with no output (this step captures rather than inherits stdio).
|
||||
result = spawnSync("npx", ["--no-install", "vite", "build"], {
|
||||
// `viteEntry` (resolved above) pins to the locally installed (repo-pinned)
|
||||
// Vite: the whole point is proving the message-keyed gate fires against THIS
|
||||
// Vite, so nothing may silently fetch a different version from the registry
|
||||
// when clients/web/node_modules is missing/partial. `timeout` bounds a hung
|
||||
// build: spawnSync sets `result.error` (ETIMEDOUT) on timeout, so that branch
|
||||
// below reports it — otherwise a hang would burn to the GitHub job's 360-min
|
||||
// default with no output (this step captures rather than inherits stdio).
|
||||
result = spawnSync(process.execPath, [viteEntry, "build"], {
|
||||
cwd: webDir,
|
||||
encoding: "utf8",
|
||||
timeout: 10 * 60_000,
|
||||
|
|
@ -266,7 +280,7 @@ if (afterRestore !== original) {
|
|||
);
|
||||
}
|
||||
|
||||
// A spawn failure (e.g. `npx` missing) leaves `status` null with no output —
|
||||
// A spawn failure (or the timeout above) leaves `status` null with no output —
|
||||
// surface it as itself rather than falling through to the "not via the gate"
|
||||
// diagnosis, which would send someone chasing a build regression that isn't real.
|
||||
if (result.error) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue