- ClawdDotNet.Web: Kestrel im Prozess der Anwendung, Token-Absicherung (TokenGate mit FixedTimeEquals), Endpunkte unter /api, Ereignisstrom unter /events (Server-Sent Events) und fortlaufendes Log-Streaming. - Weboberflaeche (wwwroot/): Vollstaendiges Single-Page-Frontend mit nativem HTML5, CSS3 und ES-Modulen ohne Buildschritt (strikt textContent, #-Routing, sofortiger Login-Sprung bei fehlendem Token, Light/Dark-Theme, lokale Barlow-Schriften). - ClawdDotNet.App: Fachlogik aus den Avalonia-ViewModels in Dienste herausgeloest (AgentAdministration, JobAdministration, ServiceAdministration, RunCoordinator) und ueber InstanceChange synchronisiert. - Tests: ClawdDotNet.Web.Tests und ClawdDotNet.App.Tests hinzugefuegt, alle 684 Tests erfolgreich.
96 lines
3.2 KiB
Python
96 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Den erzeugten Zustandsblock in docs/Roadmap.md schreiben und die Prosa
|
|
abgleichen.
|
|
|
|
Das Gegenstueck `php cli/roadmap.php --write` laeuft auf der Maschine des
|
|
Deploymentcenters und kennt unsere Datei nicht. Der Weg ueber die API ist in
|
|
ROADMAP_MIGRATION.md, Schritt 5 und 6, beschrieben.
|
|
|
|
python roadmap_block.py --write Block holen und zwischen die Marker setzen
|
|
python roadmap_block.py --check Prosa gegen die Items abgleichen
|
|
|
|
Rueckgabewert von --check: 0 in Ordnung (Warnungen erlaubt), 1 bei Fehlern.
|
|
Damit laesst sich der Aufruf in einen Pre-Commit-Hook haengen.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
import urllib.parse
|
|
import urllib.request
|
|
|
|
BASIS = os.environ.get('DC_BASE_URL', 'https://dc.mhdf.de')
|
|
SLUG = 'clawddotnet'
|
|
DATEI = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(
|
|
os.path.abspath(__file__)))), 'docs', 'Roadmap.md')
|
|
|
|
ANFANG = '<!-- dc:roadmap'
|
|
ENDE = '<!-- /dc:roadmap -->'
|
|
|
|
|
|
def ruf(token, methode, query, koerper=None):
|
|
url = BASIS + '/api/bugtracker/v1/roadmap.php?' + urllib.parse.urlencode(query)
|
|
daten = json.dumps(koerper).encode('utf-8') if koerper is not None else None
|
|
anfrage = urllib.request.Request(url, data=daten, method=methode)
|
|
anfrage.add_header('Authorization', 'Bearer ' + token)
|
|
anfrage.add_header('Content-Type', 'application/json')
|
|
with urllib.request.urlopen(anfrage, timeout=30) as antwort:
|
|
return json.loads(antwort.read().decode('utf-8'))
|
|
|
|
|
|
def grenzen(text):
|
|
"""Die Marker finden. Fehlen sie, ist das ein Fehler und kein Anhaengen -
|
|
ein Block am Dateiende waere beim naechsten Lauf ein zweiter."""
|
|
a = text.find(ANFANG)
|
|
e = text.find(ENDE)
|
|
if a < 0 or e < 0 or e < a:
|
|
raise SystemExit('Marker %s ... %s nicht gefunden in %s' % (ANFANG, ENDE, DATEI))
|
|
return a, e + len(ENDE)
|
|
|
|
|
|
def main():
|
|
p = argparse.ArgumentParser()
|
|
p.add_argument('--write', action='store_true')
|
|
p.add_argument('--check', action='store_true')
|
|
a = p.parse_args()
|
|
|
|
token = os.environ.get('DC_TOKEN', '')
|
|
if not token:
|
|
raise SystemExit('DC_TOKEN fehlt')
|
|
|
|
with open(DATEI, encoding='utf-8') as f:
|
|
inhalt = f.read()
|
|
|
|
if a.write:
|
|
block = ruf(token, 'GET', {'action': 'render', 'project_slug': SLUG})['markdown']
|
|
anfang, ende = grenzen(inhalt)
|
|
neu = inhalt[:anfang] + block.rstrip() + inhalt[ende:]
|
|
with open(DATEI, 'w', encoding='utf-8', newline='\n') as f:
|
|
f.write(neu)
|
|
print('Block geschrieben: %d Zeichen.' % len(block))
|
|
return
|
|
|
|
if a.check:
|
|
ergebnis = ruf(token, 'POST', {'action': 'check', 'project_slug': SLUG},
|
|
{'content': inhalt})
|
|
befunde = ergebnis.get('findings') or ergebnis.get('befunde') or []
|
|
if not befunde:
|
|
print('Abgleich ohne Befund.')
|
|
return
|
|
fehler = 0
|
|
for b in befunde:
|
|
grad = b.get('severity') or b.get('level') or '?'
|
|
print('%-8s %-24s %s' % (grad, b.get('code', '?'), b.get('message', b)))
|
|
if grad in ('error', 'fehler'):
|
|
fehler += 1
|
|
sys.exit(1 if fehler else 0)
|
|
|
|
raise SystemExit('--write oder --check angeben')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|