Compare commits
2 Commits
03150e539d
...
73a38cfa55
| Author | SHA1 | Date | |
|---|---|---|---|
| 73a38cfa55 | |||
| c366cd7789 |
76
README.md
76
README.md
@@ -1,8 +1,82 @@
|
||||
# Ryan's Dotfiles
|
||||
|
||||
Minimal Bash dotfiles for SSH and remote environments.
|
||||
A comprehensive dotfiles repository for SSH, remote environments, and development setups.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
~/.dotfiles/
|
||||
├── stow/ # Configs organized for GNU Stow
|
||||
│ ├── bash/ # Bash configuration (.bashrc, .bash_aliases, .inputrc)
|
||||
│ ├── git/ # Git configuration (.gitconfig)
|
||||
│ └── [other configs] # Additional configurations as needed
|
||||
├── packages/
|
||||
│ ├── base.txt # MUST HAVE - Core system utilities
|
||||
│ ├── cli-tools.txt # Nice-to-haves - Enhanced CLI tools
|
||||
│ ├── dev.txt # Development tools (docker, node, etc)
|
||||
│ └── gui.txt # Desktop applications
|
||||
├── bootstrap.sh # Get repo and select branch
|
||||
├── setup.sh # Main orchestrator - runs all configuration
|
||||
└── README.md # Documentation
|
||||
```
|
||||
|
||||
## Install
|
||||
|
||||
**From internet:**
|
||||
|
||||
```bash
|
||||
bash <(curl -sL https://ryans.tools/dotfiles)
|
||||
```
|
||||
|
||||
**From local copy (USB drive, etc.):**
|
||||
|
||||
```bash
|
||||
./bootstrap.sh
|
||||
```
|
||||
|
||||
The installer will:
|
||||
|
||||
1. **Bootstrap:** Clone repository (or use local copy), select branch if desired
|
||||
2. **Setup:** Check dependencies, backup conflicts, install packages, configure with GNU Stow
|
||||
|
||||
## Updates
|
||||
|
||||
**Regular updates (recommended):**
|
||||
|
||||
```bash
|
||||
dotpull
|
||||
```
|
||||
|
||||
**Branch switching or major re-setup:**
|
||||
|
||||
```bash
|
||||
cd ~/.dotfiles && ./bootstrap.sh
|
||||
```
|
||||
|
||||
## Package Categories
|
||||
|
||||
- **Base**: Essential system utilities (curl, git, stow, vim, etc.)
|
||||
- **CLI Tools**: Enhanced command-line tools (bat, fzf, ripgrep, etc.)
|
||||
- **Development**: Programming languages and dev tools (nodejs, python, docker, etc.)
|
||||
- **GUI**: Desktop applications (firefox, code, vlc, etc.)
|
||||
|
||||
## Updating
|
||||
|
||||
After initial installation, use the `dotpull` alias to update your dotfiles:
|
||||
|
||||
```bash
|
||||
dotpull
|
||||
```
|
||||
|
||||
This alias will pull the latest changes and re-stow all configurations.
|
||||
|
||||
## Manual Management
|
||||
|
||||
To manage individual configurations:
|
||||
|
||||
```bash
|
||||
cd ~/.dotfiles/stow
|
||||
stow -t ~ bash # Link bash config
|
||||
stow -t ~ git # Link git config
|
||||
stow -D bash # Unlink bash config
|
||||
```
|
||||
|
||||
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
|
||||
51
install.sh
51
install.sh
@@ -1,51 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
DOTFILES_REPO="https://gitea.purpleraft.com/ryan/dotfiles"
|
||||
DOTFILES_DIR="$HOME/.dotfiles"
|
||||
|
||||
# Source utilities if available
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if [ -f "$SCRIPT_DIR/scripts/utils.sh" ]; then
|
||||
source "$SCRIPT_DIR/scripts/utils.sh"
|
||||
else
|
||||
# Fallback logging functions if utils not available
|
||||
log_info() { echo "ℹ️ $1"; }
|
||||
log_success() { echo "✅ $1"; }
|
||||
log_warning() { echo "⚠️ $1"; }
|
||||
log_error() { echo "❌ $1"; }
|
||||
fi
|
||||
|
||||
log_info "🚀 Starting dotfiles installation..."
|
||||
|
||||
# Clone or update the repo
|
||||
if [ -d "$DOTFILES_DIR/.git" ]; then
|
||||
log_info "Updating existing dotfiles repo..."
|
||||
git -C "$DOTFILES_DIR" pull --quiet
|
||||
else
|
||||
log_info "Cloning dotfiles into $DOTFILES_DIR..."
|
||||
git clone "$DOTFILES_REPO" "$DOTFILES_DIR"
|
||||
fi
|
||||
|
||||
cd "$DOTFILES_DIR"
|
||||
|
||||
# Auto-discover and run setup scripts
|
||||
if [ -d "scripts" ]; then
|
||||
log_info "Running setup scripts..."
|
||||
|
||||
# Look for numbered scripts and run them in order
|
||||
for script in 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..."
|
||||
"$script" "$DOTFILES_DIR"
|
||||
fi
|
||||
done
|
||||
else
|
||||
log_error "No scripts directory found! Something went wrong with the installation."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "Dotfiles install complete!"
|
||||
@@ -12,17 +12,41 @@ backup_files() {
|
||||
|
||||
log_info "Checking for file conflicts..."
|
||||
|
||||
# Files that might conflict with stow
|
||||
local files_to_check=(.bashrc .bash_aliases .inputrc .gitconfig)
|
||||
local backed_up=()
|
||||
|
||||
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
|
||||
# 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[*]}"
|
||||
|
||||
@@ -10,16 +10,43 @@ source "$SCRIPT_DIR/utils.sh"
|
||||
setup_stow() {
|
||||
local dotfiles_dir="$1"
|
||||
|
||||
# Change to dotfiles directory and use stow to create symlinks
|
||||
cd "$dotfiles_dir"
|
||||
log_info "Using Stow to symlink dotfiles..."
|
||||
|
||||
if ! stow --adopt -t "$HOME" . 2>/dev/null; then
|
||||
log_warning "Adopting failed, trying regular stow..."
|
||||
stow -t "$HOME" .
|
||||
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
|
||||
|
||||
log_success "Stow setup complete"
|
||||
}
|
||||
|
||||
# Run if called directly
|
||||
|
||||
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
|
||||
@@ -34,3 +34,49 @@ command_exists() {
|
||||
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