From 1afeb594bb169f4c6e03307017fcb81864d82cad Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 21 Nov 2025 00:33:45 -0500 Subject: [PATCH 1/3] add simple echoargs executable to be used in tests (possible precursor to just-test executable for #425) --- Cargo.toml | 5 +++++ src/bin/echoargs.rs | 6 ++++++ 2 files changed, 11 insertions(+) create mode 100644 src/bin/echoargs.rs diff --git a/Cargo.toml b/Cargo.toml index e1a0355f..b8f50a4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,6 +93,11 @@ path = "src/main.rs" name = "just" test = false +[[bin]] +path = "src/bin/echoargs.rs" +name = "echoargs" +test = false + # The public documentation is minimal and doesn't change between # platforms, so we only build them for linux on docs.rs to save # their build machines some cycles. diff --git a/src/bin/echoargs.rs b/src/bin/echoargs.rs new file mode 100644 index 00000000..53c3f9ae --- /dev/null +++ b/src/bin/echoargs.rs @@ -0,0 +1,6 @@ +use std::env; + +fn main() { + let args: Vec = env::args().skip(1).collect(); + println!("{}", args.join(" ")); +} From 3a527ada6f71e32a8715230715dcf53cc77f92fa Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 21 Nov 2025 01:12:57 -0500 Subject: [PATCH 2/3] add failing test --- tests/lib.rs | 2 ++ tests/windows_path_resolution.rs | 41 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/windows_path_resolution.rs diff --git a/tests/lib.rs b/tests/lib.rs index 450ac295..9a01ce0e 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -132,6 +132,8 @@ mod unstable; mod which_function; #[cfg(windows)] mod windows; +#[cfg(windows)] +mod windows_path_resolution; #[cfg(target_family = "windows")] mod windows_shell; mod working_directory; diff --git a/tests/windows_path_resolution.rs b/tests/windows_path_resolution.rs new file mode 100644 index 00000000..1641ee5c --- /dev/null +++ b/tests/windows_path_resolution.rs @@ -0,0 +1,41 @@ +use super::*; + +#[cfg(windows)] +#[test] +fn windows_path_resolution() { + + // Goal: Confirm that $PATH entries are respected *before* C:\Windows\System32 when + // locating shell executable. This can happen when PATH is configured to prefer + // Git-for-Windows' bash.exe, and just must not call C:\Windows\System32\bash.exe + // https://github.com/casey/just/issues/2947 + + // Copy echoargs.exe to temp directory as where.exe, to intentionally match + // the name of an executable in C:\Windows\System32 + let mut echoargs_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + echoargs_path.push("target"); + echoargs_path.push("debug"); + echoargs_path.push("echoargs.exe"); + let tmp = tempdir(); + let tmp_subdir= tmp.path().join("subdir"); + let tmp_subdir_path = tmp_subdir.as_path(); + let exe_path = tmp_subdir_path.join("where.exe"); + std::fs::create_dir(tmp_subdir_path).expect("Failed to create temp subdirectory"); + std::fs::copy(&echoargs_path, &exe_path) + .expect("Failed to copy exe to temp directory"); + + // Prepend temp directory to PATH + let new_path = tmp_subdir_path.to_str().unwrap().to_owned() + ";" + &env::var("PATH").unwrap(); + + Test::with_tempdir(tmp) + .shell(false) + .env("Path", &new_path) + .justfile( + r#" + set shell := ['where.exe'] + @default: + test_marker + "#, + ) + .stdout("test_marker\n") + .run(); +} From 2f2952177bd54947d4753f902b92c23415ff624c Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 21 Nov 2025 01:52:51 -0500 Subject: [PATCH 3/3] Set explicit $PATH on all spawned child processes on Windows --- src/command_ext.rs | 14 ++++++++++++++ src/executor.rs | 1 + src/justfile.rs | 4 +++- src/platform/windows.rs | 1 + src/settings.rs | 1 + src/subcommand.rs | 2 +- 6 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/command_ext.rs b/src/command_ext.rs index 1cd7ef03..b47e5cf1 100644 --- a/src/command_ext.rs +++ b/src/command_ext.rs @@ -1,6 +1,8 @@ use super::*; pub(crate) trait CommandExt { + fn with_explicit_path(&mut self) -> &mut Command; + fn export( &mut self, settings: &Settings, @@ -19,6 +21,18 @@ pub(crate) trait CommandExt { } impl CommandExt for Command { + fn with_explicit_path(&mut self) -> &mut Command { + // On Windows, set child's path explicitly so that spawned executable + // is located on path before System32 fallback, not the other way around. + // https://blog.rust-lang.org/2022/01/13/Rust-1.58.0/#reduced-windows-command-search-path + #[cfg(windows)] + if let Some(path) = std::env::var_os("Path") { + self.env("Path", path); + } + + self + } + fn export( &mut self, settings: &Settings, diff --git a/src/executor.rs b/src/executor.rs index 49c7c89b..1887a166 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -16,6 +16,7 @@ impl Executor<'_> { match self { Self::Command(interpreter) => { let mut command = Command::new(&interpreter.command.cooked); + command.with_explicit_path(); if let Some(working_directory) = working_directory { command.current_dir(working_directory); diff --git a/src/justfile.rs b/src/justfile.rs index 558a6317..e6997153 100644 --- a/src/justfile.rs +++ b/src/justfile.rs @@ -157,7 +157,9 @@ impl<'src> Justfile<'src> { command.arg(binary); command } else { - Command::new(binary) + let mut command = Command::new(binary); + command.with_explicit_path(); + command }; command diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 13c5f329..44bf753c 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -32,6 +32,7 @@ impl PlatformInterface for Platform { }; let mut cmd = Command::new(command.as_ref()); + cmd.with_explicit_path(); if let Some(working_directory) = working_directory { cmd.current_dir(working_directory); diff --git a/src/settings.rs b/src/settings.rs index 1b352d61..da886c84 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -106,6 +106,7 @@ impl<'src> Settings<'src> { let (command, args) = self.shell(config); let mut cmd = Command::new(command); + cmd.with_explicit_path(); cmd.args(args); diff --git a/src/subcommand.rs b/src/subcommand.rs index 9b15b520..1928b898 100644 --- a/src/subcommand.rs +++ b/src/subcommand.rs @@ -307,7 +307,7 @@ impl Subcommand { .or_else(|| env::var_os("EDITOR")) .unwrap_or_else(|| "vim".into()); - let error = Command::new(&editor) + let error = Command::new(&editor).with_explicit_path() .current_dir(&search.working_directory) .arg(&search.justfile) .status();