fix(pyenv): preserve shims in login shells
macOS path_helper runs after .zshenv and can move system Python ahead of pyenv. Add an idempotent login hook and preserve it across later easyzsh migrations.
This commit is contained in:
@@ -41,7 +41,9 @@ From a git checkout (shell stubs only):
|
|||||||
| `~/.config/zshrc/*.zsh` | Personal/tool overlays before oh-my-zsh (interactive) | Left alone |
|
| `~/.config/zshrc/*.zsh` | Personal/tool overlays before oh-my-zsh (interactive) | Left alone |
|
||||||
| `~/.local/bin/` | User tools + shims (e.g. `grok` → `~/.grok/bin/grok`) | Left alone |
|
| `~/.local/bin/` | User tools + shims (e.g. `grok` → `~/.grok/bin/grok`) | Left alone |
|
||||||
|
|
||||||
easyzsh does **not** manage `~/.zprofile`. Put machine/login hooks in `local.zsh`.
|
Core deploy does not manage `~/.zprofile`. The pyenv add-on appends one login
|
||||||
|
hook so macOS `path_helper` cannot outrank pyenv shims. Put other machine hooks
|
||||||
|
in `local.zsh`.
|
||||||
|
|
||||||
Do **not** put `~/.grok/bin` on PATH wholesale.
|
Do **not** put `~/.grok/bin` on PATH wholesale.
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,11 @@ is_easyzsh_zprofile_stub() {
|
|||||||
grep -Eq 'easyzsh|PATH lives in|Leave this file empty|prefer.*local\.zsh' "$f"
|
grep -Eq 'easyzsh|PATH lives in|Leave this file empty|prefer.*local\.zsh' "$f"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
has_pyenv_login_hook() {
|
||||||
|
local hook='[[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/zsh/pyenv.zsh" ]] && . "${XDG_CONFIG_HOME:-$HOME/.config}/zsh/pyenv.zsh"'
|
||||||
|
[[ -f "$1" ]] && grep -Fqx "$hook" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
echo "Deploying easyzsh shell stubs into $TARGET_HOME"
|
echo "Deploying easyzsh shell stubs into $TARGET_HOME"
|
||||||
|
|
||||||
mkdir -p \
|
mkdir -p \
|
||||||
@@ -101,7 +106,10 @@ fi
|
|||||||
|
|
||||||
# Optional: fold a real (non-stub) ~/.zprofile into local.zsh.
|
# Optional: fold a real (non-stub) ~/.zprofile into local.zsh.
|
||||||
if [[ "$MIGRATE_ZPROFILE" -eq 1 && -f "$ZPROFILE" ]]; then
|
if [[ "$MIGRATE_ZPROFILE" -eq 1 && -f "$ZPROFILE" ]]; then
|
||||||
if is_easyzsh_zprofile_stub "$ZPROFILE"; then
|
if has_pyenv_login_hook "$ZPROFILE"; then
|
||||||
|
# ponytail: keep the whole profile; split migration only if per-line ownership is needed.
|
||||||
|
echo "Keeping ~/.zprofile: contains the pyenv login hook"
|
||||||
|
elif is_easyzsh_zprofile_stub "$ZPROFILE"; then
|
||||||
echo "Removing easyzsh ~/.zprofile stub (hooks live in local.zsh / .zshenv)"
|
echo "Removing easyzsh ~/.zprofile stub (hooks live in local.zsh / .zshenv)"
|
||||||
rm -f "$ZPROFILE"
|
rm -f "$ZPROFILE"
|
||||||
elif [[ ! -f "$LOCAL_ZSH" ]]; then
|
elif [[ ! -f "$LOCAL_ZSH" ]]; then
|
||||||
|
|||||||
@@ -23,5 +23,11 @@ fi
|
|||||||
|
|
||||||
# Always-on fragment: ~/.config/zsh/pyenv.zsh (sourced from ~/.zshenv)
|
# Always-on fragment: ~/.config/zsh/pyenv.zsh (sourced from ~/.zshenv)
|
||||||
curl -fsSL https://git.miomio.moe/mio/easyzsh/raw/branch/master/patch.sh | bash -s -- zsh/pyenv
|
curl -fsSL https://git.miomio.moe/mio/easyzsh/raw/branch/master/patch.sh | bash -s -- zsh/pyenv
|
||||||
|
# macOS path_helper runs after ~/.zshenv in login shells, so reapply pyenv afterwards.
|
||||||
|
PYENV_LOGIN_HOOK='[[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/zsh/pyenv.zsh" ]] && . "${XDG_CONFIG_HOME:-$HOME/.config}/zsh/pyenv.zsh"'
|
||||||
|
if ! grep -Fqx "$PYENV_LOGIN_HOOK" "$HOME/.zprofile" 2>/dev/null; then
|
||||||
|
[[ ! -s "$HOME/.zprofile" ]] || printf '\n' >> "$HOME/.zprofile"
|
||||||
|
printf '%s\n' "$PYENV_LOGIN_HOOK" >> "$HOME/.zprofile"
|
||||||
|
fi
|
||||||
# Drop legacy interactive-only copy if present
|
# Drop legacy interactive-only copy if present
|
||||||
rm -f "$HOME/.config/zshrc/pyenv.zsh"
|
rm -f "$HOME/.config/zshrc/pyenv.zsh"
|
||||||
|
|||||||
+33
-3
@@ -165,14 +165,44 @@ HOME="$fnm_env_home" PATH=/usr/bin:/bin \
|
|||||||
|
|
||||||
# Non-interactive zsh must resolve pyenv's global Python via .zshenv.
|
# Non-interactive zsh must resolve pyenv's global Python via .zshenv.
|
||||||
pyenv_env_home="$TEMP_DIR/pyenv-env-home"
|
pyenv_env_home="$TEMP_DIR/pyenv-env-home"
|
||||||
mkdir -p "$pyenv_env_home/.pyenv/shims" "$pyenv_env_home/.config/zsh"
|
pyenv_env_xdg="$pyenv_env_home/xdg"
|
||||||
|
mkdir -p "$pyenv_env_home/.pyenv/shims" "$pyenv_env_xdg/zsh"
|
||||||
ln -s /usr/bin/true "$pyenv_env_home/.pyenv/shims/python3"
|
ln -s /usr/bin/true "$pyenv_env_home/.pyenv/shims/python3"
|
||||||
cp "$ROOT/zsh/pyenv.zsh" "$pyenv_env_home/.config/zsh/pyenv.zsh"
|
cp "$ROOT/zsh/pyenv.zsh" "$pyenv_env_xdg/zsh/pyenv.zsh"
|
||||||
HOME="$pyenv_env_home" bash "$ROOT/deploy.sh" --home "$pyenv_env_home" >/dev/null
|
HOME="$pyenv_env_home" bash "$ROOT/deploy.sh" --home "$pyenv_env_home" >/dev/null
|
||||||
HOME="$pyenv_env_home" PYENV_ROOT="$pyenv_env_home/.pyenv" PATH=/usr/bin:/bin \
|
HOME="$pyenv_env_home" XDG_CONFIG_HOME="$pyenv_env_xdg" \
|
||||||
|
PYENV_ROOT="$pyenv_env_home/.pyenv" PATH=/usr/bin:/bin \
|
||||||
zsh -c '[[ "$(command -v python3)" == "$HOME/.pyenv/shims/python3" ]]' ||
|
zsh -c '[[ "$(command -v python3)" == "$HOME/.pyenv/shims/python3" ]]' ||
|
||||||
fail "pyenv Python is not available to non-interactive zsh via .zshenv"
|
fail "pyenv Python is not available to non-interactive zsh via .zshenv"
|
||||||
|
|
||||||
|
# Login zsh must reapply pyenv after the system profile changes PATH.
|
||||||
|
pyenv_login_hook=$(sed -n "s/^PYENV_LOGIN_HOOK='\(.*\)'$/\1/p" "$ROOT/install_pyenv.sh")
|
||||||
|
[[ -n "$pyenv_login_hook" ]] || fail "install_pyenv.sh is missing its login hook"
|
||||||
|
printf '%s\n' "$pyenv_login_hook" > "$pyenv_env_home/.zprofile"
|
||||||
|
HOME="$pyenv_env_home" XDG_CONFIG_HOME="$pyenv_env_xdg" \
|
||||||
|
PYENV_ROOT="$pyenv_env_home/.pyenv" PATH=/usr/bin:/bin \
|
||||||
|
zsh -lc '[[ "$(command -v python3)" == "$HOME/.pyenv/shims/python3" ]]' ||
|
||||||
|
fail "pyenv Python is not restored in login zsh after the system profile"
|
||||||
|
|
||||||
|
# A later easyzsh migration must not move the login hook before the system profile.
|
||||||
|
HOME="$pyenv_env_home" bash "$ROOT/deploy.sh" --home "$pyenv_env_home" --migrate-zprofile >/dev/null
|
||||||
|
grep -Fqx "$pyenv_login_hook" "$pyenv_env_home/.zprofile" ||
|
||||||
|
fail "deploy.sh migrated the pyenv login hook out of .zprofile"
|
||||||
|
HOME="$pyenv_env_home" XDG_CONFIG_HOME="$pyenv_env_xdg" \
|
||||||
|
PYENV_ROOT="$pyenv_env_home/.pyenv" PATH=/usr/bin:/bin \
|
||||||
|
zsh -lc '[[ "$(command -v python3)" == "$HOME/.pyenv/shims/python3" ]]' ||
|
||||||
|
fail "pyenv Python was lost after a later easyzsh migration"
|
||||||
|
|
||||||
|
# A comment mentioning the fragment is not an active pyenv login hook.
|
||||||
|
profile_comment_home="$TEMP_DIR/profile-comment-home"
|
||||||
|
mkdir -p "$profile_comment_home"
|
||||||
|
printf '%s\n' '# pyenv config: ~/.config/zsh/pyenv.zsh' 'ordinary-login-hook' > "$profile_comment_home/.zprofile"
|
||||||
|
HOME="$profile_comment_home" bash "$ROOT/deploy.sh" --home "$profile_comment_home" --migrate-zprofile >/dev/null
|
||||||
|
[[ ! -e "$profile_comment_home/.zprofile" ]] ||
|
||||||
|
fail "deploy.sh treated a pyenv path comment as an active login hook"
|
||||||
|
grep -Fxq 'ordinary-login-hook' "$profile_comment_home/.config/zsh/local.zsh" ||
|
||||||
|
fail "deploy.sh did not migrate an ordinary profile mentioning pyenv"
|
||||||
|
|
||||||
# patch.sh path form installs into ~/.config/zsh/
|
# patch.sh path form installs into ~/.config/zsh/
|
||||||
path_patch_home="$TEMP_DIR/path-patch-home"
|
path_patch_home="$TEMP_DIR/path-patch-home"
|
||||||
path_bin="$TEMP_DIR/path-bin"
|
path_bin="$TEMP_DIR/path-bin"
|
||||||
|
|||||||
+3
-2
@@ -1,7 +1,8 @@
|
|||||||
# pyenv (Python Version Manager), always-on via ~/.zshenv
|
# pyenv (Python Version Manager), always-on via ~/.zshenv
|
||||||
export PYENV_ROOT="${PYENV_ROOT:-$HOME/.pyenv}"
|
export PYENV_ROOT="${PYENV_ROOT:-$HOME/.pyenv}"
|
||||||
[[ -d "$PYENV_ROOT/bin" ]] && export PATH="$PYENV_ROOT/bin:$PATH"
|
[[ -d "$PYENV_ROOT/bin" ]] && path=("$PYENV_ROOT/bin" ${path:#$PYENV_ROOT/bin})
|
||||||
[[ -d "$PYENV_ROOT/shims" ]] && export PATH="$PYENV_ROOT/shims:$PATH"
|
[[ -d "$PYENV_ROOT/shims" ]] && path=("$PYENV_ROOT/shims" ${path:#$PYENV_ROOT/shims})
|
||||||
|
export PATH
|
||||||
if [[ -o interactive ]] && command -v pyenv &> /dev/null; then
|
if [[ -o interactive ]] && command -v pyenv &> /dev/null; then
|
||||||
eval "$(pyenv init - zsh)"
|
eval "$(pyenv init - zsh)"
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user