[ZSH] Extract shared startup modules

Keep the server entrypoint focused on server policy while moving update checks, Antidote loading, interactive defaults, and prompt sourcing into reusable modules.
This commit is contained in:
Fabian Ising
2026-07-27 10:55:58 +02:00
parent 30d1e8c364
commit a44f109dc7
5 changed files with 142 additions and 187 deletions
+23 -187
View File
@@ -1,18 +1,16 @@
export PATH="/snap/bin:$HOME/.local/bin:$HOME/dotfiles/scripts:$PATH"
export XDG_CONFIG_HOME="$HOME/.config" # Fix nvim bug
export XDG_CONFIG_HOME="$HOME/.config"
[[ -z $SSH_AUTH_SOCK ]] || export FORWARD_SOCK=$SSH_AUTH_SOCK
[[ -f ~/.zsh/.powerline_config ]] && source ~/.zsh/.powerline_config
if [[ -f ~/.zsh/.shared_config ]] && [[ "$SUDO_USER" != "fabian" ]] ; then
if [[ -f ~/.zsh/.shared_config && "$SUDO_USER" != "fabian" ]]; then
shared_config=1
else
shared_config=0
fi
# Workaround for HOME directory in sudo nvim (caused by snap)
# Work around snap's HOME handling for root.
if [[ "$USER" == "root" ]]; then
if [[ ! -e /home/root ]]; then
ln -s /root/ /home/root
@@ -20,214 +18,52 @@ if [[ "$USER" == "root" ]]; then
ln -s /root/.config /home/root/.config
fi
fi
[[ -f ~/.zsh/.shared_config ]] && touch ~/.config/nvim/.shared_config
if [[ -f ~/.zsh/.shared_config ]] ; then
touch ~/.config/nvim/.shared_config
fi
source "${ZDOTDIR:-$HOME}/.zsh/lib/updates.zsh"
# On shell start, bring ~/dotfiles up to date by fast-forwarding the CURRENT
# branch to its upstream only. Never rebase or stash in the background: if the
# branch diverged or local edits block it, warn and leave it for a manual pull.
# Per-branch, so `server` on each device fast-forwards to origin/server.
update_dotfiles() {
local repo="$HOME/dotfiles"
local cache_dir="${ZDOTDIR:-$HOME}/.cache/zsh"
local notice_file="$cache_dir/dotfiles-update-notice"
local notice_tmp="$notice_file.$$.tmp"
# Bound the network call so a stalled fetch can't wedge this backgrounded
# (&!) job into a days-long orphan holding unreaped children. `timeout` is
# absent on macOS, so only use it when present. Disable ssh multiplexing so
# no daemonized mux master can inherit and keep our fds open past git's exit.
local -a TO; (( $+commands[timeout] )) && TO=(timeout -k 5 15)
GIT_SSH_COMMAND='ssh -o BatchMode=yes -o ConnectTimeout=5 -o ControlMaster=no -o ControlPath=none' \
$TO git -C "$repo" fetch --quiet </dev/null 2>/dev/null || return
git -C "$repo" rev-parse --abbrev-ref '@{u}' >/dev/null 2>&1 || return # no upstream
git -C "$repo" merge-base --is-ancestor '@{u}' HEAD 2>/dev/null && return # already current
git -C "$repo" merge --ff-only --quiet '@{u}' 2>/dev/null && return
local notice="⚠ dotfiles: $(git -C "$repo" symbolic-ref --short HEAD) can't fast-forward to @{u} — run: git -C ~/dotfiles pull"
mkdir -p "$cache_dir" || return
print -r -- "$notice" >| "$notice_tmp" || return
mv -f "$notice_tmp" "$notice_file"
}
update_dotfiles &!
if [ "$shared_config" -eq 0 ]; then
if (( ! shared_config )); then
export GIT_AUTHOR_NAME="Fabian Ising"
export GIT_AUTHOR_EMAIL="f.ising@fh-muenster.de"
export GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
export GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL
fi
# On shell start, check the security tools repo for upstream updates and queue a
# one-line notice if the checkout is behind. A precmd hook prints it between
# commands, rather than letting this background job write across other startup
# output or a prompt that is already being edited. Notify only (never auto-pull).
# The check stays read-only so it works even in root-owned /opt/tools:
# - `safe.directory=*` sidesteps git's "dubious ownership" refusal on a repo
# owned by root, which would otherwise fail every git command.
# - `ls-remote` queries the remote without writing into .git (a normal user
# can't write a root-owned .git, so `fetch` would fail there).
# Uses the first candidate that is a git checkout; covers global and per-user layouts.
check_tools_repo() {
local repo branch remote mergeref remote_sha local_sha cmd notice
local cache_dir="${ZDOTDIR:-$HOME}/.cache/zsh"
local notice_file="$cache_dir/tools-update-notice"
local notice_tmp="$notice_file.$$.tmp"
# See update_dotfiles: bound the network probe (timeout, absent on macOS) and
# disable ssh multiplexing so a stalled ls-remote can't orphan this &! job.
local -a TO; (( $+commands[timeout] )) && TO=(timeout -k 5 10)
for repo in /opt/tools "$HOME/tools" "$HOME/tools/tools-repo"; do
local -a g=(git -c 'safe.directory=*' -C "$repo")
$g rev-parse --is-inside-work-tree >/dev/null 2>&1 || continue
branch=$($g symbolic-ref --short HEAD 2>/dev/null) || return
remote=$($g config "branch.$branch.remote" 2>/dev/null) || return # no upstream
mergeref=$($g config "branch.$branch.merge" 2>/dev/null) || return
remote_sha=$(GIT_SSH_COMMAND='ssh -o BatchMode=yes -o ConnectTimeout=5 -o ControlMaster=no -o ControlPath=none' \
$TO $g ls-remote "$remote" "$mergeref" </dev/null 2>/dev/null | awk '{print $1}')
local_sha=$($g rev-parse HEAD 2>/dev/null)
[[ -n "$remote_sha" && -n "$local_sha" ]] || return # remote unreachable
[[ "$remote_sha" == "$local_sha" ]] && return # already current
$g merge-base --is-ancestor "$remote_sha" HEAD 2>/dev/null && return # remote is an ancestor: ahead/equal (also true if we lack the object -> falls through to "behind")
[[ -w "$repo/.git" ]] && cmd="git -C $repo pull" || cmd="sudo git -C $repo pull"
notice="⬆ tools: updates available in $repo — run: $cmd"
mkdir -p "$cache_dir" || return
print -r -- "$notice" >| "$notice_tmp" || return
mv -f "$notice_tmp" "$notice_file"
return
done
}
check_tools_repo &!
show_update_notices() {
local cache_dir="${ZDOTDIR:-$HOME}/.cache/zsh"
local notice_file claimed_notice
for notice_file in "$cache_dir"/{dotfiles,tools}-update-notice; do
[[ -s "$notice_file" ]] || continue
claimed_notice="$notice_file.$$.show"
mv "$notice_file" "$claimed_notice" 2>/dev/null || continue
command cat "$claimed_notice"
command rm -f "$claimed_notice"
done
}
autoload -Uz add-zsh-hook
add-zsh-hook precmd show_update_notices
# Load Antidote
mkdir -p ${ZDOTDIR:-~}/.cache/zsh
static_file=${ZDOTDIR:-~}/.cache/zsh/.zsh_plugins.zsh
if [ $shared_config -eq 0 ]; then
plugins_txt=${ZDOTDIR:-~}/.zsh/.zsh_plugins.txt
# Vi mode
static_file=${ZDOTDIR:-$HOME}/.cache/zsh/.zsh_plugins.zsh
if (( ! shared_config )); then
plugins_txt=${ZDOTDIR:-$HOME}/.zsh/.zsh_plugins.txt
bindkey -v
VI_MODE_SET_CURSOR=true
else
plugins_txt=${ZDOTDIR:-~}/.zsh/.zsh_plugins_shared.txt
static_file=${ZDOTDIR:-~}/.cache/zsh/.zsh_shared_plugins.zsh
fi
# clone antidote if necessary
if ! [[ -e ${ZDOTDIR:-~}/.antidote ]]; then
git clone https://github.com/mattmc3/antidote.git ${ZDOTDIR:-~}/.antidote
plugins_txt=${ZDOTDIR:-$HOME}/.zsh/.zsh_plugins_shared.txt
static_file=${ZDOTDIR:-$HOME}/.cache/zsh/.zsh_shared_plugins.zsh
fi
source "${ZDOTDIR:-$HOME}/.zsh/lib/antidote.zsh"
#zstyle ':omz:plugins:docker' legacy-completion yes
zstyle ':completion:*:ssh:*' hosts off
zstyle ':completion:*:scp:*' hosts off
# Run rehash for external commands
zstyle ":completion:*:commands" rehash 1
# source antidote and load plugins from `${ZDOTDIR:-~}/.zsh_plugins.txt`
source ${ZDOTDIR:-~}/.antidote/antidote.zsh
antidote load ${plugins_txt} ${static_file}
export SSH_REAL_SOCK=$SSH_AUTH_SOCK
[[ -z $FORWARD_SOCK ]] || export SSH_AUTH_SOCK=$FORWARD_SOCK
setopt interactivecomments
source "${ZDOTDIR:-$HOME}/.zsh/lib/interactive.zsh"
# History options
HISTSIZE=100000 # Set the amount of lines you want saved
SAVEHIST=100000 # This is required to actually save them, needs to match with HISTSIZE
setopt EXTENDED_HISTORY # Write the history file in the ":start:elapsed;command" format.
setopt INC_APPEND_HISTORY # Write to the history file immediately, not when the shell exits.
setopt SHARE_HISTORY # Share history between all sessions.
setopt HIST_EXPIRE_DUPS_FIRST # Expire duplicate entries first when trimming history.
setopt HIST_IGNORE_DUPS # Don\'t record an entry that was just recorded again.
setopt HIST_IGNORE_ALL_DUPS # Delete old recorded entry if new entry is a duplicate.
setopt HIST_FIND_NO_DUPS # Do not display a line previously found.
setopt HIST_IGNORE_SPACE # Don\'t record an entry starting with a space.
setopt HIST_SAVE_NO_DUPS # Don\'t write duplicate entries in the history file.
setopt HIST_REDUCE_BLANKS # Remove superfluous blanks before recording entry.
# Clear screen by ctrl+q
bindkey '^q' clear-screen
(( $+commands[nvim] )) && alias vim=nvim
alias sudo='sudo '
alias cgrep="grep --color=always"
alias cdiff="git diff --color-words --no-index"
(( $+commands[nvim] )) && export EDITOR='nvim'
(( $+commands[nvim] )) && export VISUAL='nvim'
# Allow access to all libvirt vms
export LIBVIRT_DEFAULT_URI="qemu:///system"
alias ls="ls --color=always"
if [ "$shared_config" -eq 0 ]; then
# Use safecp/safemv from the tools-repo when available
(( $+commands[safecp] )) && alias cp=safecp
(( $+commands[safemv] )) && alias mv=safemv
(( $+commands[safescp] )) && alias scp=safescp
if (( ! shared_config )); then
(( $+commands[safecp] )) && alias cp=safecp
(( $+commands[safemv] )) && alias mv=safemv
(( $+commands[safescp] )) && alias scp=safescp
fi
delzip() {
unzip -Z -1 "$@" | xargs -I{} rm -rf {}
}
# mitmproxy
export MITMPROXY_SSLKEYLOGFILE="~/.mitmproxy/sslkeylogfile.txt"
[[ -f ~/.zsh/.mac_config ]] && source ~/.zsh/.mac_config
# Workaround for async issues https://github.com/romkatv/powerlevel10k/issues/1554
unset ZSH_AUTOSUGGEST_USE_ASYNC
# Powerlevel 10k
# Remove padding on right side
ZLE_RPROMPT_INDENT=0
# To customize prompt, run `p10k configure` or edit ~/dotfiles/zsh/.p10k.zsh.
function load_p10k() {
if zmodload zsh/terminfo && (( terminfo[colors] >= 256 )) && [ $shared_config -eq 0 ]; then
[[ ! -f ~/dotfiles/zsh/.p10k.zsh ]] || source ~/dotfiles/zsh/.p10k.zsh
else
[[ ! -f ~/dotfiles/zsh/.p10k_shared.zsh ]] || source ~/dotfiles/zsh/.p10k_shared.zsh
fi
}
load_p10k
#
# This speeds up pasting w/ autosuggest
# https://github.com/zsh-users/zsh-autosuggestions/issues/238
pasteinit() {
OLD_SELF_INSERT=${${(s.:.)widgets[self-insert]}[2,3]}
zle -N self-insert url-quote-magic # I wonder if you'd need `.url-quote-magic`?
}
pastefinish() {
zle -N self-insert $OLD_SELF_INSERT
}
zstyle :bracketed-paste-magic paste-init pasteinit
zstyle :bracketed-paste-magic paste-finish pastefinish
# https://github.com/zsh-users/zsh-autosuggestions/issues/351
ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=(bracketed-paste)
export LANG="en_US.UTF-8"
export LC_CTYPE="en_US.UTF-8"
export TIME_STYLE="long-iso"
if zmodload zsh/terminfo && (( terminfo[colors] >= 256 )) && (( ! shared_config )); then
p10k_config=~/dotfiles/zsh/.p10k.zsh
else
p10k_config=~/dotfiles/zsh/.p10k_shared.zsh
fi
source "${ZDOTDIR:-$HOME}/.zsh/lib/prompt.zsh"
[[ -f ~/.zsh/.user_config ]] && source ~/.zsh/.user_config
[[ -f ~/.zsh/.virtual_env_config.zsh ]] && source ~/.zsh/.virtual_env_config.zsh
[[ -f ~/.zsh/.local_config ]] && source ~/.zsh/.local_config
[[ -f ~/.zsh/.os_config.zsh ]] && source ~/.zsh/.os_config.zsh
# Remove duplicate entries added by inherited and machine-specific PATH config
# while preserving the first occurrence and therefore command precedence.
typeset -U path PATH