diff --git a/crates/uv-install-wheel/src/wheel.rs b/crates/uv-install-wheel/src/wheel.rs index 8d518b9e0..559237e16 100644 --- a/crates/uv-install-wheel/src/wheel.rs +++ b/crates/uv-install-wheel/src/wheel.rs @@ -143,7 +143,7 @@ fn format_shebang(executable: impl AsRef, os_name: &str, relocatable: bool // (note: the Windows trampoline binaries natively support relative paths to executable) if shebang_length > 127 || executable.contains(' ') || relocatable { let prefix = if relocatable { - r#""$(CDPATH= cd -- "$(dirname -- "$0")" && echo "$PWD")"/"# + r#""$(dirname -- "$(realpath -- "$0")")"/"# } else { "" }; @@ -1019,7 +1019,7 @@ mod test { let os_name = "posix"; assert_eq!( format_shebang(executable, os_name, true), - "#!/bin/sh\n'''exec' \"$(CDPATH= cd -- \"$(dirname -- \"$0\")\" && echo \"$PWD\")\"/'python3' \"$0\" \"$@\"\n' '''" + "#!/bin/sh\n'''exec' \"$(dirname -- \"$(realpath -- \"$0\")\")\"/'python3' \"$0\" \"$@\"\n' '''" ); // Except on Windows... diff --git a/crates/uv-virtualenv/src/virtualenv.rs b/crates/uv-virtualenv/src/virtualenv.rs index d46839707..70f0ca22e 100644 --- a/crates/uv-virtualenv/src/virtualenv.rs +++ b/crates/uv-virtualenv/src/virtualenv.rs @@ -298,9 +298,7 @@ pub(crate) fn create( let virtual_env_dir = match (relocatable, name.to_owned()) { (true, "activate") => { - // Extremely verbose, but should cover all major POSIX shells, - // as well as platforms where `readlink` does not implement `-f`. - r#"'"$(dirname -- "$(CDPATH= cd -- "$(dirname -- "$SCRIPT_PATH")" > /dev/null && echo "$PWD")")"'"# + r#"'"$(dirname -- "$(dirname -- "$(realpath -- "$SCRIPT_PATH")")")"'"# } (true, "activate.bat") => r"%~dp0..", (true, "activate.fish") => { diff --git a/crates/uv/tests/pip_install.rs b/crates/uv/tests/pip_install.rs index be6ee3a49..610a3c3e0 100644 --- a/crates/uv/tests/pip_install.rs +++ b/crates/uv/tests/pip_install.rs @@ -6810,6 +6810,18 @@ fn install_relocatable() -> Result<()> { .success() .stdout(predicate::str::contains("Hello world!")); + // Relocatable entrypoint should still be usable even if symlinked. + // Only testable on POSIX since symlinks require elevated privilege on Windows + #[cfg(unix)] + { + let script_symlink_path = context.temp_dir.join("black"); + std::os::unix::fs::symlink(script_path, script_symlink_path.clone())?; + Command::new(script_symlink_path.as_os_str()) + .assert() + .success() + .stdout(predicate::str::contains("Hello world!")); + } + Ok(()) } diff --git a/crates/uv/tests/venv.rs b/crates/uv/tests/venv.rs index 53afcee72..b7dd0d6cf 100644 --- a/crates/uv/tests/venv.rs +++ b/crates/uv/tests/venv.rs @@ -957,7 +957,9 @@ fn verify_pyvenv_cfg_relocatable() { let activate_sh = scripts.child("activate"); activate_sh.assert(predicates::path::is_file()); - activate_sh.assert(predicates::str::contains(r#"VIRTUAL_ENV=''"$(dirname -- "$(CDPATH= cd -- "$(dirname -- "$SCRIPT_PATH")" > /dev/null && echo "$PWD")")"''"#)); + activate_sh.assert(predicates::str::contains( + r#"VIRTUAL_ENV=''"$(dirname -- "$(dirname -- "$(realpath -- "$SCRIPT_PATH")")")"''"#, + )); let activate_bat = scripts.child("activate.bat"); activate_bat.assert(predicates::path::is_file());