anurag008w commited on
Commit
cd9637b
·
unverified ·
1 Parent(s): 27f94dd

Improve state snapshotting and legacy migration

Browse files

Refactor snapshotting to use a staging directory for atomic backups and migrate legacy state from hidden directory.

Files changed (1) hide show
  1. openclaw-sync.py +42 -6
openclaw-sync.py CHANGED
@@ -87,20 +87,33 @@ def count_files(path: Path) -> int:
87
  def snapshot_state_into_workspace() -> None:
88
  try:
89
  STATE_DIR.mkdir(parents=True, exist_ok=True)
90
- if OPENCLAW_STATE_BACKUP_DIR.exists():
91
- shutil.rmtree(OPENCLAW_STATE_BACKUP_DIR, ignore_errors=True)
92
- OPENCLAW_STATE_BACKUP_DIR.mkdir(parents=True, exist_ok=True)
 
 
 
 
93
 
94
  for source_path in OPENCLAW_HOME.iterdir():
95
  if source_path.name in EXCLUDED_STATE_NAMES:
96
  continue
97
 
98
- backup_path = OPENCLAW_STATE_BACKUP_DIR / source_path.name
99
  if source_path.is_dir():
100
  shutil.copytree(source_path, backup_path)
101
  elif source_path.is_file():
102
  shutil.copy2(source_path, backup_path)
 
 
 
 
 
103
  except Exception as exc:
 
 
 
 
104
  print(f"Warning: could not snapshot OpenClaw state: {exc}")
105
 
106
  try:
@@ -135,6 +148,23 @@ def snapshot_state_into_workspace() -> None:
135
 
136
  def restore_embedded_state() -> None:
137
  state_backup_root = STATE_DIR / "openclaw"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  if state_backup_root.is_dir():
139
  for source_path in state_backup_root.iterdir():
140
  name = source_path.name
@@ -401,8 +431,14 @@ def loop() -> int:
401
  time.sleep(INITIAL_DELAY)
402
  print(f"Workspace sync started: every {INTERVAL}s -> {repo_id}")
403
 
404
- last_fingerprint, last_marker = sync_once()
405
- print("Initial sync complete.")
 
 
 
 
 
 
406
 
407
  while not STOP_EVENT.is_set():
408
  try:
 
87
  def snapshot_state_into_workspace() -> None:
88
  try:
89
  STATE_DIR.mkdir(parents=True, exist_ok=True)
90
+ # Atomic snapshot: copy to a staging dir first, then rename.
91
+ # This prevents a half-written (or empty) backup if we crash mid-copy,
92
+ # which would otherwise be uploaded and overwrite the real HF backup.
93
+ staging_dir = STATE_DIR / ".openclaw-staging"
94
+ if staging_dir.exists():
95
+ shutil.rmtree(staging_dir, ignore_errors=True)
96
+ staging_dir.mkdir(parents=True, exist_ok=True)
97
 
98
  for source_path in OPENCLAW_HOME.iterdir():
99
  if source_path.name in EXCLUDED_STATE_NAMES:
100
  continue
101
 
102
+ backup_path = staging_dir / source_path.name
103
  if source_path.is_dir():
104
  shutil.copytree(source_path, backup_path)
105
  elif source_path.is_file():
106
  shutil.copy2(source_path, backup_path)
107
+
108
+ # Atomically swap staging → real backup dir
109
+ if OPENCLAW_STATE_BACKUP_DIR.exists():
110
+ shutil.rmtree(OPENCLAW_STATE_BACKUP_DIR, ignore_errors=True)
111
+ staging_dir.rename(OPENCLAW_STATE_BACKUP_DIR)
112
  except Exception as exc:
113
+ # Clean up staging on failure so it doesn't interfere next time
114
+ staging_dir = STATE_DIR / ".openclaw-staging"
115
+ if staging_dir.exists():
116
+ shutil.rmtree(staging_dir, ignore_errors=True)
117
  print(f"Warning: could not snapshot OpenClaw state: {exc}")
118
 
119
  try:
 
148
 
149
  def restore_embedded_state() -> None:
150
  state_backup_root = STATE_DIR / "openclaw"
151
+
152
+ # Migration fix: old backups stored state in ".huggingclaw-state/openclaw"
153
+ # (hidden dir). If new path doesn't exist but old hidden path does, use it
154
+ # and migrate it to the new path so future syncs write to the right place.
155
+ if not state_backup_root.is_dir():
156
+ legacy_state = WORKSPACE / ".huggingclaw-state" / "openclaw"
157
+ if legacy_state.is_dir():
158
+ print("Found legacy state backup at .huggingclaw-state/; migrating to huggingclaw-state/...")
159
+ try:
160
+ STATE_DIR.mkdir(parents=True, exist_ok=True)
161
+ shutil.copytree(legacy_state, state_backup_root)
162
+ legacy_root = WORKSPACE / ".huggingclaw-state"
163
+ shutil.rmtree(legacy_root, ignore_errors=True)
164
+ print("Legacy state migrated and .huggingclaw-state/ removed.")
165
+ except Exception as exc:
166
+ print(f"Warning: could not migrate legacy state: {exc}")
167
+
168
  if state_backup_root.is_dir():
169
  for source_path in state_backup_root.iterdir():
170
  name = source_path.name
 
431
  time.sleep(INITIAL_DELAY)
432
  print(f"Workspace sync started: every {INTERVAL}s -> {repo_id}")
433
 
434
+ # Take a fingerprint of the workspace AS RESTORED (after snapshotting state)
435
+ # so the first loop iteration only uploads if something genuinely changed.
436
+ # Previously this was None, which forced an unconditional upload every restart
437
+ # — even when restore had failed silently and the workspace was empty.
438
+ snapshot_state_into_workspace()
439
+ last_fingerprint = fingerprint_dir(WORKSPACE)
440
+ last_marker = metadata_marker(WORKSPACE)
441
+ print("Initial workspace fingerprint captured.")
442
 
443
  while not STOP_EVENT.is_set():
444
  try: