Push-Skript auf einen Commit pro Lauf umgestellt
This commit is contained in:
@@ -1,72 +1,103 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Laedt Dateien ueber die Gitea-Contents-API hoch (create oder update)."""
|
||||
"""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
|
||||
|
||||
def env():
|
||||
# Von den Workflows gepflegt, nie von hier ueberschreiben:
|
||||
NICHT_UEBERSCHREIBEN = {'artikel-index.json', 'status-importe.json'}
|
||||
NICHT_UEBERSCHREIBEN_ORDNER = ('briefe/', 'nachbessern/', 'artikel/')
|
||||
AUS_ORDNER = {'.git', 'node_modules', '__pycache__'}
|
||||
AUS_DATEIEN = {'.env'}
|
||||
|
||||
|
||||
def env(pfad='.env'):
|
||||
d = {}
|
||||
for line in open('.env', encoding='utf-8'):
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#') or '=' not in line: continue
|
||||
k, v = line.split('=', 1)
|
||||
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('\\"', '"')
|
||||
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']}/contents"
|
||||
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=60) as a:
|
||||
return a.status, json.loads(a.read().decode())
|
||||
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:
|
||||
return e.code, json.loads(e.read().decode() or '{}')
|
||||
roh = e.read().decode()
|
||||
return e.code, (json.loads(roh) if roh.startswith(('{', '[')) else {'text': roh[:300]})
|
||||
|
||||
def hoch(pfad, nachricht):
|
||||
with open(pfad, 'rb') as f:
|
||||
inhalt = base64.b64encode(f.read()).decode()
|
||||
ziel = pfad.replace(os.sep, '/')
|
||||
code, _ = ruf('GET', f"{REPO}/{ziel}?ref={E.get('branch','main')}")
|
||||
sha = None
|
||||
|
||||
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:
|
||||
code_g, body = ruf('GET', f"{REPO}/{ziel}?ref=main")
|
||||
sha = body.get('sha')
|
||||
daten = {'content': inhalt, 'message': nachricht, 'branch': 'main'}
|
||||
if sha:
|
||||
daten['sha'] = sha
|
||||
c, b = ruf('PUT', f"{REPO}/{ziel}", daten)
|
||||
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:
|
||||
c, b = ruf('POST', f"{REPO}/{ziel}", daten)
|
||||
ok = c in (200, 201)
|
||||
print(f" {'ok ' if ok else 'FEHLER'} {ziel}" + ('' if ok else f" -> {c} {str(b)[:160]}"))
|
||||
return ok
|
||||
print(f"FEHLER {code}: {json.dumps(antwort)[:400]}")
|
||||
sys.exit(1)
|
||||
|
||||
AUS = {'.git', '.env', 'node_modules', '__pycache__'}
|
||||
|
||||
# Diese Pfade schreiben die Workflows selbst. Wuerde das Skript sie hochladen,
|
||||
# ueberschreibt ein lokaler Altstand die Ergebnisse eines Laufs.
|
||||
NICHT_UEBERSCHREIBEN = {'artikel-index.json', 'status-importe.json'}
|
||||
NICHT_UEBERSCHREIBEN_ORDNER = ('briefe' + os.sep, 'nachbessern' + os.sep, 'artikel' + os.sep)
|
||||
dateien = []
|
||||
for wurzel, ordner, namen in os.walk('.'):
|
||||
ordner[:] = [o for o in ordner if o not in AUS]
|
||||
for n in namen:
|
||||
p = os.path.relpath(os.path.join(wurzel, n), '.')
|
||||
if p in AUS or p.startswith('.env') and p != '.env.example': continue
|
||||
if p == '.env': continue
|
||||
if p in NICHT_UEBERSCHREIBEN or p.startswith(NICHT_UEBERSCHREIBEN_ORDNER):
|
||||
continue
|
||||
dateien.append(p)
|
||||
|
||||
nachricht = sys.argv[1] if len(sys.argv) > 1 else 'Update'
|
||||
print("Uebersprungen (von den Workflows gepflegt): "
|
||||
+ ', '.join(sorted(NICHT_UEBERSCHREIBEN)) + ', ' + ', '.join(NICHT_UEBERSCHREIBEN_ORDNER))
|
||||
fehler = 0
|
||||
for p in sorted(dateien):
|
||||
if not hoch(p, nachricht): fehler += 1
|
||||
print(f"\n{len(dateien)} Dateien, {fehler} Fehler")
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user