TP36 A4/A5/A6: Index/Status-SSoT-Cutover nach Gitea abgeschlossen
- teilprojekte.md: Quick-Index-Tabelle + "Ist-Stand (Kurz)"-Block entfernt (-493 Z.), Kopf zeigt aufs Gitea-Board (Labels status:*/thema:*); Governance/Genehmigte Ausnahmen unter eigenem Header behalten; TP36 reframed (Nautobot->Gitea, Scope = Index/Status, Nautobot nur noch Live-Geraetedaten). Alle 33 ### TPnn-Prosa-Abschnitte intakt. - gitea-tp-sync.py: an das neue SSoT-Modell angepasst - aktive TPs sind SSoT direkt in Gitea, das Tool gleicht nur noch das Archiv ab und fasst offene Issues nie an. Dry-Run 0/0. - CLAUDE.md: Pflichtlektuere #1 = Gitea-Board (Status) + Prosa-Datei (Detail); neue Sektion "SSoT & Branch-Konvention"; TP-Management-Sektion nachgezogen. /projektstatus-Skill (beide globalen Kopien ~/.claude + ~/.config/opencode) auf die Gitea-API umgestellt (Modus A Board / Modus B Markdown-Fallback) - liegt ausserhalb des Repos. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,11 +3,15 @@
|
||||
TP36 (reframed) — teilprojekte.md Index/Status → Gitea Issues (admin/bf-infra).
|
||||
|
||||
Idempotenter Sync (get-or-create, Match ueber "TPnn" im Titel):
|
||||
- Quick-Index-Tabelle in docs/teilprojekte.md -> aktive Issues (open/closed je Marker)
|
||||
- Archiv-Header in docs/teilprojekte-archiv.md -> closed Issues (nur TPs, die NICHT aktiv sind)
|
||||
- Archiv-Header in docs/teilprojekte-archiv.md -> closed Issues (nur TPs, die in Gitea
|
||||
NICHT offen sind; ein reopentes TP mit Archiv-Zeiger bleibt offen).
|
||||
- Labels status:* (Marker) + thema:* (Gruppierung) werden vorab get-or-create.
|
||||
|
||||
SSoT-Modell: Gitea fuehrt Index+Status; die per-TP-Prosa bleibt in docs/teilprojekte.md.
|
||||
SSoT-Modell (seit TP36-Cutover 2026-09-07): Gitea fuehrt Index+Status — aktive TPs werden
|
||||
DIREKT als Gitea-Issue angelegt/gepflegt, NICHT mehr aus einer Datei-Tabelle abgeleitet
|
||||
(die Quick-Index-Tabelle wurde aus teilprojekte.md entfernt). Dieses Werkzeug haelt nur noch
|
||||
die ARCHIVIERTEN (closed) TPs mit teilprojekte-archiv.md idempotent abgeglichen und fasst
|
||||
offene Issues nie an. Die per-TP-Prosa bleibt in docs/teilprojekte.md / teilprojekte-archiv.md.
|
||||
Token: ~/.hermes/.env (GITEA_ACCESS_TOKEN, GITEA_HOST) — wird NIE ausgegeben.
|
||||
|
||||
Aufruf:
|
||||
@@ -100,25 +104,9 @@ def api(method, path, body=None):
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- parse
|
||||
def parse_active():
|
||||
"""Quick-Index-Zeilen: > | TP44 | ⏳ | Kurztitel … | Datum |"""
|
||||
out = []
|
||||
row = re.compile(r"^>\s*\|\s*(TP[\d.]+)\s*\|\s*([^\s|]+)\s*\|\s*(.+?)\s*\|\s*(.*?)\s*\|\s*$")
|
||||
for line in ACTIVE.read_text().splitlines():
|
||||
m = row.match(line)
|
||||
if not m:
|
||||
continue
|
||||
tp, marker, title, date = m.groups()
|
||||
num = tp[2:]
|
||||
# Titel auf den Kern kuerzen (vor dem ersten " — " / " (" wenn lang)
|
||||
short = re.split(r"\s+—\s+|\s*\(", title)[0].strip()
|
||||
short = re.sub(r"\*\*|`", "", short)[:80]
|
||||
out.append({"tp": tp, "num": num, "marker": marker.strip(),
|
||||
"title": short, "date": date.strip(), "src": "active"})
|
||||
return out
|
||||
|
||||
|
||||
def parse_archiv(active_nums):
|
||||
def parse_archiv(exclude_nums):
|
||||
"""Archiv-Header -> archivierte TPs. exclude_nums = TP-Nummern, die in Gitea OFFEN sind
|
||||
(reopente TPs mit Archiv-Zeiger) -> die bleiben offen, nicht als closed abgleichen."""
|
||||
out = []
|
||||
hdr = re.compile(r"^#{2,4}\s*(TP[\d.\-A-Za-z]+)\s*—\s*(.+?)\s*▸", )
|
||||
seen = set()
|
||||
@@ -128,7 +116,7 @@ def parse_archiv(active_nums):
|
||||
continue
|
||||
tp, title = m.groups()
|
||||
num = tp[2:]
|
||||
if num in active_nums or num in seen:
|
||||
if num in exclude_nums or num in seen:
|
||||
continue
|
||||
seen.add(num)
|
||||
short = re.sub(r"\*\*|`", "", title).strip()[:80]
|
||||
@@ -164,22 +152,26 @@ def all_issues():
|
||||
|
||||
def main():
|
||||
print(f"Gitea {HOST} repo {OWNER_REPO} mode={'COMMIT' if COMMIT else 'DRY-RUN'}")
|
||||
active = parse_active()
|
||||
active_nums = {a["num"] for a in active}
|
||||
archiv = parse_archiv(active_nums)
|
||||
items = active + archiv
|
||||
print(f"Geparst: {len(active)} aktive + {len(archiv)} archivierte = {len(items)} TP\n")
|
||||
|
||||
st, existing_labels = api("GET", f"/repos/{OWNER_REPO}/labels?limit=100")
|
||||
label_id = ensure_labels(existing_labels if isinstance(existing_labels, list) else [])
|
||||
|
||||
issues = all_issues()
|
||||
by_tp = {}
|
||||
open_nums = set()
|
||||
for iss in issues:
|
||||
# Titel ist "TPxx — …"; volle ID inkl. Buchstaben/Bindestrich (TP14.2a, TP-R) bis zum " — "
|
||||
m = re.match(r"^(TP[\w.\-]*?)\s+—", iss["title"])
|
||||
if m:
|
||||
by_tp[m.group(1)] = iss
|
||||
if iss.get("state") == "open":
|
||||
open_nums.add(m.group(1)[2:])
|
||||
|
||||
# Aktive TPs sind seit dem TP36-Cutover SSoT direkt in Gitea -> nur das Archiv abgleichen,
|
||||
# und offene (reopente) TPs davon ausschliessen, damit sie nicht faelschlich geschlossen werden.
|
||||
items = parse_archiv(open_nums)
|
||||
print(f"Geparst: {len(items)} archivierte TP (aktive TPs = SSoT in Gitea: "
|
||||
f"{len(open_nums)} offen, nicht angetastet)\n")
|
||||
|
||||
created = updated = 0
|
||||
for it in items:
|
||||
|
||||
Reference in New Issue
Block a user