mirror of
https://github.com/kossakovsky/n8n-install.git
synced 2026-03-07 14:23:08 +00:00
feat(update): preserve user directories during git reset
backup and restore user-customizable directories (like python-runner/) during update to prevent data loss from git reset --hard closes #22
This commit is contained in:
@@ -3,13 +3,18 @@
|
||||
# update.sh - Main update orchestrator
|
||||
# =============================================================================
|
||||
# Performs a full system and service update:
|
||||
# 1. Pulls latest changes from the git repository (git reset --hard + pull)
|
||||
# 2. Updates Ubuntu system packages (apt-get update && upgrade)
|
||||
# 3. Delegates to apply_update.sh for service updates
|
||||
# 1. Backs up user-customizable directories (e.g., python-runner/)
|
||||
# 2. Pulls latest changes from the git repository (git reset --hard + pull)
|
||||
# 3. Restores backed up directories to preserve user modifications
|
||||
# 4. Updates Ubuntu system packages (apt-get update && upgrade)
|
||||
# 5. Delegates to apply_update.sh for service updates
|
||||
#
|
||||
# This two-stage approach ensures apply_update.sh itself gets updated before
|
||||
# running, so new update logic is always applied.
|
||||
#
|
||||
# Preserved directories: Defined in PRESERVE_DIRS array in utils.sh.
|
||||
# These directories contain user-customizable content that survives git reset.
|
||||
#
|
||||
# Usage: make update OR sudo bash scripts/update.sh
|
||||
# =============================================================================
|
||||
|
||||
@@ -19,6 +24,20 @@ set -e
|
||||
source "$(dirname "$0")/utils.sh"
|
||||
init_paths
|
||||
|
||||
# Global variable to track backup path for cleanup
|
||||
BACKUP_PATH=""
|
||||
|
||||
# Cleanup function for interrupted updates
|
||||
cleanup_on_exit() {
|
||||
local exit_code=$?
|
||||
if [ -n "$BACKUP_PATH" ] && [ -d "$BACKUP_PATH" ]; then
|
||||
log_warning "Cleaning up backup directory: $BACKUP_PATH"
|
||||
rm -rf "$BACKUP_PATH"
|
||||
fi
|
||||
exit $exit_code
|
||||
}
|
||||
trap cleanup_on_exit INT TERM
|
||||
|
||||
# Path to the apply_update.sh script
|
||||
APPLY_UPDATE_SCRIPT="$SCRIPT_DIR/apply_update.sh"
|
||||
|
||||
@@ -44,10 +63,40 @@ if ! command -v git &> /dev/null; then
|
||||
else
|
||||
# Change to project root for git pull
|
||||
cd "$PROJECT_ROOT" || { log_error "Failed to change directory to $PROJECT_ROOT"; exit 1; }
|
||||
git reset --hard HEAD || { log_warning "Failed to reset repository. Continuing update with potentially unreset local changes..."; }
|
||||
git pull || { log_warning "Failed to pull latest repository changes. Continuing update with potentially old version of apply_update.sh..."; }
|
||||
# Change back to script dir or ensure apply_update.sh uses absolute paths or cd's itself
|
||||
# (apply_update.sh already handles cd to PROJECT_ROOT, so we're good)
|
||||
|
||||
# Backup user-customizable directories before git reset (uses PRESERVE_DIRS from utils.sh)
|
||||
if ! BACKUP_PATH=$(backup_preserved_dirs); then
|
||||
log_error "Backup failed. Aborting update to prevent data loss."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$BACKUP_PATH" ]; then
|
||||
log_info "Backup created at: $BACKUP_PATH"
|
||||
fi
|
||||
|
||||
# Git operations
|
||||
if ! git reset --hard HEAD; then
|
||||
log_error "Git reset failed."
|
||||
restore_preserved_dirs "$BACKUP_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! git pull; then
|
||||
log_error "Git pull failed."
|
||||
restore_preserved_dirs "$BACKUP_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Restore user-customizable directories after git pull
|
||||
if ! restore_preserved_dirs "$BACKUP_PATH"; then
|
||||
log_error "Failed to restore user directories from backup."
|
||||
log_error "Backup may still be available at: $BACKUP_PATH"
|
||||
BACKUP_PATH="" # Prevent cleanup from deleting it
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clear backup path after successful restore
|
||||
BACKUP_PATH=""
|
||||
fi
|
||||
|
||||
# Update Ubuntu packages before running apply_update
|
||||
|
||||
118
scripts/utils.sh
118
scripts/utils.sh
@@ -12,6 +12,7 @@
|
||||
# - Validation helpers: require_command, require_file, ensure_file_exists
|
||||
# - Profile management: is_profile_active, update_compose_profiles
|
||||
# - Doctor output helpers: print_ok, print_warning, print_error
|
||||
# - Directory preservation: backup_preserved_dirs, restore_preserved_dirs
|
||||
#
|
||||
# Usage: source "$(dirname "$0")/utils.sh" && init_paths
|
||||
# =============================================================================
|
||||
@@ -603,3 +604,120 @@ wt_parse_choices() {
|
||||
local cleaned="${choices//\"/}"
|
||||
read -ra arr <<< "$cleaned"
|
||||
}
|
||||
|
||||
#=============================================================================
|
||||
# DIRECTORY PRESERVATION (for git updates)
|
||||
#=============================================================================
|
||||
# Directories containing user-customizable content that should survive git reset.
|
||||
# Used by update.sh to backup before git operations and restore after.
|
||||
PRESERVE_DIRS=("python-runner")
|
||||
|
||||
# Backup preserved directories before git reset
|
||||
# Usage: backup_path=$(backup_preserved_dirs) || exit 1
|
||||
# Returns: 0 on success (prints backup path), 1 on failure
|
||||
# Default backup location: secure temp directory via mktemp
|
||||
backup_preserved_dirs() {
|
||||
local backup_base=""
|
||||
local has_content=0
|
||||
|
||||
# Check if any directories need backup
|
||||
for dir in "${PRESERVE_DIRS[@]}"; do
|
||||
if [ -d "$PROJECT_ROOT/$dir" ] && [ -n "$(ls -A "$PROJECT_ROOT/$dir" 2>/dev/null)" ]; then
|
||||
has_content=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# No content to backup
|
||||
if [ $has_content -eq 0 ]; then
|
||||
echo ""
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Create secure temporary directory
|
||||
backup_base=$(mktemp -d /tmp/n8n-install-backup.XXXXXXXXXX) || {
|
||||
log_error "Failed to create backup directory"
|
||||
return 1
|
||||
}
|
||||
chmod 700 "$backup_base"
|
||||
|
||||
# Backup each directory
|
||||
for dir in "${PRESERVE_DIRS[@]}"; do
|
||||
# Validate directory name (no path traversal)
|
||||
if [[ "$dir" =~ \.\.|^/ ]]; then
|
||||
log_error "Invalid directory name in PRESERVE_DIRS: $dir"
|
||||
rm -rf "$backup_base"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -d "$PROJECT_ROOT/$dir" ] && [ -n "$(ls -A "$PROJECT_ROOT/$dir" 2>/dev/null)" ]; then
|
||||
log_info "Backing up $dir/ before git reset..."
|
||||
if ! cp -rp "$PROJECT_ROOT/$dir" "$backup_base/$dir"; then
|
||||
log_error "Failed to backup $dir/. Aborting to prevent data loss."
|
||||
rm -rf "$backup_base"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo "$backup_base"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Restore preserved directories after git pull
|
||||
# Usage: restore_preserved_dirs <backup_base_path>
|
||||
# Returns: 0 on success, 1 on failure
|
||||
restore_preserved_dirs() {
|
||||
local backup_base="$1"
|
||||
|
||||
# Nothing to restore
|
||||
if [ -z "$backup_base" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ ! -d "$backup_base" ]; then
|
||||
log_warning "Backup directory not found: $backup_base"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Safety checks for PROJECT_ROOT
|
||||
if [ -z "$PROJECT_ROOT" ]; then
|
||||
log_error "PROJECT_ROOT is not set. Refusing to restore."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$PROJECT_ROOT" = "/" ] || [ "$PROJECT_ROOT" = "/root" ] || [ "$PROJECT_ROOT" = "/home" ]; then
|
||||
log_error "PROJECT_ROOT is set to a dangerous path: $PROJECT_ROOT"
|
||||
return 1
|
||||
fi
|
||||
|
||||
for dir in "${PRESERVE_DIRS[@]}"; do
|
||||
# Validate directory name
|
||||
if [[ "$dir" =~ \.\.|^/ ]] || [ -z "$dir" ]; then
|
||||
log_error "Invalid directory name: $dir"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ -d "$backup_base/$dir" ]; then
|
||||
log_info "Restoring $dir/ after git pull..."
|
||||
|
||||
# Remove the git-restored version
|
||||
if [ -d "$PROJECT_ROOT/$dir" ]; then
|
||||
if ! rm -rf "$PROJECT_ROOT/$dir"; then
|
||||
log_error "Failed to remove $PROJECT_ROOT/$dir"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Restore from backup
|
||||
if ! mv "$backup_base/$dir" "$PROJECT_ROOT/$dir"; then
|
||||
log_error "Failed to restore $dir/ from backup"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Cleanup backup directory
|
||||
rm -rf "$backup_base"
|
||||
return 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user