tenfyzhong commited on
Commit
bcc81e2
·
unverified ·
1 Parent(s): 773a181

fix(backup): default backup source to state dir (#13)

Browse files
Dockerfile CHANGED
@@ -92,7 +92,7 @@ ENV OPENCLAW_LLM_PROVIDER=thirdparty
92
  ENV OPENCLAW_LLM_API=openai-completions
93
  ENV OPENCLAW_BACKUP_ENABLED=true
94
  ENV OPENCLAW_BACKUP_CRON="*/30 * * * *"
95
- ENV OPENCLAW_BACKUP_SOURCE_DIR=/root
96
  ENV OPENCLAW_SSHX_AUTO_START=false
97
  ENV OPENCLAW_BACKUP_PRIVATE=true
98
  ENV OPENCLAW_BACKUP_PATH_PREFIX=backups
 
92
  ENV OPENCLAW_LLM_API=openai-completions
93
  ENV OPENCLAW_BACKUP_ENABLED=true
94
  ENV OPENCLAW_BACKUP_CRON="*/30 * * * *"
95
+ ENV OPENCLAW_BACKUP_SOURCE_DIR=/root/.openclaw
96
  ENV OPENCLAW_SSHX_AUTO_START=false
97
  ENV OPENCLAW_BACKUP_PRIVATE=true
98
  ENV OPENCLAW_BACKUP_PATH_PREFIX=backups
README.md CHANGED
@@ -66,7 +66,7 @@ In that case, you can still configure from inside the container (for example via
66
  - `OPENCLAW_CONFIG_PATH` (default: `/root/.openclaw/openclaw.json`)
67
  - `OPENCLAW_WORKSPACE_DIR` (default: `/root/.openclaw/workspace`)
68
  - `OPENCLAW_BACKUP_CRON` (default: `*/30 * * * *`, backup every 30 minutes)
69
- - `OPENCLAW_BACKUP_SOURCE_DIR` (default: `/root`, backup/restore base directory)
70
  - `OPENCLAW_BACKUP_PATH_PREFIX` (default: `backups`)
71
  - `OPENCLAW_BACKUP_LATEST_NAME` (default: `latest-backup.tar.gz`)
72
  - `OPENCLAW_BACKUP_KEEP_COUNT` (default: `48`, keep newest N timestamped backup archives; older ones are auto-deleted)
@@ -291,7 +291,7 @@ For this project, if you need stable dashboard access without cold starts, use p
291
 
292
  ## Backup/Restore Flow
293
 
294
- - Startup restore: on container startup, it tries to fetch `latest-backup.tar.gz` from the Dataset and restore it to `OPENCLAW_BACKUP_SOURCE_DIR` (default `/root`)
295
  - Scheduled backup: cron runs based on `OPENCLAW_BACKUP_CRON`
296
  - Shutdown backup: when the container receives a stop signal, one final backup is uploaded before exit
297
  - Each backup upload includes:
 
66
  - `OPENCLAW_CONFIG_PATH` (default: `/root/.openclaw/openclaw.json`)
67
  - `OPENCLAW_WORKSPACE_DIR` (default: `/root/.openclaw/workspace`)
68
  - `OPENCLAW_BACKUP_CRON` (default: `*/30 * * * *`, backup every 30 minutes)
69
+ - `OPENCLAW_BACKUP_SOURCE_DIR` (default: `/root/.openclaw`, backup/restore base directory)
70
  - `OPENCLAW_BACKUP_PATH_PREFIX` (default: `backups`)
71
  - `OPENCLAW_BACKUP_LATEST_NAME` (default: `latest-backup.tar.gz`)
72
  - `OPENCLAW_BACKUP_KEEP_COUNT` (default: `48`, keep newest N timestamped backup archives; older ones are auto-deleted)
 
291
 
292
  ## Backup/Restore Flow
293
 
294
+ - Startup restore: on container startup, it tries to fetch `latest-backup.tar.gz` from the Dataset and restore it to `OPENCLAW_BACKUP_SOURCE_DIR` (default `/root/.openclaw`)
295
  - Scheduled backup: cron runs based on `OPENCLAW_BACKUP_CRON`
296
  - Shutdown backup: when the container receives a stop signal, one final backup is uploaded before exit
297
  - Each backup upload includes:
openclaw_hf/backup.py CHANGED
@@ -57,7 +57,11 @@ class BackupConfig:
57
  raise ValueError("OPENCLAW_BACKUP_DATASET_REPO is not set")
58
 
59
  state_dir = Path(os.getenv("OPENCLAW_STATE_DIR", "/root/.openclaw")).resolve()
60
- backup_source_dir = Path(os.getenv("OPENCLAW_BACKUP_SOURCE_DIR", "/root")).resolve()
 
 
 
 
61
  work_dir = Path(os.getenv("OPENCLAW_BACKUP_WORK_DIR", "/tmp/openclaw-backup")).resolve()
62
  repo_type = (os.getenv("OPENCLAW_BACKUP_REPO_TYPE") or "dataset").strip() or "dataset"
63
  path_prefix = (os.getenv("OPENCLAW_BACKUP_PATH_PREFIX") or "backups").strip("/")
 
57
  raise ValueError("OPENCLAW_BACKUP_DATASET_REPO is not set")
58
 
59
  state_dir = Path(os.getenv("OPENCLAW_STATE_DIR", "/root/.openclaw")).resolve()
60
+ backup_source_raw = (os.getenv("OPENCLAW_BACKUP_SOURCE_DIR") or "").strip()
61
+ if backup_source_raw:
62
+ backup_source_dir = Path(backup_source_raw).resolve()
63
+ else:
64
+ backup_source_dir = state_dir
65
  work_dir = Path(os.getenv("OPENCLAW_BACKUP_WORK_DIR", "/tmp/openclaw-backup")).resolve()
66
  repo_type = (os.getenv("OPENCLAW_BACKUP_REPO_TYPE") or "dataset").strip() or "dataset"
67
  path_prefix = (os.getenv("OPENCLAW_BACKUP_PATH_PREFIX") or "backups").strip("/")
scripts/openclaw-entrypoint.sh CHANGED
@@ -18,7 +18,7 @@ OPENCLAW_LLM_BASE_URL_ENV="${OPENCLAW_LLM_BASE_URL_ENV:-OPENCLAW_LLM_BASE_URL}"
18
  OPENCLAW_LLM_API_KEY_ENV="${OPENCLAW_LLM_API_KEY_ENV:-OPENCLAW_LLM_API_KEY}"
19
  OPENCLAW_BACKUP_ENABLED="${OPENCLAW_BACKUP_ENABLED:-true}"
20
  OPENCLAW_BACKUP_CRON="${OPENCLAW_BACKUP_CRON:-*/30 * * * *}"
21
- OPENCLAW_BACKUP_SOURCE_DIR="${OPENCLAW_BACKUP_SOURCE_DIR:-/root}"
22
  OPENCLAW_BACKUP_ENV_FILE_PATH="${OPENCLAW_BACKUP_ENV_FILE_PATH:-/etc/openclaw-backup.env}"
23
  OPENCLAW_STDOUT_LOG_PATH="${OPENCLAW_STDOUT_LOG_PATH:-/var/log/openclaw/gateway.stdout.log}"
24
  OPENCLAW_STDERR_LOG_PATH="${OPENCLAW_STDERR_LOG_PATH:-/var/log/openclaw/gateway.stderr.log}"
 
18
  OPENCLAW_LLM_API_KEY_ENV="${OPENCLAW_LLM_API_KEY_ENV:-OPENCLAW_LLM_API_KEY}"
19
  OPENCLAW_BACKUP_ENABLED="${OPENCLAW_BACKUP_ENABLED:-true}"
20
  OPENCLAW_BACKUP_CRON="${OPENCLAW_BACKUP_CRON:-*/30 * * * *}"
21
+ OPENCLAW_BACKUP_SOURCE_DIR="${OPENCLAW_BACKUP_SOURCE_DIR:-${OPENCLAW_STATE_DIR}}"
22
  OPENCLAW_BACKUP_ENV_FILE_PATH="${OPENCLAW_BACKUP_ENV_FILE_PATH:-/etc/openclaw-backup.env}"
23
  OPENCLAW_STDOUT_LOG_PATH="${OPENCLAW_STDOUT_LOG_PATH:-/var/log/openclaw/gateway.stdout.log}"
24
  OPENCLAW_STDERR_LOG_PATH="${OPENCLAW_STDERR_LOG_PATH:-/var/log/openclaw/gateway.stderr.log}"
tests/test_backup.py CHANGED
@@ -30,7 +30,7 @@ class FakeApi:
30
 
31
 
32
  class BackupTests(unittest.TestCase):
33
- def test_from_env_defaults_to_root_paths(self):
34
  env = {
35
  "OPENCLAW_BACKUP_DATASET_REPO": "user/demo-backup",
36
  }
@@ -38,9 +38,25 @@ class BackupTests(unittest.TestCase):
38
  config = BackupConfig.from_env()
39
 
40
  self.assertEqual(config.state_dir, Path("/root/.openclaw").resolve())
41
- self.assertEqual(config.backup_source_dir, Path("/root").resolve())
42
  self.assertEqual(config.keep_count, 48)
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  def test_from_env_uses_explicit_backup_source_dir(self):
45
  with tempfile.TemporaryDirectory() as tmp:
46
  tmp_path = Path(tmp)
 
30
 
31
 
32
  class BackupTests(unittest.TestCase):
33
+ def test_from_env_defaults_backup_source_to_state_dir(self):
34
  env = {
35
  "OPENCLAW_BACKUP_DATASET_REPO": "user/demo-backup",
36
  }
 
38
  config = BackupConfig.from_env()
39
 
40
  self.assertEqual(config.state_dir, Path("/root/.openclaw").resolve())
41
+ self.assertEqual(config.backup_source_dir, config.state_dir)
42
  self.assertEqual(config.keep_count, 48)
43
 
44
+ def test_from_env_defaults_backup_source_to_explicit_state_dir_when_unset(self):
45
+ with tempfile.TemporaryDirectory() as tmp:
46
+ tmp_path = Path(tmp)
47
+ state_dir = tmp_path / "state"
48
+ state_dir.mkdir(parents=True)
49
+
50
+ env = {
51
+ "OPENCLAW_BACKUP_DATASET_REPO": "user/demo-backup",
52
+ "OPENCLAW_STATE_DIR": str(state_dir),
53
+ }
54
+ with mock.patch.dict("os.environ", env, clear=True):
55
+ config = BackupConfig.from_env()
56
+
57
+ self.assertEqual(config.state_dir, state_dir.resolve())
58
+ self.assertEqual(config.backup_source_dir, state_dir.resolve())
59
+
60
  def test_from_env_uses_explicit_backup_source_dir(self):
61
  with tempfile.TemporaryDirectory() as tmp:
62
  tmp_path = Path(tmp)
tests/test_runtime_user_defaults.py CHANGED
@@ -24,6 +24,7 @@ class RuntimeUserDefaultsTests(unittest.TestCase):
24
  self.assertIn("ENV OPENCLAW_HOME=/root", dockerfile)
25
  self.assertIn("ENV OPENCLAW_USER=root", dockerfile)
26
  self.assertIn("ENV OPENCLAW_GROUP=root", dockerfile)
 
27
  self.assertIn('OPENCLAW_BIN="$(command -v openclaw || true)"', dockerfile)
28
  self.assertIn('if [[ -z "$OPENCLAW_BIN" ]] && [[ -x /root/.npm-global/bin/openclaw ]]; then', dockerfile)
29
  self.assertIn('elif [[ -z "$OPENCLAW_BIN" ]] && [[ -x /root/.local/bin/openclaw ]]; then', dockerfile)
@@ -42,6 +43,7 @@ class RuntimeUserDefaultsTests(unittest.TestCase):
42
  self.assertIn('OPENCLAW_USER="${OPENCLAW_USER:-root}"', entrypoint)
43
  self.assertIn('OPENCLAW_GROUP="${OPENCLAW_GROUP:-root}"', entrypoint)
44
  self.assertIn('OPENCLAW_HOME="${OPENCLAW_HOME:-/root}"', entrypoint)
 
45
 
46
  def test_backup_cron_uses_openclaw_user(self):
47
  entrypoint = self._entrypoint_path().read_text(encoding="utf-8")
 
24
  self.assertIn("ENV OPENCLAW_HOME=/root", dockerfile)
25
  self.assertIn("ENV OPENCLAW_USER=root", dockerfile)
26
  self.assertIn("ENV OPENCLAW_GROUP=root", dockerfile)
27
+ self.assertIn("ENV OPENCLAW_BACKUP_SOURCE_DIR=/root/.openclaw", dockerfile)
28
  self.assertIn('OPENCLAW_BIN="$(command -v openclaw || true)"', dockerfile)
29
  self.assertIn('if [[ -z "$OPENCLAW_BIN" ]] && [[ -x /root/.npm-global/bin/openclaw ]]; then', dockerfile)
30
  self.assertIn('elif [[ -z "$OPENCLAW_BIN" ]] && [[ -x /root/.local/bin/openclaw ]]; then', dockerfile)
 
43
  self.assertIn('OPENCLAW_USER="${OPENCLAW_USER:-root}"', entrypoint)
44
  self.assertIn('OPENCLAW_GROUP="${OPENCLAW_GROUP:-root}"', entrypoint)
45
  self.assertIn('OPENCLAW_HOME="${OPENCLAW_HOME:-/root}"', entrypoint)
46
+ self.assertIn('OPENCLAW_BACKUP_SOURCE_DIR="${OPENCLAW_BACKUP_SOURCE_DIR:-${OPENCLAW_STATE_DIR}}"', entrypoint)
47
 
48
  def test_backup_cron_uses_openclaw_user(self):
49
  entrypoint = self._entrypoint_path().read_text(encoding="utf-8")