#!/usr/bin/env bash # Daily retention sweep — keeps the newest KEEP_DAYS distinct calendar # dates of backups, on BOTH local disk and S3, regardless of how old # those dates are. So a long quiet period leaves backups intact instead # of letting them age out. # # Idempotent. Safe to run on a cron. set -uo pipefail CONFIG_DIR="/home/ubuntu/gitea-backups" LOG="${CONFIG_DIR}/logs/retention.log" KEEP_DAYS=7 S3_REMOTE="s3" S3_BUCKET="toqqer-gitea-backup" RCLONE_CONF="/home/ubuntu/.config/rclone/rclone.conf" QUIET=0 [[ "${1:-}" == "--quiet" ]] && QUIET=1 log() { printf '%s %s\n' "$(date -u +%FT%TZ)" "$*" >> "$LOG"; } say() { [[ $QUIET -eq 0 ]] && echo "$*"; log "$*"; } # ---- S3: keep newest KEEP_DAYS top-level YYYY-MM-DD folders ---- mapfile -t s3_dates < <( rclone --config "$RCLONE_CONF" lsd "${S3_REMOTE}:${S3_BUCKET}/" 2>/dev/null \ | awk '{print $NF}' \ | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' \ | sort ) n=${#s3_dates[@]} if (( n <= KEEP_DAYS )); then say "S3: ${n} date-folder(s) present — all kept (limit ${KEEP_DAYS})" else to_delete=$(( n - KEEP_DAYS )) deleted=0 for ((i=0; i>"$LOG"; then say "S3: deleted ${d}/" deleted=$((deleted+1)) else say "S3: FAIL deleting ${d}/ (see log)" fi done say "S3: kept newest ${KEEP_DAYS} date-folder(s), deleted ${deleted} older" fi # ---- LOCAL: same semantics on /home/ubuntu/gitea-backups/{repos,db}/ ---- # All filenames are date-prefixed (YYYY-MM-DDTHH-MM-SSZ...), so we group by # the leading date and keep files only from the newest KEEP_DAYS dates seen. mapfile -t local_dates < <( { find "${CONFIG_DIR}/repos" -type f -name '*.bundle' 2>/dev/null find "${CONFIG_DIR}/db" -type f -name '*.db.gz' 2>/dev/null } | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' | sort -u ) ln=${#local_dates[@]} if (( ln <= KEEP_DAYS )); then say "LOCAL: ${ln} date(s) present — all kept (limit ${KEEP_DAYS})" else keep_from=$(( ln - KEEP_DAYS )) keep_set="$(printf '%s\n' "${local_dates[@]:$keep_from}")" deleted_files=0 while IFS= read -r f; do [[ -z "$f" ]] && continue fd=$(grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' <<< "$(basename "$f")" | head -1) if ! grep -qx "$fd" <<< "$keep_set"; then rm -f "$f" && deleted_files=$((deleted_files+1)) fi done < <( find "${CONFIG_DIR}/repos" -type f -name '*.bundle' 2>/dev/null find "${CONFIG_DIR}/db" -type f -name '*.db.gz' 2>/dev/null ) find "${CONFIG_DIR}/repos" -mindepth 2 -type d -empty -delete 2>>"$LOG" || true say "LOCAL: kept newest ${KEEP_DAYS} date(s), deleted ${deleted_files} file(s)" fi exit 0