feat: add restore.sh scaffold with arg parsing and usage

This commit is contained in:
kbe
2026-07-25 18:54:01 +02:00
parent d6f30c506e
commit 49ed87b46b
2 changed files with 188 additions and 0 deletions
Executable
+129
View File
@@ -0,0 +1,129 @@
#!/bin/bash
# =============================================================================
# Restore tooling for the borg-backup.sh / dump_db.sh backup system.
# Modes:
# restore.sh full [--archive NAME] [--force] [--dry-run]
# restore.sh db <db_name> [--archive NAME] [--yes] [--dry-run]
# restore.sh file <path-within-target> [--archive NAME] [--dest DIR] [--dry-run]
# restore.sh --list-archives
# =============================================================================
set -euo pipefail
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH"
umask 077
# ========================= CONFIGURATION =========================
NAME="borg-2025"
REPO="/home/srv/files/backups/$NAME"
TARGET="/home/srv/files/content"
ARCHIVE_TARGET_PATH="${TARGET#/}"
DB_CONTAINER="mariadb"
DB_START_TIMEOUT=180
BORG_PASSPHRASE_FILE="${BORG_PASSPHRASE_FILE:-/root/.borg-passphrase}"
ROOT_PASSWORD_FILE="${ROOT_PASSWORD_FILE:-/root/.mariadb-root.pw}"
DUMP_SUBDIR="mariadb/dump"
LOGDIR="${RESTORE_LOGDIR:-/var/log/borg}"
mkdir -p "$LOGDIR" 2>/dev/null || LOGDIR="/tmp"
LOGFILE="$LOGDIR/restore-$(date +%Y-%m-%d-%H%M%S).log"
export BORG_REPO="$REPO"
export BORG_PASSCOMMAND="cat $BORG_PASSPHRASE_FILE"
# =================================================================
log() {
local line
line="[$(date '+%F %T')] $*"
echo "$line"
echo "$line" >> "$LOGFILE" 2>/dev/null || true
}
step() { echo; echo "=== $* ==="; }
die() { log "ERROR: $*"; exit 1; }
run_cmd() {
log "[RUN] $*"
"$@"
}
usage() {
cat <<'EOF'
Usage:
restore.sh full [--archive NAME] [--force] [--dry-run]
restore.sh db <db_name> [--archive NAME] [--yes] [--dry-run]
restore.sh file <path-within-target> [--archive NAME] [--dest DIR] [--dry-run]
restore.sh --list-archives
restore.sh -h | --help
EOF
}
DRY_RUN=false
FORCE=false
YES=false
ARCHIVE_OVERRIDE=""
DEST=""
parse_common_flags() {
while [[ $# -gt 0 ]]; do
case "$1" in
--archive) ARCHIVE_OVERRIDE="$2"; shift 2 ;;
--force) FORCE=true; shift ;;
--yes) YES=true; shift ;;
--dry-run) DRY_RUN=true; shift ;;
--dest) DEST="$2"; shift 2 ;;
*) die "unknown flag: $1" ;;
esac
done
}
main() {
local cmd="${1:-}"
case "$cmd" in
"")
usage
exit 1
;;
-h|--help)
usage
exit 0
;;
--list-archives)
shift
parse_common_flags "$@"
list_archives
;;
full)
shift
parse_common_flags "$@"
cmd_full
;;
db)
shift
local dbname="${1:-}"
[[ -n "$dbname" ]] || die "db: missing <db_name>"
shift
parse_common_flags "$@"
cmd_db "$dbname"
;;
file)
shift
local relpath="${1:-}"
[[ -n "$relpath" ]] || die "file: missing <path>"
shift
parse_common_flags "$@"
cmd_file "$relpath"
;;
*)
usage
die "unknown command: $cmd"
;;
esac
}
main "$@"
+59
View File
@@ -0,0 +1,59 @@
#!/bin/bash
set -uo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
RESTORE="$HERE/../restore.sh"
FAILURES=0
assert_eq() {
local expected="$1" actual="$2" msg="$3"
if [[ "$expected" != "$actual" ]]; then
echo "FAIL: $msg (expected '$expected', got '$actual')"
FAILURES=$((FAILURES + 1))
else
echo "PASS: $msg"
fi
}
assert_contains() {
local haystack="$1" needle="$2" msg="$3"
if [[ "$haystack" != *"$needle"* ]]; then
echo "FAIL: $msg (expected to contain '$needle', got '$haystack')"
FAILURES=$((FAILURES + 1))
else
echo "PASS: $msg"
fi
}
test_help_exits_zero() {
local out rc
out="$(bash "$RESTORE" -h)"
rc=$?
assert_eq "0" "$rc" "help exits 0"
assert_contains "$out" "Usage:" "help prints usage"
}
test_no_args_exits_one() {
local rc
bash "$RESTORE" >/dev/null 2>&1
rc=$?
assert_eq "1" "$rc" "no args exits 1"
}
test_unknown_command_exits_one() {
local rc
bash "$RESTORE" bogus >/dev/null 2>&1
rc=$?
assert_eq "1" "$rc" "unknown command exits 1"
}
test_help_exits_zero
test_no_args_exits_one
test_unknown_command_exits_one
echo "-----"
if [[ "$FAILURES" -gt 0 ]]; then
echo "$FAILURES failure(s)"
exit 1
fi
echo "All tests passed"