Files
easyzsh/deploy.sh
T
Jeffrey f8dcc29a2f refactor(shell): thin stubs with managed ~/.config/zsh tree
Split always-on PATH/env, login hooks, and interactive UX into a
managed tree under ~/.config/zsh, with never-overwrite local/ hooks.
Agents and interactive shells share one PATH owner; deploy.sh applies
the layout safely across machines.
2026-08-06 14:36:08 +08:00

133 lines
4.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Install managed easyzsh shell layout into a home directory.
# Overwrites: ~/.zshenv ~/.zshrc ~/.zprofile ~/.config/zsh/{env,interactive,login}
# Never overwrites: ~/.config/zsh/local/** ~/.config/zshrc/** ~/.local/bin/**
#
# Usage:
# ./deploy.sh
# ./deploy.sh --home /path/to/home
# ./deploy.sh --migrate-zprofile # move existing ~/.zprofile body into login.local
set -euo pipefail
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
TARGET_HOME="${HOME}"
MIGRATE_ZPROFILE=0
while [[ $# -gt 0 ]]; do
case "$1" in
--home)
TARGET_HOME="$2"
shift 2
;;
--migrate-zprofile)
MIGRATE_ZPROFILE=1
shift
;;
-h|--help)
sed -n '2,12p' "$0"
exit 0
;;
*)
echo "Unknown argument: $1" >&2
exit 2
;;
esac
done
if [[ ! -d "$TARGET_HOME" ]]; then
echo "Home does not exist: $TARGET_HOME" >&2
exit 1
fi
ZSH_CFG="${TARGET_HOME}/.config/zsh"
ts=$(date +%Y%m%d-%H%M%S)
backup_file() {
local f="$1"
if [[ -e "$f" || -L "$f" ]]; then
cp -Pp "$f" "${f}.backup.${ts}"
echo "Backed up $f -> ${f}.backup.${ts}"
fi
}
echo "Deploying easyzsh managed shell layout into $TARGET_HOME"
mkdir -p \
"${ZSH_CFG}/env" \
"${ZSH_CFG}/interactive" \
"${ZSH_CFG}/login" \
"${ZSH_CFG}/local" \
"${TARGET_HOME}/.config/zshrc" \
"${TARGET_HOME}/.cache/zsh" \
"${TARGET_HOME}/.local/bin"
# Migrate legacy early env if present and new path missing.
if [[ -f "${TARGET_HOME}/.config/zshenv.local" && ! -f "${ZSH_CFG}/local/env.local.zsh" ]]; then
cp -Pp "${TARGET_HOME}/.config/zshenv.local" "${ZSH_CFG}/local/env.local.zsh"
echo "Migrated ~/.config/zshenv.local -> ~/.config/zsh/local/env.local.zsh"
fi
# Optionally migrate existing zprofile body into login.local (strip redundant PATH).
if [[ "$MIGRATE_ZPROFILE" -eq 1 ]]; then
if [[ -f "${TARGET_HOME}/.zprofile" && ! -f "${ZSH_CFG}/local/login.local.zsh" ]]; then
# Drop easyzsh stub lines and pipx ~/.local/bin PATH (now owned by env/00-path.zsh).
awk '
/^# easyzsh stub/ { next }
/^EASYZSH_HOME=/ { next }
/for _easyzsh_f in/ { skip=1 }
skip && /unset _easyzsh_f/ { skip=0; next }
skip { next }
/login\.local\.zsh/ { next }
/Created by `pipx`/ { skip_pipx=1; next }
skip_pipx && /local\/bin/ { skip_pipx=0; next }
skip_pipx { next }
{ print }
' "${TARGET_HOME}/.zprofile" > "${ZSH_CFG}/local/login.local.zsh"
# Trim trailing blank lines noise if file became empty-ish
if ! grep -q '[^[:space:]]' "${ZSH_CFG}/local/login.local.zsh"; then
rm -f "${ZSH_CFG}/local/login.local.zsh"
echo "Existing .zprofile had no login-local content after migration"
else
echo "Migrated ~/.zprofile body -> ~/.config/zsh/local/login.local.zsh"
fi
fi
fi
backup_file "${TARGET_HOME}/.zshenv"
backup_file "${TARGET_HOME}/.zshrc"
backup_file "${TARGET_HOME}/.zprofile"
install -m 644 "${ROOT}/.zshenv" "${TARGET_HOME}/.zshenv"
install -m 644 "${ROOT}/.zshrc" "${TARGET_HOME}/.zshrc"
install -m 644 "${ROOT}/.zprofile" "${TARGET_HOME}/.zprofile"
# Replace managed modules only (never touch local/).
rm -f "${ZSH_CFG}/env"/*.zsh
rm -f "${ZSH_CFG}/interactive"/*.zsh
rm -f "${ZSH_CFG}/login"/*.zsh
install -m 644 "${ROOT}/config/env/"*.zsh "${ZSH_CFG}/env/"
install -m 644 "${ROOT}/config/interactive/"*.zsh "${ZSH_CFG}/interactive/"
# login may be empty of modules
if compgen -G "${ROOT}/config/login/"*.zsh > /dev/null; then
install -m 644 "${ROOT}/config/login/"*.zsh "${ZSH_CFG}/login/"
fi
# Seed examples only when missing (never overwrite user locals).
for example in env.local.zsh login.local.zsh interactive.local.zsh; do
src="${ROOT}/local/${example}.example"
dst="${ZSH_CFG}/local/${example}.example"
if [[ -f "$src" ]]; then
install -m 644 "$src" "$dst"
fi
done
# Ensure intentional grok shim when the binary tree exists.
if [[ -e "${TARGET_HOME}/.grok/bin/grok" ]]; then
ln -sfn "${TARGET_HOME}/.grok/bin/grok" "${TARGET_HOME}/.local/bin/grok"
echo "Ensured ~/.local/bin/grok -> ~/.grok/bin/grok"
fi
echo "Deploy complete."
echo "Verify: env -i HOME=\"$TARGET_HOME\" PATH=/usr/bin:/bin zsh -c '. ~/.zshenv; command -v grok; echo PATH=\$PATH'"