diff --git a/default/bash/env-bootstrap b/default/bash/env-bootstrap index f9b39891..2bc6d1f3 100644 --- a/default/bash/env-bootstrap +++ b/default/bash/env-bootstrap @@ -23,5 +23,19 @@ if [ "$OMARCHY_PATH" != /usr/share/omarchy ]; then *":${OMARCHY_PATH%/}/bin:"*) ;; *) PATH="${OMARCHY_PATH%/}/bin${PATH:+:$PATH}" ;; esac - export PATH fi + +# User-level tool paths, appended so system binaries keep precedence. This is +# what lets login shells (ssh, bash -lc) and the uwsm session find mise-managed +# tools without an interactive rc. Keep these directories in sync with the PAM +# PATH line from install/config/ssh-command-path.sh, which covers SSH commands +# that run no shell setup at all. +case ":$PATH:" in + *":$HOME/.local/share/mise/shims:"*) ;; + *) PATH="${PATH:+$PATH:}$HOME/.local/share/mise/shims" ;; +esac +case ":$PATH:" in + *":$HOME/.local/bin:"*) ;; + *) PATH="${PATH:+$PATH:}$HOME/.local/bin" ;; +esac +export PATH diff --git a/default/bashrc b/default/bashrc index 43810a91..b1950a2d 100644 --- a/default/bashrc +++ b/default/bashrc @@ -1,9 +1,11 @@ -# If not running interactively, don't do anything (leave this at the top of this file) +# Omarchy environment (OMARCHY_PATH + PATH), needed even for non-interactive shells +[[ -r /usr/share/omarchy/default/bash/env-bootstrap ]] && source /usr/share/omarchy/default/bash/env-bootstrap + +# If not running interactively, don't do anything else (leave this above the rc source) [[ $- != *i* ]] && return # All the default Omarchy aliases and functions # (don't mess with these directly, just overwrite them here!) -[[ -r /usr/share/omarchy/default/bash/env-bootstrap ]] && source /usr/share/omarchy/default/bash/env-bootstrap source "$OMARCHY_PATH/default/bash/rc" # Add your own exports, aliases, and functions here. diff --git a/install/config/all.sh b/install/config/all.sh index c4c70752..ba2247b9 100644 --- a/install/config/all.sh +++ b/install/config/all.sh @@ -2,6 +2,7 @@ run_logged "$OMARCHY_INSTALL/config/theme-system.sh" run_logged "$OMARCHY_INSTALL/config/increase-lockout-limit.sh" run_logged "$OMARCHY_INSTALL/config/lockscreen-pam.sh" run_logged "$OMARCHY_INSTALL/config/fix-powerprofilesctl-shebang.sh" +run_logged "$OMARCHY_INSTALL/config/ssh-command-path.sh" run_logged "$OMARCHY_INSTALL/config/docker.sh" run_logged "$OMARCHY_INSTALL/config/snapper.sh" run_logged "$OMARCHY_INSTALL/config/locate.sh" diff --git a/install/config/ssh-command-path.sh b/install/config/ssh-command-path.sh new file mode 100644 index 00000000..b31b0ccf --- /dev/null +++ b/install/config/ssh-command-path.sh @@ -0,0 +1,12 @@ +# SSH commands (ssh host cmd) run without a login or interactive shell, so on +# Arch the PAM environment is the only place they can inherit PATH from. Add +# the user-level tool paths there so remote tools (herdr, editors, agent CLIs) +# find mise-managed installs. @{HOME} expands per-user from passwd. Keep the +# directories in sync with default/bash/env-bootstrap. +if ! grep -qE '^PATH[[:space:]]' /etc/security/pam_env.conf; then + cat >>/etc/security/pam_env.conf <<'EOF' + +# Omarchy: give SSH commands and other non-shell logins the user-level tool paths +PATH DEFAULT=/usr/local/sbin:/usr/local/bin:/usr/bin:@{HOME}/.local/share/mise/shims:@{HOME}/.local/bin +EOF +fi diff --git a/migrations/1786181929.sh b/migrations/1786181929.sh new file mode 100644 index 00000000..15dc366a --- /dev/null +++ b/migrations/1786181929.sh @@ -0,0 +1,13 @@ +echo "Give SSH commands the user-level tool paths via the PAM environment" + +# SSH commands (ssh host cmd) run without a login or interactive shell, so the +# PAM environment is the only place they can inherit PATH from. Same line that +# install/config/ssh-command-path.sh writes on fresh installs; skip if PATH is +# already managed there (by us or by the user). +grep -qE '^PATH[[:space:]]' /etc/security/pam_env.conf && exit 0 + +sudo tee -a /etc/security/pam_env.conf >/dev/null <<'EOF' + +# Omarchy: give SSH commands and other non-shell logins the user-level tool paths +PATH DEFAULT=/usr/local/sbin:/usr/local/bin:/usr/bin:@{HOME}/.local/share/mise/shims:@{HOME}/.local/bin +EOF diff --git a/test/shell.d/dev-env-path-test.sh b/test/shell.d/dev-env-path-test.sh index 0e012a94..d44e1d62 100755 --- a/test/shell.d/dev-env-path-test.sh +++ b/test/shell.d/dev-env-path-test.sh @@ -10,6 +10,7 @@ run_bootstrap() { local home="$3" local path_value="$4" + shell_bin=$(command -v "$shell_bin") HOME="$home" PATH="$path_value" "$shell_bin" -c ' . "$1" printf "%s\n%s\n" "$OMARCHY_PATH" "$PATH" @@ -53,6 +54,9 @@ default_path=${default_result[1]} [[ ${default_result[0]} == /usr/share/omarchy ]] || fail "env-bootstrap resolves default OMARCHY_PATH" "actual: ${default_result[0]}" pass "env-bootstrap resolves default OMARCHY_PATH" assert_path_present "$default_path" "$tmpdir/unrelated/bin" "env-bootstrap preserves PATH entries in default mode" +assert_path_present "$default_path" "$home/.local/share/mise/shims" "env-bootstrap appends mise shims" +assert_path_present "$default_path" "$home/.local/bin" "env-bootstrap appends ~/.local/bin" +assert_path_first "$default_path" "$tmpdir/unrelated/bin" "env-bootstrap appends user-level paths after existing entries" printf 'export OMARCHY_PATH="%s"\n' "$tmpdir/active" >"$tmpdir/omarchy.conf" mapfile -t linked_result < <(run_bootstrap bash "$bootstrap" "$home" "$tmpdir/unrelated/bin:/usr/bin") @@ -63,10 +67,16 @@ pass "env-bootstrap resolves linked OMARCHY_PATH" assert_path_first "$linked_path" "$tmpdir/active/bin" "env-bootstrap prepends active checkout bin in linked mode" assert_path_present "$linked_path" "$tmpdir/unrelated/bin" "env-bootstrap preserves unrelated PATH entries in linked mode" -mapfile -t linked_duplicate_result < <(run_bootstrap bash "$bootstrap" "$home" "$tmpdir/active/bin:/usr/bin") +mapfile -t linked_duplicate_result < <(run_bootstrap bash "$bootstrap" "$home" "$tmpdir/active/bin:/usr/bin:$home/.local/share/mise/shims:$home/.local/bin") linked_duplicate_path=${linked_duplicate_result[1]} -[[ $linked_duplicate_path == "$tmpdir/active/bin:/usr/bin" ]] || fail "env-bootstrap does not duplicate active checkout bin" "actual PATH: $linked_duplicate_path" -pass "env-bootstrap does not duplicate active checkout bin" +[[ $linked_duplicate_path == "$tmpdir/active/bin:/usr/bin:$home/.local/share/mise/shims:$home/.local/bin" ]] || fail "env-bootstrap does not duplicate PATH entries" "actual PATH: $linked_duplicate_path" +pass "env-bootstrap does not duplicate PATH entries" + +# An empty PATH must not produce empty entries (a bare ":" means the cwd) +mapfile -t empty_path_result < <(run_bootstrap bash "$bootstrap" "$home" "") +empty_path=${empty_path_result[1]} +[[ $empty_path == "$tmpdir/active/bin:$home/.local/share/mise/shims:$home/.local/bin" ]] || fail "env-bootstrap builds a clean PATH from an empty one" "actual PATH: $empty_path" +pass "env-bootstrap builds a clean PATH from an empty one" if command -v zsh >/dev/null 2>&1; then mapfile -t zsh_result < <(run_bootstrap zsh "$bootstrap" "$home" "$tmpdir/unrelated/bin:/usr/bin")