| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- #!/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
|