From 246da476dd37f1633ae8fe71de0ff980573ee276 Mon Sep 17 00:00:00 2001 From: eric-wien Date: Thu, 9 Jul 2026 07:51:48 +0200 Subject: [PATCH] backup: skip the run when nothing changed since the last archive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fingerprint the manifest (path, size, mtime of every host-local file), keep it in the backup dir, and skip the tar when it matches — the nightly timer no longer writes identical archives. --force overrides; the first run after archives disappear always backs up. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01VQb8NS8HS9cpXnjeZ1mQYd --- bin/backup | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/bin/backup b/bin/backup index cb3bdbe..0aa95b5 100755 --- a/bin/backup +++ b/bin/backup @@ -9,6 +9,10 @@ # Archives land OUTSIDE the repo (default ~/ogma-backups) so they survive `git pull`, # a reinstall, and `bin/uninstall`. Run on demand, from the Telegram bot (ogmactl # backup), or on a schedule (systemd/ogma-backup.timer). +# +# Runs are skipped when nothing changed: a fingerprint of the manifest (path, size, +# mtime of every file) is kept in the backup dir and compared before archiving. +# `--force` overrides. set -uo pipefail OGMA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" @@ -27,19 +31,22 @@ err() { printf '%s ✗ %s%s\n' "$c_red" "$*" "$c_rst" >&2; } OUT="${OGMA_BACKUP_DIR:-$HOME/ogma-backups}" KEEP="${OGMA_BACKUP_KEEP:-14}" NOTIFY=0 +FORCE=0 ACTION="create" HOST="$(hostname -s 2>/dev/null || echo host)" ANY_HOST=0 usage() { cat </dev/null || echo '?')" +# --- Delta check: skip when nothing changed since the last archive --------------------- +# Fingerprint = path + size + mtime of every manifest file, hashed. It lives in $OUT, +# NOT in the repo: a state file inside the manifest set would dirty every next run. +# Per-host name for the same reason list_archives is host-scoped (shared/synced dirs). +# Only skip if an archive for this host actually exists — a stale fingerprint must not +# suppress the first backup after the archives were deleted. +state_file="$OUT/.ogma-backup-state-$HOST" +fingerprint="$(cd "$OGMA_DIR" && xargs -0 stat -c '%n %s %Y' < "$mf" 2>/dev/null | sha256sum | cut -d' ' -f1)" +if [ "$FORCE" = 0 ] && [ -n "$fingerprint" ] \ + && [ "$(cat "$state_file" 2>/dev/null)" = "$fingerprint" ] \ + && [ -n "$(list_archives | tail -n 1)" ]; then + ok "No changes since last backup ($count files) — skipping. Use --force to archive anyway." + [ "$NOTIFY" = 1 ] && printf '🗄️ Ogma backup: no changes since last run — skipped.' | "$OGMA_DIR/bin/tg-send" 2>/dev/null + exit 0 +fi + # --- Create the archive ---------------------------------------------------------------- mkdir -p "$OUT" || { err "cannot create $OUT"; exit 1; } # Sweep partials left by an interrupted/killed earlier run (never counted by @@ -143,6 +167,7 @@ if tar -C "$OGMA_DIR" --null -czf "$archive.partial" -T "$mf" 2>/dev/null; then size="$(du -h "$archive" 2>/dev/null | cut -f1)" ok "Backed up $count file(s) → $archive ($size)" result="🗄️ Ogma backup OK — ${count} files, ${size:-?} → $(basename "$archive")" + printf '%s\n' "$fingerprint" > "$state_file" 2>/dev/null || warn "could not record backup state in $OUT" else err "tar failed — archive not written." [ "$NOTIFY" = 1 ] && printf '⚠️ Ogma backup FAILED on %s (tar error).' "$HOST" | "$OGMA_DIR/bin/tg-send" 2>/dev/null -- 2.51.2