`just` Prioritizes System32 Over `$Path`: A Fix Guide
Hey guys! Today, we're diving deep into a quirky issue with the just command runner on Windows that can be a real head-scratcher. If you've ever wondered why just seems to ignore your $Path settings and stubbornly uses the bash.exe in System32, especially when you have WSL2 installed, you're in the right place. Let's break down the problem, explore a fix, and make sure your just commands behave as expected.
The Bug: just and the Mysterious Case of System32 Priority
The core issue? When resolving shell or interpreter executables on Windows, just, leveraging std::process::Command, doesn't quite respect the $Path environment variable the way we'd expect. Imagine you've got a bash.exe happily sitting on your $Path (maybe it's part of Git Bash), and you're all set to use it. But if you also have WSL2 installed, just might just ignore your preferred bash.exe and instead call C:\Windows\System32\bash.exe. This sneaky move launches a shell inside the WSL2 guest virtual machine, not the bash shell on your host machine. This can lead to unexpected behavior, especially when running integration tests or trying to use a specific bash environment like Git Bash.
This is a big deal because it can break integration tests on Windows, particularly if your test.rs file sets the --shell bash option. It's also a pain for anyone who wants to use Git Bash or another custom bash.exe as their just shell. So, what's the solution? Well, we need to make just play nice with $Path and respect our shell preferences.
The Fix: Embracing which and Absolute Paths
My proposed fix involves having just resolve executables to absolute paths using crates like which or pathsearch. Think of it like giving just a GPS so it can find the exact location of the executable we want. This approach can be applied consistently across various scenarios:
- The
which()function itself. - The
set shellcommand. [script()]blocks and theset script-interpretercommand.
We already have a need for which() functionality in just, so using it to resolve shells and interpreters to absolute paths is like hitting two birds with one stone. We're not only fixing a bug but also ensuring that integration tests on Windows run smoothly. It's a win-win!
Reproducing the Issue: A Step-by-Step Guide
Want to see this bug in action? If you have WSL2 installed, you can reproduce it pretty quickly. Here's how:
-
Prioritize Git Bash: Put
C:\Program Files\git\binat the top of your$Path. This tells your system to look here first when searching for executables. -
Verify Your Shell: Open any shell and run the following command to confirm your shell setup:
bash -c 'if wslinfo --version >/dev/null 2>&1; then echo BAD we are in WSL2 ; else echo GOOD, we are in Git Bash! ; fi'If you see
GOOD, we are in Git Bash!, you're on the right track. -
Create a Justfile: Now, create a file named
Justfilewith the following content:set shell := ["bash", "-c"] @default: if wslinfo --version >/dev/null 2>&1; then echo BAD we are in WSL2 ; else echo GOOD, we are in Git Bash! ; fi -
Run
just: In the same shell, run thejustcommand:justIf you see
BAD we are in WSL2, you've successfully reproduced the bug!justis ignoring your$Pathpreference and using the WSL2 bash.
I've also created a more detailed reproduction case here: https://github.com/cspotcode/repros2/tree/just-path-behavior-windows if you want to dive deeper.
Q&A: Let's Address Some Key Questions
Why on Earth Does std::process::Command Prioritize System32?
This is a puzzling one. It seems that std::process::Command makes some (buggy and undocumented) attempts to resolve the executable, but only when a $Path has been explicitly set. In just's case, it doesn't fully resolve the executable, leaving the job to CreateProcess. This Windows API function checks several locations before consulting $Path, including the local directory and, yes, System32. It's a quirky behavior that can lead to surprises.
For more details, you can check out the CreateProcess documentation.
But Isn't CreateProcessA's Resolution the Correct Behavior Since It's Built-in to Windows?
That's a valid question, but I'd argue that it's not the behavior we want in this context. The proof? Other shells intentionally avoid this behavior! They resolve the target executable according to $Path, which aligns with user expectations. They don't give C:\Windows\System32 any special treatment. We want just to behave like a good shell citizen and respect $Path.
What's the Problem with Using WSL2 bash.exe as Your Shell?
While it might work in some cases, it can break in others. For instance, the C: drive is often mounted at a different location within WSL. More importantly, just should consistently respect$Path, just like other shells do on both Windows and Linux. This ensures predictability and avoids unexpected behavior.
Related Tickets
This issue isn't entirely new. It touches on some existing discussions and problems:
Conclusion: Making just Play Nicely with $Path
So, there you have it! The mystery of why just prioritizes System32 over $Path on Windows, especially with WSL2 installed, is unveiled. By understanding the issue and implementing a fix that leverages tools like which to resolve absolute paths, we can ensure that just behaves predictably and respects our shell preferences. This not only fixes a bug but also improves the overall experience of using just on Windows. Let's make just a true $Path follower!