From 4f611724683c522333e1601d2250f793320e2ed7 Mon Sep 17 00:00:00 2001 From: Homelab Cursor Date: Mon, 22 Jun 2026 20:38:35 +0200 Subject: [PATCH] =?UTF-8?q?fix(sync):=20Guard=20gegen=20Datenverlust=20?= =?UTF-8?q?=E2=80=94=20reset=20--hard=20nur=20bei=20sauberem=20Baum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sync_state.py bricht den git-Sync ab + alarmiert per Telegram, wenn der Live-Arbeitsbaum uncommittete Aenderungen oder ungepushte Commits hat, statt sie per reset --hard FETCH_HEAD zu vernichten. Watchdog laeuft unabhaengig weiter. Behebt die Ursache von Issue #123. --- scripts/sync_state.py | 73 ++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 19 deletions(-) diff --git a/scripts/sync_state.py b/scripts/sync_state.py index 2058ae035..92ef4f27a 100755 --- a/scripts/sync_state.py +++ b/scripts/sync_state.py @@ -387,12 +387,45 @@ def git_sync(cfg: config.HomelabConfig): ["git", "-C", str(REPO), "fetch", forgejo_url, "main", "--quiet"], capture_output=True, timeout=30, ) + # -- Schutz gegen Datenverlust (Ref Issue #123) -------------------- + # Kein blindes 'reset --hard', wenn der Live-Arbeitsbaum nicht-committete + # Aenderungen oder lokale, noch nicht gepushte Commits hat -- sonst wuerde + # echte Arbeit vernichtet (das Beinahe-Desaster vom Juni 2026). + # Stattdessen: git-Sync ueberspringen + Telegram-Alarm; Mensch loest auf. + dirty = subprocess.run( + ["git", "-C", str(REPO), "status", "--porcelain", "--untracked-files=no"], + capture_output=True, text=True, timeout=15, + ).stdout.strip() + ahead = subprocess.run( + ["git", "-C", str(REPO), "rev-list", "--count", "FETCH_HEAD..HEAD"], + capture_output=True, text=True, timeout=15, + ).stdout.strip() + + if dirty or (ahead.isdigit() and int(ahead) > 0): + n_dirty = len(dirty.splitlines()) if dirty else 0 + n_ahead = ahead if ahead.isdigit() else "?" + log("GUARD: git-Sync abgebrochen -- Arbeitsbaum nicht sauber " + "(%s geaenderte Datei(en), %s ungepushte Commit(s)). " + "reset --hard uebersprungen (Schutz vor Datenverlust, Ref #123)." + % (n_dirty, n_ahead)) + tg_alert( + cfg, "sync_guard", + "*Homelab git-Sync ABGEBROCHEN*%0A%0A" + "Live-Repo (CT116) ist nicht sauber:%0A" + "- " + str(n_dirty) + " uncommittete Datei(en)%0A" + "- " + str(n_ahead) + " ungepushte Commit(s)%0A%0A" + "reset --hard FETCH_HEAD wurde UEBERSPRUNGEN, um Arbeit nicht zu " + "vernichten (Ref Issue #123).%0ABitte manuell aufloesen: committen + " + "pushen oder gezielt verwerfen. Watchdog laeuft normal weiter." + ) + return forgejo_url, False + subprocess.run( ["git", "-C", str(REPO), "reset", "--hard", "FETCH_HEAD"], capture_output=True, timeout=15, ) - return forgejo_url + return forgejo_url, True def git_commit_and_push(cfg: config.HomelabConfig, forgejo_url: str): @@ -436,33 +469,35 @@ def main(): log("Sync startet...") cfg = config.parse_config(REPO / "homelab.conf") - forgejo_url = git_sync(cfg) + forgejo_url, repo_clean = git_sync(cfg) cfg = config.parse_config(REPO / "homelab.conf") - # Watchdog - log("Watchdog läuft...") + # Watchdog (laeuft immer, unabhaengig vom git-Zustand) + log("Watchdog laeuft...") check_service(cfg, 109, "rss-manager", "RSS Manager") check_docker(cfg, 101, "wordpress-app", "WordPress Docker") - # STATE.md Dateien generieren - states = { - "arakava-news/STATE.md": generate_arakava_state(cfg), - "infrastructure/STATE.md": generate_infra_state(cfg), - "smart-home/STATE.md": generate_smarthome_state(cfg), - } + if repo_clean: + states = { + "arakava-news/STATE.md": generate_arakava_state(cfg), + "infrastructure/STATE.md": generate_infra_state(cfg), + "smart-home/STATE.md": generate_smarthome_state(cfg), + } - for path, content in states.items(): - full_path = REPO / path - full_path.parent.mkdir(parents=True, exist_ok=True) - full_path.write_text(content) - CHANGED = True - log(f"{path} aktualisiert") + for path, content in states.items(): + full_path = REPO / path + full_path.parent.mkdir(parents=True, exist_ok=True) + full_path.write_text(content) + CHANGED = True + log("%s aktualisiert" % path) - if CHANGED: - git_commit_and_push(cfg, forgejo_url) + if CHANGED: + git_commit_and_push(cfg, forgejo_url) + else: + log("Keine Aenderungen") else: - log("Keine Änderungen") + log("git-Sync uebersprungen (Guard aktiv) -- STATE-Regen + Push ausgelassen") log("Sync abgeschlossen")