diff --git a/pyproject.toml b/pyproject.toml index 8b5864258..2b7448267 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ dev = [ "pytest-mock>=3.12", "ruff>=0.4", "mypy>=2.1.0", + "pyyaml>=6.0", ] [project.urls] diff --git a/scripts/hooks/plugin-hook-bootstrap.js b/scripts/hooks/plugin-hook-bootstrap.js index 7a787ec2f..fd31a6930 100644 --- a/scripts/hooks/plugin-hook-bootstrap.js +++ b/scripts/hooks/plugin-hook-bootstrap.js @@ -97,7 +97,10 @@ function findShellBinary() { windowsHide: true, timeout: 30000, }); - if (!probe.error) { + // A candidate is only usable if it both spawns AND exits cleanly. The + // Windows System32 bash.exe WSL launcher spawns without error but exits + // non-zero when no distro is installed, so !probe.error alone is not enough. + if (!probe.error && probe.status === 0) { _cachedShell = candidate; return _cachedShell; } @@ -118,7 +121,9 @@ function findBashBinary() { for (const candidate of candidates) { const probe = spawnSync(candidate, ['-c', ':'], { stdio: 'ignore', windowsHide: true, timeout: 30000 }); - if (!probe.error) { + // Require a clean exit, not just a successful spawn: the Windows System32 + // bash.exe WSL stub spawns fine but exits non-zero with no distro installed. + if (!probe.error && probe.status === 0) { _cachedBash = candidate; return _cachedBash; }