diff --git a/install-web.sh b/install-web.sh new file mode 100755 index 0000000..067595e --- /dev/null +++ b/install-web.sh @@ -0,0 +1,171 @@ +#!/bin/bash +# One-line web installer for Modern Dotfiles Management System +# Usage: curl -fsSL https://your-domain.com/install | bash + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color + +# Configuration +REPO_URL="https://github.com/your-username/dotfiles" +DOTFILES_DIR="$HOME/.dotfiles" +BRANCH="main" + +# Function to print colored output +print_color() { + local color=$1 + local message=$2 + echo -e "${color}${message}${NC}" +} + +# Function to check prerequisites +check_prerequisites() { + print_color "$BLUE" "🔍 Checking prerequisites..." + + # Check if git is installed + if ! command -v git >/dev/null 2>&1; then + print_color "$YELLOW" "âš ī¸ Git not found. Installing git..." + + # Detect OS and install git + if command -v apt >/dev/null 2>&1; then + sudo apt update && sudo apt install -y git + elif command -v yum >/dev/null 2>&1; then + sudo yum install -y git + elif command -v dnf >/dev/null 2>&1; then + sudo dnf install -y git + elif command -v brew >/dev/null 2>&1; then + brew install git + else + print_color "$RED" "❌ Cannot install git automatically. Please install git and try again." + exit 1 + fi + fi + + # Check if curl is installed (should be, since we're using it) + if ! command -v curl >/dev/null 2>&1; then + print_color "$RED" "❌ Curl is required but not found." + exit 1 + fi + + print_color "$GREEN" "✅ Prerequisites satisfied" +} + +# Function to backup existing dotfiles +backup_existing() { + if [[ -d "$DOTFILES_DIR" ]]; then + local backup_dir="${DOTFILES_DIR}_backup_$(date +%Y%m%d_%H%M%S)" + print_color "$YELLOW" "đŸ“Ļ Backing up existing dotfiles to: $backup_dir" + mv "$DOTFILES_DIR" "$backup_dir" + fi +} + +# Function to clone repository +clone_repository() { + print_color "$BLUE" "đŸ“Ĩ Cloning dotfiles repository..." + + if git clone --depth 1 -b "$BRANCH" "$REPO_URL" "$DOTFILES_DIR"; then + print_color "$GREEN" "✅ Repository cloned successfully" + else + print_color "$RED" "❌ Failed to clone repository. Please check:" + print_color "$RED" " â€ĸ Internet connection" + print_color "$RED" " â€ĸ Repository URL: $REPO_URL" + print_color "$RED" " â€ĸ Branch: $BRANCH" + exit 1 + fi +} + +# Function to run installation +run_installation() { + print_color "$BLUE" "🚀 Running dotfiles installation..." + + cd "$DOTFILES_DIR" + + # Make install script executable + chmod +x install.sh + + # Run the installer + if ./install.sh; then + print_color "$GREEN" "🎉 Installation completed successfully!" + else + print_color "$RED" "❌ Installation failed. Check logs in ~/.dotfiles/ for details." + exit 1 + fi +} + +# Function to show completion message +show_completion() { + print_color "$CYAN" "" + print_color "$CYAN" "╔══════════════════════════════════════════════════════════════╗" + print_color "$CYAN" "║ 🎉 INSTALLATION COMPLETE! 🎉 ║" + print_color "$CYAN" "╚══════════════════════════════════════════════════════════════╝" + print_color "$CYAN" "" + print_color "$GREEN" "Your intelligent dotfiles system is ready to use!" + print_color "$CYAN" "" + print_color "$YELLOW" "💡 Quick commands to try:" + echo " dotstatus - Show sync and system status" + echo " dotprofile - Show your machine profile" + echo " dotpkgs - Show installed packages" + echo " dotupdatecheck - Check for package updates" + echo " l - Enhanced file listing" + echo " g - Git shortcut" + echo " .. - Go up one directory" + print_color "$CYAN" "" + print_color "$YELLOW" "📚 Learn more:" + echo " cat ~/.dotfiles/docs/README.md" + echo " cat ~/.dotfiles/docs/CONFIGURATION.md" + print_color "$CYAN" "" + print_color "$BLUE" "🔄 Restart your shell to activate all features:" + print_color "$BLUE" " exec \$SHELL" + print_color "$CYAN" "" +} + +# Main installation function +main() { + print_color "$CYAN" "╔══════════════════════════════════════════════════════════════╗" + print_color "$CYAN" "║ Modern Dotfiles Management System ║" + print_color "$CYAN" "║ One-Line Web Installer ║" + print_color "$CYAN" "╚══════════════════════════════════════════════════════════════╝" + print_color "$CYAN" "" + + # Show what will be installed + print_color "$BLUE" "This installer will:" + echo " â€ĸ Clone the dotfiles repository to ~/.dotfiles" + echo " â€ĸ Auto-detect your machine type (server/dev/personal)" + echo " â€ĸ Install appropriate packages for your profile" + echo " â€ĸ Set up universal aliases and shortcuts" + echo " â€ĸ Configure automatic syncing and updates" + print_color "$CYAN" "" + + # Confirm installation + if [[ -t 0 ]]; then # Only prompt if stdin is a terminal + print_color "$YELLOW" "Continue with installation? [Y/n]: " + read -r response + case "$response" in + [nN]|[nN][oO]) + print_color "$YELLOW" "Installation cancelled." + exit 0 + ;; + esac + fi + + print_color "$CYAN" "" + + # Run installation steps + check_prerequisites + backup_existing + clone_repository + run_installation + show_completion +} + +# Error handling +trap 'print_color "$RED" "❌ Installation failed. Check the error message above."; exit 1' ERR + +# Run main function +main "$@" \ No newline at end of file diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..2bb2596 --- /dev/null +++ b/install.sh @@ -0,0 +1,92 @@ +#!/bin/bash +# Main installation script + +set -e + +DOTFILES_DIR="$HOME/.dotfiles" +BACKUP_DIR="$HOME/.dotfiles_backup_$(date +%Y%m%d_%H%M%S)" + +echo "🚀 Setting up dotfiles..." + +# Source profile manager library +if [[ -f "$DOTFILES_DIR/lib/profile_manager.sh" ]]; then + source "$DOTFILES_DIR/lib/profile_manager.sh" + echo "🤖 Initializing machine profile..." + MACHINE_PROFILE=$(initialize_profile) + echo "" +else + echo "âš ī¸ Profile manager not found. Using default package set." + MACHINE_PROFILE="dev-lite" +fi + +# Source package manager library +if [[ -f "$DOTFILES_DIR/lib/package_manager.sh" ]]; then + source "$DOTFILES_DIR/lib/package_manager.sh" +else + echo "âš ī¸ Package manager library not found. Skipping package installation." +fi + +# Create backup directory +mkdir -p "$BACKUP_DIR" + +# Function to backup and symlink +backup_and_link() { + local source="$1" + local target="$2" + + if [[ -e "$target" && ! -L "$target" ]]; then + echo "đŸ“Ļ Backing up existing $target" + mv "$target" "$BACKUP_DIR/" + fi + + ln -sf "$source" "$target" + echo "🔗 Linked $source -> $target" +} + +# Check and install packages +if command -v install_all_packages >/dev/null 2>&1; then + echo "" + echo "📋 Checking and installing required packages..." + install_all_packages + echo "" +else + echo "âš ī¸ Package installation functions not available. Continuing with dotfiles setup." +fi + +# Symlink shared configs +backup_and_link "$DOTFILES_DIR/git/.gitconfig" "$HOME/.gitconfig" +backup_and_link "$DOTFILES_DIR/vim/.vimrc" "$HOME/.vimrc" + +# Detect shell and configure accordingly +CURRENT_SHELL=$(basename "$SHELL") + +case "$CURRENT_SHELL" in + "zsh") + backup_and_link "$DOTFILES_DIR/zsh/.zshrc" "$HOME/.zshrc" + echo "✅ Configured for zsh" + ;; + "bash") + backup_and_link "$DOTFILES_DIR/bash/.bashrc" "$HOME/.bashrc" + backup_and_link "$DOTFILES_DIR/bash/.bash_profile" "$HOME/.bash_profile" + echo "✅ Configured for bash" + ;; + *) + echo "âš ī¸ Unknown shell: $CURRENT_SHELL" + echo "Manually symlink appropriate shell configs" + ;; +esac + +echo "" +echo "🎉 Dotfiles installation complete!" +echo "📁 Backups stored in: $BACKUP_DIR" +echo "🔄 Restart your shell or run 'source ~/.${CURRENT_SHELL}rc' to apply changes" +echo "" +echo "📊 Installation summary:" +echo " â€ĸ Configuration files symlinked" +echo " â€ĸ Packages checked and installed" +echo " â€ĸ Shell configuration applied" +echo "" +echo "💡 Useful commands:" +echo " â€ĸ dotstatus - Check sync status" +echo " â€ĸ dotsync - Manually sync dotfiles" +echo " â€ĸ dotson/off - Enable/disable auto-sync" \ No newline at end of file diff --git a/reset.sh b/reset.sh new file mode 100755 index 0000000..9df5e05 --- /dev/null +++ b/reset.sh @@ -0,0 +1,454 @@ +#!/bin/bash +# Dotfiles Reset Script +# Restore system to plain vanilla state + +set -e + +DOTFILES_DIR="$HOME/.dotfiles" +RESET_LOG="$DOTFILES_DIR/.reset.log" +BACKUP_SUFFIX="_dotfiles_backup_$(date +%Y%m%d_%H%M%S)" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color + +# Function to log reset operations +reset_log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$RESET_LOG" +} + +# Function to print colored output +print_color() { + local color=$1 + local message=$2 + echo -e "${color}${message}${NC}" +} + +# Function to confirm dangerous operations +confirm_action() { + local message="$1" + local default="${2:-n}" + + echo -e "${YELLOW}${message}${NC}" + if [[ "$default" == "y" ]]; then + echo "Continue? [Y/n]: " + else + echo "Continue? [y/N]: " + fi + + read -r response + case "$response" in + [yY]|[yY][eS]) return 0 ;; + [nN]|[nN][oO]) return 1 ;; + "") + if [[ "$default" == "y" ]]; then + return 0 + else + return 1 + fi + ;; + *) return 1 ;; + esac +} + +# Function to backup current dotfiles +backup_dotfiles() { + print_color "$BLUE" "đŸ“Ļ Creating backup of current dotfiles..." + + local backup_dir="$HOME/dotfiles_backup_$(date +%Y%m%d_%H%M%S)" + mkdir -p "$backup_dir" + + # Backup dotfiles directory itself + if [[ -d "$DOTFILES_DIR" ]]; then + cp -r "$DOTFILES_DIR" "$backup_dir/" + reset_log "Backed up dotfiles directory to: $backup_dir" + fi + + # Backup symlinked configs + local configs=(".bashrc" ".bash_profile" ".zshrc" ".gitconfig" ".vimrc") + for config in "${configs[@]}"; do + if [[ -L "$HOME/$config" ]]; then + # It's a symlink, save the target + local target=$(readlink "$HOME/$config") + echo "$target" > "$backup_dir/${config}_symlink_target" + reset_log "Recorded symlink target for $config: $target" + elif [[ -f "$HOME/$config" ]]; then + # It's a regular file, copy it + cp "$HOME/$config" "$backup_dir/" + reset_log "Backed up file: $config" + fi + done + + print_color "$GREEN" "✅ Backup created at: $backup_dir" + echo "$backup_dir" > "$DOTFILES_DIR/.last_backup_location" +} + +# Function to remove symlinks +remove_symlinks() { + print_color "$BLUE" "🔗 Removing dotfiles symlinks..." + + local configs=(".bashrc" ".bash_profile" ".zshrc" ".gitconfig" ".vimrc") + for config in "${configs[@]}"; do + if [[ -L "$HOME/$config" ]]; then + local target=$(readlink "$HOME/$config") + if [[ "$target" == *".dotfiles"* ]]; then + rm "$HOME/$config" + print_color "$CYAN" " Removed symlink: $config" + reset_log "Removed symlink: $config -> $target" + else + print_color "$YELLOW" " Skipped $config (not a dotfiles symlink)" + fi + elif [[ -f "$HOME/$config" ]]; then + print_color "$YELLOW" " $config exists but is not a symlink (keeping as-is)" + fi + done +} + +# Function to restore original configs +restore_original_configs() { + print_color "$BLUE" "📄 Restoring original configuration files..." + + # Look for backup directories created by install.sh + local backup_dirs=("$HOME"/.dotfiles_backup_*) + local latest_backup="" + + # Find the most recent backup + for backup_dir in "${backup_dirs[@]}"; do + if [[ -d "$backup_dir" ]] && [[ "$backup_dir" > "$latest_backup" ]]; then + latest_backup="$backup_dir" + fi + done + + if [[ -n "$latest_backup" && -d "$latest_backup" ]]; then + print_color "$CYAN" " Found backup directory: $latest_backup" + + # Restore backed up files + local configs=(".bashrc" ".bash_profile" ".zshrc" ".gitconfig" ".vimrc") + for config in "${configs[@]}"; do + if [[ -f "$latest_backup/$config" ]]; then + cp "$latest_backup/$config" "$HOME/" + print_color "$GREEN" " ✅ Restored: $config" + reset_log "Restored original: $config" + fi + done + else + print_color "$YELLOW" " No backup directory found. Creating minimal configs..." + create_minimal_configs + fi +} + +# Function to create minimal default configs +create_minimal_configs() { + print_color "$BLUE" "🔧 Creating minimal default configurations..." + + # Create minimal .bashrc + if [[ ! -f "$HOME/.bashrc" ]]; then + cat > "$HOME/.bashrc" << 'EOF' +# ~/.bashrc: executed by bash(1) for non-login shells. + +# If not running interactively, don't do anything +case $- in + *i*) ;; + *) return;; +esac + +# Basic history settings +HISTCONTROL=ignoreboth +HISTSIZE=1000 +HISTFILESIZE=2000 +shopt -s histappend + +# Check window size after each command +shopt -s checkwinsize + +# Make less more friendly for non-text input files +[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" + +# Set a fancy prompt (color, if available) +if [ "$color_prompt" = yes ]; then + PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' +else + PS1='\u@\h:\w\$ ' +fi + +# Enable color support of ls and add handy aliases +if [ -x /usr/bin/dircolors ]; then + test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" + alias ls='ls --color=auto' + alias grep='grep --color=auto' +fi + +# Some more ls aliases +alias ll='ls -alF' +alias la='ls -A' +alias l='ls -CF' +EOF + print_color "$GREEN" " ✅ Created minimal .bashrc" + reset_log "Created minimal .bashrc" + fi + + # Create minimal .bash_profile + if [[ ! -f "$HOME/.bash_profile" ]]; then + cat > "$HOME/.bash_profile" << 'EOF' +# ~/.bash_profile: executed by bash(1) for login shells. + +# Source .bashrc if it exists +if [ -f ~/.bashrc ]; then + . ~/.bashrc +fi +EOF + print_color "$GREEN" " ✅ Created minimal .bash_profile" + reset_log "Created minimal .bash_profile" + fi + + # Create minimal .gitconfig (if git is installed) + if command -v git >/dev/null 2>&1 && [[ ! -f "$HOME/.gitconfig" ]]; then + cat > "$HOME/.gitconfig" << 'EOF' +[user] + name = Your Name + email = your.email@example.com +[core] + editor = vim +[color] + ui = auto +EOF + print_color "$GREEN" " ✅ Created minimal .gitconfig" + reset_log "Created minimal .gitconfig" + fi + + # Create minimal .vimrc (if vim is installed) + if command -v vim >/dev/null 2>&1 && [[ ! -f "$HOME/.vimrc" ]]; then + cat > "$HOME/.vimrc" << 'EOF' +" Basic vim configuration +syntax on +set number +set autoindent +set tabstop=4 +set shiftwidth=4 +set expandtab +EOF + print_color "$GREEN" " ✅ Created minimal .vimrc" + reset_log "Created minimal .vimrc" + fi +} + +# Function to uninstall packages +uninstall_packages() { + print_color "$BLUE" "đŸ“Ļ Uninstalling dotfiles packages..." + + # Source package manager to get current profile packages + if [[ -f "$DOTFILES_DIR/lib/package_manager.sh" ]] && [[ -f "$DOTFILES_DIR/lib/profile_manager.sh" ]]; then + source "$DOTFILES_DIR/lib/profile_manager.sh" + source "$DOTFILES_DIR/lib/package_manager.sh" + + local current_profile=$(get_current_profile) + print_color "$CYAN" " Current profile: $current_profile" + + # Uninstall npm packages + print_color "$CYAN" " Uninstalling npm packages..." + local npm_packages="claude-code gemini-cli task-master-ai pm2" + for package in $npm_packages; do + if command -v npm >/dev/null 2>&1; then + case "$package" in + "claude-code") cmd_check="claude" ;; + "gemini-cli") cmd_check="gemini" ;; + "task-master-ai") cmd_check="task-master" ;; + "pm2") cmd_check="pm2" ;; + esac + + if command -v "$cmd_check" >/dev/null 2>&1; then + if npm uninstall -g "$package" >/dev/null 2>&1; then + print_color "$GREEN" " ✅ Uninstalled: $package" + reset_log "Uninstalled npm package: $package" + else + print_color "$YELLOW" " âš ī¸ Failed to uninstall: $package" + fi + fi + fi + done + + # Remove GitHub packages + print_color "$CYAN" " Removing GitHub packages..." + local github_tools=("fzf" "bat" "rg" "fd" "delta") + for tool in "${github_tools[@]}"; do + # Remove from ~/.local/bin + if [[ -f "$HOME/.local/bin/$tool" ]]; then + rm "$HOME/.local/bin/$tool" + print_color "$GREEN" " ✅ Removed: $tool" + reset_log "Removed GitHub tool: $tool" + fi + done + + # Remove fzf installation + if [[ -d "$HOME/.fzf" ]]; then + if confirm_action "Remove fzf installation directory (~/.fzf)?"; then + rm -rf "$HOME/.fzf" + print_color "$GREEN" " ✅ Removed: ~/.fzf" + reset_log "Removed fzf directory" + fi + fi + + # Remove zsh plugins + if [[ -d "$HOME/.oh-my-zsh" ]]; then + print_color "$CYAN" " Removing custom zsh plugins..." + local plugins_dir="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins" + local plugins=("zsh-syntax-highlighting" "zsh-autosuggestions") + for plugin in "${plugins[@]}"; do + if [[ -d "$plugins_dir/$plugin" ]]; then + rm -rf "$plugins_dir/$plugin" + print_color "$GREEN" " ✅ Removed: $plugin" + reset_log "Removed zsh plugin: $plugin" + fi + done + fi + + else + print_color "$YELLOW" " Package manager not available, skipping automatic package removal" + fi +} + +# Function to clean up dotfiles directory +cleanup_dotfiles_directory() { + if confirm_action "đŸ—‘ī¸ Remove entire dotfiles directory ($DOTFILES_DIR)?"; then + # Move instead of delete for safety + local moved_dir="$HOME/dotfiles_removed_$(date +%Y%m%d_%H%M%S)" + mv "$DOTFILES_DIR" "$moved_dir" + print_color "$GREEN" "✅ Dotfiles directory moved to: $moved_dir" + print_color "$CYAN" " (You can safely delete this later if you're sure)" + reset_log "Moved dotfiles directory to: $moved_dir" + else + print_color "$YELLOW" "Keeping dotfiles directory (disabled only)" + fi +} + +# Function to display help +show_help() { + echo "Dotfiles Reset Script" + echo "====================" + echo "" + echo "Options:" + echo " --help, -h Show this help message" + echo " --soft Soft reset (remove symlinks, restore configs)" + echo " --hard Hard reset (soft + uninstall packages)" + echo " --nuclear Nuclear reset (hard + remove dotfiles directory)" + echo " --backup-only Create backup without making changes" + echo "" + echo "Reset levels:" + echo " Soft: Remove symlinks, restore original configs" + echo " Hard: Soft + uninstall dotfiles packages" + echo " Nuclear: Hard + remove dotfiles directory entirely" + echo "" +} + +# Main execution +main() { + local reset_type="interactive" + + # Parse command line arguments + case "${1:-}" in + --help|-h) + show_help + exit 0 + ;; + --soft) + reset_type="soft" + ;; + --hard) + reset_type="hard" + ;; + --nuclear) + reset_type="nuclear" + ;; + --backup-only) + reset_type="backup" + ;; + esac + + # Create log file + touch "$RESET_LOG" + reset_log "Starting dotfiles reset (type: $reset_type)" + + print_color "$RED" "🔄 DOTFILES RESET SCRIPT" + print_color "$RED" "=======================" + echo "" + + if [[ "$reset_type" == "interactive" ]]; then + print_color "$YELLOW" "Choose reset level:" + echo "1) Soft Reset - Remove symlinks, restore original configs" + echo "2) Hard Reset - Soft + uninstall packages" + echo "3) Nuclear Reset - Hard + remove dotfiles directory" + echo "4) Backup Only - Create backup without changes" + echo "5) Cancel" + echo "" + echo "Enter choice [1-5]: " + read -r choice + + case "$choice" in + 1) reset_type="soft" ;; + 2) reset_type="hard" ;; + 3) reset_type="nuclear" ;; + 4) reset_type="backup" ;; + 5|*) + print_color "$YELLOW" "Reset cancelled" + exit 0 + ;; + esac + fi + + # Always create backup first + backup_dotfiles + echo "" + + case "$reset_type" in + "backup") + print_color "$GREEN" "🎉 Backup completed successfully!" + ;; + "soft") + remove_symlinks + echo "" + restore_original_configs + echo "" + print_color "$GREEN" "🎉 Soft reset completed!" + print_color "$CYAN" " Dotfiles configurations removed, original configs restored" + ;; + "hard") + remove_symlinks + echo "" + restore_original_configs + echo "" + uninstall_packages + echo "" + print_color "$GREEN" "🎉 Hard reset completed!" + print_color "$CYAN" " Configs reset and packages uninstalled" + ;; + "nuclear") + remove_symlinks + echo "" + restore_original_configs + echo "" + uninstall_packages + echo "" + cleanup_dotfiles_directory + echo "" + print_color "$GREEN" "🎉 Nuclear reset completed!" + print_color "$CYAN" " Everything removed - system restored to vanilla state" + ;; + esac + + echo "" + print_color "$BLUE" "💡 Notes:" + echo " â€ĸ Backup location saved for reference" + echo " â€ĸ You may need to restart your shell" + echo " â€ĸ Check ~/.dotfiles/.reset.log for detailed logs" + + if [[ "$reset_type" != "nuclear" ]]; then + echo " â€ĸ Run './reset.sh --nuclear' to completely remove dotfiles" + fi +} + +# Run main function with all arguments +main "$@" \ No newline at end of file diff --git a/sync.sh b/sync.sh new file mode 100755 index 0000000..a22c04c --- /dev/null +++ b/sync.sh @@ -0,0 +1,172 @@ +#!/bin/bash +# Dotfiles sync script +# Automatically checks for updates and applies them + +set -e + +DOTFILES_DIR="$HOME/.dotfiles" +SYNC_LOG="$DOTFILES_DIR/.sync.log" +LAST_SYNC_FILE="$DOTFILES_DIR/.last_sync" +SYNC_ENABLED_FILE="$DOTFILES_DIR/.sync_enabled" + +# Check if sync is enabled (default: enabled) +if [[ -f "$SYNC_ENABLED_FILE" ]]; then + SYNC_ENABLED=$(cat "$SYNC_ENABLED_FILE") +else + SYNC_ENABLED="true" + echo "true" > "$SYNC_ENABLED_FILE" +fi + +if [[ "$SYNC_ENABLED" != "true" ]]; then + exit 0 +fi + +# Function to log with timestamp +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$SYNC_LOG" +} + +# Function to check if we should sync (avoid too frequent checks) +should_sync() { + local current_time=$(date +%s) + local last_sync_time=0 + local manual_sync=${1:-false} + + if [[ -f "$LAST_SYNC_FILE" ]]; then + last_sync_time=$(cat "$LAST_SYNC_FILE") + fi + + # If manual sync, always allow + if [[ "$manual_sync" == "true" ]]; then + return 0 + fi + + # For auto-sync, only sync if more than 24 hours (86400 seconds) have passed + local time_diff=$((current_time - last_sync_time)) + if [[ $time_diff -lt 86400 ]]; then + return 1 + fi + + return 0 +} + +# Function to perform the sync +sync_dotfiles() { + cd "$DOTFILES_DIR" + + # Check if we're in a git repository + if ! git rev-parse --git-dir > /dev/null 2>&1; then + log "Not in a git repository, skipping sync" + return 0 + fi + + # Store current commit hash + local current_commit=$(git rev-parse HEAD 2>/dev/null || echo "") + + # Fetch latest changes (suppress output for smooth terminal startup) + if git fetch origin > /dev/null 2>&1; then + log "Successfully fetched from origin" + else + log "Failed to fetch from origin" + return 1 + fi + + # Check if there are updates available + local remote_commit=$(git rev-parse origin/$(git branch --show-current) 2>/dev/null || echo "") + + if [[ -n "$current_commit" && -n "$remote_commit" && "$current_commit" != "$remote_commit" ]]; then + log "Updates available, pulling changes" + + # Check for local changes + if ! git diff-index --quiet HEAD -- 2>/dev/null; then + log "Local changes detected, stashing before pull" + git stash push -m "Auto-stash before sync $(date)" > /dev/null 2>&1 + local stashed=true + fi + + # Pull changes + if git pull origin $(git branch --show-current) > /dev/null 2>&1; then + log "Successfully pulled updates" + + # Re-run install script to apply any new configurations + if [[ -x "$DOTFILES_DIR/install.sh" ]]; then + log "Re-running install script" + "$DOTFILES_DIR/install.sh" > /dev/null 2>&1 + fi + + # Check for any missing packages after sync + if [[ -f "$DOTFILES_DIR/lib/package_manager.sh" ]]; then + log "Checking for missing packages after sync" + source "$DOTFILES_DIR/lib/package_manager.sh" + install_all_packages true > /dev/null 2>&1 # Skip optional packages for sync + fi + + # Restore stashed changes if any + if [[ "$stashed" == "true" ]]; then + log "Restoring stashed changes" + git stash pop > /dev/null 2>&1 || log "Failed to restore stashed changes" + fi + + # Notify user (only in interactive shells) + if [[ $- == *i* ]]; then + echo "🔄 Dotfiles updated! Changes applied automatically." + fi + else + log "Failed to pull updates" + return 1 + fi + else + log "No updates available" + fi + + # Update last sync time + echo "$(date +%s)" > "$LAST_SYNC_FILE" + return 0 +} + +# Main execution +main() { + local manual_sync=false + + # Check if this is a manual sync + if [[ "${1:-}" == "--manual" ]] || [[ "${1:-}" == "--force" ]]; then + manual_sync=true + fi + + # Create log file if it doesn't exist + touch "$SYNC_LOG" + + # Check if we should sync + if should_sync "$manual_sync"; then + if [[ "$manual_sync" == "true" ]]; then + log "Starting manual sync" + echo "🔄 Running manual dotfiles sync..." + else + log "Starting daily auto-sync check" + fi + sync_dotfiles + else + if [[ "$manual_sync" == "true" ]]; then + # Even for manual sync, show when last sync was + if [[ -f "$LAST_SYNC_FILE" ]]; then + local last_sync=$(cat "$LAST_SYNC_FILE") + local last_sync_date=$(date -d "@$last_sync" 2>/dev/null || echo "Unknown") + echo "â„šī¸ Last sync: $last_sync_date" + fi + log "Manual sync requested but running anyway" + sync_dotfiles + else + log "Auto-sync skipped - last sync was less than 24 hours ago" + fi + fi +} + +# Run sync in background to avoid blocking terminal startup (only for auto-sync) +if [[ "${1:-}" == "--background" ]]; then + main & + disown +elif [[ "${1:-}" == "--manual" ]] || [[ "${1:-}" == "--force" ]]; then + main "$1" +else + main +fi \ No newline at end of file