Spaces:
Running
Running
| set -euo pipefail | |
| DATA_ROOT="${DATA_ROOT:-/data/root}" | |
| SYNC_INTERVAL="${SYNC_INTERVAL:-300}" | |
| CONFIG_PATH=".openclaw/openclaw.json" | |
| EXCLUDES=( | |
| "--exclude=.cache" | |
| "--exclude=.npm/_cacache" | |
| "--exclude=.npm/_update-notifier-last-checked*" | |
| "--exclude=.pnpm-store" | |
| "--exclude=node_modules" | |
| "--exclude=.next" | |
| "--exclude=dist" | |
| "--exclude=build" | |
| "--exclude=coverage" | |
| "--exclude=tmp" | |
| ) | |
| data_available() { | |
| [ -d /data ] || mkdir -p /data 2>/dev/null || return 1 | |
| mkdir -p "$DATA_ROOT" 2>/dev/null || return 1 | |
| } | |
| # /root → /data (--update: hanya file lebih baru di root yg masuk data) | |
| persist_root() { | |
| if data_available; then | |
| echo "Syncing /root → $DATA_ROOT (update: root wins if newer)..." | |
| rsync -rlptD --no-specials --no-devices --update --delete "${EXCLUDES[@]}" /root/ "$DATA_ROOT/" || true | |
| else | |
| echo "Persistent /data is not available; skipping persist." | |
| fi | |
| } | |
| # /data → /root (FORCE: data selalu menang, tanpa --update) | |
| restore_root() { | |
| if data_available; then | |
| echo "Restoring $DATA_ROOT → /root (force: data always wins)..." | |
| rsync -rlptD --no-specials --no-devices "${EXCLUDES[@]}" "$DATA_ROOT/" /root/ || true | |
| else | |
| echo "Persistent /data is not available; skipping restore." | |
| fi | |
| } | |
| sync_config_newer() { | |
| local root_config="/root/$CONFIG_PATH" | |
| local data_config="$DATA_ROOT/$CONFIG_PATH" | |
| if [ -f "$data_config" ] && { [ ! -f "$root_config" ] || [ "$data_config" -nt "$root_config" ]; }; then | |
| mkdir -p "$(dirname "$root_config")" | |
| cp -p "$data_config" "$root_config" || true | |
| echo "Pulled newer OpenClaw config: $data_config → $root_config" | |
| elif [ -f "$root_config" ] && { [ ! -f "$data_config" ] || [ "$root_config" -nt "$data_config" ]; }; then | |
| mkdir -p "$(dirname "$data_config")" | |
| cp -p "$root_config" "$data_config" || true | |
| echo "Persisted newer OpenClaw config: $root_config → $data_config" | |
| fi | |
| } | |
| # Urutan penting: | |
| # 1. persist → /root → /data (--update, perubahan root masuk data dulu) | |
| # 2. config → two-way reconcile openclaw.json | |
| # 3. restore → /data → /root (force, data selalu menang sebagai sumber kebenaran) | |
| # | |
| # Efeknya: | |
| # - Root berubah → masuk data di step 1, restore step 3 no-op (sudah sama) ✓ | |
| # - Data berubah → step 1 tidak tumpuk data (--update), step 3 force ke root ✓ | |
| reconcile_root_data() { | |
| if data_available; then | |
| persist_root | |
| sync_config_newer | |
| restore_root | |
| else | |
| echo "Persistent /data is not available; skipping two-way sync." | |
| fi | |
| } | |
| case "${1:-loop}" in | |
| restore) | |
| restore_root | |
| ;; | |
| persist) | |
| persist_root | |
| ;; | |
| reconcile) | |
| reconcile_root_data | |
| ;; | |
| loop) | |
| while true; do | |
| reconcile_root_data | |
| sleep "$SYNC_INTERVAL" | |
| done | |
| ;; | |
| *) | |
| echo "Usage: $0 {restore|persist|reconcile|loop}" | |
| exit 2 | |
| ;; | |
| esac |