fix(backup): add verified gitea bundle fallback
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
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
This commit is contained in:
143
scripts/backup/gitea-repo-bundle-backup.sh
Executable file
143
scripts/backup/gitea-repo-bundle-backup.sh
Executable file
@@ -0,0 +1,143 @@
|
||||
#!/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"
|
||||
Reference in New Issue
Block a user