feat(cli): allow overriding the shell program with USAGE_SHELL_<SHELL> - #767
Conversation
|
Warning Review limit reached
Next review available in: 1 minute Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Central YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughShell execution now supports ChangesShell execution
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant ShellExecution
participant Environment
participant ChildProcess
participant WSLHint
User->>ShellExecution: run script with requested shell
ShellExecution->>Environment: read USAGE_SHELL_* override
Environment-->>ShellExecution: selected executable or default
ShellExecution->>ChildProcess: spawn executable with script arguments
ChildProcess-->>ShellExecution: exit status
ShellExecution->>WSLHint: inspect Bash status and Windows path
WSLHint-->>User: print targeted guidance when matched
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cli/tests/shell_override.rs`:
- Around line 49-56: Update another_shell_can_stand_in so the shell selected by
USAGE_SHELL_BASH uses a POSIX-compatible fixture without set -o pipefail, or
replace /bin/sh with an interpreter that supports the existing usage_bash
fixture syntax. Preserve the test’s successful output comparison.
In `@docs/cli/scripts.md`:
- Around line 124-126: Correct the override example around USAGE_SHELL_BASH by
marking the fenced block as Command Prompt/batch syntax and using the properly
quoted set form; do not leave it labeled as Bash. If this section explicitly
targets multiple Windows terminals, also provide the corresponding PowerShell
assignment.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Central YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: 1bee0899-a406-4c95-abec-29953af61185
📒 Files selected for processing (4)
cli/src/cli/shell.rscli/src/env.rscli/tests/shell_override.rsdocs/cli/scripts.md
dfb4e9e to
53b3f5f
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/cli/scripts.md`:
- Around line 116-119: Update the fenced code block containing the “usage bash
C:/work/mycli” example to include a language identifier such as text or console,
while preserving the example content.
- Around line 146-149: Update the documentation describing the `USAGE_SHELL_*`
value to state that an empty or whitespace-only value is treated the same as an
unset value, while preserving the existing explanation of inheritance and shell
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Central YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: 18cf24b8-9996-4a03-8ef6-0fcdb98f0221
📒 Files selected for processing (4)
cli/src/cli/shell.rscli/src/env.rscli/tests/shell_override.rsdocs/cli/scripts.md
🚧 Files skipped from review as they are similar to previous changes (3)
- cli/tests/shell_override.rs
- cli/src/env.rs
- cli/src/cli/shell.rs
53b3f5f to
b5ebe20
Compare
Greptile SummaryThe PR adds per-shell environment-variable overrides for selecting the executable used by shell subcommands.
Confidence Score: 5/5The PR appears safe to merge, with only a non-blocking diagnostic-guidance issue remaining. The shell override behavior has no identified blocking failure; the remaining prior finding affects the remediation wording shown for drive-relative Windows paths. Files Needing Attention: cli/src/cli/shell.rs Important Files Changed
Reviews (3): Last reviewed commit: "feat(cli): allow overriding the shell pr..." | Re-trigger Greptile |
b5ebe20 to
4132764
Compare
On Windows, `usage bash C:/work/mycli` fails with
/bin/bash: C:/work/mycli: No such file or directory
Two facts combine. The Win32 executable search order puts the system
directory ahead of PATH, and installing WSL puts `bash.exe` there — so
`Command::new("bash")` picks the WSL launcher on such a machine regardless of
what else is installed. And the WSL bash cannot open a Windows path.
Translating the path is not a fix: the target spelling depends on which shell
was resolved (`/c/...` for msys, `/cygdrive/c/...` for cygwin, `/mnt/c/...`
for WSL, and WSL's mount point is itself configurable via /etc/wsl.conf).
usage has no way to know what `Command::new` resolved to short of
reimplementing the CreateProcess search order, which would be wrong in a
harder-to-diagnose way the moment it diverged from the real one.
So instead: name the shell. `USAGE_SHELL_BASH` (and _ZSH, _FISH, _PWSH)
replaces the program `usage <shell>` runs. The variable is keyed by the
program rather than the subcommand — `usage powershell` runs `pwsh`, so its
variable is USAGE_SHELL_PWSH, which also lets it point at powershell.exe on a
machine without pwsh. mise settled on the same shape for the same reason,
letting its shell settings hold an absolute path.
The value is a program, not a command line: shells on Windows live at paths
like `C:\Program Files\Git\bin\bash.exe`, and `Command` passes the program
and each argument separately, so a path with spaces needs no quoting and
usage takes on no quoting rules of its own.
Unset, blank, or a value that is only whitespace all mean "run the shell as
before", so nothing changes for anyone not setting it.
Two smaller improvements ride along. A shell that cannot be started now
reports which program was tried, and whether a variable pointed there — the
bare io error said only "No such file or directory". And on Windows, when
`bash` exits 127 having been handed a Windows path, usage explains what
probably happened and how to fix it; the guess is hedged and does not change
the exit code, because a script really can exit 127 on its own.
4132764 to
40c453e
Compare
Not for upstream as-is. Measuring what a Windows job actually needs. Round 1 found two things, both now compensated for here: `bash` on windows-latest is the WSL launcher. The image carries System32's bash.exe with no distribution installed, and the executable search order puts it ahead of Git Bash, so `usage bash` exits 1 with "Windows Subsystem for Linux has no installed distributions". My assumption that a stock runner would resolve to a real bash was wrong. `USAGE_SHELL_BASH` names the one meant, which is what jdx#767 added it for. git defaults to core.autocrlf=true on Windows and the repo has no .gitattributes, so fixtures and expected outputs arrived with CRLF and seven help tests plus two markdown tests failed on an ending nobody can see — the diff printed identical text. Set before checkout, or it is too late. `skip_if_shell_missing` now only refuses to skip under CI on Unix, because the workflow installs zsh and fish for the Linux job alone; on Windows their absence is expected rather than a configuration bug. mise draws the same line. Round 1 confirmed this works: shell_completions_integration passed 19/19 with zsh and fish skipping and pwsh running. `skip_if_posix_shell_missing` in complete_word.rs stays strict on purpose, so an unusable bash is reported rather than skipped quietly. In round 1 it fired, correctly. The job runs `cargo test --no-fail-fast` directly rather than `mise r test`, so one failing binary does not hide the rest, and skips render and lint: render asserts a clean git diff, which line endings make unreliable here, and lint is a property of the source rather than the platform.
Not for upstream as-is. Measuring what a Windows job actually needs. Round 1 found two things, both now compensated for here: `bash` on windows-latest is the WSL launcher. The image carries System32's bash.exe with no distribution installed, and the executable search order puts it ahead of Git Bash, so `usage bash` exits 1 with "Windows Subsystem for Linux has no installed distributions". My assumption that a stock runner would resolve to a real bash was wrong. `USAGE_SHELL_BASH` names the one meant, which is what jdx#767 added it for. git defaults to core.autocrlf=true on Windows and the repo has no .gitattributes, so fixtures and expected outputs arrived with CRLF and seven help tests plus two markdown tests failed on an ending nobody can see — the diff printed identical text. Set before checkout, or it is too late. `skip_if_shell_missing` now only refuses to skip under CI on Unix, because the workflow installs zsh and fish for the Linux job alone; on Windows their absence is expected rather than a configuration bug. mise draws the same line. Round 1 confirmed this works: shell_completions_integration passed 19/19 with zsh and fish skipping and pwsh running. `skip_if_posix_shell_missing` in complete_word.rs stays strict on purpose, so an unusable bash is reported rather than skipped quietly. In round 1 it fired, correctly. The job runs `cargo test --no-fail-fast` directly rather than `mise r test`, so one failing binary does not hide the rest, and skips render and lint: render asserts a clean git diff, which line endings make unreliable here, and lint is a property of the source rather than the platform.
Not for upstream as-is. Measuring what a Windows job actually needs. Round 1 found two things, both now compensated for here: `bash` on windows-latest is the WSL launcher. The image carries System32's bash.exe with no distribution installed, and the executable search order puts it ahead of Git Bash, so `usage bash` exits 1 with "Windows Subsystem for Linux has no installed distributions". My assumption that a stock runner would resolve to a real bash was wrong. `USAGE_SHELL_BASH` names the one meant, which is what jdx#767 added it for. git defaults to core.autocrlf=true on Windows and the repo has no .gitattributes, so fixtures and expected outputs arrived with CRLF and seven help tests plus two markdown tests failed on an ending nobody can see — the diff printed identical text. Set before checkout, or it is too late. `skip_if_shell_missing` now only refuses to skip under CI on Unix, because the workflow installs zsh and fish for the Linux job alone; on Windows their absence is expected rather than a configuration bug. mise draws the same line. Round 1 confirmed this works: shell_completions_integration passed 19/19 with zsh and fish skipping and pwsh running. `skip_if_posix_shell_missing` in complete_word.rs stays strict on purpose, so an unusable bash is reported rather than skipped quietly. In round 1 it fired, correctly. The job runs `cargo test --no-fail-fast` directly rather than `mise r test`, so one failing binary does not hide the rest, and skips render and lint: render asserts a clean git diff, which line endings make unreliable here, and lint is a property of the source rather than the platform.
Not for upstream as-is. Measuring what a Windows job actually needs. Round 1 found two things, both now compensated for here: `bash` on windows-latest is the WSL launcher. The image carries System32's bash.exe with no distribution installed, and the executable search order puts it ahead of Git Bash, so `usage bash` exits 1 with "Windows Subsystem for Linux has no installed distributions". My assumption that a stock runner would resolve to a real bash was wrong. `USAGE_SHELL_BASH` names the one meant, which is what jdx#767 added it for. git defaults to core.autocrlf=true on Windows and the repo has no .gitattributes, so fixtures and expected outputs arrived with CRLF and seven help tests plus two markdown tests failed on an ending nobody can see — the diff printed identical text. Set before checkout, or it is too late. `skip_if_shell_missing` now only refuses to skip under CI on Unix, because the workflow installs zsh and fish for the Linux job alone; on Windows their absence is expected rather than a configuration bug. mise draws the same line. Round 1 confirmed this works: shell_completions_integration passed 19/19 with zsh and fish skipping and pwsh running. `skip_if_posix_shell_missing` in complete_word.rs stays strict on purpose, so an unusable bash is reported rather than skipped quietly. In round 1 it fired, correctly. The job runs `cargo test --no-fail-fast` directly rather than `mise r test`, so one failing binary does not hide the rest, and skips render and lint: render asserts a clean git diff, which line endings make unreliable here, and lint is a property of the source rather than the platform.
Not for upstream as-is. Measuring what a Windows job actually needs. Round 1 found two things, both now compensated for here: `bash` on windows-latest is the WSL launcher. The image carries System32's bash.exe with no distribution installed, and the executable search order puts it ahead of Git Bash, so `usage bash` exits 1 with "Windows Subsystem for Linux has no installed distributions". My assumption that a stock runner would resolve to a real bash was wrong. `USAGE_SHELL_BASH` names the one meant, which is what jdx#767 added it for. git defaults to core.autocrlf=true on Windows and the repo has no .gitattributes, so fixtures and expected outputs arrived with CRLF and seven help tests plus two markdown tests failed on an ending nobody can see — the diff printed identical text. Set before checkout, or it is too late. `skip_if_shell_missing` now only refuses to skip under CI on Unix, because the workflow installs zsh and fish for the Linux job alone; on Windows their absence is expected rather than a configuration bug. mise draws the same line. Round 1 confirmed this works: shell_completions_integration passed 19/19 with zsh and fish skipping and pwsh running. `skip_if_posix_shell_missing` in complete_word.rs stays strict on purpose, so an unusable bash is reported rather than skipped quietly. In round 1 it fired, correctly. The job runs `cargo test --no-fail-fast` directly rather than `mise r test`, so one failing binary does not hide the rest, and skips render and lint: render asserts a clean git diff, which line endings make unreliable here, and lint is a property of the source rather than the platform.
Not for upstream as-is. Measuring what a Windows job actually needs. Round 1 found two things, both now compensated for here: `bash` on windows-latest is the WSL launcher. The image carries System32's bash.exe with no distribution installed, and the executable search order puts it ahead of Git Bash, so `usage bash` exits 1 with "Windows Subsystem for Linux has no installed distributions". My assumption that a stock runner would resolve to a real bash was wrong. `USAGE_SHELL_BASH` names the one meant, which is what jdx#767 added it for. git defaults to core.autocrlf=true on Windows and the repo has no .gitattributes, so fixtures and expected outputs arrived with CRLF and seven help tests plus two markdown tests failed on an ending nobody can see — the diff printed identical text. Set before checkout, or it is too late. `skip_if_shell_missing` now only refuses to skip under CI on Unix, because the workflow installs zsh and fish for the Linux job alone; on Windows their absence is expected rather than a configuration bug. mise draws the same line. Round 1 confirmed this works: shell_completions_integration passed 19/19 with zsh and fish skipping and pwsh running. `skip_if_posix_shell_missing` in complete_word.rs stays strict on purpose, so an unusable bash is reported rather than skipped quietly. In round 1 it fired, correctly. The job runs `cargo test --no-fail-fast` directly rather than `mise r test`, so one failing binary does not hide the rest, and skips render and lint: render asserts a clean git diff, which line endings make unreliable here, and lint is a property of the source rather than the platform.
usage is used on Windows and released for it, but nothing has been testing it there. jdx#771 made the suite runnable on the platform; this runs it. Build and test only. `render` asserts a clean git diff, which line endings and the checked-in symlinks make unreliable, and `lint` is a property of the source rather than of the platform — the Linux job already covers both. mise's Windows jobs are shaped the same way. Two settings the job needs, both measured on a runner rather than assumed: `core.autocrlf false`, set before checkout. git defaults it to true on Windows and the repo has no .gitattributes, so fixtures and expected output arrive with CRLF. Seven help tests in lib/tests/parse.rs and two in cli/tests/markdown.rs fail on it, and the failure is near-unreadable: pretty_assertions prints both sides as identical, because the only difference is invisible. `USAGE_SHELL_BASH`, because the image carries System32's bash.exe — the WSL launcher, with no distribution installed. `where bash` lists Git Bash first and CreateProcess still reaches System32, since it searches the system directory before PATH. Without the variable `usage bash` exits 1 with "Windows Subsystem for Linux has no installed distributions". This is the case USAGE_SHELL_BASH was added for in jdx#767, and the runner turns out to be an example of it. zsh and fish are not installed, and are not worth installing: nothing on Windows ships them, and the completion tests skip a shell they cannot run. So the `CI` rule in `skip_if_shell_missing` — which refuses to skip, on the grounds that a missing shell on a runner that installs one is a configuration bug — now applies on Unix only. Only the Linux job installs shells, so only there is absence a bug. mise draws the same line, installing no POSIX shells on Windows at all. Measured on windows-latest: 538 passed, 0 failed, 0 skipped.
usage is used on Windows and released for it, but nothing has been testing it there. jdx#771 made the suite runnable on the platform; this runs it. Build and test only. `render` asserts a clean git diff, which line endings and the checked-in symlinks make unreliable, and `lint` is a property of the source rather than of the platform — the Linux job already covers both. mise's Windows jobs are shaped the same way. Two settings the job needs, both measured on a runner rather than assumed: `core.autocrlf false`, set before checkout. git defaults it to true on Windows and the repo has no .gitattributes, so fixtures and expected output arrive with CRLF. Seven help tests in lib/tests/parse.rs and two in cli/tests/markdown.rs fail on it, and the failure is near-unreadable: pretty_assertions prints both sides as identical, because the only difference is invisible. `USAGE_SHELL_BASH`, because the image carries System32's bash.exe — the WSL launcher, with no distribution installed. `where bash` lists Git Bash first and CreateProcess still reaches System32, since it searches the system directory before PATH. Without the variable `usage bash` exits 1 with "Windows Subsystem for Linux has no installed distributions". This is the case USAGE_SHELL_BASH was added for in jdx#767, and the runner turns out to be an example of it. zsh and fish are not installed, and are not worth installing: nothing on Windows ships them, and the completion tests skip a shell they cannot run. So the `CI` rule in `skip_if_shell_missing` — which refuses to skip, on the grounds that a missing shell on a runner that installs one is a configuration bug — now applies on Unix only. Only the Linux job installs shells, so only there is absence a bug. mise draws the same line, installing no POSIX shells on Windows at all. Measured on windows-latest: 538 passed, 0 failed, 0 skipped.
usage is used on Windows and released for it, but nothing has been testing it there. jdx#771 made the suite runnable on the platform; this runs it. Build and test only. `render` asserts a clean git diff, which line endings and the checked-in symlinks make unreliable, and `lint` is a property of the source rather than of the platform — the Linux job already covers both. mise's Windows jobs are shaped the same way. Two settings the job needs, both measured on a runner rather than assumed: `core.autocrlf false`, set before checkout. git defaults it to true on Windows and the repo has no .gitattributes, so fixtures and expected output arrive with CRLF. Seven help tests in lib/tests/parse.rs and two in cli/tests/markdown.rs fail on it, and the failure is near-unreadable: pretty_assertions prints both sides as identical, because the only difference is invisible. `USAGE_SHELL_BASH`, because the image carries System32's bash.exe — the WSL launcher, with no distribution installed. `where bash` lists Git Bash first and CreateProcess still reaches System32, since it searches the system directory before PATH. Without the variable `usage bash` exits 1 with "Windows Subsystem for Linux has no installed distributions". This is the case USAGE_SHELL_BASH was added for in jdx#767, and the runner turns out to be an example of it. zsh and fish are not installed, and are not worth installing: nothing on Windows ships them, and the completion tests skip a shell they cannot run. So the `CI` rule in `skip_if_shell_missing` — which refuses to skip, on the grounds that a missing shell on a runner that installs one is a configuration bug — now applies on Unix only. Only the Linux job installs shells, so only there is absence a bug. mise draws the same line, installing no POSIX shells on Windows at all. Measured on windows-latest: 538 passed, 0 failed, 0 skipped.
usage is used on Windows and released for it, but nothing has been testing it there. jdx#771 made the suite runnable on the platform; this runs it. Build and test only. `render` asserts a clean git diff, which line endings and the checked-in symlinks make unreliable, and `lint` is a property of the source rather than of the platform — the Linux job already covers both. mise's Windows jobs are shaped the same way. Two settings the job needs, both measured on a runner rather than assumed: `core.autocrlf false`, set before checkout. git defaults it to true on Windows and the repo has no .gitattributes, so fixtures and expected output arrive with CRLF. Seven help tests in lib/tests/parse.rs and two in cli/tests/markdown.rs fail on it, and the failure is near-unreadable: pretty_assertions prints both sides as identical, because the only difference is invisible. `USAGE_SHELL_BASH`, because the image carries System32's bash.exe — the WSL launcher, with no distribution installed. `where bash` lists Git Bash first and CreateProcess still reaches System32, since it searches the system directory before PATH. Without the variable `usage bash` exits 1 with "Windows Subsystem for Linux has no installed distributions". This is the case USAGE_SHELL_BASH was added for in jdx#767, and the runner turns out to be an example of it. zsh and fish are not installed, and are not worth installing: nothing on Windows ships them, and the completion tests skip a shell they cannot run. So the `CI` rule in `skip_if_shell_missing` — which refuses to skip, on the grounds that a missing shell on a runner that installs one is a configuration bug — now applies on Unix only. Only the Linux job installs shells, so only there is absence a bug. mise draws the same line, installing no POSIX shells on Windows at all. Measured on windows-latest: 538 passed, 0 failed, 0 skipped.
usage is used on Windows and released for it, but nothing has been testing it there. jdx#771 made the suite runnable on the platform; this runs it. Build and test only. `render` asserts a clean git diff, which line endings and the checked-in symlinks make unreliable, and `lint` is a property of the source rather than of the platform — the Linux job already covers both. mise's Windows jobs are shaped the same way. Two settings the job needs, both measured on a runner rather than assumed: `core.autocrlf false`, set before checkout. git defaults it to true on Windows and the repo has no .gitattributes, so fixtures and expected output arrive with CRLF. Seven help tests in lib/tests/parse.rs and two in cli/tests/markdown.rs fail on it, and the failure is near-unreadable: pretty_assertions prints both sides as identical, because the only difference is invisible. `USAGE_SHELL_BASH`, because the image carries System32's bash.exe — the WSL launcher, with no distribution installed. `where bash` lists Git Bash first and CreateProcess still reaches System32, since it searches the system directory before PATH. Without the variable `usage bash` exits 1 with "Windows Subsystem for Linux has no installed distributions". This is the case USAGE_SHELL_BASH was added for in jdx#767, and the runner turns out to be an example of it. zsh and fish are not installed, and are not worth installing: nothing on Windows ships them, and the completion tests skip a shell they cannot run. So the `CI` rule in `skip_if_shell_missing` — which refuses to skip, on the grounds that a missing shell on a runner that installs one is a configuration bug — now applies on Unix only. Only the Linux job installs shells, so only there is absence a bug. mise draws the same line, installing no POSIX shells on Windows at all. Measured on windows-latest: 538 passed, 0 failed, 0 skipped.
usage is used on Windows and released for it, but nothing has been testing it there. jdx#771 made the suite runnable on the platform; this runs it. Build and test only. `render` asserts a clean git diff, which line endings and the checked-in symlinks make unreliable, and `lint` is a property of the source rather than of the platform — the Linux job already covers both. mise's Windows jobs are shaped the same way. Two settings the job needs, both measured on a runner rather than assumed: `core.autocrlf false`, set before checkout. git defaults it to true on Windows and the repo has no .gitattributes, so fixtures and expected output arrive with CRLF. Seven help tests in lib/tests/parse.rs and two in cli/tests/markdown.rs fail on it, and the failure is near-unreadable: pretty_assertions prints both sides as identical, because the only difference is invisible. `USAGE_SHELL_BASH`, because the image carries System32's bash.exe — the WSL launcher, with no distribution installed. `where bash` lists Git Bash first and CreateProcess still reaches System32, since it searches the system directory before PATH. Without the variable `usage bash` exits 1 with "Windows Subsystem for Linux has no installed distributions". This is the case USAGE_SHELL_BASH was added for in jdx#767, and the runner turns out to be an example of it. zsh and fish are not installed, and are not worth installing: nothing on Windows ships them, and the completion tests skip a shell they cannot run. So the `CI` rule in `skip_if_shell_missing` — which refuses to skip, on the grounds that a missing shell on a runner that installs one is a configuration bug — now applies on Unix only. Only the Linux job installs shells, so only there is absence a bug. mise draws the same line, installing no POSIX shells on Windows at all. Measured on windows-latest: 538 passed, 0 failed, 0 skipped.
usage is used on Windows and released for it, but nothing has been testing it there. jdx#771 made the suite runnable on the platform; this runs it. Build and test only. `render` asserts a clean git diff, which line endings and the checked-in symlinks make unreliable, and `lint` is a property of the source rather than of the platform — the Linux job already covers both. mise's Windows jobs are shaped the same way. Two settings the job needs, both measured on a runner rather than assumed: `core.autocrlf false`, set before checkout. git defaults it to true on Windows and the repo has no .gitattributes, so fixtures and expected output arrive with CRLF. Seven help tests in lib/tests/parse.rs and two in cli/tests/markdown.rs fail on it, and the failure is near-unreadable: pretty_assertions prints both sides as identical, because the only difference is invisible. `USAGE_SHELL_BASH`, because the image carries System32's bash.exe — the WSL launcher, with no distribution installed. `where bash` lists Git Bash first and CreateProcess still reaches System32, since it searches the system directory before PATH. Without the variable `usage bash` exits 1 with "Windows Subsystem for Linux has no installed distributions". This is the case USAGE_SHELL_BASH was added for in jdx#767, and the runner turns out to be an example of it. zsh and fish are not installed, and are not worth installing: nothing on Windows ships them, and the completion tests skip a shell they cannot run. So the `CI` rule in `skip_if_shell_missing` — which refuses to skip, on the grounds that a missing shell on a runner that installs one is a configuration bug — now applies on Unix only. Only the Linux job installs shells, so only there is absence a bug. mise draws the same line, installing no POSIX shells on Windows at all. Measured on windows-latest: 538 passed, 0 failed, 0 skipped.
⚠️ **CAUTION: this is a major update, indicating a breaking change!**⚠️ This MR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [usage](https://github.com/jdx/usage) | tools | major | `3.5.6` → `5.1.0` | MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot). **Proposed changes to behavior should be submitted there as MRs.** --- ### Release Notes <details> <summary>jdx/usage (usage)</summary> ### [`v5.1.0`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#510---2026-08-09) [Compare Source](jdx/usage@v5.0.0...v5.1.0) ##### 🚀 Features - **(spec)** parse usage comments from strings by [@​jdx](https://github.com/jdx) in [#​782](jdx/usage#782) ##### 🐛 Bug Fixes - **(spec)** avoid inferred metadata from included specs by [@​jdx](https://github.com/jdx) in [#​786](jdx/usage#786) ##### 🧪 Testing - **(windows)** make the suite runnable on Windows by [@​JamBalaya56562](https://github.com/JamBalaya56562) in [#​771](jdx/usage#771) ##### 📦️ Dependency Updates - update rust crate rmcp to v3 by [@​renovate\[bot\]](https://github.com/renovate\[bot]) in [#​780](jdx/usage#780) ### [`v5.0.0`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#500---2026-08-02) [Compare Source](jdx/usage@v4.1.0...v5.0.0) ##### 🚀 Features - **(cli)** allow overriding the shell program with USAGE\_SHELL\_<SHELL> by [@​JamBalaya56562](https://github.com/JamBalaya56562) in [#​767](jdx/usage#767) ##### 🐛 Bug Fixes - **(cli)** forward parsed args to WSL bash via WSLENV on windows by [@​JamBalaya56562](https://github.com/JamBalaya56562) in [#​764](jdx/usage#764) - **(cli)** let generate markdown write to stdout by [@​JamBalaya56562](https://github.com/JamBalaya56562) in [#​766](jdx/usage#766) - **(complete)** use `type -P` so the CLI-presence guard ignores shell functions by [@​JamBalaya56562](https://github.com/JamBalaya56562) in [#​760](jdx/usage#760) - **(parse)** enforce double\_dash="required" for positional args by [@​JamBalaya56562](https://github.com/JamBalaya56562) in [#​762](jdx/usage#762) - **(windows)** run `run=` scripts with sh when available by [@​JamBalaya56562](https://github.com/JamBalaya56562) in [#​765](jdx/usage#765) ##### 🎨 Styling - fix clippy and deprecation warnings in test and bench targets by [@​JamBalaya56562](https://github.com/JamBalaya56562) in [#​763](jdx/usage#763) ### [`v4.1.0`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#410---2026-07-30) [Compare Source](jdx/usage@v4.0.0...v4.1.0) ##### 🚀 Features - **(cli)** declare what each usage command does to the world by [@​jdx](https://github.com/jdx) in [#​751](jdx/usage#751) - **(mcp)** serve a usage spec to an agent over stdio by [@​jdx](https://github.com/jdx) in [#​746](jdx/usage#746) - **(spec)** add a top-level `repository` field by [@​jdx](https://github.com/jdx) in [#​747](jdx/usage#747) ##### 🐛 Bug Fixes - **(parse)** keep a re-declared global's aliases on one flag by [@​jdx](https://github.com/jdx) in [#​752](jdx/usage#752) - complete repeated variadic args by [@​Jai-JAP](https://github.com/Jai-JAP) in [#​753](jdx/usage#753) ##### New Contributors - [@​Jai-JAP](https://github.com/Jai-JAP) made their first contribution in [#​753](jdx/usage#753) ### [`v4.0.0`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#400---2026-07-25) [Compare Source](jdx/usage@v3.6.0...v4.0.0) ##### 🚀 Features - **(spec)** allow effect= on flags and args by [@​jdx](https://github.com/jdx) in [#​742](jdx/usage#742) ### [`v3.6.0`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#360---2026-07-25) [Compare Source](jdx/usage@v3.5.7...v3.6.0) ##### 🚀 Features - **(spec)** add effect= to declare what a command does to the world by [@​jdx](https://github.com/jdx) in [#​739](jdx/usage#739) ##### 🚜 Refactor - **(spec)** make missed SpecCommand fields a compile error, and fix the four that were already missed by [@​jdx](https://github.com/jdx) in [#​740](jdx/usage#740) ### [`v3.5.7`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#357---2026-07-25) [Compare Source](jdx/usage@v3.5.6...v3.5.7) ##### 🐛 Bug Fixes - **(parse)** don't leak the mounting CLI's flags into mounted commands; scan past non-global flags by [@​jdx](https://github.com/jdx) in [#​738](jdx/usage#738) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this MR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box --- This MR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODguMCIsInVwZGF0ZWRJblZlciI6IjQzLjI4OC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiLCJhdXRvbWF0aW9uOmJvdC1hdXRob3JlZCIsImRlcGVuZGVuY3ktdHlwZTo6bWFqb3IiXX0=-->
Problem
On Windows, handing
usage bashan absolute path fails:Two facts combine. The Win32 executable search order is application directory → current directory → system directory → … → PATH, with the system directory ahead of PATH. Installing WSL puts
bash.exethere, soCommand::new("bash")picks the WSL launcher on such a machine no matter what else is installed — confirmed on mine:OSTYPE=linux-gnu,uname -s = Linux, while PowerShell'sGet-Command bashreports Git Bash. And WSL's bash cannot open aC:\…path.Why not translate the path
The target spelling depends on which shell was resolved:
/c/…for msys/Git Bash,/cygdrive/c/…for Cygwin,/mnt/c/…for WSL — and WSL's mount point is itself configurable through/etc/wsl.conf. So even knowing it is WSL is not enough to translate correctly.More fundamentally, usage has no way to learn what
Command::new("bash")resolved to. Finding out would mean reimplementing the CreateProcess search order — App Paths,PATHEXT, safe-search mode — and that becomes a harder-to-diagnose bug the moment it diverges from what the OS actually does.Fix: name the shell
USAGE_SHELL_BASHreplaces the programusage bashruns.usage bashUSAGE_SHELL_BASHusage zshUSAGE_SHELL_ZSHusage fishUSAGE_SHELL_FISHusage powershellUSAGE_SHELL_PWSHThe variable is keyed by the program, not the subcommand.
usage powershellrunspwsh, so its variable is named for that — which also lets it point atpowershell.exeon a machine that only has Windows PowerShell. If this ever extends torun=execution,USAGE_SHELL_SHfalls out of the same rule.mise reached the same shape for the same reason: its
unix_default_*_shell_args/windows_default_*_shell_argsaccept an absolute path rather than trying to work out which shell you have.The value is a program, not a command line. Shells on Windows live at paths like
C:\Program Files\Git\bin\bash.exe, andCommandpasses the program and each argument separately, so a path with spaces needs no quoting — and usage takes on no quoting rules of its own. mise can afford the command-line form because its settings file also accepts arrays; a single environment variable cannot.Unset, empty, or whitespace-only all mean "run the shell as before", matching the
FOO= cmdconvention for switching something off. Nothing changes for anyone not setting it.Nothing checks that the program exists: the value need not be an absolute path, so validating would mean reimplementing
PATH,PATHEXTand permission lookup, then racing the spawn that follows. A bad value surfaces as a spawn error naming it.Two smaller improvements
A shell that cannot be started now says which program was tried, and whether a variable pointed there. The bare io error said only
No such file or directory (os error 2).And on Windows, when
bashexits 127 having been handed a Windows path, usage explains what probably happened:It fires only when all of: Windows, no override set, exit 127,
shell == "bash"(the only one of the four that ships in the system directory), and the script is a drive-letter or UNC path. It is hedged, prints after the child's own stderr, and does not touch the exit code — a script really can exit 127 on its own.Verified
Windows 11 with WSL installed. Before, with an absolute path: exit 127 plus the hint above. After pointing the variable at Git Bash, the same command works:
Five integration tests (
cli/tests/shell_override.rs,#[cfg(unix)]) cover the override actually replacing the program (USAGE_SHELL_BASH=/bin/echoprints the argv instead of running the script), a blank value falling back, a name resolved throughPATHworking like an absolute one, an unstartable program naming itself and the variable, and another shell's variable being ignored. Unit tests cover variable-name derivation, trimming, blank handling, and the hint's conditions and wording — all pure, so they run on the Linux CI.Those integration tests are Unix-only and I develop on Windows, so they are also run on Linux before pushing, not just type-checked and linted for
x86_64-unknown-linux-gnu.cargo test -p usage-lib --all-features,cargo clippy --all --all-features -- -D warningsandcargo fmt --all -- --checkpass. Help text is untouched, so no generated artifact changes.Deliberately not doing
bashourselves ahead of the system directory. It would change the resolution order for every user, away from what the OS does, and needs a new dependency. Worth discussing separately.usage exec. Its interpreter is already named by the caller, so a shebang can point straight at one:#!/usr/bin/env -S usage exec "C:/msys64/usr/bin/bash.exe". Generalising the variable to arbitrary program names would also mean a third party could redirect the interpreter a script author chose. Documented as the escape hatch instead.test.ymlisubuntu-latestonly, so the Windows path here rests on manual verification. That belongs to a separate CI change.This pull request was generated by Claude Code.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation