fix(shell): use single ~/.config/zsh/local.zsh for machine local

Replace the local/ directory of split env/login/interactive files with
one always-on local.zsh sourced from .zshenv. Deploy migrates old
fragments and never overwrites local.zsh.
This commit is contained in:
Jeffrey
2026-08-06 14:41:31 +08:00
parent f8dcc29a2f
commit caac9235f6
11 changed files with 99 additions and 81 deletions
+1 -6
View File
@@ -1,5 +1,5 @@
# easyzsh stub: login shells only. Not the PATH source of truth (see env/00-path.zsh). # easyzsh stub: login shells only. Not the PATH source of truth (see env/00-path.zsh).
# Put OS/vendor login hooks in ~/.config/zsh/local/login.local.zsh. # Machine-local overrides live in ~/.config/zsh/local.zsh (loaded from .zshenv).
EASYZSH_HOME="${XDG_CONFIG_HOME:-$HOME/.config}/zsh" EASYZSH_HOME="${XDG_CONFIG_HOME:-$HOME/.config}/zsh"
if [[ -d "$EASYZSH_HOME/login" ]]; then if [[ -d "$EASYZSH_HOME/login" ]]; then
@@ -9,8 +9,3 @@ if [[ -d "$EASYZSH_HOME/login" ]]; then
done done
unset _easyzsh_f unset _easyzsh_f
fi fi
if [[ -f "$EASYZSH_HOME/local/login.local.zsh" ]]; then
# shellcheck disable=SC1090
. "$EASYZSH_HOME/local/login.local.zsh"
fi
+3 -3
View File
@@ -1,5 +1,5 @@
# easyzsh stub: every zsh loads this. Keep it thin; logic lives under ~/.config/zsh/. # easyzsh stub: every zsh loads this. Keep it thin; logic lives under ~/.config/zsh/.
# Machine-local early env: ~/.config/zsh/local/env.local.zsh (never overwritten). # Machine-local overrides: ~/.config/zsh/local.zsh (never overwritten by deploy).
EASYZSH_HOME="${XDG_CONFIG_HOME:-$HOME/.config}/zsh" EASYZSH_HOME="${XDG_CONFIG_HOME:-$HOME/.config}/zsh"
if [[ -d "$EASYZSH_HOME/env" ]]; then if [[ -d "$EASYZSH_HOME/env" ]]; then
@@ -10,9 +10,9 @@ if [[ -d "$EASYZSH_HOME/env" ]]; then
unset _easyzsh_f unset _easyzsh_f
fi fi
if [[ -f "$EASYZSH_HOME/local/env.local.zsh" ]]; then if [[ -f "$EASYZSH_HOME/local.zsh" ]]; then
# shellcheck disable=SC1090 # shellcheck disable=SC1090
. "$EASYZSH_HOME/local/env.local.zsh" . "$EASYZSH_HOME/local.zsh"
elif [[ -f "$HOME/.config/zshenv.local" ]]; then elif [[ -f "$HOME/.config/zshenv.local" ]]; then
# Legacy path from earlier easyzsh layouts. # Legacy path from earlier easyzsh layouts.
# shellcheck disable=SC1090 # shellcheck disable=SC1090
+1 -6
View File
@@ -1,6 +1,6 @@
# easyzsh stub: interactive shells only. Keep it thin; logic lives under ~/.config/zsh/. # easyzsh stub: interactive shells only. Keep it thin; logic lives under ~/.config/zsh/.
# Personal interactive overlays: ~/.config/zshrc/*.zsh (before oh-my-zsh). # Personal interactive overlays: ~/.config/zshrc/*.zsh (before oh-my-zsh).
# Machine-local interactive: ~/.config/zsh/local/interactive.local.zsh (after omz). # Machine-local overrides: ~/.config/zsh/local.zsh (loaded from .zshenv for every zsh).
EASYZSH_HOME="${XDG_CONFIG_HOME:-$HOME/.config}/zsh" EASYZSH_HOME="${XDG_CONFIG_HOME:-$HOME/.config}/zsh"
if [[ -d "$EASYZSH_HOME/interactive" ]]; then if [[ -d "$EASYZSH_HOME/interactive" ]]; then
@@ -10,8 +10,3 @@ if [[ -d "$EASYZSH_HOME/interactive" ]]; then
done done
unset _easyzsh_f unset _easyzsh_f
fi fi
if [[ -f "$EASYZSH_HOME/local/interactive.local.zsh" ]]; then
# shellcheck disable=SC1090
source "$EASYZSH_HOME/local/interactive.local.zsh"
fi
+6 -13
View File
@@ -27,8 +27,7 @@ every zsh login only interactive only
────────── ────────── ──────────────── ────────── ────────── ────────────────
~/.zshenv (stub) ~/.zprofile (stub) ~/.zshrc (stub) ~/.zshenv (stub) ~/.zprofile (stub) ~/.zshrc (stub)
→ config/zsh/env/* → config/zsh/login/* → config/zsh/interactive/* → config/zsh/env/* → config/zsh/login/* → config/zsh/interactive/*
local/env.local.zsh → local/login.local.zsh → ~/.config/zshrc/* (before omz) config/zsh/local.zsh → ~/.config/zshrc/* (before omz)
→ local/interactive.local.zsh
``` ```
PATH for user tools is built **once** in `~/.config/zsh/env/00-path.zsh` PATH for user tools is built **once** in `~/.config/zsh/env/00-path.zsh`
@@ -41,7 +40,7 @@ PATH for user tools is built **once** in `~/.config/zsh/env/00-path.zsh`
| `~/.config/zsh/env/` | Managed always-on env (PATH, Homebrew) | Overwritten | | `~/.config/zsh/env/` | Managed always-on env (PATH, Homebrew) | Overwritten |
| `~/.config/zsh/interactive/` | Managed interactive UX (omz, aliases) | Overwritten | | `~/.config/zsh/interactive/` | Managed interactive UX (omz, aliases) | Overwritten |
| `~/.config/zsh/login/` | Managed login hooks (usually empty) | Overwritten | | `~/.config/zsh/login/` | Managed login hooks (usually empty) | Overwritten |
| `~/.config/zsh/local/*` | Machine-local env/login/interactive | **Never overwritten** | | `~/.config/zsh/local.zsh` | Machine-local overrides (every zsh) | **Never overwritten** |
| `~/.config/zshrc/*.zsh` | Personal/tool overlays (before omz) | Left alone | | `~/.config/zshrc/*.zsh` | Personal/tool overlays (before omz) | Left alone |
| `~/.local/bin/` | User tools + intentional shims (e.g. `grok`) | Left alone | | `~/.local/bin/` | User tools + intentional shims (e.g. `grok`) | Left alone |
@@ -50,18 +49,12 @@ other tools). Link wanted entrypoints into `~/.local/bin` instead.
## Personal configuration ## Personal configuration
### Always-on (agents see this) ### Machine-local (agents see this)
```bash ```bash
# ~/.config/zsh/local/env.local.zsh # ~/.config/zsh/local.zsh
. "$HOME/.cargo/env" . "$HOME/.cargo/env"
``` source ~/.orbstack/shell/init.zsh 2>/dev/null || :
### Login only (vendor hooks)
```bash
# ~/.config/zsh/local/login.local.zsh
# OrbStack, MacPorts, product installers — not primary PATH policy
``` ```
### Interactive overlays (before oh-my-zsh) ### Interactive overlays (before oh-my-zsh)
@@ -81,4 +74,4 @@ curl -fsSL https://git.miomio.moe/mio/easyzsh/raw/branch/master/patch.sh | bash
Current fragments: `fnm`, `nvm`, `pyenv`, `p10k`, `merlin_devbox`, `merlin_worker`. Current fragments: `fnm`, `nvm`, `pyenv`, `p10k`, `merlin_devbox`, `merlin_worker`.
Aliases that must beat oh-my-zsh belong after omz loads (see managed Aliases that must beat oh-my-zsh belong after omz loads (see managed
`interactive/20-post-omz.zsh`) or in `~/.config/zsh/local/interactive.local.zsh`. `interactive/20-post-omz.zsh`).
+62 -33
View File
@@ -1,12 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Install managed easyzsh shell layout into a home directory. # Install managed easyzsh shell layout into a home directory.
# Overwrites: ~/.zshenv ~/.zshrc ~/.zprofile ~/.config/zsh/{env,interactive,login} # Overwrites: ~/.zshenv ~/.zshrc ~/.zprofile ~/.config/zsh/{env,interactive,login}
# Never overwrites: ~/.config/zsh/local/** ~/.config/zshrc/** ~/.local/bin/** # Never overwrites: ~/.config/zsh/local.zsh ~/.config/zshrc/** ~/.local/bin/**
# #
# Usage: # Usage:
# ./deploy.sh # ./deploy.sh
# ./deploy.sh --home /path/to/home # ./deploy.sh --home /path/to/home
# ./deploy.sh --migrate-zprofile # move existing ~/.zprofile body into login.local # ./deploy.sh --migrate-zprofile # fold pre-easyzsh ~/.zprofile into local.zsh
set -euo pipefail set -euo pipefail
@@ -41,6 +41,7 @@ if [[ ! -d "$TARGET_HOME" ]]; then
fi fi
ZSH_CFG="${TARGET_HOME}/.config/zsh" ZSH_CFG="${TARGET_HOME}/.config/zsh"
LOCAL_ZSH="${ZSH_CFG}/local.zsh"
ts=$(date +%Y%m%d-%H%M%S) ts=$(date +%Y%m%d-%H%M%S)
backup_file() { backup_file() {
@@ -51,47 +52,81 @@ backup_file() {
fi fi
} }
file_has_content() {
[[ -f "$1" ]] && grep -q '[^[:space:]]' "$1"
}
echo "Deploying easyzsh managed shell layout into $TARGET_HOME" echo "Deploying easyzsh managed shell layout into $TARGET_HOME"
mkdir -p \ mkdir -p \
"${ZSH_CFG}/env" \ "${ZSH_CFG}/env" \
"${ZSH_CFG}/interactive" \ "${ZSH_CFG}/interactive" \
"${ZSH_CFG}/login" \ "${ZSH_CFG}/login" \
"${ZSH_CFG}/local" \
"${TARGET_HOME}/.config/zshrc" \ "${TARGET_HOME}/.config/zshrc" \
"${TARGET_HOME}/.cache/zsh" \ "${TARGET_HOME}/.cache/zsh" \
"${TARGET_HOME}/.local/bin" "${TARGET_HOME}/.local/bin"
# Migrate legacy early env if present and new path missing. # Migrate split local/* and legacy paths into ~/.config/zsh/local.zsh once.
if [[ -f "${TARGET_HOME}/.config/zshenv.local" && ! -f "${ZSH_CFG}/local/env.local.zsh" ]]; then if [[ ! -f "$LOCAL_ZSH" ]]; then
cp -Pp "${TARGET_HOME}/.config/zshenv.local" "${ZSH_CFG}/local/env.local.zsh" tmp=$(mktemp)
echo "Migrated ~/.config/zshenv.local -> ~/.config/zsh/local/env.local.zsh" : >"$tmp"
if file_has_content "${TARGET_HOME}/.config/zshenv.local"; then
cat "${TARGET_HOME}/.config/zshenv.local" >>"$tmp"
printf '\n' >>"$tmp"
fi
if [[ -d "${ZSH_CFG}/local" ]]; then
for part in env.local.zsh login.local.zsh interactive.local.zsh; do
if file_has_content "${ZSH_CFG}/local/${part}"; then
cat "${ZSH_CFG}/local/${part}" >>"$tmp"
printf '\n' >>"$tmp"
fi
done
fi
if file_has_content "$tmp"; then
# De-dup consecutive identical non-empty lines lightly by collapsing exact file repeats later if needed.
cp "$tmp" "$LOCAL_ZSH"
echo "Migrated machine-local fragments -> ~/.config/zsh/local.zsh"
fi
rm -f "$tmp"
fi fi
# Optionally migrate existing zprofile body into login.local (strip redundant PATH). # After migration (or if local.zsh already exists), drop obsolete split files.
if [[ "$MIGRATE_ZPROFILE" -eq 1 ]]; then if [[ -d "${ZSH_CFG}/local" ]]; then
if [[ -f "${TARGET_HOME}/.zprofile" && ! -f "${ZSH_CFG}/local/login.local.zsh" ]]; then rm -f \
# Drop easyzsh stub lines and pipx ~/.local/bin PATH (now owned by env/00-path.zsh). "${ZSH_CFG}/local/env.local.zsh" \
"${ZSH_CFG}/local/login.local.zsh" \
"${ZSH_CFG}/local/interactive.local.zsh" \
"${ZSH_CFG}/local/env.local.zsh.example" \
"${ZSH_CFG}/local/login.local.zsh.example" \
"${ZSH_CFG}/local/interactive.local.zsh.example"
rmdir "${ZSH_CFG}/local" 2>/dev/null || true
fi
# Fold a pre-easyzsh ~/.zprofile into local.zsh. Skip if already an easyzsh stub.
if [[ "$MIGRATE_ZPROFILE" -eq 1 && -f "${TARGET_HOME}/.zprofile" ]]; then
if grep -Fq 'easyzsh stub' "${TARGET_HOME}/.zprofile"; then
echo "Skipping zprofile migration: already an easyzsh stub"
elif [[ ! -f "$LOCAL_ZSH" ]]; then
zprofile_extract=$(mktemp)
awk ' 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 } /Created by `pipx`/ { skip_pipx=1; next }
skip_pipx && /local\/bin/ { skip_pipx=0; next } skip_pipx && /local\/bin/ { skip_pipx=0; next }
skip_pipx { next } skip_pipx { next }
{ print } { print }
' "${TARGET_HOME}/.zprofile" > "${ZSH_CFG}/local/login.local.zsh" ' "${TARGET_HOME}/.zprofile" >"$zprofile_extract"
# Trim trailing blank lines noise if file became empty-ish if file_has_content "$zprofile_extract"; then
if ! grep -q '[^[:space:]]' "${ZSH_CFG}/local/login.local.zsh"; then cp "$zprofile_extract" "$LOCAL_ZSH"
rm -f "${ZSH_CFG}/local/login.local.zsh" echo "Migrated ~/.zprofile body -> ~/.config/zsh/local.zsh"
echo "Existing .zprofile had no login-local content after migration" fi
else rm -f "$zprofile_extract"
echo "Migrated ~/.zprofile body -> ~/.config/zsh/local/login.local.zsh"
fi fi
fi fi
# Remove a broken local.zsh produced by an earlier stub-body migration.
if [[ -f "$LOCAL_ZSH" ]] && grep -Fq 'local/login.local.zsh' "$LOCAL_ZSH" && ! grep -Eq 'cargo|OrbStack|MacPorts|Kiro|fnm' "$LOCAL_ZSH"; then
backup_file "$LOCAL_ZSH"
rm -f "$LOCAL_ZSH"
echo "Removed broken stub residue from local.zsh"
fi fi
backup_file "${TARGET_HOME}/.zshenv" backup_file "${TARGET_HOME}/.zshenv"
@@ -102,25 +137,19 @@ install -m 644 "${ROOT}/.zshenv" "${TARGET_HOME}/.zshenv"
install -m 644 "${ROOT}/.zshrc" "${TARGET_HOME}/.zshrc" install -m 644 "${ROOT}/.zshrc" "${TARGET_HOME}/.zshrc"
install -m 644 "${ROOT}/.zprofile" "${TARGET_HOME}/.zprofile" install -m 644 "${ROOT}/.zprofile" "${TARGET_HOME}/.zprofile"
# Replace managed modules only (never touch local/). # Replace managed modules only (never touch local.zsh).
rm -f "${ZSH_CFG}/env"/*.zsh rm -f "${ZSH_CFG}/env"/*.zsh
rm -f "${ZSH_CFG}/interactive"/*.zsh rm -f "${ZSH_CFG}/interactive"/*.zsh
rm -f "${ZSH_CFG}/login"/*.zsh rm -f "${ZSH_CFG}/login"/*.zsh
install -m 644 "${ROOT}/config/env/"*.zsh "${ZSH_CFG}/env/" install -m 644 "${ROOT}/config/env/"*.zsh "${ZSH_CFG}/env/"
install -m 644 "${ROOT}/config/interactive/"*.zsh "${ZSH_CFG}/interactive/" 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 if compgen -G "${ROOT}/config/login/"*.zsh > /dev/null; then
install -m 644 "${ROOT}/config/login/"*.zsh "${ZSH_CFG}/login/" install -m 644 "${ROOT}/config/login/"*.zsh "${ZSH_CFG}/login/"
fi fi
# Seed examples only when missing (never overwrite user locals). if [[ -f "${ROOT}/local.zsh.example" ]]; then
for example in env.local.zsh login.local.zsh interactive.local.zsh; do install -m 644 "${ROOT}/local.zsh.example" "${ZSH_CFG}/local.zsh.example"
src="${ROOT}/local/${example}.example"
dst="${ZSH_CFG}/local/${example}.example"
if [[ -f "$src" ]]; then
install -m 644 "$src" "$dst"
fi fi
done
# Ensure intentional grok shim when the binary tree exists. # Ensure intentional grok shim when the binary tree exists.
if [[ -e "${TARGET_HOME}/.grok/bin/grok" ]]; then if [[ -e "${TARGET_HOME}/.grok/bin/grok" ]]; then
+1 -3
View File
@@ -269,9 +269,7 @@ for rel in \
config/env/00-path.zsh \ config/env/00-path.zsh \
config/interactive/10-bootstrap.zsh \ config/interactive/10-bootstrap.zsh \
config/interactive/20-post-omz.zsh \ config/interactive/20-post-omz.zsh \
local/env.local.zsh.example \ local.zsh.example
local/login.local.zsh.example \
local/interactive.local.zsh.example
do do
download_easyzsh_file "$rel" download_easyzsh_file "$rel"
done done
+6
View File
@@ -0,0 +1,6 @@
# Copy to ~/.config/zsh/local.zsh (deploy never overwrites local.zsh).
# Sourced from ~/.zshenv after managed env modules — every zsh sees this,
# including non-interactive agent shells.
#
# . "$HOME/.cargo/env"
# source ~/.orbstack/shell/init.zsh 2>/dev/null || :
-4
View File
@@ -1,4 +0,0 @@
# Copy to ~/.config/zsh/local/env.local.zsh (installer never overwrites local/).
# Runs for every zsh after managed env modules.
#
# . "$HOME/.cargo/env"
-2
View File
@@ -1,2 +0,0 @@
# Copy to ~/.config/zsh/local/interactive.local.zsh (installer never overwrites local/).
# Interactive only, after oh-my-zsh and managed post-omz aliases.
-3
View File
@@ -1,3 +0,0 @@
# Copy to ~/.config/zsh/local/login.local.zsh (installer never overwrites local/).
# Login shells only: OS/vendor hooks (OrbStack, MacPorts, product installers).
# Do not put primary user-tool PATH here; that belongs in env/00-path.zsh.
+18 -7
View File
@@ -104,8 +104,8 @@ grep -Fq 'deploy.sh' "$ROOT/install.sh" || fail "install.sh does not invoke depl
grep -Fq 'brew shellenv' "$ROOT/config/env/00-path.zsh" || fail "Homebrew is not initialized in env path module" 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 '/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 '${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 'local.zsh' "$ROOT/.zshenv" || fail "machine-local local.zsh hook is missing"
grep -Fq 'login.local.zsh' "$ROOT/.zprofile" || fail "machine-local login.local.zsh hook is missing" grep -Fq 'local.zsh' "$ROOT/.zshenv" || fail "local.zsh is not sourced from .zshenv"
if grep -Fq 'brew shellenv' "$ROOT/.zshrc" "$ROOT/config/interactive"/*.zsh; then if grep -Fq 'brew shellenv' "$ROOT/.zshrc" "$ROOT/config/interactive"/*.zsh; then
fail "Homebrew still initializes from interactive-only files" fail "Homebrew still initializes from interactive-only files"
fi fi
@@ -114,20 +114,31 @@ if grep -Eq '^[[:space:]]*export MLX_USER_TOKEN=' "$ROOT"/zshrc/*.zsh; then
fail "a static MLX user token remains" fail "a static MLX user token remains"
fi fi
# deploy.sh installs managed layout and preserves local/ # deploy.sh installs managed layout and preserves local.zsh
deploy_home="$TEMP_DIR/deploy-home" deploy_home="$TEMP_DIR/deploy-home"
mkdir -p "$deploy_home/.config/zsh/local" "$deploy_home/.local/bin" "$deploy_home/.grok/bin" mkdir -p "$deploy_home/.config/zsh" "$deploy_home/.local/bin" "$deploy_home/.grok/bin"
printf '%s\n' 'machine-only' >"$deploy_home/.config/zsh/local/env.local.zsh" printf '%s\n' 'machine-only' >"$deploy_home/.config/zsh/local.zsh"
printf '%s\n' '#!/bin/sh' 'echo grok-ok' >"$deploy_home/.grok/bin/grok" printf '%s\n' '#!/bin/sh' 'echo grok-ok' >"$deploy_home/.grok/bin/grok"
chmod +x "$deploy_home/.grok/bin/grok" chmod +x "$deploy_home/.grok/bin/grok"
HOME="$deploy_home" bash "$ROOT/deploy.sh" --home "$deploy_home" HOME="$deploy_home" bash "$ROOT/deploy.sh" --home "$deploy_home"
[[ -f "$deploy_home/.zshenv" ]] || fail "deploy.sh did not install .zshenv" [[ -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/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" [[ -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" || grep -Fxq 'machine-only' "$deploy_home/.config/zsh/local.zsh" ||
fail "deploy.sh overwrote local/env.local.zsh" fail "deploy.sh overwrote local.zsh"
[[ -L "$deploy_home/.local/bin/grok" ]] || fail "deploy.sh did not create grok shim" [[ -L "$deploy_home/.local/bin/grok" ]] || fail "deploy.sh did not create grok shim"
# deploy migrates split local/* into local.zsh
split_home="$TEMP_DIR/split-home"
mkdir -p "$split_home/.config/zsh/local"
printf '%s\n' 'from-env-local' >"$split_home/.config/zsh/local/env.local.zsh"
printf '%s\n' 'from-login-local' >"$split_home/.config/zsh/local/login.local.zsh"
HOME="$split_home" bash "$ROOT/deploy.sh" --home "$split_home" >/dev/null
grep -Fq 'from-env-local' "$split_home/.config/zsh/local.zsh" ||
fail "deploy.sh did not migrate env.local.zsh into local.zsh"
grep -Fq 'from-login-local' "$split_home/.config/zsh/local.zsh" ||
fail "deploy.sh did not migrate login.local.zsh into local.zsh"
# Non-interactive zsh must resolve ~/.local/bin without sourcing .zshrc. # Non-interactive zsh must resolve ~/.local/bin without sourcing .zshrc.
local_bin_home="$TEMP_DIR/local-bin-home" local_bin_home="$TEMP_DIR/local-bin-home"
mkdir -p "$local_bin_home/.local/bin" mkdir -p "$local_bin_home/.local/bin"