# -*- coding: utf-8 -*- """Laedt alle Projektdateien in EINEM Commit nach Gitea (ChangeFiles-API). Aufruf: python3 build/push_gitea.py "Commit-Nachricht" Uebersprungen werden die Pfade, die die Workflows selbst pflegen. Sonst wuerde ein lokaler Altstand die Ergebnisse eines Laufs ueberschreiben. """ import base64, json, os, sys, urllib.request, urllib.error # Von den Workflows gepflegt, nie von hier ueberschreiben: NICHT_UEBERSCHREIBEN = {'artikel-index.json', 'status-importe.json'} NICHT_UEBERSCHREIBEN_ORDNER = ('briefe/', 'nachbessern/', 'artikel/', 'build/lokal/') AUS_ORDNER = {'.git', 'node_modules', '__pycache__'} AUS_DATEIEN = {'.env'} def env(pfad='.env'): d = {} for zeile in open(pfad, encoding='utf-8'): zeile = zeile.strip() if not zeile or zeile.startswith('#') or '=' not in zeile: continue k, v = zeile.split('=', 1) v = v.strip() if v.startswith('"') and v.endswith('"'): v = v[1:-1].replace('\\$', '$').replace('\\"', '"') d[k] = v return d E = env() BASE = E['GITEA_BASE_URL'].rstrip('/') REPO = f"{BASE}/api/v1/repos/{E['GITEA_OWNER']}/{E['GITEA_REPO']}" KOPF = {'Authorization': 'token ' + E['GITEA_TOKEN'], 'Content-Type': 'application/json'} def ruf(methode, url, daten=None): r = urllib.request.Request(url, method=methode, headers=KOPF, data=json.dumps(daten).encode() if daten else None) try: with urllib.request.urlopen(r, timeout=90) as a: roh = a.read().decode() return a.status, (json.loads(roh) if roh else {}) except urllib.error.HTTPError as e: roh = e.read().decode() return e.code, (json.loads(roh) if roh.startswith(('{', '[')) else {'text': roh[:300]}) def sammle(): raus = [] for wurzel, ordner, namen in os.walk('.'): ordner[:] = [o for o in ordner if o not in AUS_ORDNER] for n in namen: p = os.path.relpath(os.path.join(wurzel, n), '.').replace(os.sep, '/') if p in AUS_DATEIEN or p in NICHT_UEBERSCHREIBEN: continue if p.startswith(NICHT_UEBERSCHREIBEN_ORDNER): continue raus.append(p) return sorted(raus) def main(): nachricht = sys.argv[1] if len(sys.argv) > 1 else 'Update' dateien = sammle() # Vorhandene Dateien brauchen operation "update" plus sha, neue "create". code, vorhanden = ruf('GET', f"{REPO}/git/trees/main?recursive=1") bekannt = {} if code == 200: for e in (vorhanden.get('tree') or []): if e.get('type') == 'blob': bekannt[e['path']] = e['sha'] eintraege = [] for p in dateien: with open(p, 'rb') as f: inhalt = base64.b64encode(f.read()).decode() e = {'path': p, 'content': inhalt} if p in bekannt: e['operation'] = 'update' e['sha'] = bekannt[p] else: e['operation'] = 'create' eintraege.append(e) code, antwort = ruf('POST', f"{REPO}/contents", {'files': eintraege, 'message': nachricht, 'branch': 'main'}) if code in (200, 201): sha = ((antwort.get('commit') or {}).get('sha') or '')[:8] neu = sum(1 for e in eintraege if e['operation'] == 'create') print(f"Ein Commit {sha}: {len(eintraege)} Dateien ({neu} neu, {len(eintraege)-neu} aktualisiert)") print("Uebersprungen (von den Workflows gepflegt): " + ', '.join(sorted(NICHT_UEBERSCHREIBEN) + list(NICHT_UEBERSCHREIBEN_ORDNER))) else: print(f"FEHLER {code}: {json.dumps(antwort)[:400]}") sys.exit(1) if __name__ == '__main__': main()