fix(cli): forward parsed args to WSL bash via WSLENV on windows - #764
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Central YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughThe change centralizes parsed environment application for ChangesParsed environment propagation
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant ExecOrShell
participant apply_parsed_env
participant Command
participant WSLENV
ExecOrShell->>apply_parsed_env: pass parsed environment
apply_parsed_env->>Command: set environment variables
apply_parsed_env->>WSLENV: append valid names on Windows
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 |
Greptile SummaryThis PR centralizes parsed-environment application for shell and exec commands and augments
Confidence Score: 5/5The PR appears safe to merge based on the currently established evidence. No blocking failure remains established. Important Files Changed
Reviews (2): Last reviewed commit: "fix(cli): forward parsed args to WSL bas..." | Re-trigger Greptile |
On Windows with WSL installed, `usage bash script.sh myws --region eu-1`
runs the script with every `usage_*` variable unset. No error, no warning —
the script just sees empty values.
Two independent facts combine:
- The Win32 executable search order puts the system directory ahead of PATH,
so `Command::new("bash")` resolves to `C:\Windows\System32\bash.exe`, the
WSL launcher, regardless of what else is on PATH. (`OSTYPE=linux-gnu`,
`uname -s = Linux`, while PowerShell's `Get-Command bash` reports Git Bash —
which is why looking at PATH does not reveal this.)
- WSL only carries a Win32 variable across the boundary if `WSLENV` names it.
So the `cmd.env(...)` calls in `shell.rs` and `exec.rs` are a no-op there.
Both call sites now go through `env::apply_parsed_env`, which additionally
adds the variable names to `WSLENV` on Windows. Names are added bare: usage
does not know whether a value is a path, and `/p` would silently rewrite
anything that merely looks like one, so values cross verbatim as they do on
Unix.
Entries already in `WSLENV` are never rewritten or dropped, but an inherited
entry for one of these names only suppresses usage's own when it delivers the
value unchanged. Measured against WSL, only a bare name and `/u` do: `/w` is
the opposite direction, `/uw` transfers in neither, and `/p` and `/l`
translate the value as a path, which drops anything that is not one — and
these values are arbitrary strings off a command line. Where the inherited
entry would lose the value, usage appends its own bare entry after it; WSL
then takes the one that transfers.
7974065 to
e3463ad
Compare
⚠️ **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=-->
On Windows with WSL installed, a script run through
usage bashsees everyusage_*variable unset — no error, no warning, just empty values.Cause
Two independent facts combine.
1. The
bashthat gets resolved is always WSL's. The Win32 executable search order is application directory → current directory → system directory → … → PATH, with the system directory ahead of PATH. Installing WSL putsC:\Windows\System32\bash.exethere, soCommand::new("bash")incli/src/cli/shell.rspicks the WSL launcher no matter what else is on PATH. A diagnostic script confirms it:OSTYPE=linux-gnu,uname -s = Linux. Note that PowerShell'sGet-Command bashreports Git Bash on the same machine, which is why inspecting PATH does not reveal this.2. WSL only carries a Win32 variable across the boundary if
WSLENVnames it. So thecmd.env(key, val)loop inshell.rs(and the identical one inexec.rs) transfers nothing.Fix
Both call sites now go through a shared
env::apply_parsed_env, which sets the variables as before and, on Windows, also appends their names toWSLENV.Names are added bare, with no flags.
WSLENVsupports/p(translate as a path) and/l(as a path list), but usage has no idea whether a given value is a path —/pwould silently rewrite anything that merely looks like one, and variadic values are joined withshell_words::join, so they are not a;-separated path list either. Unflagged names copy the value verbatim, so a script receives the same bytes it would on Unix.Entries already present in
WSLENVare preserved exactly, flags included: a name the user configured is theirs, not ours to redefine.cfg!(windows)rather than#[cfg(windows)], becausetest.ymlruns onubuntu-latestonly (the Windows job inpublish-cli.ymlis a tag-triggeredcargo buildwith no test or clippy). A#[cfg]block here would never be compiled, type-checked or linted anywhere.Why not change which shell gets resolved
Deliberately out of scope. Changing the resolution would break scripts that already depend on WSL bash, and there is no single right answer — some Windows users want WSL, others want Git Bash or MSYS2. For comparison, mise does not guess either: it defaults to
cmd /con Windows and lets the shell be specified explicitly, including as an absolute path. usage-lib already follows the same philosophy inlib/src/sh.rs, which usessh -con Unix andcmd /con Windows. Only theusage <shell> <script>path assumes bash, and that assumption comes from the script's own shebang.This PR restores a transport that was silently dropping data; it does not decide policy.
Verified
Windows 11 + WSL2, before and after:
With a pre-existing
WSLENV=MY_EXISTINGandMY_EXISTING=hello, that entry still transfers alongside the new ones (MY_EXISTING=[hello]).11 unit tests cover the
WSLENVstring building — ordering, appending, dedup against existing bare and flagged names, empty-segment handling, and the no-flags guarantee. They are pure-function tests, so they run on the Linux CI. One further test pins the invariant they rely on: thatas_env()never produces a key containing:or/, which would otherwise corrupt the list.No integration test is added — it would need Windows plus WSL2, and there is no Windows runner in CI, so a
#[cfg(windows)] #[ignore]test would simply rot.Not covered
Path translation (a Windows path passed as an argument still arrives as
C:\..., unusable inside WSL), shell resolution, and any behaviour off Windows (WSLENVis untouched there).This pull request was generated by Claude Code.
Summary by CodeRabbit
WSLENVsettings and flags are preserved, while duplicate or invalid entries are safely excluded.