From 35fd5e07e4a2554f62ed350b9d193a255901ce02 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 21 Sep 2026 19:22:54 +0000 Subject: publish-apk: cap the backups it creates Four publishes in one evening left 448MB of .bak copies of a 112MB APK, in a public web directory. A backup policy with no retention becomes the problem it was guarding against. Keeps the two most recent, configurable with --keep. Prunes before copying, so a failed publish doesn't skip the pruning as well. The first version of the test gave all four fixture backups the same mtime, so 'ls -t' ordered them arbitrarily and 'keeps the most recent' proved nothing -- it only failed because the arbitrary order happened to go the wrong way. Worth recording: a test that passes for the wrong reason is the same class of problem as the dead code these guards exist to catch. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B --- bin/publish-apk | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'bin') diff --git a/bin/publish-apk b/bin/publish-apk index faf6db8..520eeae 100755 --- a/bin/publish-apk +++ b/bin/publish-apk @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# publish-apk [--host tungsten] [--dir /site/static.terst.org/public/files] +# publish-apk [--host H] [--dir D] [--keep N] # # Publishes a built APK to the static site, verifying every step that has gone wrong before: # the host, that the file is really an APK, that what landed is byte-identical to what was @@ -15,6 +15,9 @@ die() { echo "publish-apk: $1" >&2; exit 2; } EXPECT_HOST="tungsten" DEST_DIR="/site/static.terst.org/public/files" +# Each backup is a full copy of a large APK, in a public directory. Four publishes in one +# evening left 448MB of them. Keep enough to roll back, not enough to become the problem. +KEEP_BACKUPS=2 [ $# -ge 2 ] || die "usage: publish-apk [--host H] [--dir D]" APK="$1"; NAME="$2"; shift 2 @@ -22,6 +25,7 @@ while [ $# -gt 0 ]; do case "$1" in --host) EXPECT_HOST="$2"; shift 2 ;; --dir) DEST_DIR="$2"; shift 2 ;; + --keep) KEEP_BACKUPS="$2"; shift 2 ;; *) die "unknown option '$1'" ;; esac done @@ -71,6 +75,13 @@ else echo "backup : none needed (new file)" fi +# Prune before copying, so a failed publish doesn't leave the pruning undone too. +PRUNED=$(ls -t "$DEST".*.bak 2>/dev/null | tail -n +$((KEEP_BACKUPS + 1))) +if [ -n "$PRUNED" ]; then + echo "$PRUNED" | while read -r old; do rm -f "$old"; done + echo "pruned : $(echo "$PRUNED" | wc -l) old backup(s), keeping the newest $KEEP_BACKUPS" +fi + cp "$APK" "$DEST" || die "copy failed" chmod 644 "$DEST" || die "chmod failed" chown root:root "$DEST" 2>/dev/null || true -- cgit v1.2.3