From e91ebc68b54efb65e30c00fa3d1b99d1ace22d3e Mon Sep 17 00:00:00 2001 From: "courtmanr@gmail.com" Date: Wed, 4 Jun 2025 17:45:06 +0100 Subject: [PATCH] feat: add comprehensive backup/restore functionality to installer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added backup prompt during Pulse removal with user choice - Implemented automatic backup detection during fresh installs - Added interactive restore menu showing available backups with dates/versions - Comprehensive backup includes: user data, .env config, and config directory - Seamless restore process with proper file permissions - Backup location: /tmp/pulse-backup-YYYYMMDD-HHMMSS with info file - Prevents data loss during removal/reinstallation workflows 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- scripts/install-pulse.sh | 145 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 1 deletion(-) diff --git a/scripts/install-pulse.sh b/scripts/install-pulse.sh index 7dc1a3158..486fabd68 100755 --- a/scripts/install-pulse.sh +++ b/scripts/install-pulse.sh @@ -201,6 +201,94 @@ print_welcome() { echo "" } +# Backup and restore functions +create_backup() { + local backup_dir="/tmp/pulse-backup-$(date +%Y%m%d-%H%M%S)" + + if [ ! -d "$PULSE_DIR" ]; then + return 1 + fi + + print_info "Creating backup at $backup_dir..." + mkdir -p "$backup_dir" + + # Backup user data directory + if [ -d "$PULSE_DIR/data" ]; then + cp -r "$PULSE_DIR/data" "$backup_dir/" + print_success "Backed up user data" + fi + + # Backup configuration files + if [ -f "$PULSE_DIR/.env" ]; then + cp "$PULSE_DIR/.env" "$backup_dir/" + print_success "Backed up .env configuration" + fi + + # Backup config directory if it exists + if [ -d "$PULSE_DIR/config" ]; then + cp -r "$PULSE_DIR/config" "$backup_dir/" + print_success "Backed up config directory" + fi + + # Create backup info file + cat > "$backup_dir/backup-info.txt" << EOF +Pulse Backup Created: $(date) +Original Installation: $PULSE_DIR +Pulse Version: $(get_current_version 2>/dev/null || echo "Unknown") +Backup Contents: +$(ls -la "$backup_dir") +EOF + + echo "$backup_dir" + return 0 +} + +detect_backups() { + find /tmp -maxdepth 1 -name "pulse-backup-*" -type d 2>/dev/null | sort -r +} + +restore_from_backup() { + local backup_dir="$1" + + if [ ! -d "$backup_dir" ]; then + print_error "Backup directory does not exist: $backup_dir" + return 1 + fi + + print_info "Restoring from backup: $backup_dir" + + # Ensure target directories exist + mkdir -p "$PULSE_DIR" + chown "$PULSE_USER:$PULSE_USER" "$PULSE_DIR" + + # Restore user data + if [ -d "$backup_dir/data" ]; then + print_info "Restoring user data..." + cp -r "$backup_dir/data" "$PULSE_DIR/" + chown -R "$PULSE_USER:$PULSE_USER" "$PULSE_DIR/data" + print_success "User data restored" + fi + + # Restore .env file + if [ -f "$backup_dir/.env" ]; then + print_info "Restoring configuration..." + cp "$backup_dir/.env" "$PULSE_DIR/" + chown "$PULSE_USER:$PULSE_USER" "$PULSE_DIR/.env" + chmod 600 "$PULSE_DIR/.env" + print_success "Configuration restored" + fi + + # Restore config directory + if [ -d "$backup_dir/config" ]; then + print_info "Restoring config directory..." + cp -r "$backup_dir/config" "$PULSE_DIR/" + chown -R "$PULSE_USER:$PULSE_USER" "$PULSE_DIR/config" + print_success "Config directory restored" + fi + + return 0 +} + # Get latest release tag from GitHub get_latest_release_tag() { local api_url="https://api.github.com/repos/rcourtman/Pulse/releases/latest" @@ -352,6 +440,35 @@ check_installation() { else INSTALL_MODE="install" print_info "Pulse is not installed" + + # Check for existing backups + local backups=($(detect_backups)) + if [ ${#backups[@]} -gt 0 ]; then + echo "" + print_info "Found ${#backups[@]} backup(s) from previous installations:" + for i in "${!backups[@]}"; do + local backup_dir="${backups[$i]}" + local backup_date=$(basename "$backup_dir" | sed 's/pulse-backup-//' | sed 's/-/ /' | sed 's/\([0-9]\{4\}\)\([0-9]\{2\}\)\([0-9]\{2\}\) \([0-9]\{2\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)/\3\/\2\/\1 \4:\5:\6/') + local backup_info="" + if [ -f "$backup_dir/backup-info.txt" ]; then + backup_info=" - $(grep "Pulse Version:" "$backup_dir/backup-info.txt" 2>/dev/null | cut -d: -f2 | xargs)" + fi + echo " $((i+1))) $(basename "$backup_dir") (Created: $backup_date$backup_info)" + done + echo " $((${#backups[@]}+1))) Fresh install (no restore)" + echo "" + read -p "Choose backup to restore [1-$((${#backups[@]}+1))]: " backup_choice + + if [[ "$backup_choice" =~ ^[0-9]+$ ]] && [ "$backup_choice" -ge 1 ] && [ "$backup_choice" -le ${#backups[@]} ]; then + RESTORE_BACKUP="${backups[$((backup_choice-1))]}" + print_info "Will restore from: $(basename "$RESTORE_BACKUP")" + elif [ "$backup_choice" -eq $((${#backups[@]}+1)) ]; then + print_info "Will perform fresh installation" + else + print_error "Invalid choice" + exit 1 + fi + fi fi } @@ -578,6 +695,12 @@ perform_install() { configure_environment setup_systemd_service + # Restore backup if selected + if [ -n "$RESTORE_BACKUP" ]; then + echo "" + restore_from_backup "$RESTORE_BACKUP" + fi + echo "" print_success "Installation complete!" } @@ -652,7 +775,27 @@ perform_update() { # Remove Pulse perform_remove() { print_warning "This will completely remove Pulse" - read -p "Are you sure? [y/N]: " confirm + + # Offer backup option + if [ -d "$PULSE_DIR" ] && ([ -d "$PULSE_DIR/data" ] || [ -f "$PULSE_DIR/.env" ] || [ -d "$PULSE_DIR/config" ]); then + echo "" + print_info "Would you like to backup your data and configuration before removal?" + print_info "This includes: user data, .env settings, and config files" + read -p "Create backup? [Y/n]: " backup_confirm + + if [[ ! "$backup_confirm" =~ ^[Nn]$ ]]; then + backup_path=$(create_backup) + if [ $? -eq 0 ]; then + print_success "Backup created at: $backup_path" + print_info "You can restore this backup during future installations" + else + print_warning "Backup failed, but continuing with removal" + fi + fi + echo "" + fi + + read -p "Are you sure you want to remove Pulse? [y/N]: " confirm if [[ ! "$confirm" =~ ^[Yy]$ ]]; then print_info "Cancelled"