Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 151 additions & 3 deletions tests/zsh/startup.bats
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ EOF
XDG_CACHE_HOME="$TEST_HOME/.cache" \
FPATH="$BATS_TEST_TMPDIR/functions" \
PATH="$TEST_BIN:$PATH" \
zsh -dfi -c "source '$REPO_ROOT/zsh/.zshrc'"
zsh -dfi -c "source '$REPO_ROOT/zsh/.zshenv'; source '$REPO_ROOT/zsh/.zshrc'"

[ "$status" -eq 0 ]
grep -Fx -- "--quiet -S hl=herdr --session main" "$ABBR_LOG"
Expand All @@ -61,7 +61,7 @@ EOF
XDG_CACHE_HOME="$TEST_HOME/.cache" \
FPATH="$BATS_TEST_TMPDIR/functions" \
PATH="$TEST_BIN:$PATH" \
zsh -dfi -c "source '$REPO_ROOT/zsh/.zshrc'"
zsh -dfi -c "source '$REPO_ROOT/zsh/.zshenv'; source '$REPO_ROOT/zsh/.zshrc'"

[ "$status" -eq 0 ]
grep -Fx -- \
Expand All @@ -75,7 +75,7 @@ EOF
XDG_CACHE_HOME="$TEST_HOME/.cache" \
FPATH="$BATS_TEST_TMPDIR/functions" \
PATH="$TEST_BIN:$PATH" \
zsh -dfi -c "source '$REPO_ROOT/zsh/.zshrc'"
zsh -dfi -c "source '$REPO_ROOT/zsh/.zshenv'; source '$REPO_ROOT/zsh/.zshrc'"

[ "$status" -eq 0 ]
[ "$(cat "$COMPINIT_LOG")" = "-C -d $TEST_HOME/.cache/zsh/.zcompdump" ]
Expand All @@ -91,6 +91,154 @@ EOF
! [[ "$stderr" == *"brew shellenv"* ]]
}

@test "clean login startup orders Nix profiles and fallback tools before system entries" {
local _user_profile="$TEST_HOME/.nix-profile/bin"
local _per_user_profile="/etc/profiles/per-user/startup-test/bin"
local _system_profile="/run/current-system/sw/bin"
local _default_profile="/nix/var/nix/profiles/default/bin"

run_startup_path "$_user_profile:$_per_user_profile:$_system_profile:$_default_profile:/usr/bin:$TEST_BIN:/bin" -lic

[ "$status" -eq 0 ]
local _expected=(
"$_user_profile"
"$_per_user_profile"
"$_system_profile"
"$_default_profile"
"$TEST_HOME/.local/bin"
"$TEST_HOME/.cargo/bin"
"$TEST_HOME/.gem/bin"
/opt/homebrew/bin
/Library/TeX/texbin
/usr/bin
"$TEST_BIN"
/bin
/Applications/Obsidian.app/Contents/MacOS
)
[ "${#lines[@]}" -eq "${#_expected[@]}" ]
local _index
for _index in "${!_expected[@]}"; do
[ "${lines[_index]}" = "${_expected[_index]}" ] || {
printf 'unexpected PATH entry at %s: %s\n' "$_index" "$output" >&2
return 1
}
done
}

@test "startup preserves inherited devShell PATH order in all shell modes" {
local _inherited="/nix/store/devshell-tools/bin:/workspace/project/bin:$TEST_HOME/.cargo/bin:/opt/homebrew/bin:$TEST_HOME/.nix-profile/bin:/usr/bin:$TEST_HOME/.local/bin:/Library/TeX/texbin:$TEST_BIN:/bin"
local _mode
for _mode in -c -ic -lic; do
run_startup_path "$_inherited:$_inherited" "$_mode"

[ "$status" -eq 0 ]
local _expected=(
/nix/store/devshell-tools/bin
/workspace/project/bin
"$TEST_HOME/.cargo/bin"
/opt/homebrew/bin
"$TEST_HOME/.nix-profile/bin"
"$TEST_HOME/.gem/bin"
/usr/bin
"$TEST_HOME/.local/bin"
/Library/TeX/texbin
"$TEST_BIN"
/bin
)
if [ "$_mode" = -lic ]; then
_expected+=(/Applications/Obsidian.app/Contents/MacOS)
fi
[ "${#lines[@]}" -eq "${#_expected[@]}" ]
local _index
for _index in "${!_expected[@]}"; do
[ "${lines[_index]}" = "${_expected[_index]}" ] || {
printf 'unexpected PATH entry in %s at %s: %s\n' "$_mode" "$_index" "$output" >&2
return 1
}
done
done
}

@test "startup inserts missing fallback tools after inherited devShell entries in all shell modes" {
local _inherited="/nix/store/devshell-tools/bin:/workspace/project/bin:$TEST_BIN"
local _mode
for _mode in -c -ic -lic; do
run_startup_path "$_inherited:/usr/bin:/bin" "$_mode"

[ "$status" -eq 0 ]
local _expected=(
/nix/store/devshell-tools/bin
/workspace/project/bin
"$TEST_BIN"
"$TEST_HOME/.local/bin"
"$TEST_HOME/.cargo/bin"
"$TEST_HOME/.gem/bin"
)
if [ "$_mode" != -c ]; then
_expected+=(/opt/homebrew/bin /Library/TeX/texbin)
fi
_expected+=(/usr/bin /bin)
if [ "$_mode" = -lic ]; then
_expected+=(/Applications/Obsidian.app/Contents/MacOS)
fi
[ "$output" = "$(printf '%s\n' "${_expected[@]}")" ] || {
printf 'unexpected PATH in %s: %s\n' "$_mode" "$output" >&2
return 1
}
done
}

