| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- #!/bin/bash
- # Dotfiles sync script
- set -euo pipefail
- # Configuration
- checkout_dir=$HOME/.dofiles
- script_dir=$checkout_dir/dotfiles
- dots_dir=$checkout_dir/dotfiles/dots
- repo="https://git.capella.pro/capella/dotfiles.git"
- ProgName=$(basename "$0")
- # Colors for output
- RED='\033[0;31m'
- GREEN='\033[0;32m'
- YELLOW='\033[1;33m'
- NC='\033[0m' # No Color
- # Logging functions
- log_info() { echo -e "${GREEN}[INFO]${NC} $*"; }
- log_warn() { echo -e "${YELLOW}[WARN]${NC} $*" >&2; }
- log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
- # Ensure we cleanup on exit
- trap 'cd "$HOME"' EXIT
- # Create checkout directory if needed
- mkdir -p "$checkout_dir"
- sub_help(){
- cat << EOF
- Usage: $ProgName <subcommand> [options]
- Subcommands:
- sync [dot_file_to_add] Sync dotfiles (optionally add a new dotfile to track)
- list List currently synced dotfiles
- status Show git status of dotfiles repo
- For help with each subcommand run:
- $ProgName <subcommand> -h|--help
- EOF
- }
- sub_sync(){
- # Clone repo if it doesn't exist
- if [[ ! -d "$checkout_dir/dotfiles/.git" ]]; then
- log_info "Cloning dotfiles repository..."
- cd "$checkout_dir"
- if ! git clone "$repo"; then
- log_error "Failed to clone repository"
- exit 1
- fi
- cd "$script_dir"
- git submodule init
- git submodule update
- fi
- # Navigate to script directory
- cd "$script_dir"
- # Get current branch
- current_branch=$(git branch --show-current)
- log_info "Pulling latest changes from $current_branch..."
- git pull origin "$current_branch" || log_warn "Git pull failed, continuing anyway"
- git submodule update --init --recursive
- # Add new dotfile if provided
- if [ -n "${1:-}" ]; then
- local file_to_add="$1"
- # Validate that file exists
- if [[ ! -e "$HOME/$file_to_add" ]]; then
- log_error "File $HOME/$file_to_add does not exist"
- exit 1
- fi
- log_info "Adding new dotfile: $file_to_add"
- real_path=$(realpath "$HOME/$file_to_add")
- to_copy=${real_path#"$HOME/"}
- # Create parent directory structure in dots_dir
- mkdir -p "$(dirname "$dots_dir/$to_copy")"
- # Copy to dots directory
- if cp -R "$real_path" "$dots_dir/$to_copy"; then
- log_info "Copied $file_to_add to $dots_dir/$to_copy"
- else
- log_error "Failed to copy $file_to_add"
- exit 1
- fi
- # Add to to_sync file if not already there
- if ! grep -qxF "$to_copy" "$script_dir/to_sync" 2>/dev/null; then
- echo "$to_copy" >> "$script_dir/to_sync"
- log_info "Added $to_copy to sync list"
- else
- log_info "$to_copy already in sync list"
- fi
- # Remove original and create symlink
- rm -rf "$real_path"
- ln -s "$dots_dir/$to_copy" "$real_path"
- log_info "Created symlink: $real_path -> $dots_dir/$to_copy"
- fi
- # Add tracked files to git
- log_info "Staging changes..."
- while IFS= read -r p; do
- [[ -z "$p" ]] && continue # Skip empty lines
- file="$dots_dir/$p"
- if [[ -e "$file" ]]; then
- git add "$file"
- else
- log_warn "Tracked file $file does not exist"
- fi
- done < "$script_dir/to_sync"
- git add start to_sync
- # Commit and push if there are changes
- if git diff --cached --quiet; then
- log_info "No changes to commit"
- else
- log_info "Committing changes..."
- if git commit -m "Sync: $(date '+%Y-%m-%d %H:%M:%S')"; then
- log_info "Pushing to remote..."
- git remote add origin "$repo" 2>/dev/null || true
- if git push -u origin "$current_branch"; then
- log_info "Successfully pushed changes"
- else
- log_error "Failed to push changes"
- exit 1
- fi
- else
- log_error "Failed to commit changes"
- exit 1
- fi
- fi
- # Create symlinks for all tracked dotfiles
- log_info "Creating symlinks..."
- while IFS= read -r p; do
- [[ -z "$p" ]] && continue # Skip empty lines
- destination="$HOME/$p"
- source="$dots_dir/$p"
- if [[ ! -e "$source" ]]; then
- log_warn "Source file $source does not exist, skipping"
- continue
- fi
- # Check if destination is already a correct symlink
- if [[ -L "$destination" ]] && [[ "$(readlink "$destination")" == "$source" ]]; then
- continue # Already correctly linked
- fi
- # Remove destination if it exists
- if [[ -e "$destination" ]] || [[ -L "$destination" ]]; then
- log_warn "Removing existing $destination"
- rm -rf "$destination"
- fi
- # Create parent directory if needed
- mkdir -p "$(dirname "$destination")"
- # Create symlink
- if ln -s "$source" "$destination"; then
- log_info "Linked: $destination -> $source"
- else
- log_error "Failed to create symlink for $p"
- fi
- done < "$script_dir/to_sync"
- log_info "Sync complete!"
- }
- sub_list(){
- if [[ ! -f "$script_dir/to_sync" ]]; then
- log_error "No to_sync file found"
- exit 1
- fi
- echo "Currently synced dotfiles:"
- while IFS= read -r p; do
- [[ -z "$p" ]] && continue
- if [[ -L "$HOME/$p" ]]; then
- echo " ✓ $p"
- else
- echo " ✗ $p (not linked)"
- fi
- done < "$script_dir/to_sync"
- }
- sub_status(){
- if [[ ! -d "$script_dir/.git" ]]; then
- log_error "Dotfiles repository not found"
- exit 1
- fi
- cd "$script_dir"
- log_info "Git status:"
- git status
- }
- subcommand=${1:-}
- case $subcommand in
- "" | "-h" | "--help")
- sub_help
- ;;
- sync|list|status)
- shift
- "sub_${subcommand}" "$@"
- ;;
- *)
- log_error "'$subcommand' is not a known subcommand."
- echo " Run '$ProgName --help' for a list of known subcommands." >&2
- exit 1
- ;;
- esac
|