fix(dev): start pnpm through a shell on Windows - #184
Conversation
Reproduction detailVerified on Windows 10 (10.0.26200), Node v22.23.2, pnpm 11.20.0, from Spawning the three relevant command shapes directly: Two details worth noting for review:
After the change, |
6b4470d to
56c0ba9
Compare
On Windows the development script resolves the package manager to `pnpm.cmd`, but `spawn` refuses to launch a `.cmd` batch file without a shell. Node raises `EINVAL` synchronously, so the promise in `run` never reaches its `error` handler and the friendly "Could not start" hint is replaced by a raw stack trace. The whole stack fails before Postgres, the API, or the web app start, which makes `pnpm dev` unusable on a Windows machine. Pass `shell` only for `.cmd` and `.bat` commands on win32 so real executables such as `docker` keep their current argument handling and POSIX platforms are untouched. Co-authored-by: Cursor <cursoragent@cursor.com>
56c0ba9 to
c87afc1
Compare
|
I reproduced a related Windows child-process problem in scripts/verify.mjs: pnpm verify fails immediately with spawnSync pnpm ENOENT, before lint starts. PR #184 appears scoped to scripts/dev.mjs, and no active PR currently touches verify.mjs. Would a separate focused issue and PR for the verification command be welcome? |
Summary
Fixes
pnpm devon Windows, where it aborted withError: spawn EINVALbefore any service started.scripts/dev.mjsresolves the package manager topnpm.cmdonwin32, butrun()spawned it without a shell. Since the fix for CVE-2024-27980 (Node 18.20 / 20.12),child_process.spawnrefuses to launch a.cmdor.batfile unlessshellis set. Everypnpmstep in the script was affected — install, the shared-package build,migrate,seedanddev:services— and the first one ended the run.The throw is also synchronous, so the existing
child.once("error", ...)handler never ran and the friendlyCould not start <label>hint was replaced by a raw stack trace.shellis applied only to.cmdand.batcommands onwin32, so real executables such asdockerkeep their current argument handling and POSIX platforms are unchanged. The predicate is exported so the behavior is testable without spawning processes.Closes #182
Test plan
node --test scripts/dev.test.mjs— newusesWindowsCmdShellassertions cover.cmdand.batonwin32, plain commands onwin32, and.cmdonlinuxpnpm.cmdwithout a shell throwsEINVALsynchronously, and succeeds withshell: truedockerstill spawns without a shell, so the compose steps are unaffectedpnpm devnow brings up Postgres, MinIO, API (:4400), worker, gateway (:4410), web (:3400) and docs (:3500) on WindowsusesWindowsCmdShellreturnsfalseand behavior is byte-for-byte unchangedNote for reviewers
scripts/dev.test.mjshas one failure on Windows that is pre-existing and unrelated to this change: the test asserting the generated.envmode is0o600sees0o666(expected: 384, actual: 438), because Windows does not honor POSIX file modes. That assertion is present onmain. I left it alone to keep this PR focused, but I'm happy to address it here or in a separate issue if you prefer.