fix(venv): make relocatable activation scripts support ksh (#5640)

It transpires that detecting the directory a script was sourced from is
non-trivial across `bash`, `ksh` and `zsh`.

The previous version was a one-liner and supported `bash` and `zsh` but
not `ksh`.

It is possible to keep the one-liner and add `ksh` support, but that is
mutually-exclusive with `zsh`.

Therefore, the only way to square this circle is to add an `if` block. A
silver lining here is that although longer, the script is probably
easier to follow as there is less code-golfing going on.
This commit is contained in:
Pavel Dikov
2024-07-31 17:18:11 +01:00
committed by GitHub
parent f266fb711c
commit d05f2b258b
5 changed files with 26 additions and 8 deletions
+11 -1
View File
@@ -21,6 +21,8 @@ pub enum Shell {
Nushell,
/// C SHell (csh)
Csh,
/// Korn SHell (ksh)
Ksh,
}
impl Shell {
@@ -43,6 +45,8 @@ impl Shell {
Some(Shell::Bash)
} else if std::env::var_os("ZSH_VERSION").is_some() {
Some(Shell::Zsh)
} else if std::env::var_os("KSH_VERSION").is_some() {
Some(Shell::Ksh)
} else if let Some(env_shell) = std::env::var_os("SHELL") {
Shell::from_shell_path(env_shell)
} else if cfg!(windows) {
@@ -99,6 +103,10 @@ impl Shell {
home_dir.join(".bashrc"),
]
}
Shell::Ksh => {
// On Ksh it's standard POSIX `.profile` for login shells, and `.kshrc` for non-login.
vec![home_dir.join(".profile"), home_dir.join(".kshrc")]
}
Shell::Zsh => {
// On Zsh, we only need to update `.zshenv`. This file is sourced for both login and
// non-login shells. However, we match rustup's logic for determining _which_
@@ -174,7 +182,7 @@ impl Shell {
pub fn prepend_path(self, path: &Path) -> Option<String> {
match self {
Shell::Nushell => None,
Shell::Bash | Shell::Zsh => Some(format!(
Shell::Bash | Shell::Zsh | Shell::Ksh => Some(format!(
"export PATH=\"{}:$PATH\"",
backslash_escape(&path.simplified_display().to_string()),
)),
@@ -208,6 +216,7 @@ impl std::fmt::Display for Shell {
Shell::Zsh => write!(f, "Zsh"),
Shell::Nushell => write!(f, "Nushell"),
Shell::Csh => write!(f, "Csh"),
Shell::Ksh => write!(f, "Ksh"),
}
}
}
@@ -220,6 +229,7 @@ fn parse_shell_from_path(path: &Path) -> Option<Shell> {
"zsh" => Some(Shell::Zsh),
"fish" => Some(Shell::Fish),
"csh" => Some(Shell::Csh),
"ksh" => Some(Shell::Ksh),
"powershell" | "powershell_ise" => Some(Shell::Powershell),
_ => None,
}
+12 -4
View File
@@ -22,10 +22,18 @@
# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly
if [ "${BASH_SOURCE-}" = "$0" ]; then
echo "You must source this script: \$ source $0" >&2
exit 33
# Get script path (only used if environment is relocatable).
if [ -n "${BASH_VERSION:+x}" ] ; then
SCRIPT_PATH="${BASH_SOURCE[0]}"
if [ "$SCRIPT_PATH" = "$0" ]; then
# Only bash has a reasonably robust check for source'dness.
echo "You must source this script: \$ source $0" >&2
exit 33
fi
elif [ -n "${ZSH_VERSION:+x}" ] ; then
SCRIPT_PATH="${(%):-%x}"
elif [ -n "${KSH_VERSION:+x}" ] ; then
SCRIPT_PATH="${.sh.file}"
fi
deactivate () {
+1 -1
View File
@@ -298,7 +298,7 @@ pub(crate) fn create(
(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 -- ${BASH_SOURCE[0]:-${(%):-%x}})" && echo "$PWD")")"'"#
r#"'"$(dirname -- "$(CDPATH= cd -- "$(dirname -- "$SCRIPT_PATH")" > /dev/null && echo "$PWD")")"'"#
}
(true, "activate.bat") => r"%~dp0..",
(true, "activate.fish") => {
+1 -1
View File
@@ -331,7 +331,7 @@ async fn venv_impl(
// Determine the appropriate activation command.
let activation = match Shell::from_env() {
None => None,
Some(Shell::Bash | Shell::Zsh) => Some(format!(
Some(Shell::Bash | Shell::Zsh | Shell::Ksh) => Some(format!(
"source {}",
shlex_posix(venv.scripts().join("activate"))
)),
+1 -1
View File
@@ -794,7 +794,7 @@ 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 -- ${BASH_SOURCE[0]:-${(%):-%x}})" && echo "$PWD")")"''"#));
activate_sh.assert(predicates::str::contains(r#"VIRTUAL_ENV=''"$(dirname -- "$(CDPATH= cd -- "$(dirname -- "$SCRIPT_PATH")" > /dev/null && echo "$PWD")")"''"#));
let activate_bat = scripts.child("activate.bat");
activate_bat.assert(predicates::path::is_file());