|
|
@@ -0,0 +1,211 @@
|
|
|
+#!/usr/bin/env bash
|
|
|
+
|
|
|
+# Exit immediately if a command exits with a non-zero status
|
|
|
+set -e
|
|
|
+
|
|
|
+function show_usage() {
|
|
|
+ echo "Usage:"
|
|
|
+ echo " $0 [--dry-run] backup <archive_name.tgz>"
|
|
|
+ echo " $0 [--dry-run] restore <archive_name.tgz> <target_home_directory> <target_username>"
|
|
|
+ echo ""
|
|
|
+ echo "Options:"
|
|
|
+ echo " --dry-run Show what would be copied/extracted without making any actual changes."
|
|
|
+ echo ""
|
|
|
+ echo "Example:"
|
|
|
+ echo " $0 --dry-run backup /tmp/my_gnome_setup.tgz"
|
|
|
+ echo " sudo $0 restore /tmp/my_gnome_setup.tgz /home/guest guest"
|
|
|
+ exit 1
|
|
|
+}
|
|
|
+
|
|
|
+# Check for dry-run flag
|
|
|
+DRY_RUN=0
|
|
|
+if [[ "$1" == "--dry-run" ]]; then
|
|
|
+ DRY_RUN=1
|
|
|
+ shift
|
|
|
+fi
|
|
|
+
|
|
|
+if [ "$#" -lt 2 ]; then
|
|
|
+ show_usage
|
|
|
+fi
|
|
|
+
|
|
|
+ACTION="$1"
|
|
|
+ARCHIVE="$2"
|
|
|
+
|
|
|
+# Define the exact paths we care about (relative to the home directory)
|
|
|
+GNOME_PATHS=(
|
|
|
+ ".config/monitors.xml"
|
|
|
+ ".config/mimeapps.list"
|
|
|
+ ".config/gtk-3.0"
|
|
|
+ ".config/gtk-4.0"
|
|
|
+ ".config/fontconfig"
|
|
|
+ ".config/user-dirs.dirs"
|
|
|
+ ".config/autostart"
|
|
|
+ ".local/share/gnome-shell/extensions"
|
|
|
+ ".local/share/backgrounds"
|
|
|
+ ".local/share/fonts"
|
|
|
+ ".local/share/themes"
|
|
|
+ ".local/share/icons"
|
|
|
+ ".themes"
|
|
|
+ ".icons"
|
|
|
+ ".fonts"
|
|
|
+)
|
|
|
+
|
|
|
+if [ "$ACTION" == "backup" ]; then
|
|
|
+ if [ "$DRY_RUN" -eq 1 ]; then
|
|
|
+ echo "==> [DRY RUN] Starting GNOME backup simulation..."
|
|
|
+ echo "--> [DRY RUN] Would dump dconf database to dconf_backup.ini"
|
|
|
+ echo "--> [DRY RUN] Would copy the following paths from $HOME:"
|
|
|
+
|
|
|
+ for path in "${GNOME_PATHS[@]}"; do
|
|
|
+ if [ -e "$HOME/$path" ]; then
|
|
|
+ echo " [DRY RUN] MATCH: $path"
|
|
|
+ else
|
|
|
+ echo " [DRY RUN] MISSING (Skipping): $path"
|
|
|
+ fi
|
|
|
+ done
|
|
|
+
|
|
|
+ echo "--> [DRY RUN] Would aggressively scrub any 'keyrings' directories."
|
|
|
+ echo "--> [DRY RUN] Would compress all matched files to: $ARCHIVE"
|
|
|
+ echo "==> [DRY RUN] Backup simulation complete. No files were actually touched."
|
|
|
+ exit 0
|
|
|
+ fi
|
|
|
+
|
|
|
+ echo "==> Starting GNOME backup..."
|
|
|
+
|
|
|
+ # Create a staging area in /tmp
|
|
|
+ STAGING_DIR="/tmp/gnome_backup_$$"
|
|
|
+ mkdir -p "$STAGING_DIR"
|
|
|
+
|
|
|
+ echo "--> Dumping dconf database..."
|
|
|
+ dconf dump / > "$STAGING_DIR/dconf_backup.ini"
|
|
|
+
|
|
|
+ echo "--> Gathering visual assets and system configs..."
|
|
|
+ for path in "${GNOME_PATHS[@]}"; do
|
|
|
+ if [ -e "$HOME/$path" ]; then
|
|
|
+ # Create the parent directory structure in the staging area
|
|
|
+ mkdir -p "$STAGING_DIR/$(dirname "$path")"
|
|
|
+ # Copy the files over, preserving their structure
|
|
|
+ cp -a "$HOME/$path" "$STAGING_DIR/$(dirname "$path")/"
|
|
|
+ echo " Saved: $path"
|
|
|
+ fi
|
|
|
+ done
|
|
|
+
|
|
|
+ echo "--> Scrubbing keyrings and password data..."
|
|
|
+ # Explicitly find and annihilate any keyrings that managed to sneak into the staging area
|
|
|
+ find "$STAGING_DIR" -type d \( -name "keyrings" -o -name "kwalletd" \) -exec rm -rf {} + 2>/dev/null || true
|
|
|
+
|
|
|
+ echo "--> Compressing to $ARCHIVE..."
|
|
|
+ # Pack it all up from the staging directory
|
|
|
+ tar -czf "$ARCHIVE" -C "$STAGING_DIR" .
|
|
|
+
|
|
|
+ # Clean up the bloody mess in /tmp
|
|
|
+ rm -rf "$STAGING_DIR"
|
|
|
+
|
|
|
+ echo "==> Backup complete! Your setup is safe at $ARCHIVE"
|
|
|
+
|
|
|
+elif [ "$ACTION" == "restore" ]; then
|
|
|
+ TARGET_DIR="$3"
|
|
|
+ TARGET_USER="$4"
|
|
|
+
|
|
|
+ if [ -z "$TARGET_DIR" ] || [ ! -d "$TARGET_DIR" ]; then
|
|
|
+ echo "Error: You must provide a valid target directory."
|
|
|
+ show_usage
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [ -z "$TARGET_USER" ]; then
|
|
|
+ echo "Error: You must provide the exact target username as the 4th argument."
|
|
|
+ show_usage
|
|
|
+ fi
|
|
|
+
|
|
|
+ # We need root to properly chown the files to the new user
|
|
|
+ if [ "$EUID" -ne 0 ] && [ "$DRY_RUN" -eq 0 ]; then
|
|
|
+ echo "Warning: You must run the 'restore' command with sudo,"
|
|
|
+ echo "otherwise fixing the file ownership for the new profile will fail."
|
|
|
+ exit 1
|
|
|
+ fi
|
|
|
+
|
|
|
+ # Deduce the primary group for the target user (usually matches the username)
|
|
|
+ TARGET_GROUP=$(id -gn "$TARGET_USER" 2>/dev/null || echo "$TARGET_USER")
|
|
|
+
|
|
|
+ if [ "$DRY_RUN" -eq 1 ]; then
|
|
|
+ echo "==> [DRY RUN] Starting GNOME restore simulation to $TARGET_DIR..."
|
|
|
+
|
|
|
+ if [ ! -f "$ARCHIVE" ]; then
|
|
|
+ echo "Error: Backup archive $ARCHIVE does not exist. Cannot simulate restore."
|
|
|
+ exit 1
|
|
|
+ fi
|
|
|
+
|
|
|
+ echo "--> [DRY RUN] Target user explicitly set to: $TARGET_USER:$TARGET_GROUP"
|
|
|
+ echo "--> [DRY RUN] Would extract the following contents from $ARCHIVE to $TARGET_DIR:"
|
|
|
+ tar -tf "$ARCHIVE" | sed 's/^/ [DRY RUN] /'
|
|
|
+
|
|
|
+ echo "--> [DRY RUN] Would exclude 'keyrings' and 'dconf_backup.ini' from file sync."
|
|
|
+ echo "--> [DRY RUN] Would scaffold standard XDG user directories (Desktop, Downloads, etc.)"
|
|
|
+ echo "--> [DRY RUN] Would run a blanket chown on $TARGET_DIR to $TARGET_USER:$TARGET_GROUP"
|
|
|
+ echo "--> [DRY RUN] Would deploy helper script $TARGET_DIR/load_gnome_settings.sh"
|
|
|
+ echo "==> [DRY RUN] Restore simulation complete. No files were actually deployed or altered."
|
|
|
+ exit 0
|
|
|
+ fi
|
|
|
+
|
|
|
+ echo "==> Starting GNOME restore to $TARGET_DIR..."
|
|
|
+
|
|
|
+ STAGING_DIR="/tmp/gnome_restore_$$"
|
|
|
+ mkdir -p "$STAGING_DIR"
|
|
|
+
|
|
|
+ echo "--> Extracting archive..."
|
|
|
+ tar -xzf "$ARCHIVE" -C "$STAGING_DIR"
|
|
|
+
|
|
|
+ echo "--> Deploying files into $TARGET_DIR..."
|
|
|
+ # Copy everything EXCEPT the dconf dump and any leftover keyrings into the target directory
|
|
|
+ rsync -a "$STAGING_DIR/" "$TARGET_DIR/" --exclude "dconf_backup.ini" --exclude "keyrings" --exclude "kwalletd"
|
|
|
+
|
|
|
+ echo "--> Scaffolding standard XDG user directories..."
|
|
|
+ # Create the empty default folders so GNOME doesn't shit a brick
|
|
|
+ for dir in Desktop Documents Downloads Music Pictures Public Templates Videos; do
|
|
|
+ mkdir -p "$TARGET_DIR/$dir"
|
|
|
+ done
|
|
|
+
|
|
|
+ echo "--> Ensuring .cache/dconf exists..."
|
|
|
+ # Pre-create the dconf cache directory so the helper script never throws a 'Permission denied'
|
|
|
+ mkdir -p "$TARGET_DIR/.cache/dconf"
|
|
|
+
|
|
|
+ echo "--> Running a blanket ownership fix across the entire home directory to $TARGET_USER:$TARGET_GROUP..."
|
|
|
+ # Force absolute ownership of every single file and folder in the home directory to the new user
|
|
|
+ chown -R "$TARGET_USER:$TARGET_GROUP" "$TARGET_DIR"
|
|
|
+
|
|
|
+ echo "--> Prepping the headless dconf restore script..."
|
|
|
+ DCONF_SCRIPT="$TARGET_DIR/load_gnome_settings.sh"
|
|
|
+ cp "$STAGING_DIR/dconf_backup.ini" "$TARGET_DIR/"
|
|
|
+
|
|
|
+ cat << 'EOF' > "$DCONF_SCRIPT"
|
|
|
+#!/usr/bin/env bash
|
|
|
+echo "Loading GNOME settings into dconf..."
|
|
|
+
|
|
|
+# If there is no active display/Wayland session, we spawn a temporary headless D-Bus
|
|
|
+# so dconf can write the local database file without throwing a fit.
|
|
|
+if [ -z "$DBUS_SESSION_BUS_ADDRESS" ]; then
|
|
|
+ echo "No graphical D-Bus session detected. Firing up a headless session..."
|
|
|
+ dbus-run-session dconf load / < "$HOME/dconf_backup.ini"
|
|
|
+else
|
|
|
+ dconf load / < "$HOME/dconf_backup.ini"
|
|
|
+fi
|
|
|
+
|
|
|
+echo "Done! You can safely delete this script and dconf_backup.ini."
|
|
|
+EOF
|
|
|
+
|
|
|
+ chmod +x "$DCONF_SCRIPT"
|
|
|
+ chown "$TARGET_USER:$TARGET_GROUP" "$DCONF_SCRIPT" "$TARGET_DIR/dconf_backup.ini"
|
|
|
+
|
|
|
+ # Clean up /tmp
|
|
|
+ rm -rf "$STAGING_DIR"
|
|
|
+
|
|
|
+ echo "==> Files restored successfully!"
|
|
|
+ echo "====================================================================="
|
|
|
+ echo "IMPORTANT: Log into the new profile ($TARGET_USER) or switch to a TTY."
|
|
|
+ echo "Run: ./load_gnome_settings.sh"
|
|
|
+ echo "====================================================================="
|
|
|
+
|
|
|
+else
|
|
|
+ echo "Error: Invalid action. Use 'backup' or 'restore'."
|
|
|
+ show_usage
|
|
|
+fi
|