mirror of
https://github.com/SeriousBug/dotfiles
synced 2025-12-06 21:12:08 -06:00
Allows setup.sh to symlink entire directories for cases where programs create new files that should be tracked (e.g., fish's funcsave command). Removes fish/functions from Dotter management in favor of direct symlink. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
274 lines
7.4 KiB
Bash
Executable file
274 lines
7.4 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
set -eo pipefail
|
|
|
|
# Function to manage directory symlinks
|
|
# Usage: ensure_dir_symlink <source_dir> <target_link> <force_flag>
|
|
# Args:
|
|
# source_dir: The actual directory to symlink to (must be absolute path)
|
|
# target_link: The symlink location to create
|
|
# force_flag: true/false - whether to force replacement if exists
|
|
ensure_dir_symlink() {
|
|
local source_dir="$1"
|
|
local target_link="$2"
|
|
local force_flag="$3"
|
|
|
|
# Expand tilde in paths
|
|
target_link="${target_link/#\~/$HOME}"
|
|
source_dir="${source_dir/#\~/$HOME}"
|
|
|
|
# Check if target exists
|
|
if [[ -e "$target_link" || -L "$target_link" ]]; then
|
|
# Check if it's already a symlink pointing to the right place
|
|
if [[ -L "$target_link" ]]; then
|
|
local current_target
|
|
current_target="$(readlink "$target_link")"
|
|
if [[ "$current_target" == "$source_dir" ]]; then
|
|
echo "✓ $target_link already symlinked correctly"
|
|
return 0
|
|
else
|
|
# Symlink exists but points to wrong location
|
|
if [[ "$force_flag" == true ]]; then
|
|
echo "Replacing symlink $target_link (currently points to $current_target)"
|
|
rm "$target_link"
|
|
else
|
|
echo "⚠ Warning: $target_link exists but points to $current_target (expected $source_dir)"
|
|
echo " Run with --force-deploy to replace it"
|
|
return 0
|
|
fi
|
|
fi
|
|
else
|
|
# Path exists but is not a symlink
|
|
if [[ "$force_flag" == true ]]; then
|
|
echo "Replacing $target_link with symlink (was a regular file/directory)"
|
|
rm -rf "$target_link"
|
|
else
|
|
echo "⚠ Warning: $target_link exists but is not a symlink"
|
|
echo " Run with --force-deploy to replace it"
|
|
return 0
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Create parent directory if needed
|
|
local parent_dir
|
|
parent_dir="$(dirname "$target_link")"
|
|
if [[ ! -d "$parent_dir" ]]; then
|
|
echo "Creating parent directory: $parent_dir"
|
|
mkdir -p "$parent_dir"
|
|
fi
|
|
|
|
# Create the symlink
|
|
echo "Creating symlink: $target_link -> $source_dir"
|
|
ln -s "$source_dir" "$target_link"
|
|
echo "✓ Successfully created symlink"
|
|
}
|
|
|
|
# Parse command line arguments
|
|
RESET_CONFIG=false
|
|
FORCE_DEPLOY=false
|
|
SKIP_BREW=false
|
|
|
|
for arg in "$@"; do
|
|
case $arg in
|
|
--reset)
|
|
RESET_CONFIG=true
|
|
echo "Resetting configuration (will re-prompt for all variables)..."
|
|
;;
|
|
--force-deploy)
|
|
FORCE_DEPLOY=true
|
|
echo "Force deployment mode enabled..."
|
|
;;
|
|
--skip-brew)
|
|
SKIP_BREW=true
|
|
echo "Skipping Homebrew package installation..."
|
|
;;
|
|
*)
|
|
echo "Unknown option: $arg"
|
|
echo "Usage: $0 [--reset] [--force-deploy] [--skip-brew]"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
echo "Setting up dotfiles environment..."
|
|
|
|
# Detect OS
|
|
OS="$(uname -s)"
|
|
|
|
if [[ "$SKIP_BREW" == false ]]; then
|
|
# Check if Homebrew is installed
|
|
if ! command -v brew &> /dev/null; then
|
|
echo "Homebrew is not installed. Please install it first:"
|
|
echo "https://brew.sh"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Updating Homebrew..."
|
|
brew update
|
|
|
|
# Install packages (available on both macOS and Linux)
|
|
PACKAGES=(
|
|
dotter
|
|
fish
|
|
dust
|
|
eza
|
|
gh
|
|
htop
|
|
go
|
|
jq
|
|
lazygit
|
|
neovim
|
|
asdf
|
|
bat
|
|
pandoc
|
|
ripgrep
|
|
zoxide
|
|
zellij
|
|
p7zip
|
|
)
|
|
|
|
echo "Installing packages..."
|
|
for package in "${PACKAGES[@]}"; do
|
|
if brew list "$package" &>/dev/null; then
|
|
echo "✓ $package already installed"
|
|
else
|
|
echo "Installing $package..."
|
|
brew install "$package"
|
|
fi
|
|
done
|
|
|
|
# Install casks on macOS only
|
|
if [[ "$OS" == "Darwin" ]]; then
|
|
echo "Installing macOS-specific casks..."
|
|
|
|
CASKS=(
|
|
iterm2
|
|
font-fira-code-nerd-font
|
|
orbstack
|
|
rectangle-pro
|
|
maccy
|
|
)
|
|
|
|
for cask in "${CASKS[@]}"; do
|
|
if brew list --cask "$cask" &>/dev/null; then
|
|
echo "✓ $cask already installed"
|
|
else
|
|
echo "Installing $cask..."
|
|
brew install --cask "$cask"
|
|
fi
|
|
done
|
|
else
|
|
echo "Skipping macOS-specific casks (not on macOS)"
|
|
fi
|
|
fi
|
|
|
|
# Set up GitHub CLI authentication
|
|
if command -v gh &> /dev/null; then
|
|
if ! gh auth status &>/dev/null; then
|
|
echo ""
|
|
echo "GitHub CLI is installed but not authenticated."
|
|
echo "Please log in to GitHub CLI:"
|
|
gh auth login
|
|
else
|
|
echo "✓ GitHub CLI already authenticated"
|
|
fi
|
|
fi
|
|
|
|
# Configure Dotter
|
|
echo ""
|
|
echo "Configuring Dotter..."
|
|
|
|
# Path to user variables file
|
|
USER_VARS_FILE=".dotter/user_vars.sh"
|
|
|
|
# Source existing variables if not resetting
|
|
if [[ "$RESET_CONFIG" == false && -f "$USER_VARS_FILE" ]]; then
|
|
echo "Loading existing configuration..."
|
|
source "$USER_VARS_FILE"
|
|
fi
|
|
|
|
# Prompt for missing variables
|
|
if [[ -z "$GIT_NAME" ]]; then
|
|
read -p "Enter your Git user name: " GIT_NAME
|
|
fi
|
|
|
|
if [[ -z "$GIT_EMAIL" ]]; then
|
|
read -p "Enter your Git email: " GIT_EMAIL
|
|
fi
|
|
|
|
# Detect Homebrew path if not set
|
|
if [[ -z "$BREW_PATH" ]]; then
|
|
if [[ "$OS" == "Darwin" ]]; then
|
|
if [[ -d "/opt/homebrew" ]]; then
|
|
BREW_PATH="/opt/homebrew"
|
|
else
|
|
BREW_PATH="/usr/local"
|
|
fi
|
|
else
|
|
BREW_PATH="/home/linuxbrew/.linuxbrew"
|
|
fi
|
|
echo "Detected Homebrew path: $BREW_PATH"
|
|
fi
|
|
|
|
# Save variables to file
|
|
echo "Saving configuration..."
|
|
cat > "$USER_VARS_FILE" << EOF
|
|
# Dotter user variables
|
|
# This file is sourced by setup.sh to avoid re-prompting for values
|
|
# To reset and re-prompt for all values, run: ./setup.sh --reset
|
|
|
|
GIT_NAME="$GIT_NAME"
|
|
GIT_EMAIL="$GIT_EMAIL"
|
|
BREW_PATH="$BREW_PATH"
|
|
EOF
|
|
|
|
# Generate local.toml
|
|
echo "Generating Dotter configuration..."
|
|
cat > .dotter/local.toml << EOF
|
|
# Machine-specific Dotter configuration
|
|
# This file is generated by setup.sh
|
|
|
|
packages = ["git", "nvim", "fish", "zellij", "htop", "asdf"]
|
|
|
|
[variables]
|
|
git_name = "$GIT_NAME"
|
|
git_email = "$GIT_EMAIL"
|
|
brew_path = "$BREW_PATH"
|
|
EOF
|
|
|
|
# Deploy dotfiles
|
|
echo ""
|
|
echo "Deploying dotfiles..."
|
|
if [[ "$FORCE_DEPLOY" == true ]]; then
|
|
if dotter deploy -f -v; then
|
|
echo "✓ Dotfiles deployed successfully (forced)!"
|
|
else
|
|
echo "⚠ Dotter deployment failed."
|
|
fi
|
|
else
|
|
if dotter deploy -v; then
|
|
echo "✓ Dotfiles deployed successfully!"
|
|
else
|
|
echo "⚠ Dotter deployment failed. You may need to run 'dotter deploy -f' to force deployment."
|
|
fi
|
|
fi
|
|
|
|
# Manage directory symlinks (not handled by Dotter)
|
|
echo ""
|
|
echo "Setting up directory symlinks..."
|
|
DOTFILES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
ensure_dir_symlink "$DOTFILES_DIR/fish/functions" "~/.config/fish/functions" "$FORCE_DEPLOY"
|
|
|
|
echo ""
|
|
echo "✓ Setup complete!"
|
|
echo ""
|
|
echo "Configuration saved to: $USER_VARS_FILE"
|
|
echo "To reconfigure, run: ./setup.sh --reset"
|
|
echo "To force deployment, run: ./setup.sh --force-deploy"
|
|
echo "To skip Homebrew packages, run: ./setup.sh --skip-brew"
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1. Consider setting fish as your default shell: chsh -s \$(which fish)"
|
|
echo " 2. Restart your terminal or source your new shell configuration"
|