Skip to content

fix(cli): forward parsed args to WSL bash via WSLENV on windows - #764

Merged
jdx merged 1 commit into
jdx:mainfrom
JamBalaya56562:fix-wslenv-windows
Aug 2, 2026
Merged

fix(cli): forward parsed args to WSL bash via WSLENV on windows#764
jdx merged 1 commit into
jdx:mainfrom
JamBalaya56562:fix-wslenv-windows

Conversation

@JamBalaya56562

@JamBalaya56562 JamBalaya56562 commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

On Windows with WSL installed, a script run through usage bash sees every usage_* variable unset — no error, no warning, just empty values.

$ usage bash ./envtest.sh myws --region eu-1
workspace: []
region: []

Cause

Two independent facts combine.

1. The bash that 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 puts C:\Windows\System32\bash.exe there, so Command::new("bash") in cli/src/cli/shell.rs picks the WSL launcher no matter what else is on PATH. A diagnostic script confirms it: OSTYPE=linux-gnu, uname -s = Linux. Note that PowerShell's Get-Command bash reports 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 WSLENV names it. So the cmd.env(key, val) loop in shell.rs (and the identical one in exec.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 to WSLENV.

Names are added bare, with no flags. WSLENV supports /p (translate as a path) and /l (as a path list), but usage has no idea whether a given value is a path — /p would silently rewrite anything that merely looks like one, and variadic values are joined with shell_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 WSLENV are preserved exactly, flags included: a name the user configured is theirs, not ours to redefine.

cfg!(windows) rather than #[cfg(windows)], because test.yml runs on ubuntu-latest only (the Windows job in publish-cli.yml is a tag-triggered cargo build with 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 /c on Windows and lets the shell be specified explicitly, including as an absolute path. usage-lib already follows the same philosophy in lib/src/sh.rs, which uses sh -c on Unix and cmd /c on Windows. Only the usage <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:

before:  --- usage_* in env ---
         (no usage_* vars in env)
         workspace=[<unset>]   region=[<unset>]

after:   --- usage_* in env ---
         usage_workspace=myws
         usage_region=eu-1
         workspace=[myws]      region=[eu-1]

With a pre-existing WSLENV=MY_EXISTING and MY_EXISTING=hello, that entry still transfers alongside the new ones (MY_EXISTING=[hello]).

11 unit tests cover the WSLENV string 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: that as_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 (WSLENV is untouched there).


This pull request was generated by Claude Code.

Summary by CodeRabbit

  • Bug Fixes
    • Improved environment-variable handling when launching commands and interactive shells.
    • On Windows, environment variables are now reliably passed through to WSL.
    • Existing WSLENV settings and flags are preserved, while duplicate or invalid entries are safely excluded.
    • Environment propagation is now more consistent across supported command execution modes.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro Plus

Run ID: c68c9339-7079-4d22-b93f-1765972afeb6

📥 Commits

Reviewing files that changed from the base of the PR and between 7974065 and e3463ad.

📒 Files selected for processing (3)
  • cli/src/cli/exec.rs
  • cli/src/cli/shell.rs
  • cli/src/env.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • cli/src/cli/shell.rs
  • cli/src/cli/exec.rs
  • cli/src/env.rs

📝 Walkthrough

Walkthrough

The change centralizes parsed environment application for Exec and Shell. On Windows, it also updates WSLENV with validated, deduplicated variable names while preserving existing entries and flags.

Changes

Parsed environment propagation

Layer / File(s) Summary
Environment application and WSLENV handling
cli/src/env.rs
Adds apply_parsed_env and append_to_wslenv. The implementation applies variables, filters unsafe keys, preserves flags, removes empty segments, prevents duplicates, and includes unit tests.
Command launch integration
cli/src/cli/exec.rs, cli/src/cli/shell.rs
Exec::run and Shell::run delegate parsed environment handling to env::apply_parsed_env.

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
Loading

Poem

A rabbit taps WSLENV with care,
While parsed keys hop through the air.
Exec and Shell share the track,
Safe names go forth and flags come back.
Tests applaud: “No duplicates there!”

🚥 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 summarizes the main change: forwarding parsed arguments to WSL bash through WSLENV on Windows.
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.

@greptile-apps

greptile-apps Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR centralizes parsed-environment application for shell and exec commands and augments WSLENV on Windows so parsed usage_* variables cross into WSL.

  • Routes both command-launch paths through apply_parsed_env.
  • Preserves inherited WSLENV entries while adding missing parsed-variable names.
  • Adds unit coverage for ordering, deduplication, flag handling, invalid names, and generated key safety.

Confidence Score: 5/5

The PR appears safe to merge based on the currently established evidence.

No blocking failure remains established.

Important Files Changed

Filename Overview
cli/src/env.rs Adds shared parsed-environment propagation and Windows-specific WSLENV construction with focused unit tests.
cli/src/cli/exec.rs Replaces direct parsed-variable injection with the shared environment helper.
cli/src/cli/shell.rs Replaces direct parsed-variable injection with the shared environment helper for shell launches.

Reviews (2): Last reviewed commit: "fix(cli): forward parsed args to WSL bas..." | Re-trigger Greptile

Comment thread cli/src/env.rs Outdated
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.
@jdx
jdx merged commit 6254b9e into jdx:main Aug 2, 2026
8 checks passed
@JamBalaya56562
JamBalaya56562 deleted the fix-wslenv-windows branch August 2, 2026 00:55
@mise-en-dev mise-en-dev mentioned this pull request Aug 2, 2026
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