|  | @@ -1,78 +1,223 @@
 | 
											
												
													
														|  |  #!/bin/bash
 |  |  #!/bin/bash
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +# Dotfiles sync script
 | 
											
												
													
														|  | 
 |  | +set -euo pipefail
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +# Configuration
 | 
											
												
													
														|  |  checkout_dir=$HOME/.dofiles
 |  |  checkout_dir=$HOME/.dofiles
 | 
											
												
													
														|  |  script_dir=$checkout_dir/dotfiles
 |  |  script_dir=$checkout_dir/dotfiles
 | 
											
												
													
														|  |  dots_dir=$checkout_dir/dotfiles/dots
 |  |  dots_dir=$checkout_dir/dotfiles/dots
 | 
											
												
													
														|  |  repo="https://git.capella.pro/capella/dotfiles.git"
 |  |  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
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -set -ex
 |  | 
 | 
											
												
													
														|  | -trap popd EXIT
 |  | 
 | 
											
												
													
														|  | 
 |  | +# 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; }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -mkdir -pv $checkout_dir
 |  | 
 | 
											
												
													
														|  | -pushd $checkout_dir
 |  | 
 | 
											
												
													
														|  | 
 |  | +# Ensure we cleanup on exit
 | 
											
												
													
														|  | 
 |  | +trap 'cd "$HOME"' EXIT
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +# Create checkout directory if needed
 | 
											
												
													
														|  | 
 |  | +mkdir -p "$checkout_dir"
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  sub_help(){
 |  |  sub_help(){
 | 
											
												
													
														|  | -    echo "Usage: $ProgName <subcommand> [options]\n"
 |  | 
 | 
											
												
													
														|  | -    echo "Subcommands:"
 |  | 
 | 
											
												
													
														|  | -    echo "    sync <dot_file_to_add>    add a dotfyle to sync"
 |  | 
 | 
											
												
													
														|  | -    echo ""
 |  | 
 | 
											
												
													
														|  | -    echo "For help with each subcommand run:"
 |  | 
 | 
											
												
													
														|  | -    echo "$ProgName <subcommand> -h|--help"
 |  | 
 | 
											
												
													
														|  | -    echo ""
 |  | 
 | 
											
												
													
														|  | 
 |  | +    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(){
 |  |  sub_sync(){
 | 
											
												
													
														|  | -    if [[ ! -d $checkout_dir/dotfiles/.git ]]; then
 |  | 
 | 
											
												
													
														|  | -        git clone $repo
 |  | 
 | 
											
												
													
														|  | -        pushd dotfiles
 |  | 
 | 
											
												
													
														|  | 
 |  | +    # 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 init
 | 
											
												
													
														|  | -        popd
 |  | 
 | 
											
												
													
														|  | 
 |  | +        git submodule update
 | 
											
												
													
														|  |      fi
 |  |      fi
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    cd $script_dir
 |  | 
 | 
											
												
													
														|  | -    git pull origin master
 |  | 
 | 
											
												
													
														|  | -    git submodule update
 |  | 
 | 
											
												
													
														|  | 
 |  | +    # 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
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    if [ ! -z "$1" ]; then
 |  | 
 | 
											
												
													
														|  | -        real_path=$(realpath $HOME/$1)
 |  | 
 | 
											
												
													
														|  | -        to_copy=${real_path#*$HOME}
 |  | 
 | 
											
												
													
														|  | -        cp -R $real_path $dots_dir/$to_copy
 |  | 
 | 
											
												
													
														|  | -        rm -rf $real_path
 |  | 
 | 
											
												
													
														|  | -        echo "${to_copy/\//}" >> $script_dir/to_sync
 |  | 
 | 
											
												
													
														|  | 
 |  | +        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
 |  |      fi
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    while read p; do
 |  | 
 | 
											
												
													
														|  | 
 |  | +    # 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"
 |  |          file="$dots_dir/$p"
 | 
											
												
													
														|  | -        git add $file
 |  | 
 | 
											
												
													
														|  | -    done < $script_dir/to_sync
 |  | 
 | 
											
												
													
														|  | 
 |  | +        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
 |  |      git add start to_sync
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    if git commit -am "$(date)"; then
 |  | 
 | 
											
												
													
														|  | -        git remote add origin $repo || true
 |  | 
 | 
											
												
													
														|  | -        git push -u origin master
 |  | 
 | 
											
												
													
														|  | 
 |  | +    # 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
 |  |      fi
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    while read p; do
 |  | 
 | 
											
												
													
														|  | 
 |  | +    # 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"
 |  |          destination="$HOME/$p"
 | 
											
												
													
														|  | -        rm -rf $destination
 |  | 
 | 
											
												
													
														|  | -        ln -s "$dots_dir/$p" $destination
 |  | 
 | 
											
												
													
														|  | -    done < $script_dir/to_sync
 |  | 
 | 
											
												
													
														|  | 
 |  | +        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
 |  | 
 | 
											
												
													
														|  | 
 |  | +subcommand=${1:-}
 | 
											
												
													
														|  |  case $subcommand in
 |  |  case $subcommand in
 | 
											
												
													
														|  |      "" | "-h" | "--help")
 |  |      "" | "-h" | "--help")
 | 
											
												
													
														|  |          sub_help
 |  |          sub_help
 | 
											
												
													
														|  |          ;;
 |  |          ;;
 | 
											
												
													
														|  | -    *)
 |  | 
 | 
											
												
													
														|  | 
 |  | +    sync|list|status)
 | 
											
												
													
														|  |          shift
 |  |          shift
 | 
											
												
													
														|  | -        sub_${subcommand} $@
 |  | 
 | 
											
												
													
														|  | -        if [ $? = 127 ]; then
 |  | 
 | 
											
												
													
														|  | -            echo "Error: '$subcommand' is not a known subcommand." >&2
 |  | 
 | 
											
												
													
														|  | -            echo "       Run '$ProgName --help' for a list of known subcommands." >&2
 |  | 
 | 
											
												
													
														|  | -            exit 1
 |  | 
 | 
											
												
													
														|  | -        fi
 |  | 
 | 
											
												
													
														|  | 
 |  | +        "sub_${subcommand}" "$@"
 | 
											
												
													
														|  | 
 |  | +        ;;
 | 
											
												
													
														|  | 
 |  | +    *)
 | 
											
												
													
														|  | 
 |  | +        log_error "'$subcommand' is not a known subcommand."
 | 
											
												
													
														|  | 
 |  | +        echo "       Run '$ProgName --help' for a list of known subcommands." >&2
 | 
											
												
													
														|  | 
 |  | +        exit 1
 | 
											
												
													
														|  |          ;;
 |  |          ;;
 | 
											
												
													
														|  |  esac
 |  |  esac
 |