File size: 4,831 Bytes
b79920e
 
 
 
420e831
 
 
 
b79920e
420e831
 
 
b79920e
 
420e831
 
 
b79920e
420e831
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b79920e
 
 
 
 
 
 
 
420e831
b79920e
420e831
 
 
 
 
 
 
b79920e
 
 
 
420e831
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b79920e
420e831
 
 
 
 
 
 
 
 
 
 
 
b79920e
420e831
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b79920e
 
420e831
b79920e
 
420e831
 
 
 
b79920e
420e831
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import os
import time
import subprocess
import datetime
import threading
import tarfile
import tempfile
from pathlib import Path

# =========================
# ENV VARIABLES
# =========================
BACKUP_REPO = os.environ.get("BACKUP_REPO")
HF_TOKEN = os.environ.get("HF_TOKEN")
BACKUP_INTERVAL = int(os.environ.get("BACKUP_INTERVAL", "5")) * 60
CONFIG_BACKUP_INTERVAL = int(os.environ.get("CONFIG_BACKUP_INTERVAL", "60")) * 60
CONFIG_KEEP_LIMIT = int(os.environ.get("CONFIG_KEEP_LIMIT", "5"))

# =========================
# PATHS
# =========================
WORKSPACE_PATH = "/home/vscode/workspace"
CONFIG_PATHS = [
    "/home/vscode/.claude",
    "/home/vscode/.config/code-server/config.yaml",
    "/home/vscode/.local/share/code-server/extensions",
    "/home/vscode/.local/share/code-server/User",
    "/home/vscode/.local/share/code-server/Machine",
    "/home/vscode/.local/share/code-server/coder.json"
]
WORKSPACE_EXCLUDES = ["__pycache__", ".git", "node_modules", ".DS_Store"]

# =========================
# HELPERS
# =========================
def hf_env():
    env = os.environ.copy()
    env["HF_HOME"] = "/tmp/hf_cache"
    env["XDG_CACHE_HOME"] = "/tmp/xdg_cache"
    env["TMPDIR"] = "/tmp"
    env["HF_TOKEN"] = HF_TOKEN
    os.makedirs(env["HF_HOME"], exist_ok=True)
    os.makedirs(env["XDG_CACHE_HOME"], exist_ok=True)
    os.makedirs(env["TMPDIR"], exist_ok=True)
    return env

def upload_folder(local_path, remote_path, excludes=None):
    env = hf_env()
    cmd = ["hf", "upload", BACKUP_REPO, local_path, remote_path, "--repo-type", "dataset"]
    if excludes:
        for e in excludes:
            cmd += ["--exclude", e]
    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, env=env)
    for line in iter(process.stdout.readline, ""):
        print("[Backup]", line.strip())
    process.wait()

def cleanup_old_backups():
    try:
        env = hf_env()
        result = subprocess.run(
            ["hf", "repo-files", "list", BACKUP_REPO, "--repo-type", "dataset"],
            stdout=subprocess.PIPE, text=True, env=env
        )
        lines = result.stdout.splitlines()
        configs = [l.strip() for l in lines if l.startswith("configs/configs_") and l.endswith(".tar.gz")]
        if len(configs) > CONFIG_KEEP_LIMIT:
            configs.sort(reverse=True)
            old = configs[CONFIG_KEEP_LIMIT:]
            print(f"[Cleanup] Found {len(old)} old config backups to delete.")
            for f in old:
                print(f"[Cleanup] Deleting {f}")
                subprocess.run(["hf", "repo-files", "delete", BACKUP_REPO, f, "--repo-type", "dataset"],
                               env=env, check=False)
        else:
            print(f"[Cleanup] No cleanup needed. Total configs: {len(configs)}")
    except Exception as e:
        print("[Cleanup] Failed:", e)

# =========================
# BACKUP ROUTINES
# =========================
def run_workspace_backup():
    while True:
        print("[Backup] Workspace backup started.")
        try:
            upload_folder(WORKSPACE_PATH, "workspace/", excludes=WORKSPACE_EXCLUDES)
            print("[Backup] Workspace backup completed at", datetime.datetime.utcnow().isoformat(), "UTC")
        except Exception as e:
            print("[Backup] Workspace backup failed:", e)
        time.sleep(BACKUP_INTERVAL)

def run_config_backup():
    while True:
        print("[Backup] Config backup started.")
        try:
            with tempfile.NamedTemporaryFile(delete=False, suffix=".tar.gz") as tmp:
                tar_path = tmp.name
            with tarfile.open(tar_path, "w:gz") as tar:
                for path in CONFIG_PATHS:
                    if os.path.exists(path):
                        arcname = os.path.relpath(path, "/home/vscode")
                        tar.add(path, arcname=arcname)
                        print(f"[Backup] Added {arcname}")
            timestamp = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H-%M-%SZ")
            remote_path = f"configs/configs_{timestamp}.tar.gz"
            upload_folder(tar_path, remote_path)
            os.remove(tar_path)
            print("[Backup] Config backup completed at", timestamp)
            cleanup_old_backups()
        except Exception as e:
            print("[Backup] Config backup failed:", e)
        time.sleep(CONFIG_BACKUP_INTERVAL)

# =========================
# MAIN ENTRY
# =========================
if __name__ == "__main__":
    if not BACKUP_REPO or not HF_TOKEN:
        print("[Backup] BACKUP_REPO or HF_TOKEN not set! Exiting.")
        exit(0)

    print("[Backup] Starting dual backup threads...")
    threading.Thread(target=run_workspace_backup, daemon=True).start()
    threading.Thread(target=run_config_backup, daemon=True).start()

    while True:
        time.sleep(3600)