Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist, same-site referer bypass, Blackbox-Exporter allowed bot, and all new exporters, cheat sheets, and automation scripts.
This commit is contained in:
@@ -0,0 +1,636 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#########################################################################################
|
||||
#### jenkins-backup.sh — Backup and restore Jenkins configuration and jobs ####
|
||||
#### Supports JENKINS_HOME tar, job XML export, and credential backup ####
|
||||
#### Requires: bash 4+, tar, curl ####
|
||||
#### ####
|
||||
#### Author: Phil Connor ####
|
||||
#### Contact: contact@mylinux.work ####
|
||||
#### License: MIT ####
|
||||
#### Version 1.01 ####
|
||||
#### ####
|
||||
#### Usage: ####
|
||||
#### export JENKINS_HOME="/var/lib/jenkins" ####
|
||||
#### ./jenkins-backup.sh --backup ####
|
||||
#### ####
|
||||
#### See --help for all options. ####
|
||||
#########################################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Defaults ──────────────────────────────────────────────────────────
|
||||
JENKINS_HOME="${JENKINS_HOME:-/var/lib/jenkins}"
|
||||
BACKUP_DIR="${BACKUP_DIR:-/var/backups/jenkins}"
|
||||
RETENTION_COUNT="${RETENTION_COUNT:-7}"
|
||||
JENKINS_URL="${JENKINS_URL:-}"
|
||||
JENKINS_USER="${JENKINS_USER:-}"
|
||||
JENKINS_TOKEN="${JENKINS_TOKEN:-}"
|
||||
BACKUP_TYPE="${BACKUP_TYPE:-full}"
|
||||
EXCLUDE_PLUGINS="${EXCLUDE_PLUGINS:-false}"
|
||||
CURL_TIMEOUT="${CURL_TIMEOUT:-30}"
|
||||
CURL_INSECURE="${CURL_INSECURE:-false}"
|
||||
VERBOSE="${VERBOSE:-false}"
|
||||
COLOR="${COLOR:-auto}"
|
||||
|
||||
# ── State ─────────────────────────────────────────────────────────────
|
||||
SCRIPT_NAME="$(basename "$0")"
|
||||
readonly SCRIPT_NAME
|
||||
RUN_MODE="backup"
|
||||
RESTORE_DIR=""
|
||||
DRY_RUN="false"
|
||||
CONFIG_ONLY="false"
|
||||
API_BACKUP="false"
|
||||
TMPDIR_WORK=""
|
||||
START_TIME=""
|
||||
|
||||
# ── Colors ────────────────────────────────────────────────────────────
|
||||
setup_colors() {
|
||||
if [[ "$COLOR" == "never" ]]; then
|
||||
RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET=""
|
||||
return
|
||||
fi
|
||||
if [[ "$COLOR" == "always" ]] || [[ -t 1 ]]; then
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m'
|
||||
BOLD='\033[1m'
|
||||
RESET='\033[0m'
|
||||
else
|
||||
RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET=""
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Logging ───────────────────────────────────────────────────────────
|
||||
log() { echo -e "${BLUE}[INFO]${RESET} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${RESET} $*" >&2; }
|
||||
err() { echo -e "${RED}[ERROR]${RESET} $*" >&2; }
|
||||
verbose() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${BLUE}[DEBUG]${RESET} $*"; fi; }
|
||||
|
||||
# ── Cleanup ───────────────────────────────────────────────────────────
|
||||
cleanup() {
|
||||
if [[ -n "$TMPDIR_WORK" && -d "$TMPDIR_WORK" ]]; then
|
||||
verbose "Cleaning up temp directory: $TMPDIR_WORK"
|
||||
rm -rf "$TMPDIR_WORK"
|
||||
fi
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
# ── Helpers ───────────────────────────────────────────────────────────
|
||||
human_size() {
|
||||
local bytes="$1"
|
||||
if [[ "$bytes" -ge 1073741824 ]]; then
|
||||
echo "$(( bytes / 1073741824 ))G"
|
||||
elif [[ "$bytes" -ge 1048576 ]]; then
|
||||
echo "$(( bytes / 1048576 ))M"
|
||||
elif [[ "$bytes" -ge 1024 ]]; then
|
||||
echo "$(( bytes / 1024 ))K"
|
||||
else
|
||||
echo "${bytes}B"
|
||||
fi
|
||||
}
|
||||
|
||||
elapsed_time() {
|
||||
local end_time
|
||||
end_time=$(date +%s)
|
||||
local duration=$(( end_time - START_TIME ))
|
||||
local mins=$(( duration / 60 ))
|
||||
local secs=$(( duration % 60 ))
|
||||
if [[ "$mins" -gt 0 ]]; then
|
||||
echo "${mins}m ${secs}s"
|
||||
else
|
||||
echo "${secs}s"
|
||||
fi
|
||||
}
|
||||
|
||||
jenkins_api() {
|
||||
local endpoint="$1"
|
||||
local output="${2:-}"
|
||||
local url="${JENKINS_URL%/}${endpoint}"
|
||||
local curl_opts=(-s -S --max-time "$CURL_TIMEOUT" -f)
|
||||
|
||||
[[ "$CURL_INSECURE" == "true" ]] && curl_opts+=(-k)
|
||||
|
||||
if [[ -n "$JENKINS_USER" && -n "$JENKINS_TOKEN" ]]; then
|
||||
curl_opts+=(-u "${JENKINS_USER}:${JENKINS_TOKEN}")
|
||||
fi
|
||||
|
||||
if [[ -n "$output" ]]; then
|
||||
curl "${curl_opts[@]}" "$url" -o "$output"
|
||||
else
|
||||
curl "${curl_opts[@]}" "$url"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Show Help ─────────────────────────────────────────────────────────
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Usage: $SCRIPT_NAME [OPTIONS]
|
||||
|
||||
Backup and restore Jenkins configuration, jobs, credentials, and plugins.
|
||||
Supports full JENKINS_HOME archives, config-only snapshots, and remote
|
||||
API exports with automatic retention.
|
||||
|
||||
OPTIONS:
|
||||
--backup Run a backup (default)
|
||||
--config-only Backup configuration files only (no workspaces/builds)
|
||||
--api-backup Export job configs via Jenkins API (remote, no filesystem)
|
||||
--restore DIR Restore from the specified backup directory or archive
|
||||
--dry-run With --restore, show what would be restored without changes
|
||||
--list List available backups
|
||||
--verify DIR Verify backup integrity via checksums
|
||||
--help, -h Show this help message
|
||||
|
||||
ENVIRONMENT VARIABLES:
|
||||
JENKINS_HOME Jenkins home directory (default: /var/lib/jenkins)
|
||||
BACKUP_DIR Root backup directory (default: /var/backups/jenkins)
|
||||
RETENTION_COUNT Number of backups to retain (default: 7)
|
||||
JENKINS_URL Jenkins base URL (required for --api-backup)
|
||||
JENKINS_USER Jenkins username (required for --api-backup)
|
||||
JENKINS_TOKEN Jenkins API token (required for --api-backup)
|
||||
BACKUP_TYPE Backup type: full, config, api (default: full)
|
||||
EXCLUDE_PLUGINS Skip plugin JPI files in config backup (default: false)
|
||||
CURL_TIMEOUT HTTP timeout in seconds (default: 30)
|
||||
CURL_INSECURE Allow self-signed certs (default: false)
|
||||
VERBOSE Enable verbose output (default: false)
|
||||
COLOR Color output: auto, always, never (default: auto)
|
||||
|
||||
EXAMPLES:
|
||||
# Full JENKINS_HOME backup
|
||||
JENKINS_HOME=/var/lib/jenkins ./jenkins-backup.sh --backup
|
||||
|
||||
# Config-only backup (XML configs + plugins, no build data)
|
||||
./jenkins-backup.sh --config-only
|
||||
|
||||
# Remote API backup (no filesystem access needed)
|
||||
JENKINS_URL=http://localhost:8080 JENKINS_USER=admin JENKINS_TOKEN=xxxx \\
|
||||
./jenkins-backup.sh --api-backup
|
||||
|
||||
# Restore from a backup
|
||||
./jenkins-backup.sh --restore /var/backups/jenkins/20260404-120000-full
|
||||
|
||||
# Dry-run restore
|
||||
./jenkins-backup.sh --restore /var/backups/jenkins/20260404-120000-full --dry-run
|
||||
|
||||
# List available backups
|
||||
./jenkins-backup.sh --list
|
||||
|
||||
# Verify a backup
|
||||
./jenkins-backup.sh --verify /var/backups/jenkins/20260404-120000-full
|
||||
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ── Parse Arguments ───────────────────────────────────────────────────
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--backup) RUN_MODE="backup"; shift ;;
|
||||
--config-only) RUN_MODE="backup"; CONFIG_ONLY="true"; BACKUP_TYPE="config"; shift ;;
|
||||
--api-backup) RUN_MODE="backup"; API_BACKUP="true"; BACKUP_TYPE="api"; shift ;;
|
||||
--restore)
|
||||
RUN_MODE="restore"
|
||||
if [[ $# -lt 2 ]]; then
|
||||
err "--restore requires a directory argument"
|
||||
exit 1
|
||||
fi
|
||||
RESTORE_DIR="$2"; shift 2
|
||||
;;
|
||||
--dry-run) DRY_RUN="true"; shift ;;
|
||||
--list) RUN_MODE="list"; shift ;;
|
||||
--verify)
|
||||
RUN_MODE="verify"
|
||||
if [[ $# -lt 2 ]]; then
|
||||
err "--verify requires a directory argument"
|
||||
exit 1
|
||||
fi
|
||||
RESTORE_DIR="$2"; shift 2
|
||||
;;
|
||||
--help|-h) show_help ;;
|
||||
*)
|
||||
err "Unknown option: $1"
|
||||
echo "Run '$SCRIPT_NAME --help' for usage." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# ── Validation ────────────────────────────────────────────────────────
|
||||
validate_backup() {
|
||||
if [[ "$API_BACKUP" == "true" ]]; then
|
||||
if [[ -z "$JENKINS_URL" ]]; then
|
||||
err "JENKINS_URL is required for --api-backup"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "$JENKINS_USER" || -z "$JENKINS_TOKEN" ]]; then
|
||||
err "JENKINS_USER and JENKINS_TOKEN are required for --api-backup"
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v curl &>/dev/null; then
|
||||
err "curl is required but not installed"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if [[ ! -d "$JENKINS_HOME" ]]; then
|
||||
err "JENKINS_HOME does not exist: $JENKINS_HOME"
|
||||
exit 1
|
||||
fi
|
||||
for cmd in tar sha256sum; do
|
||||
if ! command -v "$cmd" &>/dev/null; then
|
||||
err "$cmd is required but not installed"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
}
|
||||
|
||||
# ── Full Backup ───────────────────────────────────────────────────────
|
||||
do_full_backup() {
|
||||
local timestamp
|
||||
timestamp=$(date +%Y%m%d-%H%M%S)
|
||||
local dest="$BACKUP_DIR/${timestamp}-full"
|
||||
local archive="$dest/jenkins-full-${timestamp}.tar.gz"
|
||||
|
||||
mkdir -p "$dest"
|
||||
log "Starting full backup of $JENKINS_HOME"
|
||||
verbose "Destination: $dest"
|
||||
|
||||
local tar_excludes=(
|
||||
--exclude='workspace'
|
||||
--exclude='*/builds/*/archive'
|
||||
--exclude='*.log'
|
||||
)
|
||||
|
||||
tar czf "$archive" \
|
||||
"${tar_excludes[@]}" \
|
||||
-C "$(dirname "$JENKINS_HOME")" \
|
||||
"$(basename "$JENKINS_HOME")" 2>/dev/null || {
|
||||
err "Failed to create tar archive"
|
||||
rm -rf "$dest"
|
||||
exit 1
|
||||
}
|
||||
|
||||
sha256sum "$archive" > "$dest/checksums.sha256"
|
||||
echo "full" > "$dest/.backup-type"
|
||||
|
||||
local size
|
||||
size=$(stat -c%s "$archive" 2>/dev/null || stat -f%z "$archive" 2>/dev/null || echo 0)
|
||||
log "Full backup complete: $(human_size "$size") in $(elapsed_time)"
|
||||
log "Archive: $archive"
|
||||
}
|
||||
|
||||
# ── Config-Only Backup ────────────────────────────────────────────────
|
||||
do_config_backup() {
|
||||
local timestamp
|
||||
timestamp=$(date +%Y%m%d-%H%M%S)
|
||||
local dest="$BACKUP_DIR/${timestamp}-config"
|
||||
local archive="$dest/jenkins-config-${timestamp}.tar.gz"
|
||||
|
||||
mkdir -p "$dest"
|
||||
log "Starting config-only backup of $JENKINS_HOME"
|
||||
verbose "Destination: $dest"
|
||||
|
||||
TMPDIR_WORK=$(mktemp -d)
|
||||
local staging="$TMPDIR_WORK/jenkins-config"
|
||||
mkdir -p "$staging"
|
||||
|
||||
# Core config files
|
||||
for f in config.xml credentials.xml hudson.model.UpdateCenter.xml jenkins.model.JenkinsLocationConfiguration.xml; do
|
||||
if [[ -f "$JENKINS_HOME/$f" ]]; then
|
||||
cp "$JENKINS_HOME/$f" "$staging/"
|
||||
verbose " Copied $f"
|
||||
fi
|
||||
done
|
||||
|
||||
# Directories: users, secrets, nodes
|
||||
for d in users secrets nodes; do
|
||||
if [[ -d "$JENKINS_HOME/$d" ]]; then
|
||||
cp -r "$JENKINS_HOME/$d" "$staging/"
|
||||
verbose " Copied $d/"
|
||||
fi
|
||||
done
|
||||
|
||||
# Job configs (config.xml only)
|
||||
if [[ -d "$JENKINS_HOME/jobs" ]]; then
|
||||
find "$JENKINS_HOME/jobs" -name config.xml -type f | while IFS= read -r jobxml; do
|
||||
local relpath
|
||||
relpath="${jobxml#"$JENKINS_HOME"/}"
|
||||
local target_dir
|
||||
target_dir="$staging/$(dirname "$relpath")"
|
||||
mkdir -p "$target_dir"
|
||||
cp "$jobxml" "$target_dir/"
|
||||
done
|
||||
verbose " Copied job configs"
|
||||
fi
|
||||
|
||||
# Plugin JPI files
|
||||
if [[ "$EXCLUDE_PLUGINS" != "true" && -d "$JENKINS_HOME/plugins" ]]; then
|
||||
mkdir -p "$staging/plugins"
|
||||
find "$JENKINS_HOME/plugins" -maxdepth 1 -name '*.jpi' -exec cp {} "$staging/plugins/" \;
|
||||
local plugin_count
|
||||
plugin_count=$(find "$staging/plugins" -name '*.jpi' 2>/dev/null | wc -l)
|
||||
verbose " Copied $plugin_count plugin files"
|
||||
fi
|
||||
|
||||
tar czf "$archive" -C "$TMPDIR_WORK" "jenkins-config" 2>/dev/null || {
|
||||
err "Failed to create config archive"
|
||||
rm -rf "$dest"
|
||||
exit 1
|
||||
}
|
||||
|
||||
sha256sum "$archive" > "$dest/checksums.sha256"
|
||||
echo "config" > "$dest/.backup-type"
|
||||
|
||||
local size
|
||||
size=$(stat -c%s "$archive" 2>/dev/null || stat -f%z "$archive" 2>/dev/null || echo 0)
|
||||
log "Config backup complete: $(human_size "$size") in $(elapsed_time)"
|
||||
log "Archive: $archive"
|
||||
}
|
||||
|
||||
# ── API Backup ────────────────────────────────────────────────────────
|
||||
do_api_backup() {
|
||||
local timestamp
|
||||
timestamp=$(date +%Y%m%d-%H%M%S)
|
||||
local dest="$BACKUP_DIR/${timestamp}-api"
|
||||
|
||||
mkdir -p "$dest/jobs" "$dest/plugins"
|
||||
log "Starting API backup from $JENKINS_URL"
|
||||
verbose "Destination: $dest"
|
||||
|
||||
# Get job list — parse JSON without jq
|
||||
local job_list_json
|
||||
job_list_json=$(jenkins_api "/api/json?tree=jobs[name]") || {
|
||||
err "Failed to fetch job list from Jenkins API"
|
||||
rm -rf "$dest"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Extract job names from JSON using grep/sed
|
||||
local job_names
|
||||
job_names=$(echo "$job_list_json" | { grep -o '"name" *: *"[^"]*"' || true; } | sed 's/.*: *"//;s/"$//')
|
||||
|
||||
local job_count=0
|
||||
local job_fail=0
|
||||
if [[ -n "$job_names" ]]; then
|
||||
while IFS= read -r job_name; do
|
||||
[[ -z "$job_name" ]] && continue
|
||||
local encoded_name
|
||||
encoded_name="${job_name// /%20}"
|
||||
if jenkins_api "/job/${encoded_name}/config.xml" "$dest/jobs/${job_name}.xml" 2>/dev/null; then
|
||||
verbose " Exported job: $job_name"
|
||||
job_count=$((job_count + 1))
|
||||
else
|
||||
warn "Failed to export job: $job_name"
|
||||
job_fail=$((job_fail + 1))
|
||||
fi
|
||||
done <<< "$job_names"
|
||||
fi
|
||||
log "Exported $job_count jobs ($job_fail failed)"
|
||||
|
||||
# Plugin list
|
||||
local plugin_json
|
||||
if plugin_json=$(jenkins_api "/pluginManager/api/json?depth=1&tree=plugins[shortName,version,enabled,active]"); then
|
||||
echo "$plugin_json" > "$dest/plugins/plugin-list.json"
|
||||
local plugin_count
|
||||
plugin_count=$(echo "$plugin_json" | { grep -o '"shortName"' || true; } | wc -l)
|
||||
log "Exported plugin list: $plugin_count plugins"
|
||||
else
|
||||
warn "Failed to fetch plugin list"
|
||||
fi
|
||||
|
||||
# Create checksum file for all exported files
|
||||
if command -v sha256sum &>/dev/null; then
|
||||
find "$dest" -type f ! -name 'checksums.sha256' -exec sha256sum {} + > "$dest/checksums.sha256" 2>/dev/null || true
|
||||
fi
|
||||
echo "api" > "$dest/.backup-type"
|
||||
|
||||
local size
|
||||
size=$(du -sb "$dest" 2>/dev/null | cut -f1)
|
||||
size="${size:-0}"
|
||||
log "API backup complete: $(human_size "$size") in $(elapsed_time)"
|
||||
log "Backup directory: $dest"
|
||||
}
|
||||
|
||||
# ── Backup Entry Point ───────────────────────────────────────────────
|
||||
do_backup() {
|
||||
START_TIME=$(date +%s)
|
||||
validate_backup
|
||||
|
||||
if [[ "$API_BACKUP" == "true" ]]; then
|
||||
do_api_backup
|
||||
elif [[ "$CONFIG_ONLY" == "true" ]]; then
|
||||
do_config_backup
|
||||
else
|
||||
do_full_backup
|
||||
fi
|
||||
|
||||
prune_backups
|
||||
}
|
||||
|
||||
# ── Prune ─────────────────────────────────────────────────────────────
|
||||
prune_backups() {
|
||||
log "Pruning old backups (retaining $RETENTION_COUNT)..."
|
||||
|
||||
local backup_count
|
||||
backup_count=$(find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)
|
||||
|
||||
if [[ "$backup_count" -le "$RETENTION_COUNT" ]]; then
|
||||
log " No pruning needed ($backup_count backups present)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local remove_count=$((backup_count - RETENTION_COUNT))
|
||||
find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d | sort | head -n "$remove_count" | while IFS= read -r dir; do
|
||||
log " Removing $(basename "$dir")"
|
||||
rm -rf "$dir"
|
||||
done
|
||||
|
||||
log " Pruned $remove_count old backups"
|
||||
}
|
||||
|
||||
# ── Restore ───────────────────────────────────────────────────────────
|
||||
do_restore() {
|
||||
if [[ -z "$RESTORE_DIR" ]]; then
|
||||
err "--restore requires a directory argument"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$RESTORE_DIR" ]]; then
|
||||
err "Restore directory does not exist: $RESTORE_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find the tar archive in the backup directory
|
||||
local archive
|
||||
archive=$(find "$RESTORE_DIR" -maxdepth 1 -name '*.tar.gz' -type f | head -1)
|
||||
|
||||
if [[ -z "$archive" ]]; then
|
||||
err "No tar archive found in: $RESTORE_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Restore source: $archive"
|
||||
log "Restore target: $JENKINS_HOME"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log "${BOLD}Dry run — listing archive contents:${RESET}"
|
||||
tar tzf "$archive" | head -50
|
||||
local total
|
||||
total=$(tar tzf "$archive" | wc -l)
|
||||
log " ($total files total)"
|
||||
log "Dry run complete — no changes made"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Verify checksum if available
|
||||
if [[ -f "$RESTORE_DIR/checksums.sha256" ]]; then
|
||||
log "Verifying backup integrity..."
|
||||
if (cd "$RESTORE_DIR" && sha256sum -c checksums.sha256 --quiet 2>/dev/null); then
|
||||
log " ${GREEN}Checksums verified${RESET}"
|
||||
else
|
||||
warn "Checksum verification failed — proceed with caution"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -d "$JENKINS_HOME" ]]; then
|
||||
mkdir -p "$JENKINS_HOME"
|
||||
fi
|
||||
|
||||
log "Restoring archive..."
|
||||
tar xzf "$archive" -C "$(dirname "$JENKINS_HOME")" || {
|
||||
err "Failed to extract archive"
|
||||
exit 1
|
||||
}
|
||||
|
||||
warn "Jenkins must be restarted for changes to take effect"
|
||||
warn " sudo systemctl restart jenkins"
|
||||
log "Restore complete"
|
||||
}
|
||||
|
||||
# ── List ──────────────────────────────────────────────────────────────
|
||||
do_list() {
|
||||
if [[ ! -d "$BACKUP_DIR" ]]; then
|
||||
log "No backups found (directory does not exist: $BACKUP_DIR)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local count=0
|
||||
local format=" %-28s %-8s %s\n"
|
||||
|
||||
printf "\n"
|
||||
# shellcheck disable=SC2059
|
||||
printf "$format" "BACKUP" "TYPE" "SIZE"
|
||||
# shellcheck disable=SC2059
|
||||
printf "$format" "----------------------------" "--------" "--------"
|
||||
|
||||
for dir in "$BACKUP_DIR"/*/; do
|
||||
[[ -d "$dir" ]] || continue
|
||||
count=$((count + 1))
|
||||
|
||||
local name
|
||||
name=$(basename "$dir")
|
||||
|
||||
local btype="unknown"
|
||||
if [[ -f "$dir/.backup-type" ]]; then
|
||||
btype=$(cat "$dir/.backup-type")
|
||||
fi
|
||||
|
||||
local size
|
||||
size=$(du -sh "$dir" 2>/dev/null | cut -f1)
|
||||
size="${size:-?}"
|
||||
|
||||
# shellcheck disable=SC2059
|
||||
printf "$format" "$name" "$btype" "$size"
|
||||
done
|
||||
|
||||
printf "\n"
|
||||
if [[ "$count" -eq 0 ]]; then
|
||||
log "No backups found in $BACKUP_DIR"
|
||||
else
|
||||
log "$count backup(s) in $BACKUP_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Verify ────────────────────────────────────────────────────────────
|
||||
do_verify() {
|
||||
if [[ -z "$RESTORE_DIR" ]]; then
|
||||
err "--verify requires a directory argument"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$RESTORE_DIR" ]]; then
|
||||
err "Backup directory does not exist: $RESTORE_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Verifying backup: $RESTORE_DIR"
|
||||
|
||||
# Check backup type
|
||||
local btype="unknown"
|
||||
if [[ -f "$RESTORE_DIR/.backup-type" ]]; then
|
||||
btype=$(cat "$RESTORE_DIR/.backup-type")
|
||||
fi
|
||||
log " Backup type: $btype"
|
||||
|
||||
# Check for checksums
|
||||
if [[ -f "$RESTORE_DIR/checksums.sha256" ]]; then
|
||||
log " Verifying checksums..."
|
||||
if (cd "$RESTORE_DIR" && sha256sum -c checksums.sha256 2>/dev/null); then
|
||||
log " ${GREEN}All checksums passed${RESET}"
|
||||
else
|
||||
err "Checksum verification FAILED"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
warn "No checksum file found — cannot verify integrity"
|
||||
fi
|
||||
|
||||
# Archive contents summary
|
||||
local archive
|
||||
archive=$(find "$RESTORE_DIR" -maxdepth 1 -name '*.tar.gz' -type f | head -1)
|
||||
if [[ -n "$archive" ]]; then
|
||||
local size
|
||||
size=$(stat -c%s "$archive" 2>/dev/null || stat -f%z "$archive" 2>/dev/null || echo 0)
|
||||
log " Archive: $(basename "$archive") ($(human_size "$size"))"
|
||||
local file_count
|
||||
file_count=$(tar tzf "$archive" | wc -l)
|
||||
log " Files in archive: $file_count"
|
||||
fi
|
||||
|
||||
# API backup — list exported files
|
||||
if [[ "$btype" == "api" ]]; then
|
||||
local job_count
|
||||
job_count=$(find "$RESTORE_DIR/jobs" -name '*.xml' 2>/dev/null | wc -l)
|
||||
log " Exported jobs: $job_count"
|
||||
if [[ -f "$RESTORE_DIR/plugins/plugin-list.json" ]]; then
|
||||
local plugin_count
|
||||
plugin_count=$({ grep -o '"shortName"' "$RESTORE_DIR/plugins/plugin-list.json" || true; } | wc -l)
|
||||
log " Plugins recorded: $plugin_count"
|
||||
fi
|
||||
fi
|
||||
|
||||
log "Verification complete"
|
||||
}
|
||||
|
||||
# ── Main ──────────────────────────────────────────────────────────────
|
||||
main() {
|
||||
setup_colors
|
||||
|
||||
parse_args "$@"
|
||||
|
||||
case "$RUN_MODE" in
|
||||
backup) do_backup ;;
|
||||
restore) do_restore ;;
|
||||
list) do_list ;;
|
||||
verify) do_verify ;;
|
||||
*) err "Unknown mode: $RUN_MODE"; exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user