Spaces:
Sleeping
Sleeping
Run initial Hermes backup sooner
Browse files- README.md +2 -0
- scripts/hermes-backup-loop.sh +16 -1
- scripts/hermes-state-backup.py +27 -2
- space-entrypoint.sh +3 -1
README.md
CHANGED
|
@@ -41,6 +41,8 @@ Optional backup settings:
|
|
| 41 |
- `HF_BACKUP_TOKEN`: fine-grained Hugging Face token with read/write access to
|
| 42 |
the private backup Dataset. Set this as a Space secret, not a public variable.
|
| 43 |
- `HERMES_BACKUP_INTERVAL_SECONDS`: backup interval, default `1800`.
|
|
|
|
|
|
|
| 44 |
- `HERMES_RESTORE_ON_START`: set to `0` to disable automatic restore on boot.
|
| 45 |
- `HERMES_FORCE_RESTORE`: set to `1` once if you want to overwrite local state
|
| 46 |
from the backup archive on the next boot.
|
|
|
|
| 41 |
- `HF_BACKUP_TOKEN`: fine-grained Hugging Face token with read/write access to
|
| 42 |
the private backup Dataset. Set this as a Space secret, not a public variable.
|
| 43 |
- `HERMES_BACKUP_INTERVAL_SECONDS`: backup interval, default `1800`.
|
| 44 |
+
- `HERMES_BACKUP_INITIAL_DELAY_SECONDS`: first backup delay after container
|
| 45 |
+
start, default `300`.
|
| 46 |
- `HERMES_RESTORE_ON_START`: set to `0` to disable automatic restore on boot.
|
| 47 |
- `HERMES_FORCE_RESTORE`: set to `1` once if you want to overwrite local state
|
| 48 |
from the backup archive on the next boot.
|
scripts/hermes-backup-loop.sh
CHANGED
|
@@ -2,6 +2,7 @@
|
|
| 2 |
set -euo pipefail
|
| 3 |
|
| 4 |
interval="${HERMES_BACKUP_INTERVAL_SECONDS:-1800}"
|
|
|
|
| 5 |
|
| 6 |
case "$interval" in
|
| 7 |
''|*[!0-9]*)
|
|
@@ -10,7 +11,21 @@ case "$interval" in
|
|
| 10 |
;;
|
| 11 |
esac
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
while true; do
|
| 14 |
-
|
| 15 |
/usr/local/bin/hermes-state-backup backup || echo "WARNING: Hermes state backup failed; will retry later."
|
|
|
|
| 16 |
done
|
|
|
|
| 2 |
set -euo pipefail
|
| 3 |
|
| 4 |
interval="${HERMES_BACKUP_INTERVAL_SECONDS:-1800}"
|
| 5 |
+
initial_delay="${HERMES_BACKUP_INITIAL_DELAY_SECONDS:-300}"
|
| 6 |
|
| 7 |
case "$interval" in
|
| 8 |
''|*[!0-9]*)
|
|
|
|
| 11 |
;;
|
| 12 |
esac
|
| 13 |
|
| 14 |
+
case "$initial_delay" in
|
| 15 |
+
''|*[!0-9]*)
|
| 16 |
+
echo "WARNING: invalid HERMES_BACKUP_INITIAL_DELAY_SECONDS=$initial_delay; using 300."
|
| 17 |
+
initial_delay=300
|
| 18 |
+
;;
|
| 19 |
+
esac
|
| 20 |
+
|
| 21 |
+
echo "Hermes backup loop started. First backup in ${initial_delay}s, then every ${interval}s."
|
| 22 |
+
|
| 23 |
+
if [[ "$initial_delay" != "0" ]]; then
|
| 24 |
+
sleep "$initial_delay"
|
| 25 |
+
fi
|
| 26 |
+
|
| 27 |
while true; do
|
| 28 |
+
echo "Running Hermes state backup at $(date -u +%Y-%m-%dT%H:%M:%SZ)."
|
| 29 |
/usr/local/bin/hermes-state-backup backup || echo "WARNING: Hermes state backup failed; will retry later."
|
| 30 |
+
sleep "$interval"
|
| 31 |
done
|
scripts/hermes-state-backup.py
CHANGED
|
@@ -53,6 +53,25 @@ def state_has_content(root: Path) -> bool:
|
|
| 53 |
return False
|
| 54 |
|
| 55 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
def add_tree(tar: tarfile.TarFile, root: Path) -> None:
|
| 57 |
for item in root.rglob("*"):
|
| 58 |
rel = item.relative_to(root)
|
|
@@ -81,8 +100,10 @@ def backup() -> None:
|
|
| 81 |
token = required_env("HF_BACKUP_TOKEN")
|
| 82 |
root = Path(env("HERMES_BACKUP_DIR", "/home/hermeswebui/.hermes")).resolve()
|
| 83 |
filename = env("HERMES_BACKUP_FILENAME", "hermes-state.tar.gz")
|
|
|
|
|
|
|
| 84 |
|
| 85 |
-
if
|
| 86 |
print("Hermes state backup skipped: no meaningful local state yet.")
|
| 87 |
return
|
| 88 |
|
|
@@ -96,6 +117,7 @@ def backup() -> None:
|
|
| 96 |
|
| 97 |
with tarfile.open(archive, "w:gz") as tar:
|
| 98 |
add_tree(tar, root)
|
|
|
|
| 99 |
|
| 100 |
manifest.write_text(
|
| 101 |
json.dumps(
|
|
@@ -103,6 +125,9 @@ def backup() -> None:
|
|
| 103 |
"created_at": timestamp,
|
| 104 |
"source": "Acrabohan/hermes-webui",
|
| 105 |
"path_in_repo": filename,
|
|
|
|
|
|
|
|
|
|
| 106 |
},
|
| 107 |
indent=2,
|
| 108 |
)
|
|
@@ -127,7 +152,7 @@ def backup() -> None:
|
|
| 127 |
commit_message=f"Update Hermes backup manifest {timestamp}",
|
| 128 |
)
|
| 129 |
|
| 130 |
-
print(f"Hermes state backup uploaded to dataset {repo}.")
|
| 131 |
|
| 132 |
|
| 133 |
def restore() -> None:
|
|
|
|
| 53 |
return False
|
| 54 |
|
| 55 |
|
| 56 |
+
def state_stats(root: Path) -> tuple[int, int]:
|
| 57 |
+
files = 0
|
| 58 |
+
bytes_total = 0
|
| 59 |
+
if not root.exists():
|
| 60 |
+
return files, bytes_total
|
| 61 |
+
for child in root.rglob("*"):
|
| 62 |
+
if not child.is_file():
|
| 63 |
+
continue
|
| 64 |
+
rel = child.relative_to(root)
|
| 65 |
+
if should_exclude(rel):
|
| 66 |
+
continue
|
| 67 |
+
files += 1
|
| 68 |
+
try:
|
| 69 |
+
bytes_total += child.stat().st_size
|
| 70 |
+
except OSError:
|
| 71 |
+
pass
|
| 72 |
+
return files, bytes_total
|
| 73 |
+
|
| 74 |
+
|
| 75 |
def add_tree(tar: tarfile.TarFile, root: Path) -> None:
|
| 76 |
for item in root.rglob("*"):
|
| 77 |
rel = item.relative_to(root)
|
|
|
|
| 100 |
token = required_env("HF_BACKUP_TOKEN")
|
| 101 |
root = Path(env("HERMES_BACKUP_DIR", "/home/hermeswebui/.hermes")).resolve()
|
| 102 |
filename = env("HERMES_BACKUP_FILENAME", "hermes-state.tar.gz")
|
| 103 |
+
file_count, byte_count = state_stats(root)
|
| 104 |
+
print(f"Hermes state backup source: {root} ({file_count} files, {byte_count} bytes).")
|
| 105 |
|
| 106 |
+
if file_count == 0:
|
| 107 |
print("Hermes state backup skipped: no meaningful local state yet.")
|
| 108 |
return
|
| 109 |
|
|
|
|
| 117 |
|
| 118 |
with tarfile.open(archive, "w:gz") as tar:
|
| 119 |
add_tree(tar, root)
|
| 120 |
+
archive_size = archive.stat().st_size
|
| 121 |
|
| 122 |
manifest.write_text(
|
| 123 |
json.dumps(
|
|
|
|
| 125 |
"created_at": timestamp,
|
| 126 |
"source": "Acrabohan/hermes-webui",
|
| 127 |
"path_in_repo": filename,
|
| 128 |
+
"file_count": file_count,
|
| 129 |
+
"source_bytes": byte_count,
|
| 130 |
+
"archive_bytes": archive_size,
|
| 131 |
},
|
| 132 |
indent=2,
|
| 133 |
)
|
|
|
|
| 152 |
commit_message=f"Update Hermes backup manifest {timestamp}",
|
| 153 |
)
|
| 154 |
|
| 155 |
+
print(f"Hermes state backup uploaded to dataset {repo} ({archive_size} bytes).")
|
| 156 |
|
| 157 |
|
| 158 |
def restore() -> None:
|
space-entrypoint.sh
CHANGED
|
@@ -55,11 +55,13 @@ if [[ "${HERMES_BACKUP_DISABLE_LOOP:-0}" != "1" && -n "${HERMES_BACKUP_REPO:-}"
|
|
| 55 |
HERMES_BACKUP_REPO="${HERMES_BACKUP_REPO}" \
|
| 56 |
HERMES_BACKUP_DIR="${HERMES_HOME:-/home/hermeswebui/.hermes}" \
|
| 57 |
HERMES_BACKUP_INTERVAL_SECONDS="${HERMES_BACKUP_INTERVAL_SECONDS:-1800}" \
|
|
|
|
| 58 |
HERMES_BACKUP_FILENAME="${HERMES_BACKUP_FILENAME:-hermes-state.tar.gz}" \
|
| 59 |
/usr/local/bin/hermes-backup-loop &
|
| 60 |
fi
|
| 61 |
|
| 62 |
unset HF_BACKUP_TOKEN HERMES_BACKUP_REPO HERMES_FORCE_RESTORE HERMES_RESTORE_ON_START
|
| 63 |
-
unset HERMES_BACKUP_INTERVAL_SECONDS
|
|
|
|
| 64 |
|
| 65 |
exec /hermeswebui_init.bash
|
|
|
|
| 55 |
HERMES_BACKUP_REPO="${HERMES_BACKUP_REPO}" \
|
| 56 |
HERMES_BACKUP_DIR="${HERMES_HOME:-/home/hermeswebui/.hermes}" \
|
| 57 |
HERMES_BACKUP_INTERVAL_SECONDS="${HERMES_BACKUP_INTERVAL_SECONDS:-1800}" \
|
| 58 |
+
HERMES_BACKUP_INITIAL_DELAY_SECONDS="${HERMES_BACKUP_INITIAL_DELAY_SECONDS:-300}" \
|
| 59 |
HERMES_BACKUP_FILENAME="${HERMES_BACKUP_FILENAME:-hermes-state.tar.gz}" \
|
| 60 |
/usr/local/bin/hermes-backup-loop &
|
| 61 |
fi
|
| 62 |
|
| 63 |
unset HF_BACKUP_TOKEN HERMES_BACKUP_REPO HERMES_FORCE_RESTORE HERMES_RESTORE_ON_START
|
| 64 |
+
unset HERMES_BACKUP_INTERVAL_SECONDS HERMES_BACKUP_INITIAL_DELAY_SECONDS
|
| 65 |
+
unset HERMES_BACKUP_FILENAME HERMES_BACKUP_DISABLE_LOOP
|
| 66 |
|
| 67 |
exec /hermeswebui_init.bash
|