Replaces the Bazzite/BlueBuild image with plain Ubuntu. apt covers the system and dev tools, Flathub the desktop apps, and three scripts do the rest: backup.sh archives this machine's home to server-marvin and reads it back to verify it; setup.sh builds a fresh 26.04 install (NVIDIA 595-open, Docker with GPU access, Steam/Lutris, 27 Flatpaks, VS Code, Claude desktop, the Data drive and marvin mounts); restore.sh unpacks the backup without the Fedora/KDE parts that would fight Ubuntu. The app list comes from a scan of the running machine. The 15 Flatpaks left behind sit commented at the bottom of flatpaks.txt, and their data still rides along in the backup. No Node or .NET on the host by choice — LudosData and landingPage build in Docker. Drops the BlueBuild recipe, the cosign key and the old dev-setup script. The Bazzite setup stays in this history. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
112 lines
4.7 KiB
Bash
Executable File
112 lines
4.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Run on the Bazzite box BEFORE wiping it. Archives your home folder to server-marvin as
|
|
# tar.zst files, then reads every archive back to prove it's intact.
|
|
#
|
|
# scripts/backup.sh # -> /var/mnt/server-marvin-personal/desktop-backup-<date>
|
|
# scripts/backup.sh /some/other/dir # any directory NOT on the disk you're about to wipe
|
|
#
|
|
# Re-running skips archives that already finished (FORCE=1 redoes them).
|
|
# Close Steam, Firefox, Discord and Claude first — files that change mid-read get a warning.
|
|
set -euo pipefail
|
|
|
|
SHARE=/var/mnt/server-marvin-personal
|
|
DEST="${1:-$SHARE/desktop-backup-$(date +%F)}"
|
|
|
|
step() { printf '\n\033[1;34m== %s ==\033[0m\n' "$*"; }
|
|
warn() { printf '\033[1;33mwarning:\033[0m %s\n' "$*" >&2; }
|
|
die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
|
|
|
|
# -- Make sure the backup lands somewhere that survives the reinstall ------------------
|
|
if [[ $DEST == "$SHARE"/* ]]; then
|
|
ls "$SHARE" >/dev/null 2>&1 || true # poke the automount
|
|
mountpoint -q "$SHARE" || die "$SHARE isn't mounted — is server-marvin up?"
|
|
fi
|
|
mkdir -p "$DEST"
|
|
disk_of() { findmnt -n -o SOURCE -T "$1" | sed 's/\[.*//'; }
|
|
[[ $(disk_of "$DEST") != "$(disk_of "$HOME")" ]] \
|
|
|| die "$DEST is on the same disk as your home — that's the disk Ubuntu will wipe"
|
|
echo "Backing up $HOME -> $DEST"
|
|
df -h "$DEST" | tail -1
|
|
|
|
# -- Archive helper ---------------------------------------------------------------------
|
|
archive() { # archive <name> <tar args + members...>
|
|
local name=$1 out="$DEST/$1.tar.zst"; shift
|
|
step "$name"
|
|
if [[ -s $out && -z ${FORCE:-} ]]; then echo "already done, skipping"; return; fi
|
|
local rc=0
|
|
tar --create --file="$out.partial" --use-compress-program='zstd -T0 -3' \
|
|
--directory="$HOME" \
|
|
--checkpoint=100000 --checkpoint-action='ttyout= %{%H:%M:%S}t %T%*\r' \
|
|
"$@" || rc=$?
|
|
echo
|
|
# GNU tar exits 1 when a file changed while it was being read (an app was still open).
|
|
# Everything else still made it in, so keep the archive; anything above 1 is fatal.
|
|
(( rc <= 1 )) || die "tar failed on $name (exit $rc)"
|
|
(( rc == 0 )) || warn "$name: some files changed while being read — close apps and FORCE=1 to redo if it matters"
|
|
mv "$out.partial" "$out"
|
|
ls -lh "$out" | awk '{print " " $5}'
|
|
}
|
|
|
|
# Big, self-contained folders get their own archive so they can be restored (or skipped)
|
|
# separately. Everything else goes in home.tar.zst.
|
|
archive home \
|
|
--anchored \
|
|
--exclude=./.cache \
|
|
--exclude=./.local/share/Trash \
|
|
--exclude=./.local/share/Steam \
|
|
--exclude=./.steam \
|
|
--exclude=./.local/share/containers \
|
|
--exclude=./.local/share/flatpak \
|
|
--exclude=./.local/share/baloo \
|
|
--exclude='./.var/app/*/cache' \
|
|
--exclude='./.claude.json.tmp.*' \
|
|
--exclude=./.xlcore \
|
|
--exclude=./ComfyUI \
|
|
--no-anchored \
|
|
--exclude=node_modules \
|
|
--exclude=__pycache__ \
|
|
.
|
|
|
|
# FFXIV game install (~124 GB) — Square's patch servers make a redownload take hours.
|
|
[[ -d $HOME/.xlcore ]] && archive xlcore ./.xlcore
|
|
|
|
# ComfyUI minus its venv (rebuilt on Ubuntu); this is ~110 GB of models.
|
|
[[ -d $HOME/ComfyUI ]] && archive comfyui \
|
|
--anchored --exclude=./ComfyUI/venv --exclude=./ComfyUI/.venv \
|
|
--no-anchored --exclude=__pycache__ \
|
|
./ComfyUI
|
|
|
|
# Steam: only saves and settings. Games get redownloaded; the ones on the Data drive stay put.
|
|
archive steam-saves \
|
|
./.local/share/Steam/userdata \
|
|
./.local/share/Steam/steamapps/compatdata
|
|
|
|
# -- Reference info for the rebuild -------------------------------------------------------
|
|
step "manifest"
|
|
m="$DEST/manifest"; mkdir -p "$m"
|
|
flatpak list --app --columns=application,origin,installation > "$m/flatpaks.txt" 2>/dev/null || true
|
|
code --list-extensions > "$m/vscode-extensions.txt" 2>/dev/null || true
|
|
brew leaves > "$m/brew-leaves.txt" 2>/dev/null || true
|
|
rpm-ostree status > "$m/rpm-ostree-status.txt" 2>/dev/null || true
|
|
cp /etc/fstab "$m/fstab"
|
|
cp -r "$HOME/.local/share/flatpak/overrides" "$m/flatpak-overrides-user" 2>/dev/null || true
|
|
cp -r /var/lib/flatpak/overrides "$m/flatpak-overrides-system" 2>/dev/null || true
|
|
lsblk -f > "$m/disks.txt"
|
|
ls "$HOME/.local/share/Steam/steamapps/common" > "$m/steam-games-home.txt" 2>/dev/null || true
|
|
ls /run/media/"$USER"/Data/SteamLibrary/steamapps/common > "$m/steam-games-data-drive.txt" 2>/dev/null || true
|
|
ls "$m"
|
|
|
|
# -- Prove every archive reads back cleanly ----------------------------------------------
|
|
if [[ -z ${SKIP_VERIFY:-} ]]; then
|
|
for f in "$DEST"/*.tar.zst; do
|
|
step "verify $(basename "$f")"
|
|
tar --use-compress-program=zstd --list --file="$f" > /dev/null || die "$f is corrupt — rerun with FORCE=1"
|
|
echo "ok"
|
|
done
|
|
fi
|
|
|
|
step "done"
|
|
du -sh "$DEST"/*.tar.zst
|
|
echo "Backup is in: $DEST"
|
|
echo "Safe to wipe the 990 PRO once you've eyeballed that list."
|