#!/usr/bin/env bash # Verify one Gitea bundle can be restored into a temporary mirror clone. # This is a dry-run verifier only. It does not restore into production, mutate # Gitea, read credentials, delete repositories, or change repo visibility. set -euo pipefail ROOT="${AIOPS_GITEA_BUNDLE_ROOT:-/home/ollama/backup/110/gitea/git-bundles/latest-private-complete}" REPO="${AIOPS_GITEA_BUNDLE_SAMPLE_REPO:-wooo/awoooi}" HOST_LABEL="${AIOPS_HOST_LABEL:-188}" TEXTFILE_PATH="${AIOPS_GITEA_BUNDLE_RESTORE_TEXTFILE:-/home/ollama/node_exporter_textfiles/gitea_bundle_restore_dry_run.prom}" WRITE_TEXTFILE=0 TIMEOUT_SECONDS="${TIMEOUT_SECONDS:-120}" usage() { cat <<'USAGE' Usage: scripts/backup/gitea-bundle-sample-restore-dry-run.sh [options] Options: --root PATH Bundle root directory. --repo OWNER/NAME Repository sample to verify. Default: wooo/awoooi. --write-textfile Write node-exporter textfile metric after verification. --textfile PATH Textfile metric path. -h, --help Show this help. This verifier clones the bundle into a temporary mirror and runs git fsck. It never restores to production, never contacts Gitea, and never reads tokens. USAGE } while [ "$#" -gt 0 ]; do case "$1" in --root) shift ROOT="${1:?--root requires PATH}" ;; --repo) shift REPO="${1:?--repo requires OWNER/NAME}" ;; --write-textfile) WRITE_TEXTFILE=1 ;; --textfile) shift TEXTFILE_PATH="${1:?--textfile requires PATH}" ;; -h|--help) usage exit 0 ;; *) echo "Unknown argument: $1" >&2 usage >&2 exit 64 ;; esac shift done slug="${REPO//\//__}" repo_name="${REPO##*/}" bundle_path="" checksum_path="" if [ -f "${ROOT}/${slug}.bundle" ]; then bundle_path="${ROOT}/${slug}.bundle" elif [ -f "${ROOT}/manifest.remote.tsv" ] || [ -f "${ROOT}/manifest.tsv" ]; then manifest="${ROOT}/manifest.remote.tsv" [ -f "$manifest" ] || manifest="${ROOT}/manifest.tsv" bundle_path="$(awk -F '\t' -v repo="$REPO" 'NR > 1 && $1 == repo {print $4; exit}' "$manifest")" if [ -n "$bundle_path" ] && [ ! -f "$bundle_path" ] && [ -f "${ROOT}/$(basename "$bundle_path")" ]; then bundle_path="${ROOT}/$(basename "$bundle_path")" fi fi if [ -z "$bundle_path" ] || [ ! -f "$bundle_path" ]; then first_match="$(find "$ROOT" -maxdepth 1 -type f -name "${repo_name}-local-*.bundle" | sort | tail -n 1)" bundle_path="$first_match" fi ok=0 ref_count=0 error_reason="" tmp_root="" if [ -z "$bundle_path" ] || [ ! -f "$bundle_path" ]; then error_reason="bundle_not_found" else checksum_path="${bundle_path}.sha256" if [ -f "$checksum_path" ]; then expected="$(awk 'NR == 1 {print $1}' "$checksum_path")" if command -v sha256sum >/dev/null 2>&1; then actual="$(sha256sum "$bundle_path" | awk '{print $1}')" else actual="$(shasum -a 256 "$bundle_path" | awk '{print $1}')" fi if [ "$expected" != "$actual" ]; then error_reason="checksum_mismatch" fi fi fi if [ -z "$error_reason" ]; then tmp_root="$(mktemp -d "${TMPDIR:-/tmp}/gitea-bundle-restore.XXXXXX")" trap 'rm -rf "$tmp_root"' EXIT if ! timeout "$TIMEOUT_SECONDS" git clone --mirror --quiet "$bundle_path" "${tmp_root}/restore.git"; then error_reason="clone_failed" elif ! timeout "$TIMEOUT_SECONDS" git -C "${tmp_root}/restore.git" fsck --connectivity-only >/dev/null; then error_reason="fsck_failed" else ref_count="$(git -C "${tmp_root}/restore.git" for-each-ref --format='%(refname)' | wc -l | tr -d ' ')" ok=1 fi fi timestamp="$(date +%s)" bundle_name="$(basename "${bundle_path:-missing}")" root_label="${ROOT//\\/\\\\}" root_label="${root_label//\"/\\\"}" repo_label="${REPO//\\/\\\\}" repo_label="${repo_label//\"/\\\"}" bundle_label="${bundle_name//\\/\\\\}" bundle_label="${bundle_label//\"/\\\"}" error_label="${error_reason//\\/\\\\}" error_label="${error_label//\"/\\\"}" if [ "$WRITE_TEXTFILE" -eq 1 ]; then mkdir -p "$(dirname "$TEXTFILE_PATH")" tmp_textfile="${TEXTFILE_PATH}.$$" { echo '# HELP awoooi_gitea_bundle_sample_restore_dry_run_ok Whether sample Gitea bundle restore dry-run succeeded.' echo '# TYPE awoooi_gitea_bundle_sample_restore_dry_run_ok gauge' printf 'awoooi_gitea_bundle_sample_restore_dry_run_ok{host="%s",repo="%s",root="%s",bundle="%s",error="%s"} %s\n' "$HOST_LABEL" "$repo_label" "$root_label" "$bundle_label" "$error_label" "$ok" printf 'awoooi_gitea_bundle_sample_restore_dry_run_timestamp{host="%s",repo="%s"} %s\n' "$HOST_LABEL" "$repo_label" "$timestamp" printf 'awoooi_gitea_bundle_sample_restore_dry_run_ref_count{host="%s",repo="%s"} %s\n' "$HOST_LABEL" "$repo_label" "$ref_count" } > "$tmp_textfile" mv "$tmp_textfile" "$TEXTFILE_PATH" fi printf 'GITEA_BUNDLE_SAMPLE_RESTORE_DRY_RUN_OK=%s\n' "$ok" printf 'REPO=%s\n' "$REPO" printf 'BUNDLE=%s\n' "${bundle_path:-missing}" printf 'REF_COUNT=%s\n' "$ref_count" printf 'TEXTFILE_WRITTEN=%s\n' "$WRITE_TEXTFILE" if [ -n "$error_reason" ]; then printf 'ERROR=%s\n' "$error_reason" exit 1 fi