94 lines
2.8 KiB
Bash
94 lines
2.8 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Paths — adjust if your layout changes
|
|
GITEA_DIR="/gitea/gitea"
|
|
BACKUP_DIR="/gitea/backups"
|
|
CONTAINER="Gitea"
|
|
|
|
# Retention
|
|
KEEP_COUNT=7
|
|
B2_REMOTE="B2:SoloForge-backup"
|
|
|
|
mkdir -p "$BACKUP_DIR"
|
|
|
|
ts="$(date +%Y%m%d-%H%M%S)"
|
|
dump_file="gitea-dump-$ts.zip"
|
|
|
|
echo "[backup] Starting backup: $dump_file"
|
|
|
|
# Run dump INSIDE container
|
|
docker exec -u 1000 "$CONTAINER" sh -lc \
|
|
"gitea dump -c /data/gitea/conf/app.ini --file /data/$dump_file"
|
|
|
|
# Move dump from container-mounted volume to backup dir
|
|
mv "$GITEA_DIR/$dump_file" "$BACKUP_DIR/$dump_file"
|
|
|
|
echo "[backup] Dump created at $BACKUP_DIR/$dump_file"
|
|
|
|
# Upload to Backblaze B2 via rclone
|
|
rclone copy "$BACKUP_DIR/$dump_file" "$B2_REMOTE"
|
|
|
|
echo "[backup] Uploaded $dump_file to Backblaze B2"
|
|
|
|
# -----------------------
|
|
# Cleanup: keep latest N
|
|
# -----------------------
|
|
|
|
echo "[cleanup] Keeping newest $KEEP_COUNT local backups in $BACKUP_DIR"
|
|
|
|
mapfile -t localBackups < <(ls -1t "$BACKUP_DIR"/gitea-dump-*.zip 2>/dev/null || true)
|
|
|
|
if (( ${#localBackups[@]} > KEEP_COUNT )); then
|
|
for oldPath in "${localBackups[@]:KEEP_COUNT}"; do
|
|
oldFile="$(basename "$oldPath")"
|
|
|
|
# Extra safety: only delete files matching our exact dump pattern
|
|
if [[ "$oldFile" =~ ^gitea-dump-[0-9]{8}-[0-9]{6}\.zip$ ]]; then
|
|
echo "[cleanup] Deleting local: $oldPath"
|
|
rm -f -- "$oldPath"
|
|
else
|
|
echo "[cleanup] Skipping unexpected local filename (won't delete): $oldPath" >&2
|
|
fi
|
|
done
|
|
else
|
|
echo "[cleanup] Local backups <= $KEEP_COUNT, nothing to delete."
|
|
fi
|
|
|
|
echo "[cleanup] Keeping newest $KEEP_COUNT backups in B2 ($B2_REMOTE)"
|
|
|
|
# Safety guard: require remote to be in the form "REMOTE:bucket"
|
|
# (bucket root only, no trailing slash/path)
|
|
if ! [[ "$B2_REMOTE" =~ ^[^:]+:[^/]+$ ]]; then
|
|
echo "[cleanup] Refusing remote cleanup: B2_REMOTE must be bucket-root like 'B2:SoloForge-backup' (got: $B2_REMOTE)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Remote prune: list only root-level filenames, newest-first (timestamped name makes this safe).
|
|
# rclone lsf on a bucket root returns immediate entries only (non-recursive by default).
|
|
mapfile -t remoteBackups < <(
|
|
rclone lsf "$B2_REMOTE" \
|
|
--files-only \
|
|
--max-depth 1 \
|
|
--include "gitea-dump-*.zip" 2>/dev/null \
|
|
| sed 's:/$::' \
|
|
| sort -r
|
|
)
|
|
|
|
if (( ${#remoteBackups[@]} > KEEP_COUNT )); then
|
|
for old in "${remoteBackups[@]:KEEP_COUNT}"; do
|
|
# Extra safety: ensure we only ever delete matching root-level dump zips
|
|
if [[ "$old" =~ ^gitea-dump-[0-9]{8}-[0-9]{6}\.zip$ ]]; then
|
|
echo "[cleanup] Deleting remote: $old"
|
|
rclone deletefile "$B2_REMOTE/$old"
|
|
else
|
|
echo "[cleanup] Skipping unexpected remote filename (won't delete): $old" >&2
|
|
fi
|
|
done
|
|
else
|
|
echo "[cleanup] Remote backups <= $KEEP_COUNT, nothing to delete."
|
|
fi
|
|
|
|
echo "[backup] All done."
|
|
Update backup sctip
|