From 6b74d835f6e1abd989beab0eb2f1f28410b119e8 Mon Sep 17 00:00:00 2001 From: Pascal Michel Date: Thu, 17 Sep 2026 23:07:10 +0200 Subject: [PATCH] Automatischer Push: .vault-tools/auto-push/taeglich_push.py - .vault-tools/auto-push/taeglich_push.py Erzeugt vom PostToolUse-Hook .vault-tools/auto-push/auto_push.py. Co-Authored-By: Claude Opus 5 --- .vault-tools/auto-push/taeglich_push.py | 102 ++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 .vault-tools/auto-push/taeglich_push.py diff --git a/.vault-tools/auto-push/taeglich_push.py b/.vault-tools/auto-push/taeglich_push.py new file mode 100644 index 0000000..0ccdd0d --- /dev/null +++ b/.vault-tools/auto-push/taeglich_push.py @@ -0,0 +1,102 @@ +"""Täglicher Lauf: committet und pusht die Anweisungsschicht, für die Windows-Aufgabenplanung. + +Ersteller: Pascal Michel +Erstellt: 2026-09-17 + +WARUM LOKAL UND NICHT ALS COWORK-ROUTINE + Eine Cowork-Routine läuft in Anthropics Cloud und hat weder Zugriff auf + D:\\Claude noch auf die Umgebungsvariable GITEA_TOKEN dieser Maschine (so + wörtlich in der Routinen-Doku, gelesen am 2026-09-17). Sie könnte hier + nichts committen. Deshalb die Windows-Aufgabenplanung. + +WAS ES TUT + Genau dasselbe wie der PostToolUse-Hook: die Arbeitsfunktion aus + auto_push.py, unter derselben Sperre. Der Umfang steht in der .gitignore + des Vault-Roots, nicht hier. + +WAS ES MELDET + Jeder Lauf schreibt eine Zeile nach lauf.log im selben Ordner — auch ein + Lauf ohne Änderung. Eine Protokolldatei, die nur bei Erfolg schreibt, + lässt sich von einer nicht gestarteten Aufgabe nicht unterscheiden, und + genau das war am 2026-09-07 beim Depotbericht das Problem. + + Exit 0: gepusht oder nichts zu tun. Exit 1: Befund, Text steht im + Protokoll. Die Aufgabenplanung zeigt den Code als „Letztes Ergebnis". +""" + +from __future__ import annotations + +import importlib.util +import sys +import traceback +from datetime import datetime +from pathlib import Path + +HIER = Path(__file__).resolve().parent +PROTOKOLL = HIER / "lauf.log" +MAX_PROTOKOLL_ZEILEN = 400 + + +def lade_hook(): + spec = importlib.util.spec_from_file_location("auto_push", HIER / "auto_push.py") + if spec is None or spec.loader is None: + raise RuntimeError(f"auto_push.py nicht ladbar: {HIER / 'auto_push.py'}") + modul = importlib.util.module_from_spec(spec) + spec.loader.exec_module(modul) + return modul + + +def schreibe(zeile: str) -> None: + stempel = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + text = f"{stempel} {zeile}\n" + try: + alt = PROTOKOLL.read_text(encoding="utf-8").splitlines(keepends=True) + except OSError: + alt = [] + alt.append(text) + PROTOKOLL.write_text("".join(alt[-MAX_PROTOKOLL_ZEILEN:]), encoding="utf-8") + sys.stdout.write(text) + + +def main() -> int: + try: + hook = lade_hook() + except Exception as fehler: # noqa: BLE001 + schreibe(f"BEFUND auto_push.py nicht ladbar: {type(fehler).__name__}: {fehler}") + return 1 + + wurzel = HIER.parents[1] + sperre = wurzel / hook.SPERRE_REL + deskriptor = hook.hole_sperre(sperre) + if deskriptor is None: + # Kein Befund: ein anderer Lauf arbeitet und nimmt alles mit. + schreibe("uebersprungen Sperre belegt, ein anderer Lauf arbeitet") + return 0 + + try: + vorher = hook.git(wurzel, "rev-parse", "HEAD").strip() + hook.arbeite(wurzel) + nachher = hook.git(wurzel, "rev-parse", "HEAD").strip() + except hook.GitFehler as fehler: + schreibe(f"BEFUND {fehler.kopf} | {' '.join(fehler.rumpf.split())[:400]}") + return 1 + except Exception as fehler: # noqa: BLE001 + schreibe(f"BEFUND unerwarteter Abbruch: {type(fehler).__name__}: {fehler}") + schreibe(" " + " ".join(traceback.format_exc().split())[:400]) + return 1 + finally: + hook.gib_sperre_frei(deskriptor, sperre) + + if vorher == nachher: + schreibe(f"ok nichts zu tun, HEAD {nachher[:8]}") + else: + schreibe(f"ok gepusht, HEAD {vorher[:8]} -> {nachher[:8]}") + return 0 + + +if __name__ == "__main__": + try: + sys.stdout.reconfigure(encoding="utf-8") + except AttributeError: + pass + raise SystemExit(main())