fuzzyworld-tools / files / server / truenas / mirror-all.sh
mirror-all.sh
f7a634ab 2026-10-27 Pavel Safronov | 1 | #!/bin/sh |
| 2 | # Backup mirror of every DreamHost repo, run on TrueNAS from a cron job (System → Advanced → Cron Jobs). | |
| 3 | # | |
| 4 | # mirror-all.sh /mnt/<pool>/git-mirrors | |
| 5 | # | |
| 6 | # DEST is a ZFS dataset (snapshotted by a periodic snapshot task). DEST/.ssh/id_ed25519 is a key that | |
| 7 | # DreamHost only lets run server/mirror-only (fetch + list), so this box can read repos but never write. | |
| 8 | # New repos are cloned with --mirror; existing ones get `remote update --prune`. Repos that disappear | |
| 9 | # from DreamHost are kept here (this is a backup) and listed in the summary. Each mirror's config | |
| 10 | # carries fuzzyworld.visibility, so a restore keeps the private/public marking. | |
| 11 | set -u | |
| 12 | DEST=${1:?usage: mirror-all.sh /mnt/<pool>/git-mirrors} | |
| 13 | HOST=${FW_MIRROR_HOST:-fuzzygamer@iad1-shared-b7-30.dreamhost.com} | |
| 14 | SSH="ssh -i $DEST/.ssh/id_ed25519 -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=$DEST/.ssh/known_hosts" | |
| 15 | export GIT_SSH_COMMAND="$SSH" | |
| 16 | LOG=$DEST/.logs/mirror-$(date -u +%Y%m%d).log | |
| 17 | mkdir -p "$DEST/.logs" | |
| 18 | exec 3>&2 # keep the cron job's stderr: failures are reported there too | |
| 19 | exec >> "$LOG" 2>&1 | |
| 20 | echo "== $(date -u '+%F %T UTC') mirror from $HOST" | |
| 21 | ||
| 22 | names=$($SSH "$HOST" list) || { echo "FAIL: cannot list repos on $HOST"; echo "git mirror: cannot list repos on $HOST (log: $LOG)" >&3; exit 1; } | |
| 23 | ok=0; failed=0 | |
| 24 | for name in $names; do | |
| 25 | m="$DEST/$name.git" | |
| 26 | if [ -d "$m" ]; then | |
| 27 | git -C "$m" remote update --prune >/dev/null 2>&1 && r=0 || r=1 | |
| 28 | else | |
| 29 | git clone --quiet --mirror "$HOST:git/$name.git" "$m" && r=0 || r=1 | |
| 30 | fi | |
| 31 | if [ $r = 0 ]; then | |
| 32 | v=$($SSH "$HOST" "visibility $name") && git -C "$m" config fuzzyworld.visibility "$v" | |
| 33 | ok=$((ok + 1)) | |
| 34 | else | |
| 35 | echo "FAIL: $name"; failed=$((failed + 1)) | |
| 36 | fi | |
| 37 | done | |
| 38 | gone="" | |
| 39 | for m in "$DEST"/*.git; do | |
| 40 | [ -d "$m" ] || continue | |
| 41 | n=$(basename "$m" .git) | |
| 42 | echo "$names" | tr ' ' '\n' | grep -qxF "$n" || gone="$gone $n" | |
| 43 | done | |
| 44 | echo "mirrored $ok, failed $failed${gone:+, kept but no longer on DreamHost:$gone}" | |
| 45 | echo "== done $(date -u '+%F %T UTC')" | |
| 46 | [ $failed = 0 ] || { echo "git mirror: $failed repo(s) failed (log: $LOG)" >&3; exit 1; } |