@test "startup inserts before the first system entry or appends when none exists" {
local _system
for _system in /usr/local/bin /usr/bin /bin /usr/sbin /sbin ''; do
local _inherited="$TEST_BIN"
local _expected=(
"$TEST_BIN"
"$TEST_HOME/.local/bin"
"$TEST_HOME/.cargo/bin"
"$TEST_HOME/.gem/bin"
)
if [ -n "$_system" ]; then
_inherited+=":$_system:/custom/bin:/usr/bin"
_expected+=("$_system" /custom/bin)
if [ "$_system" != /usr/bin ]; then
_expected+=(/usr/bin)
fi
fi
run_startup_path "$_inherited" -c

[ "$status" -eq 0 ]
[ "$output" = "$(printf '%s\n' "${_expected[@]}")" ] || {
printf 'unexpected PATH for system entry %s: %s\n' "$_system" "$output" >&2
return 1
}
done
}

run_startup_path() {
# Source repository files explicitly to isolate host startup configuration.
run --separate-stderr env -i \
HOME="$TEST_HOME" \
USER=startup-test \
XDG_CACHE_HOME="$TEST_HOME/.cache" \
FPATH="$BATS_TEST_TMPDIR/functions" \
COMPINIT_LOG="$COMPINIT_LOG" \
ABBR_LOG="$ABBR_LOG" \
REPO_ROOT="$REPO_ROOT" \
PATH="$1" \
"$(command -v zsh)" -df "$2" '
source "$REPO_ROOT/zsh/.zshenv"
source "$REPO_ROOT/zsh/.zshenv"
if [[ -o login ]]; then
source "$REPO_ROOT/zsh/.zprofile"
fi
if [[ -o interactive ]]; then
source "$REPO_ROOT/zsh/.zshrc"
fi
print -rl -- "${path[@]}"
'
}

@test "Home Manager activation invokes the packaged Zsh cache builder" {
run --separate-stderr nix build --no-link --print-out-paths \
"$TEST_FLAKE#homeConfigurations.\"kaito@mbp\".activationPackage"
Expand Down
4 changes: 1 addition & 3 deletions zsh/.zprofile
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
export PATH="$HOME/.local/bin:$PATH"

# Added by OrbStack: command-line tools and integration
source ~/.orbstack/shell/init.zsh 2>/dev/null || :

# Added by Obsidian
export PATH="$PATH:/Applications/Obsidian.app/Contents/MacOS"
path+=(/Applications/Obsidian.app/Contents/MacOS)
20 changes: 19 additions & 1 deletion zsh/.zshenv
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
export PATH=$HOME/.gem/bin:$HOME/.cargo/bin:$PATH
# Preserve inherited tool precedence and keep each entry only once.
typeset -U path PATH
function _insert_fallback_paths() {
local -a _missing
local _entry _index=1
for _entry in "$@"; do
(( ${path[(Ie)$_entry]} )) || _missing+=("$_entry")
done
(( ${#_missing} )) || return 0
for _entry in "${path[@]}"; do
case "$_entry" in
/usr/local/bin|/usr/bin|/bin|/usr/sbin|/sbin) break ;;
esac
(( _index++ ))
done
path=("${(@)path[1,_index-1]}" "${_missing[@]}" "${(@)path[_index,-1]}")
}

_insert_fallback_paths "$HOME/.local/bin" "$HOME/.cargo/bin" "$HOME/.gem/bin"
# Enforced on every zsh start so stale inherited environments
# (e.g. a long-running herdr server snapshot) cannot override it.
export EDITOR=nvim
Expand Down
7 changes: 3 additions & 4 deletions zsh/.zshrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ if [[ "$OSTYPE" == darwin* && -z "${SSH_AUTH_SOCK:-}" ]]; then
}
fi

# Global tools remain fallbacks without changing inherited tool precedence.
_insert_fallback_paths /opt/homebrew/bin /Library/TeX/texbin

# Completion
ZSH_COMPDUMP="${XDG_CACHE_HOME:-$HOME/.cache}/zsh/.zcompdump"
autoload -Uz compinit
Expand Down Expand Up @@ -75,11 +78,7 @@ function command_not_found_handler() {

DOTFILES="$HOME/ghq/github.com/furedea/dotfiles"

export PATH="/usr/bin:$PATH"
export PATH="/opt/homebrew/bin:$PATH"
export XDG_CONFIG_HOME="$HOME/.config"
export PATH="/Library/TeX/texbin:$PATH"
export PATH="$HOME/.local/bin:$PATH"

# Plugins
source "$XDG_CONFIG_HOME/zsh/nix-plugins.zsh"
Expand Down
Loading