Add initial infrastructure and backup scripts for Gitea and homelab deployment
- Create README.md with project layout and quick start instructions - Implement backup scripts for Gitea, including database and repository exports - Add systemd service and timer for automated Gitea backups - Develop bootstrap scripts for homelab and VPS setup - Document architecture and restore procedures - Configure Caddy reverse proxy and Docker Compose for service management - Establish secrets management guidelines
This commit is contained in:
76
backups/scripts/retention.sh
Normal file
76
backups/scripts/retention.sh
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE="${BASE:-/srv/backups}"
|
||||
KEEP_DAILY_DAYS="${KEEP_DAILY_DAYS:-7}"
|
||||
KEEP_WEEKLY_DAYS="${KEEP_WEEKLY_DAYS:-365}"
|
||||
|
||||
python3 - <<'PY' "$BASE" "$KEEP_DAILY_DAYS" "$KEEP_WEEKLY_DAYS"
|
||||
import datetime as dt
|
||||
import os
|
||||
import sys
|
||||
|
||||
base = sys.argv[1]
|
||||
keep_daily_days = int(sys.argv[2])
|
||||
keep_weekly_days = int(sys.argv[3])
|
||||
now = dt.datetime.now().timestamp()
|
||||
|
||||
if not os.path.isdir(base):
|
||||
print(f"Backup base not found: {base}")
|
||||
sys.exit(0)
|
||||
|
||||
files = []
|
||||
for root, _, names in os.walk(base):
|
||||
parts = set(os.path.normpath(root).split(os.sep))
|
||||
preserve_latest = "latest" in parts
|
||||
for name in names:
|
||||
path = os.path.join(root, name)
|
||||
if not os.path.isfile(path):
|
||||
continue
|
||||
try:
|
||||
mtime = os.path.getmtime(path)
|
||||
except OSError:
|
||||
continue
|
||||
age_days = (now - mtime) / 86400
|
||||
files.append((path, root, mtime, age_days, preserve_latest))
|
||||
|
||||
to_delete = set()
|
||||
|
||||
# Delete anything older than weekly window (unless it's in a latest folder)
|
||||
for path, _, _, age_days, preserve_latest in files:
|
||||
if not preserve_latest and age_days > keep_weekly_days:
|
||||
to_delete.add(path)
|
||||
|
||||
# For weekly window, keep one file per week per directory (latest by mtime)
|
||||
weekly_candidates = {}
|
||||
for path, root, mtime, age_days, preserve_latest in files:
|
||||
if preserve_latest:
|
||||
continue
|
||||
if keep_daily_days < age_days <= keep_weekly_days:
|
||||
iso = dt.datetime.fromtimestamp(mtime).isocalendar()
|
||||
key = (root, iso.year, iso.week)
|
||||
best = weekly_candidates.get(key)
|
||||
if best is None or mtime > best[1]:
|
||||
weekly_candidates[key] = (path, mtime)
|
||||
|
||||
weekly_keep = {v[0] for v in weekly_candidates.values()}
|
||||
|
||||
for path, _, _, age_days, preserve_latest in files:
|
||||
if preserve_latest:
|
||||
continue
|
||||
if keep_daily_days < age_days <= keep_weekly_days and path not in weekly_keep:
|
||||
to_delete.add(path)
|
||||
|
||||
deleted = 0
|
||||
for path in sorted(to_delete):
|
||||
try:
|
||||
os.remove(path)
|
||||
print(path)
|
||||
deleted += 1
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
print(f"Retention complete for {base} (deleted {deleted} files)")
|
||||
PY
|
||||
|
||||
echo "Policy: daily <= ${KEEP_DAILY_DAYS}d, weekly <= ${KEEP_WEEKLY_DAYS}d"
|
||||
Reference in New Issue
Block a user