#!/bin/bash # FIXED backup script v3 - uses rsync with exclusions for speed set -e BACKUP_DIR="$HOME/.openclaw/workspace" STATE_DIR="$HOME/.openclaw" REPO_DIR="/tmp/openclaw-backup-cron-$$" GITEA_TOKEN="ba94c160b97c3a0fa5cf528ecc107eb2c8cddaa7" REPO_URL="http://git:${GITEA_TOKEN}@gitea.kangaroo-eel.ts.net:3000/Anthony/openclaw-backup.git" LOG_FILE="$HOME/.openclaw/workspace/logs/backup.log" GOTIFY_URL="http://runtipi.kangaroo-eel.ts.net:8129" GOTIFY_TOKEN="AGoV3cAUyUMDbyt" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } notify() { local title="$1" local message="$2" local priority="${3:-5}" curl -s -X POST \ -H 'Content-Type: application/json' \ "${GOTIFY_URL}/message?token=${GOTIFY_TOKEN}" \ -d "{\"title\": \"${title}\", \"message\": \"${message}\", \"priority\": ${priority}}" > /dev/null || true } # Ensure log directory exists mkdir -p "$(dirname "$LOG_FILE")" log "Starting backup..." # Clean up any old temp repo rm -rf "$REPO_DIR" # Clone repo fresh log "Cloning backup repo..." if ! git clone "$REPO_URL" "$REPO_DIR" 2>> "$LOG_FILE"; then log "ERROR: Failed to clone repo" notify "🚨 Backup Failed" "Failed to clone Gitea repo" 8 exit 1 fi cd "$REPO_DIR" # Backup workspace files using rsync (respects .gitignore patterns) log "Backing up workspace..." if command -v rsync &> /dev/null; then # Use rsync with delete and exclusions rsync -av --delete \ --exclude='.git' \ --exclude='target/' \ --exclude='*.rlib' \ --exclude='*.rmeta' \ --exclude='*.so' \ --exclude='node_modules/' \ "$BACKUP_DIR/" "$REPO_DIR/" 2>/dev/null || true else # Fallback to cp but clean up excluded dirs after find "$REPO_DIR" -mindepth 1 -maxdepth 1 ! -name '.git' -exec rm -rf {} + 2>/dev/null || true cp -r "$BACKUP_DIR"/* "$REPO_DIR/" 2>/dev/null || true # Remove build artifacts that shouldn't be backed up find "$REPO_DIR" -type d -name 'target' -exec rm -rf {} + 2>/dev/null || true fi # Backup state directory to openclaw-state/ log "Backing up state directory..." rm -rf "$REPO_DIR/openclaw-state" mkdir -p "$REPO_DIR/openclaw-state/cron" "$REPO_DIR/openclaw-state/devices" "$REPO_DIR/openclaw-state/skills" 2>/dev/null || true # Copy critical state files cp "$STATE_DIR/openclaw.json" "$REPO_DIR/openclaw-state/" 2>/dev/null || true cp -r "$STATE_DIR/cron"/* "$REPO_DIR/openclaw-state/cron/" 2>/dev/null || true cp "$STATE_DIR/devices/paired.json" "$REPO_DIR/openclaw-state/devices/" 2>/dev/null || true # Also backup skills metadata if it exists cp -r "$STATE_DIR/skills"/*.json "$REPO_DIR/openclaw-state/skills/" 2>/dev/null || true # Update manifest log "Updating manifest..." cat > "$REPO_DIR/BACKUP_MANIFEST.md" << EOF # OpenClaw Backup Generated: $(date '+%Y-%m-%d %H:%M:%S') ## Contents - skills/ - All installed skills and configs - automations/ - Custom automations (morning briefing, etc.) - memory/ - Long-term memory and daily notes - *.md - Core configuration files - openclaw-state/ - CRITICAL: Gateway config, cron jobs, skills metadata - openclaw.json - Gateway config (models, plugins, channels) - cron/ - All scheduled jobs - devices/ - Paired devices - skills/ - Skill metadata ## Backup Host Hostname: $(hostname) User: $(whoami) ## How to Restore 1. Clone this repo 2. Copy workspace files to ~/.openclaw/workspace/ 3. Copy openclaw-state/ files to ~/.openclaw/ preserving structure 4. Restart OpenClaw gateway See restore-from-gittea.sh in scripts/ for automated restore. EOF # Configure git git config user.email "krillyclaw@gmail.com" git config user.name "Krilly the Crab" # CRITICAL FIX: Always stage all changes (including deletions) before checking status git add -A 2>> "$LOG_FILE" # Check if there are STAGED changes (not just working tree changes) if git diff --cached --quiet 2>> "$LOG_FILE"; then log "No changes to commit" notify "⚠️ Backup" "No changes to backup (already up to date)" 3 rm -rf "$REPO_DIR" exit 0 fi # Commit the staged changes log "Committing changes..." git commit -m "Backup: $(date '+%Y-%m-%d %H:%M:%S')" 2>> "$LOG_FILE" # Push to remote log "Pushing changes..." if ! git push origin main 2>> "$LOG_FILE"; then log "ERROR: Failed to push" notify "🚨 Backup Failed" "Failed to push to Gitea" 8 rm -rf "$REPO_DIR" exit 1 fi COMMIT_HASH=$(git rev-parse --short HEAD) log "Backup complete! ($COMMIT_HASH)" # VERIFICATION: Confirm the push actually happened by checking remote log "Verifying push..." REMOTE_HASH=$(git ls-remote --heads "$REPO_URL" main 2>/dev/null | head -1 | cut -f1 | cut -c1-7) if [ "$COMMIT_HASH" != "$REMOTE_HASH" ]; then log "ERROR: Local commit ($COMMIT_HASH) doesn't match remote ($REMOTE_HASH)" notify "🚨 BACKUP FAILED" "Push verification failed - commit not on remote" 8 rm -rf "$REPO_DIR" exit 1 fi log "✅ Verified: commit $COMMIT_HASH is on remote" notify "✅ OpenClaw Backup" "Backup to Gitea completed successfully (commit $COMMIT_HASH)" 5 # Clean up rm -rf "$REPO_DIR"