#!/usr/bin/env bash
# backup/stages/rclone-sync.sh
# Ship the `latest/` tree to B2. Keep 30 daily + 12 monthly backups in
# the bucket using rclone lsjson + delete (server-side retention is
# enabled too but this script is defensive).
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=/dev/null
. "${HERE}/../../scripts/lib/log.sh"
RCLONE_CONF="${RCLONE_CONF:-/srv/homelab/secrets/rclone.conf}"
LOCAL="${LOCAL:-/srv/homelab/backup/latest}"
REMOTE="${REMOTE:-b2-homelab:homelab-backups}"
KEEP_DAILY="${KEEP_DAILY:-30}"
KEEP_MONTHLY="${KEEP_MONTHLY:-12}"
TS="$(date +%Y%m%d)"
run_rclone() {
rclone --config "${RCLONE_CONF}" "$@"
}
sync_one() {
local name="$1"
local src="${LOCAL}/${name}"
local dst="${REMOTE}/${TS}/${name}"
if [[ ! -e "${src}" ]]; then
log_info "skip ${name} (no local data)"
return 0
fi
log_info "sync ${name} -> ${dst}"
run_rclone copy --transfers 4 --checkers 8 --fast-list \
--b2-hard-delete --bwlimit 30M "${src}" "${dst}"
}
prune() {
local now monthly_cutoff daily_cutoff
now=$(date +%s)
daily_cutoff=$((now - KEEP_DAILY * 86400))
monthly_cutoff=$((now - KEEP_MONTHLY * 30 * 86400))
run_rclone lsjson --dirs-only "${REMOTE}" \
| jq -r '.[].Name' \
| while read -r day; do
local ts
ts=$(date -d "${day}" +%s 2>/dev/null || echo 0)
if (( ts == 0 )); then continue; fi
local keep=0
if (( ts >= daily_cutoff )); then keep=1; fi
if (( ts >= monthly_cutoff )) && [[ "${day: -2}" == "01" ]]; then keep=1; fi
if (( keep == 0 )); then
log_info "prune ${day}"
run_rclone purge "${REMOTE}/${day}"
fi
done
}
main() {
for sub in pg sqlite vols snapshots; do
sync_one "${sub}"
done
prune
}
main "$@"