Compare commits
3 Commits
c11d846402
...
f824ca52d3
| Author | SHA1 | Date | |
|---|---|---|---|
| f824ca52d3 | |||
| f9ff648977 | |||
| 03150e539d |
32
README.md
32
README.md
@@ -15,23 +15,43 @@ A comprehensive dotfiles repository for SSH, remote environments, and developmen
|
|||||||
│ ├── cli-tools.txt # Nice-to-haves - Enhanced CLI tools
|
│ ├── cli-tools.txt # Nice-to-haves - Enhanced CLI tools
|
||||||
│ ├── dev.txt # Development tools (docker, node, etc)
|
│ ├── dev.txt # Development tools (docker, node, etc)
|
||||||
│ └── gui.txt # Desktop applications
|
│ └── gui.txt # Desktop applications
|
||||||
├── install.sh # Bootstrap script with package management
|
├── bootstrap.sh # Get repo and select branch
|
||||||
|
├── setup.sh # Main orchestrator - runs all configuration
|
||||||
└── README.md # Documentation
|
└── README.md # Documentation
|
||||||
```
|
```
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
|
**From internet:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash <(curl -sL https://ryans.tools/dotfiles)
|
bash <(curl -sL https://ryans.tools/dotfiles)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**From local copy (USB drive, etc.):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./bootstrap.sh
|
||||||
|
```
|
||||||
|
|
||||||
The installer will:
|
The installer will:
|
||||||
|
|
||||||
1. Clone this repository to `~/.dotfiles`
|
1. **Bootstrap:** Clone repository (or use local copy), select branch if desired
|
||||||
2. Prompt you to choose which package sets to install
|
2. **Setup:** Check dependencies, backup conflicts, install packages, configure with GNU Stow
|
||||||
3. Backup any existing conflicting files
|
|
||||||
4. Use GNU Stow to symlink configurations
|
## Updates
|
||||||
5. Set up bash aliases including the `dotpull` update alias
|
|
||||||
|
**Regular updates (recommended):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotpull
|
||||||
|
```
|
||||||
|
|
||||||
|
**Branch switching or major re-setup:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/.dotfiles && ./bootstrap.sh
|
||||||
|
```
|
||||||
|
|
||||||
## Package Categories
|
## Package Categories
|
||||||
|
|
||||||
|
|||||||
71
bootstrap.sh
Normal file
71
bootstrap.sh
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DOTFILES_REPO="https://gitea.purpleraft.com/ryan/dotfiles"
|
||||||
|
DOTFILES_DIR="$HOME/.dotfiles"
|
||||||
|
|
||||||
|
echo "Starting dotfiles bootstrap..."
|
||||||
|
|
||||||
|
# Clone repo (new installs) or update for branch switching
|
||||||
|
if [ -d "$DOTFILES_DIR/.git" ]; then
|
||||||
|
echo "Updating existing dotfiles repo..."
|
||||||
|
git -C "$DOTFILES_DIR" pull --quiet
|
||||||
|
else
|
||||||
|
echo "Cloning dotfiles into $DOTFILES_DIR..."
|
||||||
|
git clone "$DOTFILES_REPO" "$DOTFILES_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$DOTFILES_DIR"
|
||||||
|
|
||||||
|
# Branch selection - allows choosing which version of dotfiles to install
|
||||||
|
# - New installs: git clone defaults to 'main', but you can switch to any branch
|
||||||
|
# - Existing installs: switch from current branch to a different one
|
||||||
|
# - Only runs in interactive terminals (skipped in automated/CI environments)
|
||||||
|
if [ -t 0 ] && [ -t 1 ] && [ -d ".git" ]; then
|
||||||
|
current_branch=$(git branch --show-current 2>/dev/null || echo "unknown")
|
||||||
|
echo "Current branch: $current_branch"
|
||||||
|
|
||||||
|
echo
|
||||||
|
read -p "Switch to a different branch? (y/N): " -n 1 -r
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Available branches:"
|
||||||
|
git branch -a --format="%(refname:short)" | grep -v "HEAD" | sed 's|^origin/||' | sort -u | nl -w2 -s'. '
|
||||||
|
|
||||||
|
branches=($(git branch -a --format="%(refname:short)" | grep -v "HEAD" | sed 's|^origin/||' | sort -u))
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
read -p "Enter branch number (1-${#branches[@]}): " branch_num
|
||||||
|
|
||||||
|
if [[ "$branch_num" =~ ^[0-9]+$ ]] && [ "$branch_num" -ge 1 ] && [ "$branch_num" -le "${#branches[@]}" ]; then
|
||||||
|
selected_branch="${branches[$((branch_num-1))]}"
|
||||||
|
echo "Switching to branch: $selected_branch"
|
||||||
|
|
||||||
|
if git show-ref --verify --quiet "refs/heads/$selected_branch"; then
|
||||||
|
git checkout "$selected_branch"
|
||||||
|
elif git show-ref --verify --quiet "refs/remotes/origin/$selected_branch"; then
|
||||||
|
git checkout -b "$selected_branch" "origin/$selected_branch"
|
||||||
|
else
|
||||||
|
echo "Error: Branch $selected_branch not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
git pull --quiet || true
|
||||||
|
echo "Switched to branch: $selected_branch"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "Invalid selection. Please enter a number between 1 and ${#branches[@]}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Hand off to setup
|
||||||
|
if [ -f "setup.sh" ]; then
|
||||||
|
echo "Starting setup..."
|
||||||
|
exec "./setup.sh"
|
||||||
|
else
|
||||||
|
echo "Error: No setup.sh found!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
188
install.sh
188
install.sh
@@ -1,188 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
DOTFILES_REPO="https://gitea.purpleraft.com/ryan/dotfiles"
|
|
||||||
DOTFILES_DIR="$HOME/.dotfiles"
|
|
||||||
|
|
||||||
# Handle branch selection
|
|
||||||
if [ $# -eq 0 ]; then
|
|
||||||
echo "🌿 Fetching available branches..."
|
|
||||||
|
|
||||||
# Get list of remote branches
|
|
||||||
branches=$(git ls-remote --heads "$DOTFILES_REPO" | sed 's/.*refs\/heads\///' | sort)
|
|
||||||
|
|
||||||
if [ -z "$branches" ]; then
|
|
||||||
echo "⚠️ Could not fetch branches, using default 'main'"
|
|
||||||
BRANCH="main"
|
|
||||||
else
|
|
||||||
echo "Available branches:"
|
|
||||||
branch_array=()
|
|
||||||
i=1
|
|
||||||
while IFS= read -r branch; do
|
|
||||||
echo "$i. $branch"
|
|
||||||
branch_array+=("$branch")
|
|
||||||
((i++))
|
|
||||||
done <<< "$branches"
|
|
||||||
|
|
||||||
echo "$i. Enter custom branch name"
|
|
||||||
echo ""
|
|
||||||
read -p "Choose branch (1-$i) [default: main]: " branch_choice
|
|
||||||
|
|
||||||
if [[ "$branch_choice" =~ ^[0-9]+$ ]] && [ "$branch_choice" -ge 1 ] && [ "$branch_choice" -lt "$i" ]; then
|
|
||||||
# Valid branch selection
|
|
||||||
BRANCH="${branch_array[$((branch_choice-1))]}"
|
|
||||||
elif [ "$branch_choice" -eq "$i" ]; then
|
|
||||||
# Custom branch name
|
|
||||||
read -p "Enter branch name: " custom_branch
|
|
||||||
BRANCH="$custom_branch"
|
|
||||||
else
|
|
||||||
# Default or invalid choice
|
|
||||||
if [[ " ${branch_array[*]} " =~ " main " ]]; then
|
|
||||||
BRANCH="main"
|
|
||||||
else
|
|
||||||
BRANCH="${branch_array[0]}" # Use first available branch
|
|
||||||
fi
|
|
||||||
echo "Using default branch: $BRANCH"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
BRANCH="$1"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Function to install packages from a file
|
|
||||||
install_packages() {
|
|
||||||
local package_file="$1"
|
|
||||||
local description="$2"
|
|
||||||
|
|
||||||
if [ -f "$package_file" ]; then
|
|
||||||
echo "📦 Installing $description..."
|
|
||||||
|
|
||||||
# Read packages into array, skipping comments and empty lines
|
|
||||||
packages=()
|
|
||||||
while IFS= read -r line; do
|
|
||||||
# Skip comments and empty lines
|
|
||||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
|
||||||
[[ -z "${line// }" ]] && continue
|
|
||||||
packages+=("$line")
|
|
||||||
done < "$package_file"
|
|
||||||
|
|
||||||
if [ ${#packages[@]} -eq 0 ]; then
|
|
||||||
echo " No packages found in $package_file"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Detect package manager and install packages
|
|
||||||
if command -v apt &> /dev/null; then
|
|
||||||
# Debian/Ubuntu
|
|
||||||
sudo apt update && sudo apt install -y "${packages[@]}"
|
|
||||||
elif command -v yum &> /dev/null; then
|
|
||||||
# RHEL/CentOS
|
|
||||||
sudo yum install -y "${packages[@]}"
|
|
||||||
elif command -v dnf &> /dev/null; then
|
|
||||||
# Fedora
|
|
||||||
sudo dnf install -y "${packages[@]}"
|
|
||||||
elif command -v pacman &> /dev/null; then
|
|
||||||
# Arch Linux
|
|
||||||
sudo pacman -S --noconfirm "${packages[@]}"
|
|
||||||
elif command -v brew &> /dev/null; then
|
|
||||||
# macOS
|
|
||||||
brew install "${packages[@]}"
|
|
||||||
else
|
|
||||||
echo "⚠️ No supported package manager found. Please install packages manually from $package_file"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if stow is installed
|
|
||||||
if ! command -v stow &> /dev/null; then
|
|
||||||
echo "❌ GNU Stow is not installed. Please install it first:"
|
|
||||||
echo " Ubuntu/Debian: sudo apt install stow"
|
|
||||||
echo " macOS: brew install stow"
|
|
||||||
echo " Or use the alias: install_stow"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "🚀 Installing dotfiles from branch: $BRANCH"
|
|
||||||
|
|
||||||
# Clone or update the repo
|
|
||||||
if [ -d "$DOTFILES_DIR/.git" ]; then
|
|
||||||
echo "🔄 Updating existing dotfiles repo..."
|
|
||||||
git -C "$DOTFILES_DIR" fetch --quiet
|
|
||||||
git -C "$DOTFILES_DIR" checkout "$BRANCH" --quiet
|
|
||||||
git -C "$DOTFILES_DIR" pull --quiet
|
|
||||||
else
|
|
||||||
echo "📥 Cloning dotfiles into $DOTFILES_DIR..."
|
|
||||||
git clone -b "$BRANCH" "$DOTFILES_REPO" "$DOTFILES_DIR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "$DOTFILES_DIR"
|
|
||||||
|
|
||||||
# Install packages (with user confirmation)
|
|
||||||
echo ""
|
|
||||||
echo "Package installation options:"
|
|
||||||
echo "1. Base packages (MUST HAVE) - curl, git, stow, vim, etc."
|
|
||||||
echo "2. CLI tools (Nice-to-haves) - bat, fzf, ripgrep, etc."
|
|
||||||
echo "3. Development tools - nodejs, python, docker, etc."
|
|
||||||
echo "4. GUI applications - firefox, code, vlc, etc."
|
|
||||||
echo "5. Skip package installation"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
read -p "Choose packages to install (1-5, or comma-separated like 1,2): " package_choice
|
|
||||||
|
|
||||||
# Install selected packages
|
|
||||||
if [[ "$package_choice" == *"1"* ]]; then
|
|
||||||
install_packages "packages/base.txt" "base packages"
|
|
||||||
fi
|
|
||||||
if [[ "$package_choice" == *"2"* ]]; then
|
|
||||||
install_packages "packages/cli-tools.txt" "CLI tools"
|
|
||||||
fi
|
|
||||||
if [[ "$package_choice" == *"3"* ]]; then
|
|
||||||
install_packages "packages/dev.txt" "development tools"
|
|
||||||
fi
|
|
||||||
if [[ "$package_choice" == *"4"* ]]; then
|
|
||||||
install_packages "packages/gui.txt" "GUI applications"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Backup existing files that would conflict with stow
|
|
||||||
echo ""
|
|
||||||
echo "🔗 Setting up dotfile symlinks..."
|
|
||||||
for config_dir in stow/*/; do
|
|
||||||
if [ -d "$config_dir" ]; then
|
|
||||||
config_name=$(basename "$config_dir")
|
|
||||||
echo " Checking for conflicts with $config_name config..."
|
|
||||||
|
|
||||||
# Find all files that would be stowed and backup if they exist
|
|
||||||
find "$config_dir" -type f | while read -r file; do
|
|
||||||
relative_file="${file#$config_dir}"
|
|
||||||
target_file="$HOME/$relative_file"
|
|
||||||
|
|
||||||
if [ -f "$target_file" ] && [ ! -L "$target_file" ]; then
|
|
||||||
echo " ⚠️ Backing up existing file: $target_file -> ${target_file}.bak"
|
|
||||||
mv "$target_file" "${target_file}.bak"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Use stow to create symlinks for each configuration
|
|
||||||
cd stow
|
|
||||||
for config_dir in */; do
|
|
||||||
if [ -d "$config_dir" ]; then
|
|
||||||
config_name=$(basename "$config_dir")
|
|
||||||
echo " 🔗 Stowing $config_name configuration..."
|
|
||||||
if ! stow -t "$HOME" "$config_name" 2>/dev/null; then
|
|
||||||
echo " ⚠️ Stow failed for $config_name, trying with --adopt..."
|
|
||||||
stow --adopt -t "$HOME" "$config_name"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Optionally source the new bashrc
|
|
||||||
if [[ $- == *i* ]] && [ -f "$HOME/.bashrc" ]; then
|
|
||||||
echo "🔄 Reloading Bash config..."
|
|
||||||
source ~/.bashrc
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "✅ Dotfiles install complete!"
|
|
||||||
echo "💡 Use 'dotpull' alias to update your dotfiles in the future."
|
|
||||||
57
scripts/00-check-dependencies.sh
Normal file
57
scripts/00-check-dependencies.sh
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/00-check-dependencies.sh - Check for required dependencies
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Source utilities
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/utils.sh"
|
||||||
|
|
||||||
|
check_dependencies() {
|
||||||
|
log_info "Checking dependencies..."
|
||||||
|
|
||||||
|
local missing_deps=()
|
||||||
|
|
||||||
|
# Check for git
|
||||||
|
if ! command_exists git; then
|
||||||
|
missing_deps+=("git")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for stow
|
||||||
|
if ! command_exists stow; then
|
||||||
|
missing_deps+=("stow")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${#missing_deps[@]} -gt 0 ]; then
|
||||||
|
log_error "Missing required dependencies: ${missing_deps[*]}"
|
||||||
|
echo ""
|
||||||
|
echo "Please install the missing dependencies:"
|
||||||
|
|
||||||
|
for dep in "${missing_deps[@]}"; do
|
||||||
|
case "$dep" in
|
||||||
|
"git")
|
||||||
|
echo " Git:"
|
||||||
|
echo " Ubuntu/Debian: sudo apt install git"
|
||||||
|
echo " macOS: brew install git"
|
||||||
|
echo " Or visit: https://git-scm.com/downloads"
|
||||||
|
;;
|
||||||
|
"stow")
|
||||||
|
echo " GNU Stow:"
|
||||||
|
echo " Ubuntu/Debian: sudo apt install stow"
|
||||||
|
echo " macOS: brew install stow"
|
||||||
|
echo " Or use the alias: install_stow"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "All dependencies satisfied"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run if called directly
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
check_dependencies
|
||||||
|
fi
|
||||||
100
scripts/05-branch-selection.sh
Normal file
100
scripts/05-branch-selection.sh
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/05-branch-selection.sh - Allow user to select git branch
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Source utilities
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/utils.sh"
|
||||||
|
|
||||||
|
select_branch() {
|
||||||
|
local dotfiles_dir="$1"
|
||||||
|
|
||||||
|
# Change to dotfiles directory
|
||||||
|
cd "$dotfiles_dir"
|
||||||
|
|
||||||
|
# Get current branch
|
||||||
|
local current_branch
|
||||||
|
current_branch=$(git branch --show-current 2>/dev/null || echo "unknown")
|
||||||
|
|
||||||
|
log_info "Current branch: $current_branch"
|
||||||
|
|
||||||
|
# Only prompt if we're in an interactive terminal
|
||||||
|
if ! is_interactive; then
|
||||||
|
log_info "Non-interactive mode, staying on current branch: $current_branch"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get available branches (both local and remote)
|
||||||
|
log_info "Available branches:"
|
||||||
|
git branch -a --format="%(refname:short)" | grep -v "HEAD" | sort -u | nl -w2 -s'. '
|
||||||
|
|
||||||
|
echo
|
||||||
|
read -p "Switch to a different branch? (y/N): " -n 1 -r
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
log_info "Staying on current branch: $current_branch"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get list of branches for selection
|
||||||
|
local branches=()
|
||||||
|
while IFS= read -r branch; do
|
||||||
|
# Clean up branch names (remove origin/ prefix, etc.)
|
||||||
|
clean_branch=$(echo "$branch" | sed 's|^origin/||' | sed 's|^remotes/origin/||')
|
||||||
|
# Skip HEAD and duplicates
|
||||||
|
if [[ "$clean_branch" != "HEAD" ]] && [[ ! " ${branches[@]} " =~ " ${clean_branch} " ]]; then
|
||||||
|
branches+=("$clean_branch")
|
||||||
|
fi
|
||||||
|
done < <(git branch -a --format="%(refname:short)" | grep -v "HEAD")
|
||||||
|
|
||||||
|
# Sort branches
|
||||||
|
IFS=$'\n' branches=($(sort <<<"${branches[*]}"))
|
||||||
|
|
||||||
|
echo "Select a branch:"
|
||||||
|
for i in "${!branches[@]}"; do
|
||||||
|
echo "$((i+1)). ${branches[i]}"
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
read -p "Enter branch number (1-${#branches[@]}): " branch_num
|
||||||
|
|
||||||
|
if [[ "$branch_num" =~ ^[0-9]+$ ]] && [ "$branch_num" -ge 1 ] && [ "$branch_num" -le "${#branches[@]}" ]; then
|
||||||
|
selected_branch="${branches[$((branch_num-1))]}"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
log_warning "Invalid selection. Please enter a number between 1 and ${#branches[@]}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Switch to selected branch
|
||||||
|
log_info "Switching to branch: $selected_branch"
|
||||||
|
|
||||||
|
if git show-ref --verify --quiet "refs/heads/$selected_branch"; then
|
||||||
|
# Local branch exists
|
||||||
|
git checkout "$selected_branch"
|
||||||
|
elif git show-ref --verify --quiet "refs/remotes/origin/$selected_branch"; then
|
||||||
|
# Remote branch exists, create local tracking branch
|
||||||
|
git checkout -b "$selected_branch" "origin/$selected_branch"
|
||||||
|
else
|
||||||
|
log_error "Branch $selected_branch not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Pull latest changes
|
||||||
|
log_info "Pulling latest changes..."
|
||||||
|
git pull --quiet || true
|
||||||
|
|
||||||
|
log_success "Switched to branch: $selected_branch"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run if called directly
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
log_error "Usage: $0 <dotfiles_directory>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
select_branch "$1"
|
||||||
|
fi
|
||||||
65
scripts/10-backup-files.sh
Normal file
65
scripts/10-backup-files.sh
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/10-backup-files.sh - Backup existing files that would conflict
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Source utilities
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/utils.sh"
|
||||||
|
|
||||||
|
backup_files() {
|
||||||
|
local dotfiles_dir="$1"
|
||||||
|
|
||||||
|
log_info "Checking for file conflicts..."
|
||||||
|
|
||||||
|
local backed_up=()
|
||||||
|
|
||||||
|
# Auto-discover files from stow directories
|
||||||
|
if [ -d "$dotfiles_dir/stow" ]; then
|
||||||
|
# Use new stow structure - scan all files in stow subdirectories
|
||||||
|
for config_dir in "$dotfiles_dir/stow"/*; do
|
||||||
|
if [ -d "$config_dir" ]; then
|
||||||
|
log_info "Checking $(basename "$config_dir") configuration for conflicts..."
|
||||||
|
|
||||||
|
# Find all files that would be stowed
|
||||||
|
find "$config_dir" -type f | while read -r file; do
|
||||||
|
# Get relative path from config directory
|
||||||
|
relative_file="${file#$config_dir/}"
|
||||||
|
target_file="$HOME/$relative_file"
|
||||||
|
|
||||||
|
if [ -f "$target_file" ] && [ ! -L "$target_file" ]; then
|
||||||
|
log_warning "Backing up existing file: $target_file -> ${target_file}.bak"
|
||||||
|
mv "$target_file" "${target_file}.bak"
|
||||||
|
backed_up+=("$relative_file")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
# Fallback to old structure - hardcoded file list
|
||||||
|
local files_to_check=(.bashrc .bash_aliases .inputrc .gitconfig)
|
||||||
|
|
||||||
|
for file in "${files_to_check[@]}"; do
|
||||||
|
if [ -f "$HOME/$file" ] && [ ! -L "$HOME/$file" ]; then
|
||||||
|
log_warning "Backing up existing file: $HOME/$file -> $HOME/${file}.bak"
|
||||||
|
mv "$HOME/$file" "$HOME/${file}.bak"
|
||||||
|
backed_up+=("$file")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${#backed_up[@]} -gt 0 ]; then
|
||||||
|
log_info "Backed up ${#backed_up[@]} file(s): ${backed_up[*]}"
|
||||||
|
else
|
||||||
|
log_info "No file conflicts found"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run if called directly
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
log_error "Usage: $0 <dotfiles_directory>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
backup_files "$1"
|
||||||
|
fi
|
||||||
59
scripts/20-setup-stow.sh
Normal file
59
scripts/20-setup-stow.sh
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/20-setup-stow.sh - Handle GNU Stow operations
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Source utilities
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/utils.sh"
|
||||||
|
|
||||||
|
setup_stow() {
|
||||||
|
local dotfiles_dir="$1"
|
||||||
|
|
||||||
|
if [ -d "$dotfiles_dir/stow" ]; then
|
||||||
|
# Use new stow structure - stow each subdirectory individually
|
||||||
|
cd "$dotfiles_dir/stow"
|
||||||
|
log_info "Using Stow to symlink dotfiles from stow/ subdirectories..."
|
||||||
|
|
||||||
|
local stowed=()
|
||||||
|
|
||||||
|
for config_dir in *; do
|
||||||
|
if [ -d "$config_dir" ]; then
|
||||||
|
log_info "Stowing $config_dir configuration..."
|
||||||
|
|
||||||
|
if ! stow --adopt -t "$HOME" "$config_dir" 2>/dev/null; then
|
||||||
|
log_warning "Adopting failed for $config_dir, trying regular stow..."
|
||||||
|
stow -t "$HOME" "$config_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
stowed+=("$config_dir")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ${#stowed[@]} -gt 0 ]; then
|
||||||
|
log_success "Stowed ${#stowed[@]} configuration(s): ${stowed[*]}"
|
||||||
|
else
|
||||||
|
log_warning "No stow subdirectories found in $dotfiles_dir/stow"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Fallback to old structure - stow entire directory
|
||||||
|
cd "$dotfiles_dir"
|
||||||
|
log_info "Using Stow to symlink dotfiles from root directory..."
|
||||||
|
|
||||||
|
if ! stow --adopt -t "$HOME" . 2>/dev/null; then
|
||||||
|
log_warning "Adopting failed, trying regular stow..."
|
||||||
|
stow -t "$HOME" .
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Stow setup complete"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run if called directly
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
log_error "Usage: $0 <dotfiles_directory>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
setup_stow "$1"
|
||||||
|
fi
|
||||||
125
scripts/30-install-packages.sh
Normal file
125
scripts/30-install-packages.sh
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/30-install-packages.sh - Install packages from package lists
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Source utilities
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/utils.sh"
|
||||||
|
|
||||||
|
# Detect package manager
|
||||||
|
detect_package_manager() {
|
||||||
|
if command_exists apt; then
|
||||||
|
echo "apt"
|
||||||
|
elif command_exists yum; then
|
||||||
|
echo "yum"
|
||||||
|
elif command_exists dnf; then
|
||||||
|
echo "dnf"
|
||||||
|
elif command_exists pacman; then
|
||||||
|
echo "pacman"
|
||||||
|
elif command_exists brew; then
|
||||||
|
echo "brew"
|
||||||
|
else
|
||||||
|
echo "unknown"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install packages with detected package manager
|
||||||
|
install_packages() {
|
||||||
|
local package_manager="$1"
|
||||||
|
shift
|
||||||
|
local packages=("$@")
|
||||||
|
|
||||||
|
if [ ${#packages[@]} -eq 0 ]; then
|
||||||
|
log_info "No packages to install"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$package_manager" in
|
||||||
|
apt)
|
||||||
|
log_info "Installing packages with apt..."
|
||||||
|
sudo apt update && sudo apt install -y "${packages[@]}"
|
||||||
|
;;
|
||||||
|
yum)
|
||||||
|
log_info "Installing packages with yum..."
|
||||||
|
sudo yum install -y "${packages[@]}"
|
||||||
|
;;
|
||||||
|
dnf)
|
||||||
|
log_info "Installing packages with dnf..."
|
||||||
|
sudo dnf install -y "${packages[@]}"
|
||||||
|
;;
|
||||||
|
pacman)
|
||||||
|
log_info "Installing packages with pacman..."
|
||||||
|
sudo pacman -S --noconfirm "${packages[@]}"
|
||||||
|
;;
|
||||||
|
brew)
|
||||||
|
log_info "Installing packages with brew..."
|
||||||
|
brew install "${packages[@]}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_error "Unknown package manager. Please install packages manually."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Read packages from file
|
||||||
|
read_package_file() {
|
||||||
|
local file="$1"
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
# Filter out comments and empty lines
|
||||||
|
grep -v '^#' "$file" | grep -v '^[[:space:]]*$' | tr '\n' ' '
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
install_package_lists() {
|
||||||
|
local dotfiles_dir="$1"
|
||||||
|
local packages_dir="$dotfiles_dir/packages"
|
||||||
|
|
||||||
|
if [ ! -d "$packages_dir" ]; then
|
||||||
|
log_warning "No packages directory found at $packages_dir"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local package_manager
|
||||||
|
package_manager=$(detect_package_manager)
|
||||||
|
|
||||||
|
if [ "$package_manager" = "unknown" ]; then
|
||||||
|
log_error "No supported package manager found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Detected package manager: $package_manager"
|
||||||
|
|
||||||
|
# Install package lists in order
|
||||||
|
local package_files=("base.txt" "cli-tools.txt" "dev.txt" "gui.txt")
|
||||||
|
|
||||||
|
for package_file in "${package_files[@]}"; do
|
||||||
|
local file_path="$packages_dir/$package_file"
|
||||||
|
if [ -f "$file_path" ]; then
|
||||||
|
log_info "Installing packages from $package_file..."
|
||||||
|
local packages
|
||||||
|
packages=$(read_package_file "$file_path")
|
||||||
|
|
||||||
|
if [ -n "$packages" ]; then
|
||||||
|
# Convert space-separated string to array
|
||||||
|
read -ra package_array <<< "$packages"
|
||||||
|
install_packages "$package_manager" "${package_array[@]}"
|
||||||
|
log_success "Completed installation from $package_file"
|
||||||
|
else
|
||||||
|
log_info "No packages found in $package_file"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "Package file $package_file not found, skipping"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run if called directly
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
log_error "Usage: $0 <dotfiles_directory>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
install_package_lists "$1"
|
||||||
|
fi
|
||||||
26
scripts/90-post-install.sh
Normal file
26
scripts/90-post-install.sh
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/90-post-install.sh - Post-installation tasks
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Source utilities
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/utils.sh"
|
||||||
|
|
||||||
|
post_install() {
|
||||||
|
log_info "Running post-installation tasks..."
|
||||||
|
|
||||||
|
# Source the new bashrc if in interactive shell
|
||||||
|
if is_interactive && [ -f "$HOME/.bashrc" ]; then
|
||||||
|
log_info "Reloading Bash config..."
|
||||||
|
source "$HOME/.bashrc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show completion message
|
||||||
|
log_success "Post-installation tasks complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run if called directly
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
post_install
|
||||||
|
fi
|
||||||
82
scripts/utils.sh
Normal file
82
scripts/utils.sh
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# scripts/utils.sh - Shared utilities and functions
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Logging functions
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}✅ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}⚠️ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}❌ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if command exists
|
||||||
|
command_exists() {
|
||||||
|
command -v "$1" &> /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if we're in an interactive shell
|
||||||
|
is_interactive() {
|
||||||
|
[[ $- == *i* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Branch selection utilities
|
||||||
|
list_branches() {
|
||||||
|
local dotfiles_dir="$1"
|
||||||
|
cd "$dotfiles_dir"
|
||||||
|
|
||||||
|
# Get available branches (both local and remote)
|
||||||
|
local branches=()
|
||||||
|
while IFS= read -r branch; do
|
||||||
|
# Clean up branch names (remove origin/ prefix, etc.)
|
||||||
|
clean_branch=$(echo "$branch" | sed 's|^origin/||' | sed 's|^remotes/origin/||')
|
||||||
|
# Skip HEAD and duplicates
|
||||||
|
if [[ "$clean_branch" != "HEAD" ]] && [[ ! " ${branches[@]} " =~ " ${clean_branch} " ]]; then
|
||||||
|
branches+=("$clean_branch")
|
||||||
|
fi
|
||||||
|
done < <(git branch -a --format="%(refname:short)" | grep -v "HEAD")
|
||||||
|
|
||||||
|
# Sort and return branches
|
||||||
|
printf '%s\n' "${branches[@]}" | sort
|
||||||
|
}
|
||||||
|
|
||||||
|
switch_to_branch() {
|
||||||
|
local dotfiles_dir="$1"
|
||||||
|
local selected_branch="$2"
|
||||||
|
|
||||||
|
cd "$dotfiles_dir"
|
||||||
|
|
||||||
|
log_info "Switching to branch: $selected_branch"
|
||||||
|
|
||||||
|
if git show-ref --verify --quiet "refs/heads/$selected_branch"; then
|
||||||
|
# Local branch exists
|
||||||
|
git checkout "$selected_branch"
|
||||||
|
elif git show-ref --verify --quiet "refs/remotes/origin/$selected_branch"; then
|
||||||
|
# Remote branch exists, create local tracking branch
|
||||||
|
git checkout -b "$selected_branch" "origin/$selected_branch"
|
||||||
|
else
|
||||||
|
log_error "Branch $selected_branch not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Pull latest changes
|
||||||
|
log_info "Pulling latest changes..."
|
||||||
|
git pull --quiet || true
|
||||||
|
|
||||||
|
log_success "Switched to branch: $selected_branch"
|
||||||
|
}
|
||||||
56
setup.sh
Normal file
56
setup.sh
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# setup.sh - Main dotfiles setup orchestrator
|
||||||
|
# Runs all numbered scripts in sequence
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Get the absolute path to the directory containing this script
|
||||||
|
# This handles symlinks and relative paths to always find the dotfiles root
|
||||||
|
DOTFILES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# Source utilities with fallback
|
||||||
|
if [ -f "$DOTFILES_DIR/scripts/utils.sh" ]; then
|
||||||
|
source "$DOTFILES_DIR/scripts/utils.sh"
|
||||||
|
else
|
||||||
|
echo "Error: scripts/utils.sh not found! Repository structure is corrupted."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "🔧 Starting dotfiles setup..."
|
||||||
|
log_info "Working directory: $DOTFILES_DIR"
|
||||||
|
|
||||||
|
# Auto-discover and run setup scripts
|
||||||
|
if [ -d "$DOTFILES_DIR/scripts" ]; then
|
||||||
|
log_info "Running setup scripts..."
|
||||||
|
|
||||||
|
# Look for numbered scripts and run them in order
|
||||||
|
script_count=0
|
||||||
|
for script in "$DOTFILES_DIR/scripts"/[0-9][0-9]-*.sh; do
|
||||||
|
if [ -f "$script" ]; then
|
||||||
|
# Make executable and run
|
||||||
|
chmod +x "$script" 2>/dev/null || true
|
||||||
|
script_name=$(basename "$script")
|
||||||
|
log_info "Running $script_name..."
|
||||||
|
|
||||||
|
if "$script" "$DOTFILES_DIR"; then
|
||||||
|
log_success "✓ $script_name completed"
|
||||||
|
else
|
||||||
|
log_error "✗ $script_name failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
((script_count++))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $script_count -eq 0 ]; then
|
||||||
|
log_warning "No numbered scripts found in scripts/ directory"
|
||||||
|
else
|
||||||
|
log_success "Completed $script_count setup script(s)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_error "No scripts directory found! Something went wrong with the installation."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "🎉 Dotfiles setup complete!"
|
||||||
Reference in New Issue
Block a user