Skip to content

Conversation

@ibetitsmike
Copy link
Contributor

Summary

Improves bash runtime detection on Windows with proper WSL support:

  1. Runtime detection: Detects available bash runtimes (WSL, Git for Windows) with automatic caching
  2. Path translation: Automatically translates Windows paths to WSL format (C:\Users\.../mnt/c/Users/...)
  3. PowerShell wrapper: Uses PowerShell + stdin to hide WSL console windows without escaping issues

Key implementation details

  • detectBashRuntimes() returns all available runtimes with a preferred choice (WSL > Git for Windows)
  • getSpawnConfig() generates appropriate spawn parameters for each runtime type
  • For WSL: passes script via stdin to avoid PowerShell argument parsing corruption of special characters like %(refname:short)
  • Uses -WindowStyle Hidden in PowerShell to prevent console window flash

Testing

  • 27 unit tests covering runtime detection, spawn config generation, and path translation
  • All existing bash tests pass

Generated with mux

- Add BashRuntime types (GitBashRuntime, WslRuntime, UnixBashRuntime)
- Detect available runtimes: WSL (preferred) and Git for Windows (fallback)
- Automatic Windows path translation for WSL (C:\ -> /mnt/c/)
- cwd embedded in script via 'cd' since WSL needs Linux paths
- Use -- separator to avoid WSL arg parsing issues
- Use absolute paths to bash for each runtime
- Always wrap commands in bash -c for consistent behavior
- Update BashExecutionService, execAsync, and LocalBaseRuntime
- Add comprehensive tests for runtime detection and path translation

_Generated with mux_
- Pass bash script via stdin instead of command-line args to avoid
  PowerShell mangling special characters like %(refname:short)
- Add comprehensive PowerShell path detection (pwsh + Windows PowerShell)
- Add stdin field to SpawnConfig interface for callers to pipe to process
- Update all spawn sites to handle stdin content

This approach completely sidesteps command-line escaping issues because
the script content never touches PowerShell's argument parser.
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 89 to 93
return command.replace(
/(["']?)([a-zA-Z]):[/\\]([^"'\s]*)\1/g,
(_match: string, quote: string, drive: string, rest: string) => {
const wslPath = `/mnt/${drive.toLowerCase()}/${rest.replace(/\\/g, "/")}`;
return quote ? `${quote}${wslPath}${quote}` : wslPath;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Translate quoted Windows paths containing spaces

translateWindowsPathsInCommand only matches drive paths up to the first whitespace because the regex uses [^"'\s]* (lines 89-93), so a quoted path like "C:\\Users\\John Doe\\repo" is rewritten to "/mnt/c/Users/John Doe\\repo" with the remainder left in Windows form. When WSL is chosen as the preferred runtime, any script that references a Windows path with spaces now receives a broken path and the command fails. The matcher should allow spaces inside quoted paths so the conversion to /mnt/... is complete.

Useful? React with 👍 / 👎.

PowerShell's $input automatic variable doesn't work reliably with -Command
because it's designed for pipeline input in functions/filters, not for
reading stdin directly. Use [Console]::In.ReadToEnd() instead.
Fixed regex to properly handle paths like "C:\Users\John Doe\repo":
- Double-quoted paths: match content up to closing quote (allows spaces)
- Single-quoted paths: match content up to closing quote (allows spaces)
- Unquoted paths: match up to whitespace (no spaces allowed)

Added 3 new tests for paths with spaces.
Stdin piping to PowerShell didn't work reliably ($input and [Console]::In
both failed). Instead, use base64 encoding to embed the script safely in
the PowerShell command line:

1. Base64 encode the bash script (completely avoids escaping issues)
2. PowerShell decodes it with [System.Text.Encoding]::UTF8.GetString()
3. Echo the decoded script and pipe to WSL bash

This approach is robust because base64 strings only contain [A-Za-z0-9+/=]
which have no special meaning in PowerShell.

Also removes the now-unused stdin field from SpawnConfig.
When using 'echo $s | wsl bash', the output from bash wasn't being
captured by PowerShell. Change to 'wsl bash -c $s' which passes the
script as an argument and properly captures stdout/stderr.
Without quotes, PowerShell expands $s and bash only receives the first
word as the -c argument. The rest becomes positional args to bash.

Use escaped double quotes (\`"$s\`") so the entire decoded script
is passed as a single argument to bash -c.
The previous approach had PowerShell decode base64 then pass to bash -c,
but the decoded script contained double quotes that conflicted with the
outer quoting.

New approach: pass base64 string directly to bash which decodes it:
  wsl bash -c 'echo BASE64 | base64 -d | bash'

This works because:
1. Base64 only contains [A-Za-z0-9+/=] - no special chars
2. The decode happens inside bash, so no quoting issues
3. Single quotes pass the string literally through PowerShell to WSL
@ibetitsmike ibetitsmike force-pushed the bash-runtime-detection-windows branch from b4ba586 to 16a2265 Compare December 5, 2025 16:32
@ibetitsmike ibetitsmike force-pushed the bash-runtime-detection-windows branch from 16a2265 to fc250b6 Compare December 5, 2025 16:32
On Windows, detached:true creates a separate console window which
interferes with output capture when combined with PowerShell's
-WindowStyle Hidden.

The detached option is primarily needed on Unix for process group
management. On Windows with the PowerShell wrapper, we don't need
it and it actively breaks stdout/stderr capture.
Scripts containing 'exec </dev/null' would break when piped to bash via
'echo BASE64 | base64 -d | bash' because the stdin redirection would
cut off bash's ability to read the rest of the script.

Solution: Use 'eval "$(echo BASE64 | base64 -d)"' which captures the
entire decoded script via command substitution before executing it.
This way stdin redirection in the script doesn't affect script reading.
eval "$(...)" had nested quote conflicts with commands containing
double quotes like --format="%(refname:short)".

Use process substitution instead: bash <(echo BASE64 | base64 -d)
This creates a file descriptor containing the decoded script, so:
- Bash reads script from file descriptor (not stdin or quoted arg)
- No quoting conflicts because script content isn't in quotes
- Script can still redirect stdin freely (exec </dev/null works)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant