Skip to content

fix(task): honor explicit and quoted shell paths on Windows (#9932)#10148

Merged
jdx merged 2 commits into
jdx:mainfrom
JamBalaya56562:fix-windows-explicit-shell-path
May 31, 2026
Merged

fix(task): honor explicit and quoted shell paths on Windows (#9932)#10148
jdx merged 2 commits into
jdx:mainfrom
JamBalaya56562:fix-windows-explicit-shell-path

Conversation

@JamBalaya56562

Copy link
Copy Markdown
Contributor

Problem

Fixes the regression reported in #9932. On Windows, an explicitly configured bash path — e.g. a task shell = "C:/msys64/usr/bin/bash.exe -c", windows_default_inline_shell_args, or a {{ env.X }}-interpolated absolute path — was ignored and silently re-resolved to a different bash (typically Git Bash) by the WSL-avoidance logic added in #9750. Trace logs showed using shell: C:/.../bash.exe but execution under C:\Program Files\Git\bin\bash.exe. This broke setups that worked before 26.5.8.

Fix

Honor explicit paths. resolve_posix_shell_program_path now returns early (keeps the program verbatim) when the program carries a directory component, so only a bare bash/bash.exe flows into the candidate-list / PATH WSL-avoidance resolution. An explicitly chosen binary is used as-is.

Parse shell strings consistently. A new split_shell_command (src/path.rs) parses every configured shell string: on Windows it treats backslashes as literal path characters and only groups double-quoted spans (\"\" -> literal \"); on Unix it keeps shell_words/POSIX semantics. A shell path with spaces (when double-quoted) or with backslashes now survives instead of being mangled. Applied to task shell, hook and [[watch_files]] shells, and the *_default_*_shell_args settings.

Fail loudly on malformed shells. Task::shell() now returns Result<Option<Vec<String>>>, so a malformed explicit shell (e.g. an unbalanced quote) errors instead of silently falling back to the default shell.

Notes

  • Cygwin is intentionally not used as the documented/tested example: mise converts PATH to MSYS /c/... form, which Cygwin's /cygdrive/c/... expectation does not match. Honoring the explicit Cygwin binary is an improvement, but full Cygwin PATH support is out of scope here.
  • docs/troubleshooting.md updated with explicit-path and spaces-in-path guidance.

Testing

  • New unit tests for split_shell_command, program_has_directory_component, explicit-path resolution, and the loud-fail behavior.
  • Verified on x86_64-pc-windows-msvc: targeted + affected-module regression tests pass; clippy clean for the new code; rustfmt applied.

🤖 Generated with Claude Code

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request improves shell command parsing and resolution, particularly on Windows, by introducing a host-aware split_shell_command function. This ensures that explicit shell paths with backslashes or spaces (when double-quoted) are parsed correctly without being mangled, and that malformed shell configurations fail loudly. Additionally, on Windows, explicit paths are now honored verbatim instead of being over-resolved. Feedback on the changes suggests a more idiomatic, allocation-free implementation for checking if a program path contains directory components using Path::components().count() > 1.

Comment thread src/task/task_executor.rs
@greptile-apps

greptile-apps Bot commented May 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes the regression from #9750 where an explicitly configured shell path on Windows (e.g. C:/msys64/usr/bin/bash.exe -c) was silently re-resolved by the WSL-avoidance logic to a different binary such as Git Bash. The fix teaches the resolver to leave any program that carries a directory component verbatim, restricting the candidate-list / WSL-avoidance logic to bare names like bash / bash.exe.

  • split_shell_command (new in src/path.rs): cross-platform shell-string parser that treats backslashes as literal path characters on Windows and routes to shell_words (POSIX semantics) on Unix, so quoted paths with spaces and backslash paths survive tokenisation intact.
  • program_has_directory_component (Windows-only in task_executor.rs): short-circuits resolve_posix_shell_program_path for any program with a directory component, making the early-return the single authoritative place that preserves explicit paths.
  • Task::shell() signature change (mod.rs + all call sites): return type promoted from Option<Vec<String>> to Result<Option<Vec<String>>> so a malformed shell string (e.g. unbalanced quote) fails loudly rather than silently falling back to the default shell.

Confidence Score: 5/5

Safe to merge; the changes are tightly scoped to Windows shell resolution and the shell-string parsing layer, with no logic paths altered on non-Windows builds beyond substituting shell_words::split for the new cross-platform wrapper.

The core fix (program_has_directory_component early-return in resolve_posix_shell_program_path) is minimal and surgical. split_shell_command_windows is fully unit-tested including edge cases (backslash literals, quoted paths with spaces, "" escape, unbalanced-quote error). All call-site migrations are mechanical, the Task::shell() signature change is propagated correctly to every consumer, and the fail-loud behavior is consistent across task_executor, task_script_parser, hooks, watch_files, and settings.

No files require special attention; the most complex new code (split_shell_command_windows and resolve_posix_shell_program_path) is thoroughly tested.

Important Files Changed

Filename Overview
src/path.rs Adds split_shell_command (cross-platform) and split_shell_command_windows (Windows-only) to parse shell command strings with proper backslash/quote handling; well-tested with edge cases including unbalanced quotes, backslash literals, and quoted paths with spaces.
src/task/task_executor.rs Adds program_has_directory_component (Windows-only) to short-circuit WSL-avoidance re-resolution for explicit shell paths; propagates Task::shell() errors with ? and switches clone_default_inline_shell to use split_shell_command; comprehensive new tests for the early-return behavior.
src/task/mod.rs Changes Task::shell() return type from Option<Vec<String>> to Result<Option<Vec<String>>>, replacing the old split_whitespace split with split_shell_command and surfacing parse errors; all call sites updated correctly.
src/config/settings.rs Replaces shell_words::split with split_shell_command in default_inline_shell and default_file_shell, applying Windows-aware parsing to the global shell settings.
src/hooks.rs Replaces shell_words::split with split_shell_command for hook shell parsing; one-line change with no logical side effects.
src/watch_files.rs Same one-line swap from shell_words::split to split_shell_command for watch-files shell parsing.
src/task/task_script_parser.rs Propagates Task::shell() error via ? when determining shell type for template expansion; error fires eagerly before .or(), matching the fail-loud-on-malformed-shell design intent.
docs/troubleshooting.md Adds guidance on explicit shell paths and quoting paths-with-spaces on Windows; clear and accurate.

Reviews (3): Last reviewed commit: "refactor(task): use Path::components for..." | Re-trigger Greptile

JamBalaya56562 added a commit to JamBalaya56562/mise that referenced this pull request May 30, 2026
jdx#10148)

Per Gemini review, replace the lossy-string separator check with
Path::new(program).components().count() > 1 — allocation-free and idiomatic,
with the same behavior for all realistic shell paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
JamBalaya56562 and others added 2 commits May 31, 2026 12:07
On Windows an explicitly configured bash path (e.g. a task shell of
C:/msys64/usr/bin/bash.exe -c, or windows_default_inline_shell_args) was
ignored and silently re-resolved to a different bash such as Git Bash, via the
WSL-avoidance logic added in jdx#9750. Honor an explicit path (absolute, or
relative with a directory component) verbatim instead of re-resolving it.

Also unify how configured shell strings are parsed. A new split_shell_command
treats backslashes as literal path characters and only groups double-quoted
spans on Windows (Unix keeps shell_words / POSIX semantics), so shell paths
with spaces (when quoted) or backslashes survive instead of being mangled. It
is used for task shell, hook and watch_files shells, and the
*_default_*_shell_args settings. Task::shell() now returns a Result so a
malformed explicit shell (e.g. an unbalanced quote) fails loudly instead of
silently falling back to the default shell.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jdx#10148)

Per Gemini review, replace the lossy-string separator check with
Path::new(program).components().count() > 1 — allocation-free and idiomatic,
with the same behavior for all realistic shell paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JamBalaya56562 JamBalaya56562 force-pushed the fix-windows-explicit-shell-path branch from 79c3481 to aae6090 Compare May 31, 2026 03:08
@jdx jdx merged commit a0dd654 into jdx:main May 31, 2026
33 checks passed
@JamBalaya56562 JamBalaya56562 deleted the fix-windows-explicit-shell-path branch May 31, 2026 04:11
This was referenced May 31, 2026
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.

2 participants