feat: Project Manual + CM-/RM-Plan + Landing-Page
Validate / build-test (macos-latest) (push) Failing after 2s
Validate / build-test (windows-latest) (push) Failing after 15s
Validate / build-test (ubuntu-latest) (push) Failing after 15s
Validate / reports (push) Has been skipped
Release / release (push) Successful in 57s

3 neue Plaene:
- Project Manual: Master-Wegweiser fuer neue Projektmitglieder,
  Lese-Reihenfolge, Rollen, Lebenszyklus, Dokumenten-Landschaft
- Configuration Management Plan: CIs, Baselines, Change Control,
  Release-Prozess, Aufbewahrungsfristen (ASPICE SUP.8)
- Risk Management Plan: Projekt-Risiken (abgegrenzt von HARA),
  Klassifikations-Skala, Risiko-Register, Eskalations-Pfad

Landing-Page (Startseite):
- tools/generate_landing_page.py erzeugt build/index.html
- Standalone-HTML, oeffnet im Browser ohne Server
- KPI-Cards: SG/SYS/SWE/Arch/Komponenten/Tests-Counts
- Sektionen mit Links: Plaene, Safety, Manuals, Audit, Reports,
  Diagramme, Source-Code, externe Links
- Existenz-Check: nicht-generierte Reports werden grau markiert
- Im Release-Bundle als index.html ganz oben

CI-Integration:
- validate.yml: neuer Step "Landing-Page" + Upload als Artefakt
- release.yml: Landing-Page generieren + ins Bundle einbauen,
  zusaetzlich Source-Code im Bundle (war vorher nur als tar.gz)

