Files
openclaw-ops/scripts/backup-openclaw-state.sh

126 lines
3.3 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# Backs up key OpenClaw state to a versioned ZIP archive.
# Includes:
# - ~/.openclaw/openclaw.json
# - ~/.openclaw/cron/
# - ~/.openclaw/credentials/
# - ~/.openclaw/delivery-queue/ (if present)
#
# Usage:
# scripts/backup-openclaw-state.sh [output_dir]
# Example:
# scripts/backup-openclaw-state.sh /home/node/.openclaw/backups
OPENCLAW_HOME="${OPENCLAW_HOME:-$HOME/.openclaw}"
OUTPUT_DIR="${1:-$OPENCLAW_HOME/backups}"
TS="$(date -u +"%Y%m%d-%H%M%SZ")"
mkdir -p "$OUTPUT_DIR"
get_version() {
local raw ver
raw="$(openclaw --version 2>/dev/null || true)"
if [[ -n "$raw" ]]; then
ver="$(printf '%s' "$raw" | head -n1 | sed -E 's/[^0-9A-Za-z._-]+/-/g; s/^-+|-+$//g')"
if [[ -n "$ver" ]]; then
printf '%s' "$ver"
return 0
fi
fi
raw="$(openclaw status 2>/dev/null | grep -E 'Channel|Update' | head -n1 || true)"
ver="$(printf '%s' "$raw" | sed -E 's/[^0-9A-Za-z._-]+/-/g; s/^-+|-+$//g')"
if [[ -n "$ver" ]]; then
printf '%s' "$ver"
else
printf 'unknown'
fi
}
VERSION="$(get_version)"
ARCHIVE_NAME="openclaw-backup-${TS}-v${VERSION}.zip"
ARCHIVE_PATH="$OUTPUT_DIR/$ARCHIVE_NAME"
# Build include list (required + optional)
INCLUDE_PATHS=()
if [[ -f "$OPENCLAW_HOME/openclaw.json" ]]; then
INCLUDE_PATHS+=("$OPENCLAW_HOME/openclaw.json")
else
echo "WARN: Missing $OPENCLAW_HOME/openclaw.json" >&2
fi
if [[ -d "$OPENCLAW_HOME/cron" ]]; then
INCLUDE_PATHS+=("$OPENCLAW_HOME/cron")
else
echo "WARN: Missing $OPENCLAW_HOME/cron" >&2
fi
if [[ -d "$OPENCLAW_HOME/credentials" ]]; then
INCLUDE_PATHS+=("$OPENCLAW_HOME/credentials")
else
echo "WARN: Missing $OPENCLAW_HOME/credentials" >&2
fi
if [[ -d "$OPENCLAW_HOME/delivery-queue" ]]; then
INCLUDE_PATHS+=("$OPENCLAW_HOME/delivery-queue")
fi
if [[ ${#INCLUDE_PATHS[@]} -eq 0 ]]; then
echo "ERROR: Nothing to back up." >&2
exit 1
fi
# Create a temp metadata file and include it in the archive.
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
META_FILE="$TMP_DIR/backup-metadata.txt"
{
echo "timestamp_utc=$TS"
echo "openclaw_version=$VERSION"
echo "openclaw_home=$OPENCLAW_HOME"
echo "archive_name=$ARCHIVE_NAME"
echo "included_paths="
for p in "${INCLUDE_PATHS[@]}"; do
echo " - $p"
done
} > "$META_FILE"
# Create zip via Python for consistent behavior.
python3 - "$ARCHIVE_PATH" "$META_FILE" "${INCLUDE_PATHS[@]}" <<'PY'
import os
import sys
import zipfile
from pathlib import Path
archive = Path(sys.argv[1])
meta_file = Path(sys.argv[2])
items = [Path(p) for p in sys.argv[3:]]
with zipfile.ZipFile(archive, "w", compression=zipfile.ZIP_DEFLATED) as zf:
# Put metadata at archive root
zf.write(meta_file, arcname="backup-metadata.txt")
for item in items:
if not item.exists():
continue
if item.is_file():
zf.write(item, arcname=str(item).lstrip("/"))
else:
for root, dirs, files in os.walk(item):
root_path = Path(root)
# preserve empty dirs
if not files and not dirs:
zi = zipfile.ZipInfo(str(root_path).lstrip("/") + "/")
zf.writestr(zi, "")
for f in files:
fp = root_path / f
zf.write(fp, arcname=str(fp).lstrip("/"))
PY
echo "Backup created: $ARCHIVE_PATH"