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.
142 lines
6.8 KiB
Bash
Executable File
142 lines
6.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
TEMP_DIR=$(mktemp -d)
|
|
trap 'rm -rf "$TEMP_DIR"' EXIT
|
|
|
|
fail() {
|
|
echo "FAIL: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
assert_download_failure_is_reported() {
|
|
local home="$TEMP_DIR/home-${1%.sh}"
|
|
mkdir -p "$home"
|
|
|
|
if HOME="$home" PATH="$TEMP_DIR/bin:/usr/bin:/bin" /bin/bash "$ROOT/$1" >"$home/output" 2>&1; then
|
|
fail "$1 returned success after curl failed"
|
|
fi
|
|
}
|
|
|
|
mkdir -p "$TEMP_DIR/bin"
|
|
ln -s /usr/bin/false "$TEMP_DIR/bin/curl"
|
|
ln -s /usr/bin/true "$TEMP_DIR/bin/unzip"
|
|
|
|
for script in install_fnm.sh install_nvm.sh install_pyenv.sh install_merlin_worker.sh; do
|
|
assert_download_failure_is_reported "$script"
|
|
done
|
|
|
|
patch_home="$TEMP_DIR/patch-home"
|
|
mkdir -p "$patch_home/.config/zshrc"
|
|
cp "$ROOT/README.md" "$patch_home/.config/zshrc/missing.zsh"
|
|
if HOME="$patch_home" PATH="$TEMP_DIR/bin:/usr/bin:/bin" /bin/bash "$ROOT/patch.sh" missing >/dev/null 2>&1; then
|
|
fail "patch.sh returned success after curl failed"
|
|
fi
|
|
cmp -s "$ROOT/README.md" "$patch_home/.config/zshrc/missing.zsh" ||
|
|
fail "patch.sh replaced an existing config after curl failed"
|
|
|
|
if HOME="$patch_home" PATH="$TEMP_DIR/bin:/usr/bin:/bin" /bin/bash "$ROOT/patch.sh" ../invalid >/dev/null 2>&1; then
|
|
fail "patch.sh accepted an unsafe config name"
|
|
fi
|
|
|
|
main_bin="$TEMP_DIR/main-bin"
|
|
main_home="$TEMP_DIR/main-home"
|
|
mkdir -p "$main_bin" "$main_home"
|
|
for command_name in zsh git curl tar fc-cache eza zoxide autoconf trash install; do
|
|
ln -s /usr/bin/true "$main_bin/$command_name"
|
|
done
|
|
# real install binary needed by deploy.sh
|
|
ln -sf "$(command -v install)" "$main_bin/install"
|
|
ln -sf "$(command -v cp)" "$main_bin/cp"
|
|
ln -sf "$(command -v mkdir)" "$main_bin/mkdir"
|
|
ln -sf "$(command -v chmod)" "$main_bin/chmod"
|
|
ln -sf "$(command -v ln)" "$main_bin/ln"
|
|
ln -sf "$(command -v rm)" "$main_bin/rm"
|
|
ln -sf "$(command -v bash)" "$main_bin/bash"
|
|
ln -sf "$(command -v mktemp)" "$main_bin/mktemp"
|
|
ln -sf "$(command -v dirname)" "$main_bin/dirname"
|
|
ln -sf "$(command -v date)" "$main_bin/date"
|
|
ln -sf "$(command -v sed)" "$main_bin/sed"
|
|
ln -sf "$(command -v grep)" "$main_bin/grep"
|
|
ln -sf "$(command -v awk)" "$main_bin/awk"
|
|
ln -sf "$(command -v compgen)" "$main_bin/compgen" 2>/dev/null || true
|
|
ln -s /usr/bin/false "$main_bin/wget"
|
|
cp "$ROOT/README.md" "$main_home/.zshrc"
|
|
printf '%s\n' 'existing-zshenv' >"$main_home/.zshenv"
|
|
if HOME="$main_home" PATH="$main_bin:/usr/bin:/bin" /bin/bash "$ROOT/install.sh" --non-interactive >/dev/null 2>&1; then
|
|
fail "install.sh returned success after the zshrc download failed"
|
|
fi
|
|
cmp -s "$ROOT/README.md" "$main_home/.zshrc" ||
|
|
fail "install.sh replaced .zshrc after its download failed"
|
|
printf '%s\n' 'existing-zshenv' | cmp -s - "$main_home/.zshenv" ||
|
|
fail "install.sh replaced .zshenv after a download failed"
|
|
|
|
fnm_home="$TEMP_DIR/fnm-home"
|
|
mkdir -p "$fnm_home/.local/share/fnm"
|
|
ln -s /usr/bin/true "$fnm_home/.local/share/fnm/fnm"
|
|
HOME="$fnm_home" PATH=/usr/bin:/bin zsh -c 'source "$1"; command -v fnm >/dev/null' zsh "$ROOT/zshrc/fnm.zsh" ||
|
|
fail "fnm is not restored to PATH in a new Zsh session"
|
|
|
|
for script in "$ROOT"/*.sh; do
|
|
bash -n "$script"
|
|
done
|
|
|
|
for config in "$ROOT/.zshenv" "$ROOT/.zshrc" "$ROOT/.zprofile" \
|
|
"$ROOT"/config/env/*.zsh \
|
|
"$ROOT"/config/interactive/*.zsh \
|
|
"$ROOT"/zshrc/*.zsh; do
|
|
[[ -f "$config" ]] || continue
|
|
zsh -n "$config"
|
|
done
|
|
|
|
grep -q 'NVM_VERSION="v0.40.4"' "$ROOT/install_nvm.sh" || fail "nvm version is stale"
|
|
grep -q 'apt-get install -y unzip' "$ROOT/install_fnm.sh" || fail "Ubuntu unzip installation is missing"
|
|
grep -q 'tail -1 || true' "$ROOT/install_merlin_worker.sh" || fail "a missing PYTHONPATH remains fatal"
|
|
grep -Fq 'mktemp "$HOME/.zshrc.download.' "$ROOT/install.sh" || fail ".zshrc is not downloaded on its target filesystem"
|
|
grep -Fq 'mktemp "$HOME/.zshenv.download.' "$ROOT/install.sh" || fail ".zshenv is not downloaded on its target filesystem"
|
|
grep -Fq 'mktemp "$HOME/.config/.easyzsh-' "$ROOT/patch.sh" || fail "patch downloads can be sourced while incomplete"
|
|
grep -Fq '"$ZSH_CONFIGS_DIR"/*(DN)' "$ROOT/config/interactive/10-bootstrap.zsh" || fail "existing extensionless Zsh configs are ignored"
|
|
grep -Fq '_resolve_trash_cmd' "$ROOT/config/interactive/20-post-omz.zsh" || fail "trash-backed rm helper is missing"
|
|
grep -Fq 'libglib2.0-bin' "$ROOT/install.sh" || fail "Ubuntu Trash support is not installed"
|
|
grep -Fq 'deploy.sh' "$ROOT/install.sh" || fail "install.sh does not invoke deploy.sh"
|
|
grep -Fq 'brew shellenv' "$ROOT/config/env/00-path.zsh" || fail "Homebrew is not initialized in env path module"
|
|
grep -Fq '/usr/local/bin/brew' "$ROOT/config/env/00-path.zsh" || fail "Intel Homebrew path is missing from env path module"
|
|
grep -Fq '${HOME}/.local/bin' "$ROOT/config/env/00-path.zsh" || fail "~/.local/bin is not added in env path module"
|
|
grep -Fq 'env.local.zsh' "$ROOT/.zshenv" || fail "machine-local env.local.zsh hook is missing"
|
|
grep -Fq 'login.local.zsh' "$ROOT/.zprofile" || fail "machine-local login.local.zsh hook is missing"
|
|
if grep -Fq 'brew shellenv' "$ROOT/.zshrc" "$ROOT/config/interactive"/*.zsh; then
|
|
fail "Homebrew still initializes from interactive-only files"
|
|
fi
|
|
[[ ! -e "$ROOT/install/fnm.sh" ]] || fail "vendored fnm installer still exists"
|
|
if grep -Eq '^[[:space:]]*export MLX_USER_TOKEN=' "$ROOT"/zshrc/*.zsh; then
|
|
fail "a static MLX user token remains"
|
|
fi
|
|
|
|
# deploy.sh installs managed layout and preserves local/
|
|
deploy_home="$TEMP_DIR/deploy-home"
|
|
mkdir -p "$deploy_home/.config/zsh/local" "$deploy_home/.local/bin" "$deploy_home/.grok/bin"
|
|
printf '%s\n' 'machine-only' >"$deploy_home/.config/zsh/local/env.local.zsh"
|
|
printf '%s\n' '#!/bin/sh' 'echo grok-ok' >"$deploy_home/.grok/bin/grok"
|
|
chmod +x "$deploy_home/.grok/bin/grok"
|
|
HOME="$deploy_home" bash "$ROOT/deploy.sh" --home "$deploy_home"
|
|
[[ -f "$deploy_home/.zshenv" ]] || fail "deploy.sh did not install .zshenv"
|
|
[[ -f "$deploy_home/.config/zsh/env/00-path.zsh" ]] || fail "deploy.sh did not install env modules"
|
|
[[ -f "$deploy_home/.config/zsh/interactive/10-bootstrap.zsh" ]] || fail "deploy.sh did not install interactive modules"
|
|
grep -Fxq 'machine-only' "$deploy_home/.config/zsh/local/env.local.zsh" ||
|
|
fail "deploy.sh overwrote local/env.local.zsh"
|
|
[[ -L "$deploy_home/.local/bin/grok" ]] || fail "deploy.sh did not create grok shim"
|
|
|
|
# Non-interactive zsh must resolve ~/.local/bin without sourcing .zshrc.
|
|
local_bin_home="$TEMP_DIR/local-bin-home"
|
|
mkdir -p "$local_bin_home/.local/bin"
|
|
printf '%s\n' '#!/bin/sh' 'echo ok' >"$local_bin_home/.local/bin/easyzsh-path-probe"
|
|
chmod +x "$local_bin_home/.local/bin/easyzsh-path-probe"
|
|
HOME="$local_bin_home" bash "$ROOT/deploy.sh" --home "$local_bin_home" >/dev/null
|
|
HOME="$local_bin_home" PATH=/usr/bin:/bin \
|
|
zsh -c '. ~/.zshenv; command -v easyzsh-path-probe >/dev/null' ||
|
|
fail "~/.local/bin is not available to non-interactive zsh via .zshenv"
|
|
|
|
echo "installer checks passed"
|