@@ -376,6 +376,24 @@ perform_tarball_update() {
376376
377377 print_info " Attempting to update Pulse to version $TARGET_TAG using tarball..."
378378
379+ # Check if release exists before proceeding
380+ if ! check_release_exists " $TARGET_TAG " ; then
381+ print_warning " Release tarball not available for $TARGET_TAG , falling back to git..."
382+ return 1
383+ fi
384+
385+ # Create comprehensive backup of user data
386+ local user_data_backup=" "
387+ if [ -d " $PULSE_DIR " ]; then
388+ cd " $PULSE_DIR " || { print_error " Failed to cd into $PULSE_DIR " ; return 1; }
389+ if user_data_backup=$( backup_user_data) ; then
390+ print_success " User data backup created for tarball update"
391+ else
392+ print_warning " Failed to create user data backup, proceeding anyway..."
393+ fi
394+ cd ..
395+ fi
396+
379397 local script_backup_path=" /tmp/${SCRIPT_NAME} .bak"
380398 if [ -n " $SCRIPT_ABS_PATH " ] && [ -f " $SCRIPT_ABS_PATH " ]; then
381399 print_info " Backing up current installer script to $script_backup_path ..."
@@ -389,17 +407,10 @@ perform_tarball_update() {
389407 script_backup_path=" "
390408 fi
391409
392- # Check if release exists before proceeding
393- if ! check_release_exists " $TARGET_TAG " ; then
394- print_warning " Release tarball not available for $TARGET_TAG , falling back to git..."
395- [ -n " $script_backup_path " ] && rm -f " $script_backup_path "
396- return 1
397- fi
398-
399- # Backup current installation directory
410+ # Backup current installation directory (full backup as fallback)
400411 local backup_dir=" /tmp/pulse-backup-$( date +%s) "
401412 if [ -d " $PULSE_DIR " ]; then
402- print_info " Backing up current installation..."
413+ print_info " Creating full installation backup as safety measure ..."
403414 if ! cp -r " $PULSE_DIR " " $backup_dir " ; then
404415 print_warning " Failed to backup current installation, continuing anyway..."
405416 fi
@@ -461,6 +472,16 @@ perform_tarball_update() {
461472 print_info " CSS assets already present and up-to-date."
462473 fi
463474
475+ # Restore user data after successful tarball extraction
476+ if [ -n " $user_data_backup " ] && [ -d " $user_data_backup " ]; then
477+ if restore_user_data " $user_data_backup " ; then
478+ print_success " User configuration restored after tarball update"
479+ else
480+ print_warning " Failed to restore some user configuration"
481+ fi
482+ rm -rf " $user_data_backup "
483+ fi
484+
464485 # Cleanup backup if successful
465486 [ -d " $backup_dir " ] && rm -rf " $backup_dir "
466487
@@ -471,16 +492,53 @@ perform_tarball_update() {
471492
472493 # Restore backup if available
473494 if [ -d " $backup_dir " ]; then
474- print_info " Restoring from backup..."
495+ print_info " Restoring from full backup..."
475496 rm -rf " $PULSE_DIR "
476497 mv " $backup_dir " " $PULSE_DIR "
477498 fi
478499
500+ # Cleanup failed backups
479501 [ -n " $script_backup_path " ] && rm -f " $script_backup_path "
502+ [ -n " $user_data_backup " ] && rm -rf " $user_data_backup "
480503 return 1
481504 fi
482505}
483506
507+ # Emergency recovery function for when updates fail
508+ emergency_recovery () {
509+ local backup_dir=" $1 "
510+
511+ print_error " ==================== EMERGENCY RECOVERY ===================="
512+ print_error " Update failed catastrophically. Attempting emergency recovery..."
513+
514+ if [ -n " $backup_dir " ] && [ -d " $backup_dir " ]; then
515+ print_info " Restoring from emergency backup: $backup_dir "
516+ if [ -d " $PULSE_DIR " ]; then
517+ rm -rf " $PULSE_DIR .failed" 2> /dev/null
518+ mv " $PULSE_DIR " " $PULSE_DIR .failed" 2> /dev/null
519+ fi
520+
521+ if mv " $backup_dir " " $PULSE_DIR " ; then
522+ print_success " Emergency recovery successful!"
523+ print_info " Your original configuration has been restored."
524+ print_info " Failed installation moved to: $PULSE_DIR .failed"
525+ return 0
526+ else
527+ print_error " Emergency recovery failed!"
528+ fi
529+ fi
530+
531+ print_error " ============== MANUAL RECOVERY REQUIRED ================"
532+ print_error " Automatic recovery failed. You may need to:"
533+ print_error " 1. Manually restore from backup if available"
534+ print_error " 2. Re-run the installer: sudo bash install-pulse.sh"
535+ print_error " 3. Reconfigure your .env file with Proxmox credentials"
536+ print_error " 4. Contact support: https://github.com/rcourtman/Pulse/issues"
537+ print_error " ======================================================="
538+
539+ return 1
540+ }
541+
484542perform_tarball_install () {
485543 if [ -z " $TARGET_TAG " ]; then
486544 print_error " Target version tag not determined. Cannot install via tarball."
@@ -999,6 +1057,100 @@ create_pulse_user() {
9991057}
10001058
10011059
1060+ # Comprehensive backup of user configuration and data
1061+ backup_user_data () {
1062+ local backup_base_dir=" /tmp/pulse-config-backup-$( date +%s) "
1063+ mkdir -p " $backup_base_dir " || {
1064+ print_error " Failed to create backup directory $backup_base_dir "
1065+ return 1
1066+ }
1067+
1068+ print_info " Creating comprehensive backup of user configuration..."
1069+
1070+ # Files to always preserve
1071+ local preserve_files=(
1072+ " .env"
1073+ " custom-config.json"
1074+ " user-settings.json"
1075+ )
1076+
1077+ # Directories to preserve (if they exist and contain user data)
1078+ local preserve_dirs=(
1079+ " data"
1080+ " logs"
1081+ " backups"
1082+ " custom"
1083+ )
1084+
1085+ local backup_success=false
1086+
1087+ # Backup individual files
1088+ for file in " ${preserve_files[@]} " ; do
1089+ if [ -f " $PULSE_DIR /$file " ]; then
1090+ local file_backup_dir=" $backup_base_dir /$( dirname " $file " ) "
1091+ mkdir -p " $file_backup_dir "
1092+ if cp " $PULSE_DIR /$file " " $backup_base_dir /$file " 2> /dev/null; then
1093+ print_success " Backed up: $file "
1094+ backup_success=true
1095+ else
1096+ print_warning " Failed to backup: $file "
1097+ fi
1098+ fi
1099+ done
1100+
1101+ # Backup directories
1102+ for dir in " ${preserve_dirs[@]} " ; do
1103+ if [ -d " $PULSE_DIR /$dir " ] && [ " $( ls -A " $PULSE_DIR /$dir " 2> /dev/null) " ]; then
1104+ if cp -r " $PULSE_DIR /$dir " " $backup_base_dir /$dir " 2> /dev/null; then
1105+ print_success " Backed up directory: $dir "
1106+ backup_success=true
1107+ else
1108+ print_warning " Failed to backup directory: $dir "
1109+ fi
1110+ fi
1111+ done
1112+
1113+ if [ " $backup_success " = true ]; then
1114+ echo " $backup_base_dir "
1115+ return 0
1116+ else
1117+ rm -rf " $backup_base_dir " 2> /dev/null
1118+ print_warning " No user data found to backup or all backups failed"
1119+ return 1
1120+ fi
1121+ }
1122+
1123+ # Restore user configuration and data
1124+ restore_user_data () {
1125+ local backup_dir=" $1 "
1126+
1127+ if [ -z " $backup_dir " ] || [ ! -d " $backup_dir " ]; then
1128+ print_warning " No backup directory provided or directory doesn't exist"
1129+ return 1
1130+ fi
1131+
1132+ print_info " Restoring user configuration from backup..."
1133+
1134+ # Restore files and directories
1135+ if [ -d " $backup_dir " ]; then
1136+ if cp -r " $backup_dir " /* " $PULSE_DIR /" 2> /dev/null; then
1137+ # Fix ownership and permissions
1138+ chown -R " $PULSE_USER " :" $PULSE_USER " " $PULSE_DIR "
1139+
1140+ # Set secure permissions on sensitive files
1141+ [ -f " $PULSE_DIR /.env" ] && chmod 600 " $PULSE_DIR /.env"
1142+
1143+ print_success " User configuration restored successfully"
1144+ return 0
1145+ else
1146+ print_error " Failed to restore user configuration"
1147+ return 1
1148+ fi
1149+ fi
1150+
1151+ return 1
1152+ }
1153+
10021154perform_update () {
10031155 if [ -z " $TARGET_TAG " ] && [ -z " $TARGET_BRANCH " ]; then
10041156 print_error " Target version tag or branch not determined. Cannot update."
@@ -1009,20 +1161,30 @@ perform_update() {
10091161 if [ -n " $TARGET_TAG " ]; then
10101162 print_info " Attempting to update Pulse to version $TARGET_TAG ..."
10111163
1012- # Try tarball update first
1164+ # Try tarball update first - this is safer and preserves user data
10131165 if perform_tarball_update; then
10141166 return 0
10151167 fi
10161168
1017- # Fall back to git update
1018- print_info " Tarball update failed, falling back to git update..."
1169+ # Fall back to git update only if tarball fails
1170+ print_warning " Tarball update failed, falling back to git update..."
1171+ print_warning " ⚠️ Git update may be more disruptive to user configuration"
10191172 else
10201173 print_info " Attempting to update Pulse to branch $TARGET_BRANCH ..."
10211174 print_warning " ⚠️ Branch installations are for testing only and may be unstable!"
10221175 fi
10231176
10241177 cd " $PULSE_DIR " || { print_error " Failed to change directory to $PULSE_DIR " ; return 1; }
10251178
1179+ # Create comprehensive backup of all user data before ANY destructive operations
1180+ local user_data_backup=" "
1181+ if user_data_backup=$( backup_user_data) ; then
1182+ print_success " User data backup created at: $user_data_backup "
1183+ else
1184+ print_warning " Failed to create comprehensive user data backup"
1185+ print_warning " Proceeding with update but configuration may be lost"
1186+ fi
1187+
10261188 local script_backup_path=" /tmp/${SCRIPT_NAME} .bak"
10271189 if [ -n " $SCRIPT_ABS_PATH " ] && [ -f " $SCRIPT_ABS_PATH " ]; then
10281190 print_info " Backing up current installer script to $script_backup_path ..."
@@ -1039,43 +1201,47 @@ perform_update() {
10391201
10401202 git config --global --add safe.directory " $PULSE_DIR " > /dev/null 2>&1 || print_warning " Could not configure safe.directory for root user."
10411203
1204+ # Check git status before destructive operations
1205+ local has_local_changes=false
1206+ if sudo -u " $PULSE_USER " git status --porcelain 2> /dev/null | grep -q . ; then
1207+ has_local_changes=true
1208+ print_warning " Local changes detected in repository"
1209+ fi
1210+
10421211 print_info " Resetting local repository to discard potential changes [running as user $PULSE_USER ]..."
10431212 if ! sudo -u " $PULSE_USER " git reset --hard HEAD; then
10441213 print_error " Failed to reset local repository. Aborting update."
10451214 [ -n " $script_backup_path " ] && rm -f " $script_backup_path "
1215+ [ -n " $user_data_backup " ] && rm -rf " $user_data_backup "
10461216 cd ..
10471217 return 1
10481218 fi
10491219
1050- # Backup .env file before cleaning to preserve user configuration
1051- local env_backup_path=" "
1052- if [ -f " $PULSE_DIR /.env" ]; then
1053- env_backup_path=" /tmp/.env.backup.$$ "
1054- print_info " Backing up .env file to preserve user configuration..."
1055- if cp " $PULSE_DIR /.env" " $env_backup_path " ; then
1056- print_success " .env file backed up temporarily."
1057- else
1058- print_warning " Failed to backup .env file. Configuration may be lost."
1059- env_backup_path=" "
1060- fi
1061- fi
1062-
1063- print_info " Cleaning untracked files and directories..."
1064- if ! sudo -u " $PULSE_USER " git clean -fd; then
1065- print_warning " Failed to clean untracked files, continuing anyway."
1066- fi
1067-
1068- # Restore .env file after cleaning
1069- if [ -n " $env_backup_path " ] && [ -f " $env_backup_path " ]; then
1070- print_info " Restoring .env file..."
1071- if cp " $env_backup_path " " $PULSE_DIR /.env" ; then
1072- chown " $PULSE_USER " :" $PULSE_USER " " $PULSE_DIR /.env"
1073- chmod 600 " $PULSE_DIR /.env"
1074- print_success " .env file restored successfully."
1075- else
1076- print_warning " Failed to restore .env file from backup."
1220+ # Only clean untracked files if absolutely necessary
1221+ # Skip git clean entirely and handle conflicts more gracefully
1222+ if [ " $has_local_changes " = true ]; then
1223+ print_info " Checking for untracked files that might conflict..."
1224+ local untracked_files=$( sudo -u " $PULSE_USER " git status --porcelain 2> /dev/null | grep ' ^??' | cut -c4-)
1225+
1226+ if [ -n " $untracked_files " ]; then
1227+ print_warning " Found untracked files that may conflict with update:"
1228+ echo " $untracked_files " | while read -r file; do
1229+ print_warning " - $file "
1230+ done
1231+
1232+ # Instead of git clean, selectively handle conflicts
1233+ print_info " Moving conflicting files to temporary backup..."
1234+ local conflict_backup=" /tmp/pulse-conflicts-$( date +%s) "
1235+ mkdir -p " $conflict_backup "
1236+
1237+ echo " $untracked_files " | while read -r file; do
1238+ if [ -f " $file " ] || [ -d " $file " ]; then
1239+ local file_dir=" $conflict_backup /$( dirname " $file " ) "
1240+ mkdir -p " $file_dir "
1241+ mv " $file " " $conflict_backup /$file " 2> /dev/null || true
1242+ fi
1243+ done
10771244 fi
1078- rm -f " $env_backup_path "
10791245 fi
10801246
10811247 print_info " Fetching latest changes from git [running as user $PULSE_USER ]..."
@@ -1175,6 +1341,17 @@ perform_update() {
11751341 print_warning " Continuing update, but frontend may not display correctly."
11761342 fi
11771343
1344+ # Restore user data BEFORE setting permissions
1345+ if [ -n " $user_data_backup " ] && [ -d " $user_data_backup " ]; then
1346+ if restore_user_data " $user_data_backup " ; then
1347+ print_success " User configuration and data restored successfully"
1348+ else
1349+ print_warning " Failed to restore some user configuration"
1350+ fi
1351+ # Cleanup backup after restoration
1352+ rm -rf " $user_data_backup "
1353+ fi
1354+
11781355 set_permissions
11791356
11801357 print_info " Ensuring systemd service $SERVICE_NAME is configured..."
0 commit comments