Skip to content

feat(cli): allow overriding the shell program with USAGE_SHELL_<SHELL> - #767

Merged
jdx merged 1 commit into
jdx:mainfrom
JamBalaya56562:feat-usage-shell-override
Aug 2, 2026
Merged

feat(cli): allow overriding the shell program with USAGE_SHELL_<SHELL>#767
jdx merged 1 commit into
jdx:mainfrom
JamBalaya56562:feat-usage-shell-override

Conversation

@JamBalaya56562

@JamBalaya56562 JamBalaya56562 commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Problem

On Windows, handing usage bash an absolute path fails:

$ usage bash C:/work/mycli
/bin/bash: C:/work/mycli: No such file or directory

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.exe there, so Command::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's Get-Command bash reports Git Bash. And WSL's bash cannot open a C:\… 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_BASH replaces the program usage bash runs.

Command Variable
usage bash USAGE_SHELL_BASH
usage zsh USAGE_SHELL_ZSH
usage fish USAGE_SHELL_FISH
usage powershell USAGE_SHELL_PWSH

The variable is keyed by the program, not the subcommand. usage powershell runs pwsh, so its variable is named for that — which also lets it point at powershell.exe on a machine that only has Windows PowerShell. If this ever extends to run= execution, USAGE_SHELL_SH falls out of the same rule.

mise reached the same shape for the same reason: its unix_default_*_shell_args / windows_default_*_shell_args accept 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, 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. 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= cmd convention 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, PATHEXT and 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).

failed to run `/nonexistent/bash-xyz` (from $USAGE_SHELL_BASH): ...

And on Windows, when bash exits 127 having been handed a Windows path, usage explains what probably happened:

usage: `bash` exited 127 (command not found) and the script was given as a Windows path.
usage: On Windows the system directory is searched before $PATH, so `bash` resolves to
usage: C:\Windows\System32\bash.exe — the WSL launcher — which cannot open `C:/work/mycli`.
usage: If that is what happened, pass the script by relative path, or point usage at the
usage: bash you meant:
usage:     set USAGE_SHELL_BASH=C:\Program Files\Git\bin\bash.exe
usage: If the script really did exit 127 on its own, ignore this.

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:

$ USAGE_SHELL_BASH="C:\Program Files\Git\usr\bin\bash.exe" usage bash C:/…/envtest.sh myws --region eu-1
OSTYPE=cygwin
uname=MINGW64_NT-10.0-26200
usage_workspace=myws
usage_region=eu-1

