From bf15ca93cfacae88b7ae4dc28467ed5dfadccdc2 Mon Sep 17 00:00:00 2001 From: Pavel Dikov <66369686+paveldikov@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:00:56 +0100 Subject: [PATCH] fix(venv.relocatable): script entrypoints should work if symlinked to (#8079) Fixes: #8058 ## Test Plan Integration test (but only for Unix, because symlinks on Windows require admin privs. Plus, they are not really all that idiomatic on Windows) --- crates/uv-install-wheel/src/wheel.rs | 4 ++-- crates/uv-virtualenv/src/virtualenv.rs | 4 +--- crates/uv/tests/pip_install.rs | 12 ++++++++++++ crates/uv/tests/venv.rs | 4 +++- 4 files changed, 18 insertions(+), 6 deletions(-) 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());