Files
claude-regelwerk/.vault-tools/auto-push/taeglich_push.py
T
Pascal MichelandClaude Opus 5 b7f325a97d 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 <[email protected]>
2026-09-17 23:07:10 +02:00

103 lines
3.4 KiB
Python

"""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())