dotfiles/install.sh
ilia 78f7874055
All checks were successful
CI / skip-ci-check (pull_request) Successful in 19s
CI / secret-scan (pull_request) Successful in 17s
Sync live zsh + Cursor pack for family Macs
Bring .zshrc up to the current machine config (portable paths), ship
homelab/ansible shell helpers, shared Cursor rules/skills, and an
install.sh so Irina can clone and run ./install.sh --ira.
2026-07-26 16:00:43 -04:00

136 lines
4.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# Install shared Levkin machine config from this repo onto the current Mac.
# Safe to re-run. Does not overwrite ~/.zshrc.local or secrets.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")" && pwd)"
BACKUP_DIR="${BACKUP_DIR:-$HOME/.dotfiles-backup-$(date +%Y%m%d-%H%M%S)}"
usage() {
cat <<'EOF'
Usage: ./install.sh [--ira] [--no-cursor] [--no-zsh] [--dry-run]
--ira Print Irina follow-up steps (clone personal-scripts, Gmail app password)
--no-cursor Skip ~/.cursor rules/skills/mcp example
--no-zsh Skip ~/.zshrc and ~/.p10k.zsh
--dry-run Show actions only
EOF
}
DO_ZSH=1
DO_CURSOR=1
IRA=0
DRY=0
while [[ $# -gt 0 ]]; do
case "$1" in
--ira) IRA=1 ;;
--no-cursor) DO_CURSOR=0 ;;
--no-zsh) DO_ZSH=0 ;;
--dry-run) DRY=1 ;;
-h|--help) usage; exit 0 ;;
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
esac
shift
done
run() {
if [[ "$DRY" -eq 1 ]]; then
echo "+ $*"
else
"$@"
fi
}
backup_if_exists() {
local path="$1"
if [[ -e "$path" || -L "$path" ]]; then
run mkdir -p "$BACKUP_DIR"
run mv "$path" "$BACKUP_DIR/$(basename "$path")"
echo "Backed up $path$BACKUP_DIR/"
fi
}
link_file() {
local src="$1" dest="$2"
if [[ -L "$dest" ]] && [[ "$(readlink "$dest")" == "$src" ]]; then
echo "OK (already linked): $dest"
return 0
fi
backup_if_exists "$dest"
run ln -s "$src" "$dest"
echo "Linked $dest$src"
}
copy_tree_merge() {
local src="$1" dest="$2"
run mkdir -p "$dest"
# Copy files; do not delete extras already on the machine
if [[ "$DRY" -eq 1 ]]; then
echo "+ rsync -a $src/ $dest/"
else
rsync -a "$src/" "$dest/"
fi
echo "Synced $src$dest"
}
echo "dotfiles root: $ROOT"
if [[ "$DO_ZSH" -eq 1 ]]; then
link_file "$ROOT/.zshrc" "$HOME/.zshrc"
link_file "$ROOT/.p10k.zsh" "$HOME/.p10k.zsh"
if [[ ! -f "$HOME/.zshrc.local" ]]; then
run cp "$ROOT/zsh/zshrc.local.example" "$HOME/.zshrc.local"
echo "Created ~/.zshrc.local from example (edit as needed)"
fi
# Global gitleaks pre-commit (optional)
if [[ -x "$ROOT/scripts/git-hooks/install.sh" ]]; then
if [[ "$DRY" -eq 1 ]]; then
echo "+ $ROOT/scripts/git-hooks/install.sh"
else
"$ROOT/scripts/git-hooks/install.sh" || echo "git-hooks install skipped/failed (non-fatal)"
fi
fi
fi
if [[ "$DO_CURSOR" -eq 1 ]]; then
run mkdir -p "$HOME/.cursor/rules" "$HOME/.cursor/skills"
copy_tree_merge "$ROOT/cursor/rules" "$HOME/.cursor/rules"
copy_tree_merge "$ROOT/cursor/skills" "$HOME/.cursor/skills"
if [[ ! -f "$HOME/.cursor/mcp.json" && -f "$ROOT/cursor/mcp.json.example" ]]; then
run cp "$ROOT/cursor/mcp.json.example" "$HOME/.cursor/mcp.json"
echo "Installed ~/.cursor/mcp.json from example (edit if needed)"
elif [[ -f "$HOME/.cursor/mcp.json" ]]; then
echo "Left existing ~/.cursor/mcp.json alone (see cursor/mcp.json.example)"
fi
fi
mkdir -p "$HOME/Documents/code" 2>/dev/null || true
cat <<EOF
Done.
Reload shell: exec zsh (or: source ~/.zshrc)
Homelab help: hhlp
Personal make: mps (after cloning personal-scripts)
EOF
if [[ "$IRA" -eq 1 ]]; then
cat <<'EOF'
── Irina next steps ──────────────────────────────────────────
1. Vaultwarden → "Gmail App Password (Ira)" (create Google App Password)
2. Clone household scripts:
git clone gitea@git.levkin.ca:ilia/personal-scripts.git ~/Documents/code/personal-scripts
(HTTPS: https://git.levkin.ca/ilia/personal-scripts.git as ira)
3. Optional infra clone (for hsp / mk ping — not required for Gmail):
git clone gitea@git.levkin.ca:ilia/ansible.git ~/Documents/code/ansible
4. Optional family docs:
git clone gitea@git.levkin.ca:ilia/levkin.git ~/Documents/code/levkin
5. In personal-scripts:
cp .env.irina.example .env.irina && chmod 600 .env.irina
# fill GMAIL_ADDRESS / GMAIL_APP_PASSWORD
6. Open personal-scripts (or ansible) in Cursor and paste the prompt from:
docs/irina-cursor-onboarding-prompt.md
EOF
fi