commit 47931d4de1736f4245bd35191e6021ed93175812
parent 2d9a6bc63a19ca7710b85f65fb2d3f5e9a46c841
Author: Chris Bracken <chris@bracken.jp>
Date: Sun, 16 Aug 2026 21:35:32 +0900
Update backup script
Diffstat:
1 file changed, 43 insertions(+), 15 deletions(-)
diff --git a/backup_git.bracken.jp.sh b/backup_git.bracken.jp.sh
@@ -1,11 +1,12 @@
#!/bin/sh
-
-# Backup script for git repos, typically invoked via a cron job on the backup
-# server.
+# Backup script for git repos, invoked from Synology Task Scheduler.
#
# Invoke with a backup tag, typically "daily", "weekly", "monthly" and the
# number of previous backups to be retained. Older backups that fall out of
# retention are deleted.
+#
+# backup_git.bracken.jp.sh daily 10
+# backup_git.bracken.jp.sh weekly 100
# Fail if any step fails, so we don't collect half-baked backups.
set -e
@@ -19,21 +20,48 @@ if [ -z "$TAG" -o -z "$RETAIN_COUNT" ]; then
exit 1
fi
-USER=git
+USER=chris
HOST=git.bracken.jp
-BACKUP_SRC_DIR="/home/git"
-BACKUP_SRC_FILES='*.git git_infra'
+PORT=1022
+
+# NOTE: the remote side runs a FORCED COMMAND configured in the server's
+# ~/.ssh/authorized_keys:
+#
+# restrict,command="tar Jcf - -C /zroot/data git-repos" ssh-ed25519 ...
+#
+# Whatever command this script sends is ignored. The archive contains
+# git-repos/ at the top level (previously '*.git' and 'git_infra'), so restore
+# paths differ from archives taken before 2026-08.
+#
+# To change WHAT is backed up, edit authorized_keys on the server, not here.
+
BACKUP_OUT_DIR="/volume1/Backups/git.bracken.jp/$TAG"
-BACKUP_OUT_FILE="$(date +"%Y-%m-%d_%H.%m").txz"
+BACKUP_OUT_FILE="$(date +"%Y-%m-%d_%H.%M").txz"
+# Deliberately on /volume1 rather than /tmp to ensure we have enough space.
+# Deliberately OUTSIDE $BACKUP_OUT_DIR, so a leftover .part file can never be
+# counted by the retention find below.
+BACKUP_TMP_FILE="/volume1/Backups/.git-backup-$TAG.$$.part"
-# Create the new backup.
-echo "[git backup] Backup of $HOST:$BACKUP_SRC_DIR ($TAG) starting"
-echo "[git backup] Backing up files: $BACKUP_SRC_FILES"
+echo "[git backup] Backup of $HOST ($TAG) starting"
mkdir -p "$BACKUP_OUT_DIR"
-ssh $USER@$HOST \
- tar Jcf - -C "$BACKUP_SRC_DIR" "$BACKUP_SRC_FILES" \
- > "$BACKUP_OUT_DIR/$BACKUP_OUT_FILE"
-echo "[git backup] Wrote archive $BACKUP_OUT_DIR/$BACKUP_OUT_FILE"
+
+# Write to a temp file outside the backup directory, and only move it into
+# place on success.
+if ssh "$USER@$HOST" -p "$PORT" > "$BACKUP_TMP_FILE"; then
+ mv "$BACKUP_TMP_FILE" "$BACKUP_OUT_DIR/$BACKUP_OUT_FILE"
+ echo "[git backup] Wrote archive $BACKUP_OUT_DIR/$BACKUP_OUT_FILE"
+else
+ echo "[git backup] ERROR: transfer failed, no archive written" >&2
+ rm -f "$BACKUP_TMP_FILE"
+ exit 1
+fi
+
+# Sanity check: a valid xz archive is never this small. Catches the case where
+# the transfer "succeeded" but produced nothing useful.
+if [ "$(stat -c %s "$BACKUP_OUT_DIR/$BACKUP_OUT_FILE")" -lt 1000000 ]; then
+ echo "[git backup] ERROR: archive suspiciously small, keeping it but not pruning" >&2
+ exit 1
+fi
# Delete archives past the retain count.
EXPIRED_ARCHIVES="$(find "$BACKUP_OUT_DIR" -maxdepth 1 -type f \
@@ -43,4 +71,4 @@ for f in $EXPIRED_ARCHIVES; do
rm -f "$f"
done
-echo "[git backup] Backup of $HOST:$BACKUP_SRC_DIR ($TAG) complete"
+echo "[git backup] Backup of $HOST ($TAG) complete"