bash-preexec arms its DEBUG trap from __bp_interactive_mode at the end of PROMPT_COMMAND, but its lazy installer runs mid-way through the first prompt because zoxide and ghostty append their hooks after it. Those hooks disarm the trap again, so the first interactive command of every session never reached preexec and atuin silently dropped it. Arm from PS0 instead, which is expanded once per interactive command before the DEBUG trap and is therefore independent of PROMPT_COMMAND ordering. Also guard the script against running twice: it is installed as both loginShellInit and interactiveShellInit, and /etc/profile sources /etc/bashrc, which registered starship's precmd/preexec hooks twice in every login shell.
142 lines
4.3 KiB
Bash
142 lines
4.3 KiB
Bash
# ---- logging ---------------------------------------------------------------
|
|
|
|
log_shell_init() {
|
|
local uid="${UID:-}"
|
|
[[ -n "$uid" ]] || uid="$(id -u 2>/dev/null || echo unknown)"
|
|
|
|
# Override with:
|
|
# export NANUQSAURUS_SHELL_INIT_LOG_FILE=/path/to/log
|
|
local log_file="${NANUQSAURUS_SHELL_INIT_LOG_FILE:-/tmp/shell-init-${uid}.log}"
|
|
|
|
printf '%s\n' "shell-init: $*" >>"$log_file" 2>/dev/null || true
|
|
}
|
|
|
|
# ---- helpers ---------------------------------------------------------------
|
|
|
|
have() { command -v "$1" >/dev/null 2>&1; }
|
|
|
|
is_interactive() {
|
|
[[ -t 1 || -n ${PS1:-} ]]
|
|
}
|
|
|
|
# ---- environment sanity ----------------------------------------------------
|
|
|
|
if [[ -z ${TERM:-} || $TERM == dumb ]]; then
|
|
export TERM=xterm-256color
|
|
fi
|
|
|
|
INTERACTIVE=0
|
|
is_interactive && INTERACTIVE=1
|
|
|
|
log_shell_init "TERM=$TERM PS1=${PS1:-<empty>} SHELL=$SHELL interactive=$INTERACTIVE"
|
|
|
|
# Ensure PS1 exists so downstream init blocks run
|
|
if (( INTERACTIVE )) && [[ -z ${PS1:-} ]]; then
|
|
PS1='\\s-\\v\\$ '
|
|
fi
|
|
|
|
(( INTERACTIVE )) || return 0
|
|
|
|
# ---- run-once guard --------------------------------------------------------
|
|
|
|
# Installed as both loginShellInit (/etc/profile) and interactiveShellInit
|
|
# (/etc/bashrc), and /etc/profile sources /etc/bashrc, so a login shell would run
|
|
# this twice and register starship's hooks twice. Not exported: child shells must
|
|
# initialise on their own.
|
|
if [[ -n ${NANUQSAURUS_SHELL_INIT_DONE:-} ]]; then
|
|
log_shell_init "already initialised, skipping"
|
|
return 0
|
|
fi
|
|
NANUQSAURUS_SHELL_INIT_DONE=1
|
|
|
|
# ---- bash-preexec ----------------------------------------------------------
|
|
|
|
if [[ -n ${BASH_VERSION:-} && -z ${bash_preexec_imported:-} && -r "@BASHPREEXEC@" ]]; then
|
|
# shellcheck disable=SC1090
|
|
. "@BASHPREEXEC@"
|
|
log_shell_init "bash-preexec loaded"
|
|
fi
|
|
|
|
# bash-preexec arms its DEBUG trap from __bp_interactive_mode, which it appends to
|
|
# the END of PROMPT_COMMAND -- but its lazy installer runs in the MIDDLE of the
|
|
# first prompt, because zoxide (below) and ghostty (~/.bashrc) append their hooks
|
|
# after it. Those hooks then disarm the trap again, so the first command of every
|
|
# session never reaches preexec and atuin never records it. PS0 is expanded once
|
|
# per interactive command, before the DEBUG trap, so arming there is independent of
|
|
# PROMPT_COMMAND ordering. Needs the non-forking ${ ...; } funsub (bash >= 5.3);
|
|
# older bash keeps the previous behaviour.
|
|
if [[ -n ${bash_preexec_imported:-} ]] &&
|
|
((BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 3))); then
|
|
# shellcheck disable=SC2016
|
|
__nanuq_bp_arm='${ __bp_preexec_interactive_mode=on; }'
|
|
[[ ${PS0-} == *"$__nanuq_bp_arm"* ]] || PS0=$__nanuq_bp_arm${PS0-}
|
|
unset __nanuq_bp_arm
|
|
log_shell_init "bash-preexec PS0 arming enabled"
|
|
fi
|
|
|
|
# ---- starship --------------------------------------------------------------
|
|
|
|
if have @STARSHIP@; then
|
|
log_shell_init "starship init"
|
|
case $SHELL in
|
|
*/bash) eval "$(@STARSHIP@ init bash)" ;;
|
|
*/zsh) eval "$(@STARSHIP@ init zsh)" ;;
|
|
esac
|
|
# registers starship_precmd/starship_preexec_all with bash-preexec directly
|
|
else
|
|
log_shell_init "starship skipped"
|
|
fi
|
|
|
|
# ---- atuin -----------------------------------------------------------------
|
|
|
|
if have @ATUIN@; then
|
|
log_shell_init "atuin init"
|
|
|
|
if [[ -z ${ATUIN_SESSION:-} ]]; then
|
|
export ATUIN_SESSION="$(@ATUIN@ uuid)"
|
|
ATUIN_HISTORY_ID=""
|
|
fi
|
|
|
|
case $SHELL in
|
|
*/bash) eval "$(@ATUIN@ init bash)" ;;
|
|
*/zsh) eval "$(@ATUIN@ init zsh)" ;;
|
|
esac
|
|
# registers __atuin_preexec/__atuin_precmd with bash-preexec itself
|
|
else
|
|
log_shell_init "atuin skipped"
|
|
fi
|
|
|
|
# ---- zoxide ---------------------------------------------------------------
|
|
|
|
if have zoxide; then
|
|
log_shell_init "zoxide init"
|
|
case $SHELL in
|
|
*/bash) eval "$(zoxide init bash)" ;;
|
|
*/zsh) eval "$(zoxide init zsh)" ;;
|
|
esac
|
|
# appends __zoxide_hook to PROMPT_COMMAND; see the PS0 note above
|
|
else
|
|
log_shell_init "zoxide skipped"
|
|
fi
|
|
|
|
# ---- aliases --------------------------------------------------------------
|
|
|
|
if have eza; then
|
|
alias ls='eza'
|
|
alias ll='eza -l --icons=auto --group-directories-first'
|
|
alias l1='eza -1'
|
|
alias l.='eza -d .*'
|
|
fi
|
|
|
|
if have ug; then
|
|
alias grep='ug'
|
|
alias egrep='ug -E'
|
|
alias fgrep='ug -F'
|
|
alias xzgrep='ug -z'
|
|
alias xzegrep='ug -zE'
|
|
alias xzfgrep='ug -zF'
|
|
fi
|
|
|
|
if have bat; then
|
|
alias cat='bat --style=plain --pager=never' 2>/dev/null
|
|
fi
|