git_infra

Git infra scripts for git.bracken.jp
git clone https://git.bracken.jp/git_infra.git
Log | Files | Refs | LICENSE

commit 47931d4de1736f4245bd35191e6021ed93175812
parent 2d9a6bc63a19ca7710b85f65fb2d3f5e9a46c841
Author: Chris Bracken <chris@bracken.jp>
Date:   Sun, 16 Aug 2026 21:35:32 +0900

Update backup script

Diffstat:
Mbackup_git.bracken.jp.sh | 58+++++++++++++++++++++++++++++++++++++++++++---------------
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"