AiBOT007 commited on
Commit
5bd484d
·
verified ·
1 Parent(s): c834917

Create sync.py

Browse files
Files changed (1) hide show
  1. sync.py +153 -0
sync.py ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import tarfile
4
+ import tempfile
5
+ from pathlib import Path
6
+ from huggingface_hub import HfApi, hf_hub_download
7
+ from huggingface_hub.utils import EntryNotFoundError, RepositoryNotFoundError, HfHubHTTPError
8
+
9
+ api = HfApi()
10
+
11
+ REPO_ID = os.getenv("HF_DATASET")
12
+ TOKEN = os.getenv("HF_TOKEN")
13
+ FILENAME = "latest_backup.tar.gz"
14
+ BASE_DIR = Path("/root/.openclaw")
15
+ PATHS_TO_BACKUP = [
16
+ BASE_DIR / "sessions",
17
+ BASE_DIR / "agents" / "main" / "sessions",
18
+ BASE_DIR / "openclaw.json",
19
+ ]
20
+
21
+
22
+ def log(msg: str) -> None:
23
+ print(msg, flush=True)
24
+
25
+
26
+ def log_err(msg: str) -> None:
27
+ print(msg, file=sys.stderr, flush=True)
28
+
29
+
30
+ def is_subpath(child: Path, parent: Path) -> bool:
31
+ try:
32
+ child.resolve().relative_to(parent.resolve())
33
+ return True
34
+ except ValueError:
35
+ return False
36
+
37
+
38
+ def safe_extract(tar: tarfile.TarFile, target_dir: Path) -> None:
39
+ target_dir = target_dir.resolve()
40
+
41
+ for member in tar.getmembers():
42
+ member_path = target_dir / member.name
43
+ if not is_subpath(member_path, target_dir):
44
+ raise RuntimeError(f"Unsafe path detected in archive: {member.name}")
45
+
46
+ tar.extractall(path=target_dir)
47
+
48
+
49
+ def restore() -> bool:
50
+ if not REPO_ID or not TOKEN:
51
+ log("[RESTORE] Skip: HF_DATASET or HF_TOKEN not set")
52
+ return False
53
+
54
+ try:
55
+ log(f"[RESTORE] Downloading {FILENAME} from dataset repo: {REPO_ID}")
56
+ path = hf_hub_download(
57
+ repo_id=REPO_ID,
58
+ filename=FILENAME,
59
+ repo_type="dataset",
60
+ token=TOKEN,
61
+ )
62
+
63
+ BASE_DIR.mkdir(parents=True, exist_ok=True)
64
+
65
+ with tarfile.open(path, "r:gz") as tar:
66
+ safe_extract(tar, BASE_DIR)
67
+
68
+ log(f"[RESTORE] Success: restored from {FILENAME}")
69
+ return True
70
+
71
+ except EntryNotFoundError:
72
+ log(f"[RESTORE] Note: {FILENAME} not found in repo, probably first run")
73
+ return False
74
+ except RepositoryNotFoundError:
75
+ log_err(f"[RESTORE] Error: dataset repo not found: {REPO_ID}")
76
+ return False
77
+ except HfHubHTTPError as e:
78
+ log_err(f"[RESTORE] Hub HTTP error: {e}")
79
+ return False
80
+ except tarfile.TarError as e:
81
+ log_err(f"[RESTORE] Invalid tar archive: {e}")
82
+ return False
83
+ except Exception as e:
84
+ log_err(f"[RESTORE] Unexpected error: {e}")
85
+ return False
86
+
87
+
88
+ def backup() -> bool:
89
+ if not REPO_ID or not TOKEN:
90
+ log("[BACKUP] Skip: HF_DATASET or HF_TOKEN not set")
91
+ return False
92
+
93
+ existing_paths = [p for p in PATHS_TO_BACKUP if p.exists()]
94
+ if not existing_paths:
95
+ log("[BACKUP] Skip: no paths to backup")
96
+ return False
97
+
98
+ temp_path = None
99
+
100
+ try:
101
+ with tempfile.NamedTemporaryFile(suffix=".tar.gz", delete=False) as tmp:
102
+ temp_path = Path(tmp.name)
103
+
104
+ with tarfile.open(temp_path, "w:gz") as tar:
105
+ for p in existing_paths:
106
+ if p.is_relative_to(BASE_DIR):
107
+ arcname = p.relative_to(BASE_DIR)
108
+ else:
109
+ arcname = p.name
110
+ tar.add(str(p), arcname=str(arcname))
111
+ log(f"[BACKUP] Added: {p} -> {arcname}")
112
+
113
+ api.upload_file(
114
+ path_or_fileobj=str(temp_path),
115
+ path_in_repo=FILENAME,
116
+ repo_id=REPO_ID,
117
+ repo_type="dataset",
118
+ token=TOKEN,
119
+ commit_message=f"Update {FILENAME}",
120
+ )
121
+
122
+ log(f"[BACKUP] Success: uploaded {FILENAME}")
123
+ return True
124
+
125
+ except RepositoryNotFoundError:
126
+ log_err(f"[BACKUP] Error: dataset repo not found: {REPO_ID}")
127
+ return False
128
+ except HfHubHTTPError as e:
129
+ log_err(f"[BACKUP] Hub HTTP error: {e}")
130
+ return False
131
+ except tarfile.TarError as e:
132
+ log_err(f"[BACKUP] Tar error: {e}")
133
+ return False
134
+ except Exception as e:
135
+ log_err(f"[BACKUP] Unexpected error: {e}")
136
+ return False
137
+ finally:
138
+ if temp_path and temp_path.exists():
139
+ try:
140
+ temp_path.unlink()
141
+ except Exception as e:
142
+ log_err(f"[BACKUP] Warning: failed to delete temp file {temp_path}: {e}")
143
+
144
+
145
+ if __name__ == "__main__":
146
+ action = sys.argv[1].strip().lower() if len(sys.argv) > 1 else "restore"
147
+
148
+ if action == "backup":
149
+ ok = backup()
150
+ sys.exit(0 if ok else 1)
151
+ else:
152
+ ok = restore()
153
+ sys.exit(0 if ok else 1)