diff --git a/README.md b/README.md index 63969c8..da0572b 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,9 @@ From a git checkout (shell stubs only): | `~/.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 | -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. diff --git a/deploy.sh b/deploy.sh index 21018b0..3a5f6d4 100755 --- a/deploy.sh +++ b/deploy.sh @@ -68,6 +68,11 @@ is_easyzsh_zprofile_stub() { 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" mkdir -p \ @@ -101,7 +106,10 @@ fi # Optional: fold a real (non-stub) ~/.zprofile into local.zsh. 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)" rm -f "$ZPROFILE" elif [[ ! -f "$LOCAL_ZSH" ]]; then diff --git a/install_pyenv.sh b/install_pyenv.sh index a469686..70c2e80 100755 --- a/install_pyenv.sh +++ b/install_pyenv.sh @@ -23,5 +23,11 @@ fi # 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 +# 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 rm -f "$HOME/.config/zshrc/pyenv.zsh" diff --git a/tests/installers.sh b/tests/installers.sh index dec1139..bbb4db1 100755 --- a/tests/installers.sh +++ b/tests/installers.sh @@ -165,14 +165,44 @@ HOME="$fnm_env_home" PATH=/usr/bin:/bin \ # Non-interactive zsh must resolve pyenv's global Python via .zshenv. 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" -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" 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" ]]' || 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/ path_patch_home="$TEMP_DIR/path-patch-home" path_bin="$TEMP_DIR/path-bin" diff --git a/zsh/pyenv.zsh b/zsh/pyenv.zsh index 0f46ed2..e282e79 100644 --- a/zsh/pyenv.zsh +++ b/zsh/pyenv.zsh @@ -1,7 +1,8 @@ # pyenv (Python Version Manager), always-on via ~/.zshenv export PYENV_ROOT="${PYENV_ROOT:-$HOME/.pyenv}" -[[ -d "$PYENV_ROOT/bin" ]] && export PATH="$PYENV_ROOT/bin:$PATH" -[[ -d "$PYENV_ROOT/shims" ]] && export PATH="$PYENV_ROOT/shims:$PATH" +[[ -d "$PYENV_ROOT/bin" ]] && path=("$PYENV_ROOT/bin" ${path:#$PYENV_ROOT/bin}) +[[ -d "$PYENV_ROOT/shims" ]] && path=("$PYENV_ROOT/shims" ${path:#$PYENV_ROOT/shims}) +export PATH if [[ -o interactive ]] && command -v pyenv &> /dev/null; then eval "$(pyenv init - zsh)" fi