bazel-contrib / bazel-contrib/rules_shell
`sh_binary` fails on Windows with `#!/usr/bin/env bash` shebang when WSL is installed
- Dominant language
- Starlark
- Stars
- 27
- Forks
- 13
- PR merge metrics
- No merged PRs in 30d
Description
On Windows with MSYS2 and WSL, `sh_binary` targets fail at runtime when the source script uses `#!/usr/bin/env bash`.
**Environment:** Windows, MSYS2 (`BAZEL_SH=C:\msys64\usr\bin\bash.exe`), WSL installed, Bazel 9.x, rules_shell 0.8.0
## Reproduction
```
git clone https://github.com/bazelbuild/rules_jvm_external.git
cd rules_jvm_external
echo 9.1.0 > .bazelversion
bazel run @duplicate_version_warning//:pin
```
```
/bin/bash: D:/.../external/+maven+duplicate_version_warning/pin: No such file or directory
```
Any `sh_binary` with a `#!/usr/bin/env bash` shebang will reproduce this.
## Cause
The `.exe` launcher invokes `bash.exe -c "path/to/script args..."`. MSYS bash `exec`s the script, and the MSYS runtime reads the shebang. `#!/usr/bin/env bash` causes `env` to search the MSYS PATH, where `/c/WINDOWS/system32/bash` (WSL) appears before `/usr/bin/bash` (MSYS):
```
$ C:\msys64\usr\bin\bash.exe -c "which -a bash"
/c/WINDOWS/system32/bash ← WSL (found first)
/usr/bin/bash ← MSYS (correct, but second)
```
WSL bash cannot resolve Windows-style paths → "No such file or directory".
Both `use_bash_launcher` code paths are affected:
- **`False`** (default): source is symlinked as-is, preserving the shebang.
- **`True`**: wrapper has `shebang = ""` but calls `exec "$(rlocation ...)" "$@"`, which triggers shebang processing of the underlying script.
### Why Bazel 9 only
`sh_binary.bzl` uses `getattr(native, "sh_binary", _sh_binary)`. In Bazel 8, the native implementation was used (no `.exe` launcher). In Bazel 9, native rules were removed (#23043), so the Starlark implementation creates the `.exe` launcher, exposing this interaction.
## Suggested fix
The shebang is redundant on Windows — the `.exe` launcher already resolves bash via `bash_bin_path` from the `sh_toolchain`.
1. **`use_bash_launcher=False` on Windows**: Instead of symlinking, copy the script and strip `#!/usr/bin/env` shebangs.
2. **`use_bash_launcher=True` on Windows**: Replace `exec "$(rlocation ...)" "$@"` with `. "$(rlocation ...)"` or `bash "$(rlocation ...)" "$@"` to avoid shebang re-processing.
Alternatively, changing `bash_launcher.cc` to invoke `bash script args` instead of `bash -c "script args"` would fix this at the Bazel core level (bash doesn't process shebangs for file arguments).
## Workaround
Change shebangs from `#!/usr/bin/env bash` to `#!/bin/bash`. MSYS2 resolves `/bin/bash` directly to `C:\msys64\usr\bin\bash.exe` via its filesystem mount, bypassing PATH.
Contributor guide
Assessment
This issue has not been assessed yet.