Five integration tests (cli/tests/shell_override.rs, #[cfg(unix)]) cover the override actually replacing the program (USAGE_SHELL_BASH=/bin/echo prints the argv instead of running the script), a blank value falling back, a name resolved through PATH working 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 warnings and cargo fmt --all -- --check pass. Help text is untouched, so no generated artifact changes.

Deliberately not doing

  • Resolving bash ourselves 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.
  • A Windows CI runner. test.yml is ubuntu-latest only, 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

    • Added environment-based shell overrides for selecting a specific shell or interpreter.
    • Added support for interpreter paths containing spaces.
    • Blank or unset overrides now safely fall back to the default shell.
  • Bug Fixes

    • Improved unavailable-shell errors with clearer Windows/WSL path guidance and diagnostics.
  • Documentation

    • Documented shell resolution, overrides, supported commands, and direct interpreter paths on Windows.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@JamBalaya56562, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 1 minute

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: abac28e4-5008-4f09-921a-b4d2647e41dd

📥 Commits

Reviewing files that changed from the base of the PR and between 4132764 and 40c453e.

📒 Files selected for processing (4)
  • cli/src/cli/shell.rs
  • cli/src/env.rs
  • cli/tests/shell_override.rs
  • docs/cli/scripts.md
📝 Walkthrough

Walkthrough

Shell execution now supports USAGE_SHELL_* program overrides. It logs the selected executable, reports clearer spawn errors, and provides targeted Windows/WSL guidance for likely Bash path failures. Unit, integration, and documentation updates cover the new behavior.

Changes

Shell execution

Layer / File(s) Summary
Shell override resolution
cli/src/env.rs, cli/src/cli/shell.rs
Shell names map to trimmed, non-empty USAGE_SHELL_* values. Shell execution uses the configured program when present.
Process handling and path diagnostics
cli/src/cli/shell.rs
Process failures identify the override variable. Child status handling now supports targeted WSL hints for Windows paths passed to Bash. Tests cover matching and suppression conditions.
Integration coverage and Windows guidance
cli/tests/shell_override.rs, docs/cli/scripts.md
Unix tests cover override behavior, fallback, failures, and variable isolation. Windows documentation describes shell resolution and direct interpreter paths.

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
Loading

Possibly related PRs

  • jdx/usage#764: Both PRs modify shell and environment handling, but this PR adds overrides and diagnostics while that PR addresses WSLENV propagation.

Poem

A rabbit hops through shells bright,
Picks an override path just right.
Bash finds Windows roads to roam,
WSL hints guide it home.
Tests and docs now softly glow.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: shell program overrides through USAGE_SHELL_ environment variables.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4b2fde7 and dfb4e9e.

📒 Files selected for processing (4)
  • cli/src/cli/shell.rs
  • cli/src/env.rs
  • cli/tests/shell_override.rs
  • docs/cli/scripts.md

Comment thread cli/tests/shell_override.rs Outdated
Comment thread docs/cli/scripts.md Outdated
@JamBalaya56562
JamBalaya56562 force-pushed the feat-usage-shell-override branch from dfb4e9e to 53b3f5f Compare August 1, 2026 14:07

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between dfb4e9e and 53b3f5f.

📒 Files selected for processing (4)
  • cli/src/cli/shell.rs
  • cli/src/env.rs
  • cli/tests/shell_override.rs
  • docs/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

Comment thread docs/cli/scripts.md Outdated
Comment thread docs/cli/scripts.md
@JamBalaya56562
JamBalaya56562 force-pushed the feat-usage-shell-override branch from 53b3f5f to b5ebe20 Compare August 1, 2026 14:37
@JamBalaya56562
JamBalaya56562 marked this pull request as ready for review August 2, 2026 01:37
@greptile-apps

greptile-apps Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds per-shell environment-variable overrides for selecting the executable used by shell subcommands.

  • Supports USAGE_SHELL_BASH, USAGE_SHELL_ZSH, USAGE_SHELL_FISH, and USAGE_SHELL_PWSH, with blank values falling back to existing behavior.
  • Improves spawn diagnostics and adds Windows/WSL path troubleshooting.
  • Adds unit and Unix integration coverage and documents Windows shell selection.

Confidence Score: 5/5

The 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

Filename Overview
cli/src/cli/shell.rs Resolves an optional shell-program override, improves spawn errors, and adds conditional Windows/WSL diagnostics.
cli/src/env.rs Adds shell override variable derivation and normalization helpers with unit coverage.
cli/tests/shell_override.rs Adds Unix integration tests for override selection, fallback, PATH lookup, error reporting, and shell-variable isolation.
docs/cli/scripts.md Documents Windows executable resolution, supported override variables, and direct interpreter usage.

Reviews (3): Last reviewed commit: "feat(cli): allow overriding the shell pr..." | Re-trigger Greptile

Comment thread cli/src/cli/shell.rs
@JamBalaya56562
JamBalaya56562 force-pushed the feat-usage-shell-override branch from b5ebe20 to 4132764 Compare August 2, 2026 01:48
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.
@JamBalaya56562
JamBalaya56562 force-pushed the feat-usage-shell-override branch from 4132764 to 40c453e Compare August 2, 2026 02:07
@jdx
jdx merged commit 6b43bb4 into jdx:main Aug 2, 2026
8 checks passed
@JamBalaya56562
JamBalaya56562 deleted the feat-usage-shell-override branch August 2, 2026 02:18
@mise-en-dev mise-en-dev mentioned this pull request Aug 2, 2026
JamBalaya56562 added a commit to JamBalaya56562/usage that referenced this pull request Aug 2, 2026
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.
JamBalaya56562 added a commit to JamBalaya56562/usage that referenced this pull request Aug 2, 2026
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.
JamBalaya56562 added a commit to JamBalaya56562/usage that referenced this pull request Aug 2, 2026
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.
JamBalaya56562 added a commit to JamBalaya56562/usage that referenced this pull request Aug 2, 2026
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.
JamBalaya56562 added a commit to JamBalaya56562/usage that referenced this pull request Aug 8, 2026
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.
JamBalaya56562 added a commit to JamBalaya56562/usage that referenced this pull request Aug 9, 2026
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.
JamBalaya56562 added a commit to JamBalaya56562/usage that referenced this pull request Aug 9, 2026
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.
JamBalaya56562 added a commit to JamBalaya56562/usage that referenced this pull request Aug 9, 2026
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.
JamBalaya56562 added a commit to JamBalaya56562/usage that referenced this pull request Aug 9, 2026
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.
JamBalaya56562 added a commit to JamBalaya56562/usage that referenced this pull request Aug 9, 2026
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.
JamBalaya56562 added a commit to JamBalaya56562/usage that referenced this pull request Aug 9, 2026
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.
JamBalaya56562 added a commit to JamBalaya56562/usage that referenced this pull request Aug 9, 2026
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.
JamBalaya56562 added a commit to JamBalaya56562/usage that referenced this pull request Aug 9, 2026
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.
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Aug 11, 2026
⚠️ **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 [@&#8203;jdx](https://github.com/jdx) in [#&#8203;782](jdx/usage#782)

##### 🐛 Bug Fixes

- **(spec)** avoid inferred metadata from included specs by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;786](jdx/usage#786)

##### 🧪 Testing

- **(windows)** make the suite runnable on Windows by [@&#8203;JamBalaya56562](https://github.com/JamBalaya56562) in [#&#8203;771](jdx/usage#771)

##### 📦️ Dependency Updates

- update rust crate rmcp to v3 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;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 [@&#8203;JamBalaya56562](https://github.com/JamBalaya56562) in [#&#8203;767](jdx/usage#767)

##### 🐛 Bug Fixes

- **(cli)** forward parsed args to WSL bash via WSLENV on windows by [@&#8203;JamBalaya56562](https://github.com/JamBalaya56562) in [#&#8203;764](jdx/usage#764)
- **(cli)** let generate markdown write to stdout by [@&#8203;JamBalaya56562](https://github.com/JamBalaya56562) in [#&#8203;766](jdx/usage#766)
- **(complete)** use `type -P` so the CLI-presence guard ignores shell functions by [@&#8203;JamBalaya56562](https://github.com/JamBalaya56562) in [#&#8203;760](jdx/usage#760)
- **(parse)** enforce double\_dash="required" for positional args by [@&#8203;JamBalaya56562](https://github.com/JamBalaya56562) in [#&#8203;762](jdx/usage#762)
- **(windows)** run `run=` scripts with sh when available by [@&#8203;JamBalaya56562](https://github.com/JamBalaya56562) in [#&#8203;765](jdx/usage#765)

##### 🎨 Styling

- fix clippy and deprecation warnings in test and bench targets by [@&#8203;JamBalaya56562](https://github.com/JamBalaya56562) in [#&#8203;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 [@&#8203;jdx](https://github.com/jdx) in [#&#8203;751](jdx/usage#751)
- **(mcp)** serve a usage spec to an agent over stdio by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;746](jdx/usage#746)
- **(spec)** add a top-level `repository` field by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;747](jdx/usage#747)

##### 🐛 Bug Fixes

- **(parse)** keep a re-declared global's aliases on one flag by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;752](jdx/usage#752)
- complete repeated variadic args by [@&#8203;Jai-JAP](https://github.com/Jai-JAP) in [#&#8203;753](jdx/usage#753)

##### New Contributors

- [@&#8203;Jai-JAP](https://github.com/Jai-JAP) made their first contribution in [#&#8203;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 [@&#8203;jdx](https://github.com/jdx) in [#&#8203;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 [@&#8203;jdx](https://github.com/jdx) in [#&#8203;739](jdx/usage#739)

##### 🚜 Refactor

- **(spec)** make missed SpecCommand fields a compile error, and fix the four that were already missed by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;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 [@&#8203;jdx](https://github.com/jdx) in [#&#8203;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=-->
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