Files
awoooi/scripts/backup/gitea-repo-bundle-backup.sh
Your Name 3b65f87624
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / build-and-deploy (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / tests (push) Has been cancelled
fix(backup): add verified gitea bundle fallback
2026-06-30 19:11:49 +08:00

144 lines
4.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# Create emergency Git bundle backups from Gitea over Git HTTP.
# This is a repository-history fallback only. It does not replace `gitea dump`,
# does not back up Gitea DB rows, settings, issues, packages, secrets, or LFS.
set -euo pipefail
GITEA_BASE_URL="${GITEA_BASE_URL:-http://192.168.0.110:3001}"
OUTPUT_ROOT="${OUTPUT_ROOT:-/home/ollama/backup/110/gitea/git-bundles}"
TIMEOUT_LS_REMOTE_SECONDS="${TIMEOUT_LS_REMOTE_SECONDS:-60}"
TIMEOUT_CLONE_SECONDS="${TIMEOUT_CLONE_SECONDS:-180}"
TIMEOUT_BUNDLE_SECONDS="${TIMEOUT_BUNDLE_SECONDS:-180}"
TIMEOUT_VERIFY_SECONDS="${TIMEOUT_VERIFY_SECONDS:-60}"
STAMP="${STAMP:-$(date +%Y%m%d-%H%M%S)}"
DRY_RUN=0
REPOS=()
usage() {
cat <<'USAGE'
Usage: scripts/backup/gitea-repo-bundle-backup.sh [options]
Options:
--repo OWNER/NAME Repository to bundle. May be passed more than once.
--output-root PATH Bundle root. Default: /home/ollama/backup/110/gitea/git-bundles
--gitea-base URL Gitea base URL. Default: http://192.168.0.110:3001
--stamp STAMP Output directory stamp. Default: current local timestamp.
--dry-run Print target URLs without cloning or writing bundles.
-h, --help Show this help.
This script uses GIT_TERMINAL_PROMPT=0 and never asks for, reads, or prints
tokens or passwords. Private repositories without an existing credential fail
closed in the manifest.
USAGE
}
while [ "$#" -gt 0 ]; do
case "$1" in
--repo)
shift
REPOS+=("${1:?--repo requires OWNER/NAME}")
;;
--output-root)
shift
OUTPUT_ROOT="${1:?--output-root requires PATH}"
;;
--gitea-base)
shift
GITEA_BASE_URL="${1:?--gitea-base requires URL}"
;;
--stamp)
shift
STAMP="${1:?--stamp requires value}"
;;
--dry-run)
DRY_RUN=1
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown argument: $1" >&2
usage >&2
exit 64
;;
esac
shift
done
if [ "${#REPOS[@]}" -eq 0 ]; then
REPOS=(
"wooo/awoooi"
"wooo/ewoooc"
"wooo/2026FIFAWorldCup"
"wooo/agent-bounty-protocol"
"wooo/AwoooGo"
"wooo/stockplatform-v2"
"wooo/vibework"
)
fi
if [ "$DRY_RUN" -eq 1 ]; then
for repo in "${REPOS[@]}"; do
printf 'DRY_RUN repo=%s url=%s/%s.git\n' "$repo" "$GITEA_BASE_URL" "$repo"
done
exit 0
fi
backup_root="${OUTPUT_ROOT%/}/${STAMP}"
tmp_root="$(mktemp -d "${TMPDIR:-/tmp}/gitea-bundle.XXXXXX")"
manifest="${backup_root}/manifest.tsv"
mkdir -p "$backup_root"
trap 'rm -rf "$tmp_root"' EXIT
printf 'repo\tstatus\thead_count\tbundle\tchecksum\n' > "$manifest"
for repo in "${REPOS[@]}"; do
slug="${repo//\//__}"
url="${GITEA_BASE_URL%/}/${repo}.git"
refs_file="${backup_root}/${slug}.refs"
err_file="${backup_root}/${slug}.err"
bundle_file="${backup_root}/${slug}.bundle"
checksum_file="${bundle_file}.sha256"
repo_tmp="${tmp_root}/${slug}.git"
if ! GIT_TERMINAL_PROMPT=0 timeout "$TIMEOUT_LS_REMOTE_SECONDS" \
git ls-remote --heads "$url" >"$refs_file" 2>"$err_file"; then
printf '%s\tls_remote_failed\t0\t\t\n' "$repo" >> "$manifest"
continue
fi
head_count="$(wc -l < "$refs_file" | tr -d ' ')"
rm -rf "$repo_tmp"
if ! GIT_TERMINAL_PROMPT=0 timeout "$TIMEOUT_CLONE_SECONDS" \
git clone --mirror --quiet "$url" "$repo_tmp" 2>>"$err_file"; then
printf '%s\tclone_failed\t%s\t\t\n' "$repo" "$head_count" >> "$manifest"
continue
fi
if ! timeout "$TIMEOUT_BUNDLE_SECONDS" \
git -C "$repo_tmp" bundle create "$bundle_file" --all >/dev/null 2>>"$err_file"; then
printf '%s\tbundle_failed\t%s\t\t\n' "$repo" "$head_count" >> "$manifest"
continue
fi
if ! timeout "$TIMEOUT_VERIFY_SECONDS" \
git -C "$repo_tmp" bundle verify "$bundle_file" >/dev/null 2>>"$err_file"; then
printf '%s\tverify_failed\t%s\t%s\t\n' "$repo" "$head_count" "$bundle_file" >> "$manifest"
continue
fi
(sha256sum "$bundle_file" 2>/dev/null || shasum -a 256 "$bundle_file") > "$checksum_file"
rm -rf "$repo_tmp"
printf '%s\tok\t%s\t%s\t%s\n' "$repo" "$head_count" "$bundle_file" "$checksum_file" >> "$manifest"
done
latest_link="${OUTPUT_ROOT%/}/latest"
if [ ! -e "$latest_link" ] || [ -L "$latest_link" ]; then
ln -sfn "$backup_root" "$latest_link"
else
echo "WARN latest path exists and is not a symlink: $latest_link" >&2
fi
cat "$manifest"