-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathinstall.sh
More file actions
executable file
·488 lines (414 loc) · 16.9 KB
/
install.sh
File metadata and controls
executable file
·488 lines (414 loc) · 16.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
#!/usr/bin/env bash
# ============================================
# DOTFILES INSTALLATION SCRIPT
# ============================================
# One-command setup for macOS development environment
#
# Usage:
# bash install.sh # Non-interactive, sensible defaults
# bash install.sh --interactive # Prompt for every choice
# bash install.sh --name "AJ" --email "aj@example.com"
# bash <(curl -fsSL https://raw.githubusercontent.com/AnjanJ/dotfiles/main/install.sh)
#
# Flags:
# --interactive Prompt for every choice (original behavior)
# --name "Name" Git user.name
# --email "a@b.com" Git personal email
# --work-email "x@y" Git work email (enables work identity)
# --work-dir "~/work" Work directory (default: ~/work)
# --theme <name> Theme: tokyo-night or aura (default: tokyo-night)
# --ssh <mode> SSH: 1password, existing, generate, skip (default: skip)
# --no-macos-defaults Skip macOS defaults
# --force Force reinstall even if already configured
# --help Show this help
#
# Environment variables (flags take precedence):
# DOTFILES_GIT_NAME, DOTFILES_GIT_EMAIL, DOTFILES_WORK_EMAIL
# DOTFILES_WORK_DIR, DOTFILES_THEME, DOTFILES_SSH_MODE
# ============================================
# ── Bootstrap: handle curl-pipe-bash ──────────
# If this script is piped via curl, clone the repo first and re-exec.
_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" 2>/dev/null && pwd)"
if [[ ! -f "$_SCRIPT_DIR/scripts/_helpers.sh" ]]; then
echo ""
echo "Bootstrapping: dotfiles repo not found locally..."
DOTFILES_REPO="https://github.com/AnjanJ/dotfiles.git"
DOTFILES_TARGET="$HOME/dotfiles"
if ! command -v git &>/dev/null; then
echo "Git not found. Installing Xcode Command Line Tools..."
xcode-select --install 2>/dev/null || true
echo "Please re-run this script after Xcode tools finish installing."
exit 1
fi
if [[ -d "$DOTFILES_TARGET/.git" ]]; then
echo "Dotfiles already cloned at $DOTFILES_TARGET, pulling latest..."
git -C "$DOTFILES_TARGET" pull origin main 2>/dev/null || true
else
git clone "$DOTFILES_REPO" "$DOTFILES_TARGET"
fi
exec bash "$DOTFILES_TARGET/install.sh" "$@"
fi
set -euo pipefail
# ── Parse Arguments ───────────────────────────
INTERACTIVE=false
FORCE_INSTALL=false
APPLY_MACOS_DEFAULTS=true
# Env var defaults (flags override these)
GIT_NAME="${DOTFILES_GIT_NAME:-}"
GIT_EMAIL="${DOTFILES_GIT_EMAIL:-}"
GIT_WORK_EMAIL="${DOTFILES_WORK_EMAIL:-}"
WORK_DIR="${DOTFILES_WORK_DIR:-}"
SELECTED_THEME="${DOTFILES_THEME:-}"
SSH_MODE="${DOTFILES_SSH_MODE:-}"
while [[ $# -gt 0 ]]; do
case $1 in
--interactive) INTERACTIVE=true; shift ;;
--force) FORCE_INSTALL=true; shift ;;
--name) GIT_NAME="$2"; shift 2 ;;
--email) GIT_EMAIL="$2"; shift 2 ;;
--work-email) GIT_WORK_EMAIL="$2"; shift 2 ;;
--work-dir) WORK_DIR="$2"; shift 2 ;;
--theme) SELECTED_THEME="$2"; shift 2 ;;
--ssh) SSH_MODE="$2"; shift 2 ;;
--no-macos-defaults) APPLY_MACOS_DEFAULTS=false; shift ;;
--help)
# Print the usage block from the header
sed -n '/^# Usage:/,/^# ====/{ /^# ====/d; s/^# //; s/^#//; p; }' "$0"
exit 0
;;
*) shift ;;
esac
done
# Export for sub-scripts
export INTERACTIVE FORCE_INSTALL GIT_NAME GIT_EMAIL GIT_WORK_EMAIL WORK_DIR SSH_MODE
# ── Setup ─────────────────────────────────────
DOTFILES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Shared colors & print functions
source "$DOTFILES_DIR/scripts/_helpers.sh"
# Check if running on macOS
if [[ "$(uname)" != "Darwin" ]]; then
print_error "This script is only for macOS"
exit 1
fi
echo ""
echo ""
echo " AJ's Dotfiles Installation"
echo " =========================================="
echo ""
echo " This will install:"
echo " - Homebrew packages"
echo " - Aerospace (window manager)"
echo " - Ghostty terminal"
echo " - Neovim + AstroNvim"
echo " - tmux + plugins"
echo " - Zellij"
echo " - Zed editor (settings, snippets, tasks)"
echo " - Starship prompt"
echo " - Shell configuration"
echo " - Git smart defaults + identity setup"
echo " - SSH keys (1Password, import, generate, or existing)"
echo " - Custom scripts (~/bin)"
echo " - Theme: Tokyo Night, Aura, or Catppuccin (your choice!)"
echo ""
echo " Idempotent -- safe to re-run anytime"
echo ""
echo ""
if [[ "$INTERACTIVE" == true ]]; then
read -r -p "Continue with installation? (y/n) " -n 1
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_warning "Installation cancelled"
exit 1
fi
fi
echo ""
print_step "Dotfiles directory: $DOTFILES_DIR"
# ============================================
# THEME SELECTION
# ============================================
source "$DOTFILES_DIR/scripts/theme-utils.sh"
if [[ -z "$SELECTED_THEME" ]]; then
if [[ "$INTERACTIVE" == true ]]; then
# --interactive: always let the user choose
SELECTED_THEME=$(prompt_theme_choice)
elif [[ -f "$HOME/.dotfiles-theme" ]]; then
# Re-run without --interactive: keep previous choice
SELECTED_THEME=$(get_current_theme)
else
# Fresh install without --interactive: prompt anyway
SELECTED_THEME=$(prompt_theme_choice)
fi
fi
if ! validate_theme "$SELECTED_THEME"; then
print_warning "Invalid theme '$SELECTED_THEME', using tokyo-night"
SELECTED_THEME="tokyo-night"
fi
echo ""
print_success "Theme: $SELECTED_THEME"
# ============================================
# 1. INSTALL HOMEBREW
# ============================================
echo ""
print_step "Step 1: Installing Homebrew..."
if ! command -v brew &> /dev/null; then
print_warning "Homebrew not found. Installing..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Add Homebrew to PATH for Apple Silicon
if [[ $(uname -m) == 'arm64' ]]; then
if ! grep -q '/opt/homebrew/bin/brew shellenv' ~/.zprofile 2>/dev/null; then
# shellcheck disable=SC2016
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
fi
eval "$(/opt/homebrew/bin/brew shellenv)"
fi
print_success "Homebrew installed"
else
print_success "Homebrew already installed"
fi
# ============================================
# 2. INSTALL PACKAGES FROM BREWFILE
# ============================================
echo ""
print_step "Step 2: Installing packages from Brewfile..."
cd "$DOTFILES_DIR"
if brew bundle install; then
print_success "All packages installed"
else
print_warning "Some packages failed to install (see errors above). Continuing..."
fi
# ============================================
# 3. CREATE NECESSARY DIRECTORIES
# ============================================
echo ""
print_step "Step 3: Creating configuration directories..."
mkdir -p ~/.config/{aerospace,ghostty,nvim,zellij,zed/snippets}
mkdir -p ~/.tmux/plugins
mkdir -p ~/bin
print_success "Directories created"
# ============================================
# 4. SET UP MISE (VERSION MANAGER)
# ============================================
echo ""
print_step "Step 4: Setting up mise (version manager)..."
# Create mise config directory
mkdir -p ~/.config/mise
# Link mise configuration
if [ -L ~/.config/mise/config.toml ] && [ "$(readlink ~/.config/mise/config.toml)" = "$DOTFILES_DIR/.config/mise/config.toml" ] && [ "$FORCE_INSTALL" = false ]; then
print_success "mise config already linked"
else
backup_if_needed ~/.config/mise/config.toml
ln -sf "$DOTFILES_DIR/.config/mise/config.toml" ~/.config/mise/config.toml
print_success "mise config linked"
fi
# Trust the mise config file (required for security)
mise trust ~/.config/mise/config.toml 2>/dev/null || true
# Install all tools defined in mise config
print_step "Installing language runtimes with mise (this may take a few minutes)..."
mise install
print_success "mise configured and tools installed"
# ============================================
# 5. CREATE SYMLINKS
# ============================================
echo ""
print_step "Step 5: Creating symlinks..."
# Helper function to create symlink with idempotent backup
create_symlink() {
local source="$1"
local target="$2"
local name="$3"
if [ -L "$target" ] && [ "$(readlink "$target")" = "$source" ] && [ "$FORCE_INSTALL" = false ]; then
print_success "$name already linked"
else
backup_if_needed "$target"
ln -sf "$source" "$target"
print_success "$name linked"
fi
}
# Shell configuration
create_symlink "$DOTFILES_DIR/.zshrc" ~/.zshrc ".zshrc"
[[ -f "$DOTFILES_DIR/.zshrc-terminal-enhancements" ]] && create_symlink "$DOTFILES_DIR/.zshrc-terminal-enhancements" ~/.zshrc-terminal-enhancements ".zshrc-terminal-enhancements"
# DHH additions (main Rails workflow file)
[[ -f "$DOTFILES_DIR/.zshrc-dhh-additions" ]] && create_symlink "$DOTFILES_DIR/.zshrc-dhh-additions" ~/.zshrc-dhh-additions ".zshrc-dhh-additions"
# Elixir additions
[[ -f "$DOTFILES_DIR/.zshrc-elixir-additions" ]] && create_symlink "$DOTFILES_DIR/.zshrc-elixir-additions" ~/.zshrc-elixir-additions ".zshrc-elixir-additions"
# Work command completions
[[ -f "$DOTFILES_DIR/.zshrc-work-completions" ]] && create_symlink "$DOTFILES_DIR/.zshrc-work-completions" ~/.zshrc-work-completions ".zshrc-work-completions"
# tmux
create_symlink "$DOTFILES_DIR/.tmux.conf" ~/.tmux.conf ".tmux.conf"
# Git global ignores
[[ -f "$DOTFILES_DIR/.gitignore_global" ]] && create_symlink "$DOTFILES_DIR/.gitignore_global" ~/.gitignore_global ".gitignore_global"
# Config directories
create_symlink "$DOTFILES_DIR/.config/aerospace" ~/.config/aerospace "aerospace config"
create_symlink "$DOTFILES_DIR/.config/ghostty" ~/.config/ghostty "ghostty config"
create_symlink "$DOTFILES_DIR/.config/nvim" ~/.config/nvim "nvim config"
create_symlink "$DOTFILES_DIR/.config/zellij" ~/.config/zellij "zellij config"
create_symlink "$DOTFILES_DIR/.config/starship.toml" ~/.config/starship.toml "starship config"
create_symlink "$DOTFILES_DIR/.config/lazygit" ~/.config/lazygit "lazygit config"
create_symlink "$DOTFILES_DIR/.config/borders" ~/.config/borders "borders config"
create_symlink "$DOTFILES_DIR/.config/sketchybar" ~/.config/sketchybar "sketchybar config"
# Zed editor
create_symlink "$DOTFILES_DIR/.config/zed/settings.json" ~/.config/zed/settings.json "zed settings"
create_symlink "$DOTFILES_DIR/.config/zed/tasks.json" ~/.config/zed/tasks.json "zed tasks"
for snippet in "$DOTFILES_DIR/.config/zed/snippets/"*.json; do
name=$(basename "$snippet")
create_symlink "$snippet" ~/.config/zed/snippets/"$name" "zed snippet: $name"
done
# Custom scripts (~/bin)
for script in "$DOTFILES_DIR/bin/"*; do
name=$(basename "$script")
create_symlink "$script" ~/bin/"$name" "bin/$name"
done
print_success "All symlinks processed"
# ============================================
# 5b. APPLY SELECTED THEME
# ============================================
echo ""
print_step "Step 5b: Applying $SELECTED_THEME theme everywhere..."
source "$DOTFILES_DIR/scripts/apply-theme.sh"
apply_theme "$SELECTED_THEME"
# ============================================
# 6. INSTALL TPM (TMUX PLUGIN MANAGER)
# ============================================
echo ""
print_step "Step 6: Installing TPM (Tmux Plugin Manager)..."
if [[ ! -d ~/.tmux/plugins/tpm ]]; then
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
print_success "TPM installed"
else
print_success "TPM already installed"
fi
# ============================================
# 7. SET UP NEOVIM
# ============================================
echo ""
print_step "Step 7: Setting up Neovim..."
# AstroNvim will auto-install on first launch
print_success "Neovim configuration linked (plugins will install on first launch)"
# ============================================
# 8. SET UP SHELL
# ============================================
echo ""
print_step "Step 8: Setting up shell..."
# Make zsh default shell if not already
if [[ "$SHELL" != "$(which zsh)" ]]; then
print_warning "Setting zsh as default shell..."
chsh -s "$(which zsh)"
print_success "Default shell changed to zsh"
else
print_success "zsh is already the default shell"
fi
# ============================================
# 8b. GIT CONFIGURATION
# ============================================
source "$DOTFILES_DIR/scripts/setup-git.sh"
setup_git
# ============================================
# 8c. SSH CONFIGURATION
# ============================================
source "$DOTFILES_DIR/scripts/setup-ssh.sh"
setup_ssh
# ============================================
# 9. MACOS DEFAULTS
# ============================================
echo ""
if [[ "$INTERACTIVE" == true ]]; then
read -r -p "Apply recommended macOS defaults? (y/n) " -n 1
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
APPLY_MACOS_DEFAULTS=false
fi
fi
if [[ "$APPLY_MACOS_DEFAULTS" == true ]]; then
print_step "Step 9: Applying macOS defaults..."
# Keyboard settings
defaults write NSGlobalDomain KeyRepeat -int 2
defaults write NSGlobalDomain InitialKeyRepeat -int 15
# Appearance
defaults write NSGlobalDomain AppleInterfaceStyle -string "Dark"
# Finder settings
defaults write com.apple.finder ShowPathbar -bool true
defaults write com.apple.finder ShowStatusBar -bool true
defaults write com.apple.finder ShowSidebar -bool true
defaults write com.apple.finder _FXShowPosixPathInTitle -bool true
defaults write com.apple.finder FXPreferredViewStyle -string "Nlsv"
defaults write com.apple.finder AppleShowAllFiles -bool true
defaults write com.apple.finder NewWindowTarget -string "PfHm"
defaults write com.apple.finder ShowExternalHardDrivesOnDesktop -bool true
defaults write com.apple.finder ShowHardDrivesOnDesktop -bool true
defaults write com.apple.finder ShowMountedServersOnDesktop -bool true
defaults write com.apple.finder ShowRemovableMediaOnDesktop -bool true
# Dock settings
defaults write com.apple.dock autohide -bool true
defaults write com.apple.dock show-recents -bool false
defaults write com.apple.dock minimize-to-application -bool true
defaults write com.apple.dock expose-group-apps -bool true
# Window behavior
defaults write NSGlobalDomain AppleMiniaturizeOnDoubleClick -bool false
defaults write NSGlobalDomain com.apple.springing.enabled -bool true
defaults write NSGlobalDomain com.apple.springing.delay -float 0.5
# Trackpad settings
defaults write com.apple.AppleMultitouchTrackpad Clicking -bool true
defaults write com.apple.AppleMultitouchTrackpad TrackpadThreeFingerDrag -bool true
defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad Clicking -bool true
defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad TrackpadThreeFingerDrag -bool true
# Menu bar clock
defaults write com.apple.menuextra.clock ShowDate -int 0
defaults write com.apple.menuextra.clock ShowDayOfWeek -bool true
# Hot corners (bottom-right → Quick Note)
defaults write com.apple.dock wvous-br-corner -int 14
defaults write com.apple.dock wvous-br-modifier -int 0
# Restart affected services
killall Finder 2>/dev/null || true
killall Dock 2>/dev/null || true
print_success "macOS defaults applied (24 settings)"
print_warning "Some settings require logout/restart to take effect"
else
print_success "macOS defaults skipped"
fi
# ============================================
# 10. RUN HEALTH CHECK
# ============================================
echo ""
print_step "Step 10: Running health check..."
bash "$DOTFILES_DIR/scripts/health-check.sh"
# ============================================
# INSTALLATION COMPLETE
# ============================================
echo ""
echo " =========================================="
echo " Installation Complete!"
echo " =========================================="
echo ""
echo "📋 Next Steps:"
echo ""
echo "1. Restart your terminal or run: source ~/.zshrc"
echo ""
echo "2. Open tmux and install plugins:"
echo " tmux"
echo " Press: Ctrl+A then Shift+I"
echo ""
echo "3. Open Neovim to install plugins:"
echo " nvim"
echo " (AstroNvim will auto-install)"
echo ""
echo "4. Start Aerospace (will start on next login):"
echo " aerospace reload"
echo ""
echo "5. Verify mise installations:"
echo " mise list"
echo ""
echo "6. Run health check anytime:"
echo " bash $DOTFILES_DIR/scripts/health-check.sh"
echo ""
echo "7. Update dotfiles in the future:"
echo " bash $DOTFILES_DIR/update.sh"
echo ""
echo "🎨 Theme: $SELECTED_THEME (applied everywhere)"
echo " Switch anytime: dotfiles theme"
echo ""
if [[ -d "$BACKUP_DIR" ]]; then
echo "🔧 Backup location: $BACKUP_DIR"
echo ""
fi
echo "Enjoy your new setup! 🚀"
echo ""