#!/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 " echo " $0 [--dry-run] restore " 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