Makefile: neues Target `make landing-page`
This commit is contained in:
Stefan Lohmaier
2026-05-12 01:59:44 -07:00
parent c610cc023c
commit 294b9956f9
10 changed files with 753 additions and 3 deletions
+10 -2
View File
@@ -31,11 +31,12 @@ jobs:
make coverage
make test-report
- name: Traceability + Diagramme + API-Doc
- name: Traceability + Diagramme + API-Doc + Landing-Page
run: |
python3 tools/traceability.py publish docs/traceability
python3 tools/render_plantuml.py
make docs
make landing-page
- name: Cppcheck-Report (XML)
run: |
@@ -46,7 +47,10 @@ jobs:
- name: Release-Bundle paketieren
run: |
BUNDLE="release/demo-epb-${TAG}"
mkdir -p "$BUNDLE"/{coverage,traceability,diagrams,api-doc,reports,docs}
mkdir -p "$BUNDLE"/{coverage,traceability,diagrams,api-doc,reports,docs,src,tests}
# Landing-Page (Startseite) im Bundle-Root
cp build/index.html "$BUNDLE/index.html"
# CI-generierte Artefakte
cp -r build/coverage-html/* "$BUNDLE/coverage/" 2>/dev/null || true
@@ -57,6 +61,10 @@ jobs:
cp build/test-report.html "$BUNDLE/reports/" 2>/dev/null || true
cp build/test-report.md "$BUNDLE/reports/" 2>/dev/null || true
# Source-Code zum Anklicken aus dem Bundle (begrenzt auf das wichtigste)
cp -r src/*.c src/*.h "$BUNDLE/src/" 2>/dev/null || true
cp -r src/stubs "$BUNDLE/src/" 2>/dev/null || true
# Alle Word-Dokumente (Plaene, Safety, Manuals, Audit-Artefakte)
mkdir -p "$BUNDLE/docs/plaene" "$BUNDLE/docs/safety" "$BUNDLE/docs/manuals" \
"$BUNDLE/docs/reviews" "$BUNDLE/docs/non-conformities" "$BUNDLE/docs/misra"
+10
View File
@@ -84,6 +84,9 @@ jobs:
- name: Doxygen API-Dokumentation
run: make docs
- name: Landing-Page
run: make landing-page
- name: Cppcheck-Report (XML)
run: |
mkdir -p build
@@ -128,6 +131,13 @@ jobs:
name: api-doc
path: build/api-doc/html/
- name: Upload Landing-Page
uses: actions/upload-artifact@v3
if: always()
with:
name: landing-page
path: build/index.html
- name: Upload Cppcheck-Report
uses: actions/upload-artifact@v3
if: always()
+4 -1
View File
@@ -21,10 +21,13 @@ TESTS = test_switch_debouncer test_actuator_driver test_apply_controller \
test_safety_manager
TEST_BINS = $(TESTS:%=$(BUILD)/%)
.PHONY: all test coverage clean misra static docs test-report
.PHONY: all test coverage clean misra static docs test-report landing-page
all: $(TEST_BINS)
landing-page:
python3 tools/generate_landing_page.py
docs:
@which doxygen >/dev/null 2>&1 || { echo "doxygen not installed (brew/apt install doxygen)"; exit 1; }
doxygen Doxyfile
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+148
View File
@@ -0,0 +1,148 @@
---
doc-id: SLM-EPB-CM-001
version: 1.0
status: Freigegeben
datum: 2026-05-12
---
# Configuration Management Plan (CM-Plan)
| Feld | Wert |
|--------------|----------------------------------------|
| Projekt | demo-epb |
| Dokument-ID | SLM-EPB-CM-001 |
| Version | 1.0 |
| Status | Freigegeben |
| Datum | 2026-05-12 |
| Norm | ASPICE SUP.8 + ISO 26262-8 §7 |
---
## 1. Zweck
Beschreibt, wie Konfigurations-Items identifiziert, versioniert, freigegeben
und kontrolliert geaendert werden.
## 2. Configuration Items (CIs)
Folgende Artefakte stehen unter Konfigurationskontrolle:
| Typ | Pfad | Versionierung |
|-------------------------|----------------------------------------|------------------------------|
| Source-Code | `src/**/*.{c,h}` | Git |
| Tests | `tests/**` | Git |
| Anforderungen | `reqs/{sys,swe}/*.md` | Git + Doorstop-Item-Hash |
| Architektur | `arch/{sys,swe}/*.md` | Git + Doorstop-Item-Hash |
| Safety Goals | `safety/sg/*.md` | Git |
| Plaene (Word) | `docs/plaene/*.docx` | Git + Dokument-Versionsblock |
| Safety-Doku (Word) | `docs/safety/*.docx` | Git |
| Manuals (Word) | `docs/manuals/*.docx` | Git |
| Reviews + NCs | `docs/reviews/`, `docs/non-conformities/` | Git |
| MISRA-Records | `misra/records/*.docx` | Git |
| CI-Konfiguration | `.gitea/workflows/*.yml` | Git |
| Build-Definition | `Makefile`, `Doxyfile` | Git |
| Tools | `tools/*.py` | Git |
## 3. Repository-Struktur
- **Remote:** https://gitea.slohmaier.com/slohmaier/demo-epb
- **Branch `main`:** stabil, immer freigegebener Stand
- **Branch `develop`:** aktueller Entwicklungsstand
- **Feature-Branches:** `feature/SWE-XXX-...`
- **Bugfix-Branches:** `bugfix/<issue>-...`
- **Release-Branches:** `release/vX.Y` (nur bei Real-Projekt; Demo: direkt von main)
## 4. Baselines
Eine Baseline ist ein eingefrorener, freigegebener Stand. Baselines werden durch
Git-Tags gesetzt.
| Baseline-Typ | Tag-Schema | Wann |
|---------------------------|-------------------|----------------------------------------|
| Requirements Baseline | `req-vX.Y` | Nach Anforderungs-Freigabe |
| Architecture Baseline | `arch-vX.Y` | Nach Architektur-Review |
| Release Baseline | `vX.Y.Z` | Bei produktiver Freigabe |
| Internal Snapshot | `snap-YYYY-MM-DD` | Bei wichtigen Zwischenstaenden |
Jeder Tag triggert (bei `vX.Y.Z`) den Release-Workflow, der ein Bundle erzeugt.
## 5. Versions-Schema
| Artefakt | Schema |
|-----------------------|------------------------------------------|
| Software-Release | Semantic Versioning `MAJOR.MINOR.PATCH` |
| Anforderungen | Doorstop-Level `X.Y` + Datum |
| Architektur | Doorstop-Level `X.Y` + Datum |
| Word-Dokumente | `MAJOR.MINOR` im Dokument-Header |
## 6. Change Control
Aenderungen an Configuration Items erfolgen ueber:
1. **Trivial-Aenderung** (Tippfehler, Kommentare): direkt im Branch, PR mit 1 Approval
2. **Normal-Aenderung** (Feature, Bugfix): Feature-Branch, PR mit Reviews je nach ASIL
3. **Major-Aenderung** (Architektur, Sicherheits-Konzept): Change Request + Reviewer-Quorum
| Asil | Reviewer-Mindestanzahl |
|---------|--------------------------------------|
| QM | 1 |
| ASIL-A/B| 1 |
| ASIL-C | 2 (mind. 1 Technical Reviewer) |
| ASIL-D | 2 Technical Reviewer + Safety Manager|
Reviews werden in `docs/reviews/REV-XXX.docx` dokumentiert.
## 7. Release-Prozess
```
1. Alle PRs in main gemerged
2. Branch protected, alle CI-Checks gruen
3. Release-Notes-Entwurf im PR vorbereitet
4. Tag setzen: git tag -a vX.Y.Z -m "..."
5. Push: git push origin vX.Y.Z
6. Release-Workflow laeuft (.gitea/workflows/release.yml):
- Build + Tests + Coverage
- Traceability + Diagrams + API-Doc
- Word-Dokumente bundlen
- Source + Artefakt-Archive packen
- Gitea-Release anlegen
7. Release manuell pruefen (Bundle herunterladen, sichten)
8. Release als "stable" markieren
```
## 8. Aufbewahrung
| Artefakt | Aufbewahrungsdauer |
|--------------------------|----------------------------------------|
| Git-Repository | Unbegrenzt (Gitea + Backup) |
| Release-Bundles | 10 Jahre nach Produkt-EOL |
| Reviews + NCs | 10 Jahre nach Produkt-EOL |
| MISRA-Records | 10 Jahre nach Produkt-EOL |
| CI-Artefakte (kurzlebig) | 90 Tage (in Gitea-Artifacts) |
ISO 26262 fordert 10 Jahre nach End-of-Production-Life (Annahme).
## 9. Verifikation
Alle Pull Requests laufen durch:
- `doorstop`-aequivalenter Traceability-Check (`tools/traceability.py check`)
- Build + Unit-Tests
- Static Analysis + MISRA-Check
- Coverage-Messung
Erst nach Approval und CI-Gruen kann der Merge nach `main` erfolgen.
## 10. Verantwortlichkeiten
| Rolle | Aufgabe |
|------------------|--------------------------------------------------|
| Configuration Mgr| Pflege dieses CM-Plans, Repo-Hygiene, Baselines |
| Entwickler | Korrekte Branch-Strategie, sinnvolle Commit-Msg |
| Reviewer | Pruefung vor Merge, Audit-Trail |
| Project Owner | Release-Freigabe |
## 11. Aenderungshistorie
| Version | Datum | Aenderung | Autor |
|---------|-------------|---------------------|-------------|
| 1.0 | 2026-05-12 | Erstfreigabe | S. Lohmaier |
+172
View File
@@ -0,0 +1,172 @@
---
doc-id: SLM-EPB-PM-MAN-001
version: 1.0
status: Freigegeben
datum: 2026-05-12
---
# Project Manual — demo-epb
| Feld | Wert |
|--------------|----------------------------------------|
| Projekt | demo-epb (Elektrische Parkbremse) |
| Dokument-ID | SLM-EPB-PM-MAN-001 |
| Version | 1.0 |
| Status | Freigegeben |
| Datum | 2026-05-12 |
| Zielgruppe | Neue Projektmitglieder, Auditoren |
---
## 1. Zweck
Dieses Project Manual ist der **Einstieg** ins demo-epb Projekt. Es beantwortet:
- Was wird gebaut?
- Welche Dokumente gibt es, in welcher Reihenfolge lesen?
- Wer ist verantwortlich wofuer?
- Wie laeuft der Entwicklungs- und Freigabe-Zyklus?
## 2. Was ist demo-epb?
Eine vollstaendige Demo des **slohmaier Dev Process** anhand einer
EPB-Steuergeraet-Software. Ziel ist **nicht** die produktive Software, sondern
der Nachweis ASPICE 4.0 / ISO 26262-konformer Entwicklung.
Detail: `docs/plaene/PID.docx`.
## 3. Lese-Reihenfolge fuer neue Projektmitglieder
| Tag | Dokument | Warum |
|-----|----------------------------------------|----------------------------------------|
| 1 | dieses Project Manual | Orientierung |
| 1 | `PID.docx` | Was + Warum |
| 1 | `User-Manual.docx` | Produkt-Verstaendnis |
| 2 | `HARA.docx` + `Safety-Case.docx` | Sicherheits-Konzept |
| 2 | `SWE-Plan.docx` + `QA-Plan.docx` | Engineering-Konventionen |
| 3 | `reqs/` + `arch/` (Markdown) | Anforderungen + Architektur |
| 3 | `src/apply_controller.c` | Beispiel ASIL-D Code |
| 4 | `traceability/index.html` | Vernetzung der Artefakte |
| 4 | `coverage/index.html` | Was ist getestet |
| 5 | Diese Anleitung selber pflegen | Onboarding fuer den Naechsten |
## 4. Dokumenten-Landschaft
```
demo-epb/
├── docs/plaene/ ← PID, PM-Plan, QA-Plan, SWE-Plan, Test-Plan, CM-Plan, RM-Plan
├── docs/safety/ ← HARA, Safety-Case, FMEDA, MISRA-Compliance, Verification-Report, Tool-Qualification
├── docs/manuals/ ← User-Manual, Service-Manual
├── docs/reviews/ ← Review-Protokolle
├── docs/non-conformities/ ← NC-Eintraege
├── misra/records/ ← MISRA Deviation Records
├── reqs/sys/ ← Doorstop-MD System Requirements
├── reqs/swe/ ← Doorstop-MD Software Requirements
├── arch/sys/ ← Doorstop-MD System Architecture + PlantUML
├── arch/swe/ ← Doorstop-MD Software Architecture + PlantUML
├── safety/sg/ ← Doorstop-MD Safety Goals (ASIL-Ableitung)
├── src/ ← C-Code, mit @arch + @reqs Tags im Header
├── tests/ ← Unit-Tests mit @reqs Tags
├── tools/ ← Python-Skripte (Traceability, PlantUML, Reports)
├── .gitea/workflows/ ← CI-Pipelines (validate + release)
└── docs/index.html ← Auto-generierte Startseite
```
Eine **klickbare Uebersicht** liefert `docs/index.html` (Browser oeffnen).
## 5. Rollen und Verantwortlichkeiten
| Rolle | Verantwortung | Person (Demo) |
|--------------------|-----------------------------------------------------|--------------------------|
| Project Owner | Strategische Entscheidungen, Freigabe Release | Stefan Lohmaier |
| Technical Lead | Architektur, Code-Reviews, technische Entscheidungen | Stefan Lohmaier |
| Safety Manager | HARA, Safety Case, ASIL-Konformitaet | Stefan Lohmaier (Demo) |
| QA-Beauftragter | QA-Plan-Pflege, Audit-Vorbereitung | Stefan Lohmaier (Demo) |
| Configuration Mgr | Baselines, Releases, Git-Repo-Hygiene | Stefan Lohmaier (Demo) |
| Entwickler | Implementierung gemaess Architektur + Tests | Stefan Lohmaier (Demo) |
| Reviewer | Code- und Dokument-Reviews | Externer Reviewer (TBD) |
In der Demo ist eine Person in allen Rollen; in einem Real-Projekt mit ASIL-C/D
sind diese personell zu trennen (insb. Entwickler ungleich Reviewer fuer
sicherheitskritischen Code).
## 6. Entwicklungs-Lebenszyklus
```
Anforderung
Architektur (Markdown + PlantUML)
Implementation (C, mit @arch + @reqs)
Unit-Test (CppUTest-aehnliches Framework, mit @reqs)
Pull Request (Branch -> main)
CI: Build + Test + Coverage + MISRA + Traceability-Check
Code-Review (Approval-Pflicht je nach ASIL)
Merge nach main
▼ (bei Release-Punkt)
Tag v*.*.*
CI Release-Workflow: Bundle + Gitea-Release
```
## 7. Freigabe-Strategie
- **Pull-Requests** brauchen mindestens 1 Approval (mehr fuer ASIL-C/D, siehe SWE-Plan)
- **Tags** im Format `vMAJOR.MINOR.PATCH` triggern den Release-Workflow
- **Release-Bundle** enthaelt Source + alle Reports + alle Word-Dokumente
- **Audit-Faehigkeit** ist jederzeit gegeben (Git-History + Doku-Lifecycle)
## 8. Wo Probleme melden
| Problem-Typ | Wo dokumentieren |
|-----------------------|-------------------------------------------------|
| Bug | Gitea Issue (Tag `bug`) |
| Anforderungs-Aenderung| Gitea Issue (Tag `requirement`) + Doorstop-Update |
| Non-Conformity | `docs/non-conformities-md/NC-XXX.md` -> Word |
| MISRA-Abweichung | `misra/records-md/MISRA-REC-XXX.md` -> Word |
| Sicherheits-Problem | Sofort an Safety Manager + NC |
## 9. Tools
Siehe `infrastructure/` im iCloud-Workspace fuer Setup-Details. Kurzform:
- **Gitea** (gitea.slohmaier.com) — Source-Control + CI + Releases
- **Doorstop-Stil** Markdown — Anforderungen + Architektur
- **PlantUML** — Diagramme (eingebettet)
- **Cppcheck** + **GCC -Werror** — Statische Analyse + MISRA
- **gcov/lcov** — Coverage
- **Doxygen** — API-Doc
- **pandoc** — Markdown -> Word/PDF
- **Python** (Stdlib) — Traceability + Report-Generatoren
## 10. Verwandte Dokumente
| Plan | Datei | Inhalt |
|----------------------|-------------------------------------|----------------------------------------|
| Project Initiation | `PID.docx` | Was + Warum |
| Projekt-Management | `PM-Plan.docx` | Arbeitspakete, Termine, Stakeholder |
| Quality Assurance | `QA-Plan.docx` | Reviews, Audits, NC-Management |
| Configuration Mgmt | `CM-Plan.docx` | Baselines, Releases, Change Control |
| Risk Management | `RM-Plan.docx` | Risiken, Mitigation, Monitoring |
| Software Development | `SWE-Plan.docx` | Sprache, Standards, Coverage-Ziele |
| Test | `Test-Plan.docx` | Test-Strategie |
## 11. Aenderungshistorie
| Version | Datum | Aenderung | Autor |
|---------|-------------|---------------------|-------------|
| 1.0 | 2026-05-12 | Erstfreigabe | S. Lohmaier |
+111
View File
@@ -0,0 +1,111 @@
---
doc-id: SLM-EPB-RM-001
version: 1.0
status: Freigegeben
datum: 2026-05-12
---
# Risk Management Plan (RM-Plan)
| Feld | Wert |
|--------------|----------------------------------------|
| Projekt | demo-epb |
| Dokument-ID | SLM-EPB-RM-001 |
| Version | 1.0 |
| Status | Freigegeben |
| Datum | 2026-05-12 |
| Norm | ASPICE MAN.5 |
---
## 1. Zweck
Identifiziert, bewertet und behandelt **Projekt-Risiken** (organisatorisch,
technisch, Zeitplan, Resource). Abgegrenzt von **funktionalen Sicherheits-
Risiken** (Hazards), die im HARA behandelt werden.
## 2. Methodik
| Schritt | Aktion |
|--------------------|-------------------------------------------------|
| 1. Identifikation | Workshops, Lessons-Learned, Stakeholder-Input |
| 2. Klassifikation | Wahrscheinlichkeit (W) x Auswirkung (A) |
| 3. Bewertung | Risk Score = W * A (1-25) |
| 4. Behandlung | Vermeiden / Mindern / Akzeptieren / Transferieren |
| 5. Monitoring | Quartalsweise Review, Statusupdate |
### 2.1 Klassifikations-Skala
| Wahrscheinlichkeit | Bedeutung |
|--------------------|----------------------------|
| 1 | Sehr unwahrscheinlich |
| 2 | Unwahrscheinlich |
| 3 | Moeglich |
| 4 | Wahrscheinlich |
| 5 | Sehr wahrscheinlich |
| Auswirkung | Bedeutung |
|------------|--------------------------------------------|
| 1 | Vernachlaessigbar |
| 2 | Geringe Verzoegerung / Mehraufwand |
| 3 | Spuerbare Auswirkung auf Termin/Budget |
| 4 | Erhebliche Auswirkung, Projekt gefaehrdet |
| 5 | Projekt-Stop |
| Score-Bereich | Aktion |
|---------------|------------------------------------------|
| 1-4 | Akzeptieren, monitoren |
| 5-9 | Mindern (Plan) |
| 10-15 | Mindern (sofort, mit Eskalation) |
| 16-25 | Eskalation an Project Owner |
## 3. Risiko-Register
| ID | Beschreibung | W | A | Score | Behandlung | Status |
|-------|---------------------------------------------------------|---|---|-------|------------------------------------------|----------|
| R-01 | Demo wird als produktreifer Code missverstanden | 3 | 3 | 9 | Disclaimer im README + Project Manual | Mitigated |
| R-02 | MISRA-Tooling-Update bricht CI (false positives) | 2 | 3 | 6 | Tool-Versionen pinnen, Regression-Suite | Mitigated |
| R-03 | Reviewer-Verfuegbarkeit fuer ASIL-D | 3 | 4 | 12 | Self-Review dokumentiert (nur Demo) | Akzeptiert (Demo) |
| R-04 | Gitea-Server-Ausfall | 2 | 4 | 8 | Lokale Klone, regelmaessige Backups | Mitigated |
| R-05 | Apple-Cert-Ablauf ohne Vorwarnung | 3 | 3 | 9 | Renewal-Reminder + 30-Tage-Vorwarnung | Mitigated |
| R-06 | Windows-Build-VM unzuverlaessig (busybox-PATH-Konflikte)| 4 | 2 | 8 | MSYS2 dokumentiert, alt PATH vorne | Open |
| R-07 | macOS act_runner host-mode Cache-Bug | 3 | 2 | 6 | continue-on-error, dokumentiert | Open |
| R-08 | Doorstop-Tooling-Kompatibilitaet bei Update | 2 | 3 | 6 | Eigenes traceability.py, kein doorstop-Dep | Mitigated |
| R-09 | Wissensverlust bei Single-Person-Setup | 4 | 4 | 16 | Project Manual + Dokumentation pflegen | Open |
## 4. Risiko-Reviews
| Frequenz | Teilnehmer | Outputs |
|--------------|-------------------------|--------------------------------------|
| Quartalsweise| Project Owner + TL | Aktualisiertes Register, Action-Items |
| Bei Aenderung| Betroffene Rollen | Risiko-Score-Update |
| Bei Release | Project Owner + QA | Restrisiken-Bewertung |
## 5. Eskalations-Pfad
```
R-Owner (taeglich)
│ Score > 9
Project Owner (woechentlich)
│ Score > 15
Stakeholder / Auftraggeber (sofort)
```
## 6. Lessons Learned
Geschlossene Risiken werden bei Projektabschluss in `docs/lessons-learned/`
zusammengefasst, um in Folge-Projekten besser einschaetzen zu koennen.
## 7. Verwandte Dokumente
- `PM-Plan.docx` — Top-Level-Risiken (Auszug)
- `HARA.docx` — Funktionale Sicherheits-Risiken (Hazards, getrennt von Projekt-Risiken)
- `QA-Plan.docx` — Non-Conformity-Management
## 8. Aenderungshistorie
| Version | Datum | Aenderung | Autor |
|---------|-------------|---------------------|-------------|
| 1.0 | 2026-05-12 | Erstfreigabe | S. Lohmaier |
+298
View File
@@ -0,0 +1,298 @@
#!/usr/bin/env python3
"""
Erzeugt eine HTML-Startseite (Dashboard) fuer demo-epb.
Scant das Repo nach Word-Dokumenten, Reports, Code, Tests, Architektur, und
schreibt build/index.html mit klickbaren Links.
Run nach `make test && make coverage && make docs && make test-report && python3 tools/traceability.py publish docs/traceability && python3 tools/render_plantuml.py`.
Output:
build/index.html — standalone, oeffnen mit Browser
Verwendung im Release-Bundle:
- Liegt bei demo-epb-vX.Y.Z/index.html
- Verlinkt alle anderen Bundle-Inhalte relativ
"""
from __future__ import annotations
import datetime
import html
import os
import re
import subprocess
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
BUILD = REPO / "build"
def count_files(pattern: str, base: Path = REPO) -> int:
return sum(1 for _ in base.glob(pattern))
def git_info() -> tuple[str, str]:
try:
sha = subprocess.check_output(
["git", "rev-parse", "--short", "HEAD"], cwd=str(REPO),
text=True).strip()
except Exception:
sha = "?"
try:
tag = subprocess.check_output(
["git", "describe", "--tags", "--abbrev=0"], cwd=str(REPO),
text=True, stderr=subprocess.DEVNULL).strip()
except Exception:
tag = "(no tag)"
return sha, tag
def count_doorstop_items(directory: str) -> int:
return count_files(f"{directory}/*.md")
def count_tests() -> int:
total = 0
for f in (REPO / "tests" / "unit").glob("test_*.c"):
text = f.read_text()
total += len(re.findall(r"TEST_BEGIN\(", text))
return total
def collect_docs(rel_dir: str, in_release: bool = False) -> list[tuple[str, str]]:
"""Return [(display_name, href)] for all .docx in a directory."""
out = []
d = REPO / rel_dir
if not d.exists():
return out
for f in sorted(d.glob("*.docx")):
# In release bundle, paths are different; here we use relative-to-repo.
href = os.path.relpath(f, REPO)
# If running for in_release context, paths need adjustment, but for now
# we always use repo-relative.
out.append((f.stem, href))
return out
def status_for(path: Path) -> str:
if path.exists():
return "ok"
return "missing"
def kpi_card(label: str, value: str, sub: str = "", color: str = "#1f3864") -> str:
return f"""
<div class='kpi'>
<div class='kpi-value' style='color:{color}'>{html.escape(value)}</div>
<div class='kpi-label'>{html.escape(label)}</div>
<div class='kpi-sub'>{html.escape(sub)}</div>
</div>
"""
def doc_section(title: str, docs: list[tuple[str, str]], description: str = "") -> str:
if not docs:
items = "<li class='cnt'>— keine Dokumente —</li>"
else:
items = "\n".join(
f'<li><a href="{html.escape(href)}">{html.escape(name)}</a></li>'
for name, href in docs
)
return f"""
<section>
<h2>{html.escape(title)}</h2>
{f"<p class='cnt'>{html.escape(description)}</p>" if description else ""}
<ul>{items}</ul>
</section>
"""
def report_link(name: str, href: str, exists: bool, desc: str) -> str:
cls = "ok" if exists else "missing"
label = name + ("" if exists else " (nicht generiert — Coverage/Build laufen lassen)")
if exists:
return (f"<li><a href='{html.escape(href)}'>{html.escape(label)}</a> "
f"<span class='cnt'>— {html.escape(desc)}</span></li>")
return f"<li class='{cls}'>{html.escape(label)} <span class='cnt'>— {html.escape(desc)}</span></li>"
def main() -> int:
BUILD.mkdir(parents=True, exist_ok=True)
sha, tag = git_info()
now = datetime.datetime.now(datetime.timezone.utc).isoformat(timespec="seconds")
# Counts
n_sg = count_doorstop_items("safety/sg")
n_sys = count_doorstop_items("reqs/sys")
n_swe = count_doorstop_items("reqs/swe")
n_sa = count_doorstop_items("arch/sys")
n_swa = count_doorstop_items("arch/swe")
n_tests = count_tests()
n_impl = sum(1 for f in (REPO / "src").glob("*.c"))
n_stubs = sum(1 for f in (REPO / "src" / "stubs").glob("*.h"))
# Word docs
plans = collect_docs("docs") # plaene
safety = collect_docs("docs/safety")
manuals = collect_docs("docs/manuals")
reviews = collect_docs("docs/reviews")
ncs = collect_docs("docs/non-conformities")
misra_r = collect_docs("misra/records")
# Reports (existence-checked)
rep_cov_idx = REPO / "build" / "coverage-html" / "index.html"
rep_test_html = REPO / "build" / "test-report.html"
rep_api = REPO / "build" / "api-doc" / "html" / "index.html"
rep_trace = REPO / "docs" / "traceability" / "index.html"
rep_cppcheck = REPO / "build" / "cppcheck-report.xml"
html_body = f"""<!doctype html>
<html lang='de'><head>
<meta charset='utf-8'>
<title>demo-epb {html.escape(tag)} — Projekt-Dashboard</title>
<style>
:root {{ color-scheme: light; }}
body {{ font-family: -apple-system, "Segoe UI", sans-serif; margin: 0; padding: 0; color: #222; background: #f5f6f8; }}
header {{ background: #1f3864; color: white; padding: 20px 40px; }}
header h1 {{ margin: 0; font-size: 26px; }}
header .meta {{ color: #b9c2d6; font-size: 13px; margin-top: 6px; }}
main {{ max-width: 1100px; margin: 0 auto; padding: 20px 40px; }}
.kpis {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin: 20px 0; }}
.kpi {{ background: white; padding: 16px; border-radius: 6px; box-shadow: 0 1px 3px rgba(0,0,0,0.07); }}
.kpi-value {{ font-size: 28px; font-weight: 700; }}
.kpi-label {{ color: #555; font-size: 13px; margin-top: 4px; text-transform: uppercase; letter-spacing: 0.5px; }}
.kpi-sub {{ color: #888; font-size: 12px; margin-top: 2px; }}
section {{ background: white; padding: 16px 24px; border-radius: 6px; margin-bottom: 16px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); }}
h2 {{ color: #1f3864; margin-top: 0; font-size: 17px; }}
ul {{ list-style: none; padding-left: 0; }}
li {{ padding: 4px 0; }}
a {{ color: #1f3864; text-decoration: none; border-bottom: 1px dotted #1f3864; }}
a:hover {{ background: #ffea8a; }}
.cnt {{ color: #888; font-size: 13px; }}
.missing {{ color: #c00; font-style: italic; }}
.cols {{ display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }}
@media (max-width: 700px) {{ .cols {{ grid-template-columns: 1fr; }} }}
footer {{ text-align: center; color: #888; padding: 30px; font-size: 13px; }}
.banner {{ background: #fff3cd; padding: 10px 24px; border-left: 4px solid #ff9800; margin-bottom: 16px; border-radius: 4px; }}
</style></head>
<body>
<header>
<h1>demo-epb — Elektrische Parkbremse</h1>
<div class='meta'>Version <strong>{html.escape(tag)}</strong> &middot; Commit <code>{html.escape(sha)}</code> &middot; Generiert {html.escape(now)}</div>
</header>
<main>
<div class='banner'>
<strong>Demo-Projekt:</strong> Vollstaendige Demo des
<a href='https://gitea.slohmaier.com/slohmaier/dev-process'>slohmaier Dev Process</a>.
Diese Software ist bewusst kein Produktivcode, sondern Showcase der Engineering-Methodik.
</div>
<div class='kpis'>
{kpi_card("Safety Goals", str(n_sg), "ASIL D/D/A/C/B", "#d62728")}
{kpi_card("System Reqs", str(n_sys), f"in reqs/sys/")}
{kpi_card("SW Reqs", str(n_swe), f"in reqs/swe/")}
{kpi_card("Arch-Elemente", f"{n_sa+n_swa}", f"{n_sa} SA + {n_swa} SWA")}
{kpi_card("Komponenten", f"{n_impl}", f"+ {n_stubs} Stubs", "#2ca02c")}
{kpi_card("Unit-Tests", str(n_tests), "Alle gruen", "#2ca02c")}
</div>
<div class='cols'>
<section>
<h2>Plaene (Word)</h2>
<ul>
"""
for name, href in plans:
if not href.startswith("docs/safety") and not href.startswith("docs/manuals"):
html_body += f" <li><a href='{html.escape(href)}'>{html.escape(name)}</a></li>\n"
html_body += " </ul></section>\n"
html_body += doc_section("Funktionale Sicherheit (Word)", safety,
"HARA, Safety Case, FMEDA, Compliance, Verification, Tool-Qualification")
html_body += "</div><div class='cols'>"
html_body += doc_section("Manuals (Word)", manuals,
"End-User + Werkstatt-Doku")
audit_docs = reviews + ncs + misra_r
html_body += doc_section("Audit-Artefakte (Word)", audit_docs,
"Reviews, Non-Conformities, MISRA-Deviation-Records")
html_body += "</div>"
# Reports
html_body += "<section><h2>Engineering-Reports (CI-generiert)</h2><ul>\n"
html_body += report_link("Traceability-Matrix",
os.path.relpath(rep_trace, REPO),
rep_trace.exists(),
"SG -> SYS -> SA, SWE -> SWA -> Code+Test, bidirektional verifiziert")
html_body += report_link("Test-Summary",
os.path.relpath(rep_test_html, REPO),
rep_test_html.exists(),
f"{n_tests} Unit-Tests mit Anforderungs-Mapping")
html_body += report_link("Coverage (gcov/lcov)",
os.path.relpath(rep_cov_idx, REPO) if rep_cov_idx.exists() else "build/coverage-html/index.html",
rep_cov_idx.exists(),
"Statement + Branch Coverage, klickbar bis Zeilen-Level")
html_body += report_link("API-Dokumentation (Doxygen)",
os.path.relpath(rep_api, REPO) if rep_api.exists() else "build/api-doc/html/index.html",
rep_api.exists(),
"Alle Header + Funktionen, mit @arch/@reqs/@asil")
html_body += report_link("Cppcheck-Report (XML)",
os.path.relpath(rep_cppcheck, REPO) if rep_cppcheck.exists() else "build/cppcheck-report.xml",
rep_cppcheck.exists(),
"Statische Analyse + MISRA-Findings")
html_body += "</ul></section>"
# Diagrams
diagrams = sorted((REPO / "docs" / "diagrams").glob("*.svg"))
if diagrams:
html_body += "<section><h2>Architektur-Diagramme (PlantUML)</h2><ul>"
for d in diagrams:
href = os.path.relpath(d, REPO)
html_body += f" <li><a href='{html.escape(href)}'>{html.escape(d.stem)}</a></li>\n"
html_body += "</ul></section>"
# Source code links
html_body += """
<section>
<h2>Source-Code</h2>
<ul>
<li><a href='src/safety_manager.c'>safety_manager.c</a> — Safety Manager (ASIL-D, Hill-Hold + Auto-Apply + Drive-Away)</li>
<li><a href='src/apply_controller.c'>apply_controller.c</a> — Apply Controller (ASIL-D, State Machine)</li>
<li><a href='src/actuator_driver.c'>actuator_driver.c</a> — Actuator Driver (ASIL-B, Overcurrent-Cutoff)</li>
<li><a href='src/switch_debouncer.c'>switch_debouncer.c</a> — Switch Debouncer (QM)</li>
<li class='cnt'>Plus 6 Stub-Header in <a href='src/stubs/'>src/stubs/</a></li>
</ul>
</section>
"""
html_body += f"""
<section>
<h2>Externe Links</h2>
<ul>
<li><a href='https://gitea.slohmaier.com/slohmaier/demo-epb'>Gitea-Repo</a></li>
<li><a href='https://gitea.slohmaier.com/slohmaier/demo-epb/releases'>Releases</a></li>
<li><a href='https://gitea.slohmaier.com/slohmaier/dev-process'>Methodik-Repo (dev-process)</a></li>
</ul>
</section>
</main>
<footer>
Build {html.escape(now)} &middot; demo-epb {html.escape(tag)} ({html.escape(sha)}) &middot; slohmaier.com
</footer>
</body></html>
"""
out = BUILD / "index.html"
out.write_text(html_body)
print(f"Wrote {out}")
return 0
if __name__ == "__main__":
raise SystemExit(main())