| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 | #!/bin/bash# Dotfiles sync scriptset -euo pipefail# Configurationcheckout_dir=$HOME/.dofilesscript_dir=$checkout_dir/dotfilesdots_dir=$checkout_dir/dotfiles/dotsrepo="https://git.capella.pro/capella/dotfiles.git"ProgName=$(basename "$0")# Colors for outputRED='\033[0;31m'GREEN='\033[0;32m'YELLOW='\033[1;33m'NC='\033[0m' # No Color# Logging functionslog_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 exittrap 'cd "$HOME"' EXIT# Create checkout directory if neededmkdir -p "$checkout_dir"sub_help(){    cat << EOFUsage: $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 repoFor help with each subcommand run:    $ProgName <subcommand> -h|--helpEOF}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
 |