Projektwissen: Git-Vorgaben, Ticketzugriff, Tests und Einstieg
Erster Stand des Repositories AG_QGIS/Projektwissen (bisher Arbeitstitel "Workspace", ADR 0006). Der lokale Ordner heisst weiter AG_QGIS, weil darin auch die Plugin-Klone liegen. Git verbindlich (ADR 0007), aus dem bisherigen Wiki-Konzept entwickelt: - docs/leitfaden/05_git_arbeitsweise.md mit Kanal-Kette, Branch-Namen und Gitea-Branch-Schutz. - Der Einstiegspunkt eines Fixes richtet sich nach dem Kanal, in dem der Fehler sitzt; ein Fix an testing oder stable ist erst mit der Rueckfuehrung nach unten abgeschlossen. Das fehlte bisher. - Der stabile Kanal heisst stable, nicht main: der Release-Workflow in Repository:hidden/workflows fuehrt git checkout "$CHANNEL" aus, der Branchname muss also dem Kanalnamen entsprechen. Er existiert in keinem Plugin-Repo und ist vom Maintainer anzulegen. - Der Mensch drueckt ab: .claude/settings.json sperrt push, merge, tag. - scripts/hallo.py und /hallo holen den Stand aller Klone, per Fast-Forward wo gefahrlos; nie mergen, stashen oder verwerfen. Ticketzugriff: - scripts/gitea.py liest Issues, Kommentare und PRs ueber die oeffentliche API, ohne Token, nur GET. Schreibendes nur als Entwurf in entwuerfe/. - Skill sn-ticket. Tests (ADR 0009): - docs/leitfaden/06_tests.md: Testebenen, Testpflicht, und wie eine Fachperson beurteilt, ob Tests fachlich sinnvoll sind. - scripts/testkatalog.py erzeugt docs/tests/testkatalog.md per ast-Parsing, ohne Import und ohne QGIS. Befund: nur 100 von 332 Tests haben einen Docstring, sn_basis keinen einzigen von 87. - Vorlage und erste Testkonzept-Seite mit Mermaid. Dabei gefunden und gegen origin geprueft: _ermittle_gebietstyp_aus_fbschl hat keinen Test, obwohl PR #44/#45 genau diese Funktion tolerant gemacht hat. - docs/konzept/07_qgis_tests_plan.md: Stufenplan fuer Tests gegen echtes QGIS. Dabei aufgefallen: QGIS 3.40 ist seit Januar 2026 End of Life, qgis_min steht aber noch darauf. Werkzeuge (ADR 0008): - 04_werkzeuge_einrichten.md nur noch Claude Code als CLI und in VS Code. - .vscode/ entfernt, brauchbare Einstellungen als persoenliche Vorlage. Ausserdem: README als Einstieg neu aufgebaut, Mermaid-Diagramm in schnittstellen.md repariert (unmaskierte Klammern, rendert seither nicht). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
# Konzept: Gemeinsame KI-gestützte Entwicklung über mehrere Behörden und Werkzeuge
|
||||
|
||||
Stand 02.09.2026. Vorschlag zur Abnahme durch die AG QGIS. Die Umsetzung liegt als Struktur bereits in diesem
|
||||
Workspace vor; was das Team anders entscheidet, wird hier und in `docs/entscheidungen/` nachgezogen.
|
||||
|
||||
## 1. Ausgangslage und Ziele
|
||||
|
||||
**Randbedingungen**
|
||||
|
||||
- Mehrere Entwickler in verschiedenen Flurbereinigungsbehörden, überwiegend Fachleute ohne Software-Ausbildung.
|
||||
- Jeder nutzt eigene KI-Agenten: Claude Code, OpenAI Codex, teils GitHub Copilot in VS Code (Issue Basis #36).
|
||||
*Nachtrag 04.09.2026: Das Team arbeitet mit Claude Code; die Doku beschreibt nur noch dieses Werkzeug, die
|
||||
Dateistruktur bleibt werkzeugneutral (ADR 0008).*
|
||||
Alle arbeiten ordnerbasiert und lesen Anweisungsdateien aus dem Repository.
|
||||
- Elf Repositories in einer Gitea-Organisation, eigener Release-Prozess, Zielplattform Windows/OSGeo4W mit QGIS 3.40
|
||||
und 4. Die Agenten laufen meist ohne QGIS.
|
||||
- Der Code ist gewachsen und wird später grundlegend refaktoriert; bis dahin soll die Arbeit trotzdem sicher und
|
||||
effizient sein.
|
||||
|
||||
**Ziele**
|
||||
|
||||
1. Jeder Agent und jeder Entwickler bekommt **denselben Projektkontext**, unabhängig vom Werkzeug.
|
||||
2. Wissen fließt **zurück ins Repository**, nicht in Chatverläufe oder lokale Gedächtnisse.
|
||||
3. **Tokensparsamkeit**: Kontext wird schichtweise geladen; Verifikation (Tests) ist billig und schnell.
|
||||
4. **Verbindlicher, leichter Arbeitszyklus**, der für Fachleute in einer Stunde erlernbar ist.
|
||||
5. Vorbereitung des Refactorings, ohne es vorwegzunehmen.
|
||||
|
||||
## 2. Leitprinzipien
|
||||
|
||||
- **Eine Wahrheit, versioniert.** Alles, was mehr als eine Person wissen muss, steht in Git. Werkzeugspezifische
|
||||
Dateien sind nur dünne Zeiger auf die kanonische Quelle.
|
||||
- **Kontext in Schichten (progressive Offenlegung).** Immer geladen: eine kurze Anweisungsdatei. Auf Abruf: Modulkarte,
|
||||
Schnittstellen, Fallstricke. Bei Bedarf: Skills mit Ablaufwissen. Nur wenn nötig: der Code selbst.
|
||||
- **Verträge vor Code.** Agenten arbeiten sicher, wenn sie die impliziten Regeln kennen (Wrapper, Variablen, Dock,
|
||||
Release). Deshalb sind Verträge ausdrücklich dokumentiert und in Regeln übersetzt.
|
||||
- **Verifikation ist Pflicht und billig.** Mock-Tests in Sekunden, Baseline gegen Verschlechterung, QGIS-Tests dort,
|
||||
wo Mocks nicht reichen.
|
||||
- **Kleine Schritte, sichtbare Übergaben.** Ein Issue, ein Branch, ein PR, eine Übergabenotiz. Keine Sammel-PRs.
|
||||
- **Menschen entscheiden, Agenten arbeiten.** Fachliche Entscheidungen und Merges bleiben bei Personen; Agenten
|
||||
liefern Vorschläge mit Nachweis.
|
||||
|
||||
## 3. Struktur des Projekts: drei Varianten
|
||||
|
||||
| Variante | Beschreibung | Vorteile | Nachteile |
|
||||
|---|---|---|---|
|
||||
| **A. Workspace-Meta-Repository mit Klonen** (empfohlen, umgesetzt) | Neues Repo `AG_QGIS/Projektwissen` mit `AGENTS.md`, `docs/`, Skills, Skripten, Manifest. Plugin-Repos werden als Unterordner geklont (per `.gitignore` ausgenommen). | Kein Eingriff in bestehende Repos und Release-Prozess; ein Ordner = ganzer Projektkontext; sofort nutzbar; Meta-Repo versioniert die Wissensbasis; Manifest hält kompatible Stände fest | Zwei Ebenen von Git; Entwickler müssen im Workspace-Root starten; per-Repo-Dateien bleiben nötig für Einzelklone |
|
||||
| B. Monorepo | Alle Plugins in ein Repository überführen | Ein Kontext, ein PR über Plugin-Grenzen, einfache Verträge | Release-Prozess, Tags und Feeds müssten umgebaut werden; Historie-Migration; das ist Teil des Refactorings, nicht der Vorbereitung |
|
||||
| C. Nur per-Repo-Dateien | `AGENTS.md` in jedem Plugin-Repo, keine Meta-Ebene | Minimal | Gesamtkontext fehlt genau dort, wo er gebraucht wird (Basis ↔ Fachplugin); Wissensbasis müsste dupliziert werden |
|
||||
|
||||
**Empfehlung A**, mit per-Repo-`AGENTS.md` als Ergänzung (Vorlage in `docs/vorlagen/AGENTS.repo.md`). Variante B bleibt als
|
||||
Option für die Refactoring-Phase offen; der Workspace bereitet sie vor (Manifest, Verträge, Tests).
|
||||
|
||||
## 4. Anweisungsdateien: kanonisch `AGENTS.md`
|
||||
|
||||
| Werkzeug | liest | Lösung |
|
||||
|---|---|---|
|
||||
| Claude Code (genutzt) | `CLAUDE.md` (mit `@`-Importen), Skills in `.claude/skills/` | `CLAUDE.md` = `@AGENTS.md` + wenige Claude-Zeilen; Skills werden gespiegelt |
|
||||
| andere (Codex, Copilot, Cursor, Gemini CLI, …) | überwiegend `AGENTS.md` und Agent-Skills-Standard | direkt, ohne dass wir etwas ändern müssten – wir dokumentieren die Einrichtung aber nicht (ADR 0008) |
|
||||
|
||||
Alternativen und warum nicht: getrennte Dateien je Werkzeug (Drift), Symlink `CLAUDE.md → AGENTS.md` (unter Windows
|
||||
Administratorrechte nötig; Import ist gleichwertig).
|
||||
|
||||
**Inhalt und Grenzen der `AGENTS.md`:** unter 150 Zeilen; nur Fakten, die in jeder Sitzung gelten (Regeln, Befehle,
|
||||
Leseliste, Arbeitsablauf in Kurzform). Alles Prozedurale wandert in Skills, alles Beschreibende in `docs/`.
|
||||
Sie wird bei jeder Sitzung in den Kontext geladen und ist damit die teuerste Datei des Projekts.
|
||||
|
||||
**Per-Repo-`AGENTS.md`** (kurz, ≤ 40 Zeilen): Rolle des Repos, Aufbau, Zusatzregeln, Testbefehl, Verweis auf den
|
||||
Workspace. Nötig, weil Codex Anweisungen nur bis zur Wurzel des jeweiligen Git-Repos sucht und weil Entwickler
|
||||
gelegentlich nur ein Repo klonen. Für Claude Code liegt daneben eine `CLAUDE.md` mit `@AGENTS.md`.
|
||||
|
||||
## 5. Bausteine der Wissensbasis
|
||||
|
||||
```
|
||||
AG_QGIS/ Klon von Projektwissen (Meta)
|
||||
├── AGENTS.md, CLAUDE.md Schicht 1: immer geladen
|
||||
├── workspace.toml Manifest: Repos, Branches, Paketnamen, Rollen
|
||||
├── scripts/ setup_workspace.py, run_tests.py, link_qgis_profile.py
|
||||
├── .agents/skills/<name>/SKILL.md Schicht 3: Ablaufwissen auf Abruf
|
||||
├── docs/status/STATUS.md Schicht 2: aktueller Stand (Pflichtlektüre)
|
||||
├── docs/architektur/ Modulkarte, Schnittstellen (lebend)
|
||||
├── docs/wissen/ Fallstricke, Glossar, externe Dienste (geteiltes Gedächtnis)
|
||||
├── docs/entscheidungen/ ADRs: warum etwas so ist
|
||||
├── docs/vorlagen/ Issue, PR, Übergabe, Repo-AGENTS.md, CI-Workflow
|
||||
├── docs/analyse|konzept|leitfaden Momentaufnahmen und stabile Texte
|
||||
└── Plugin_SN_*/ … eigenständige Klone, je mit AGENTS.md + CLAUDE.md
|
||||
```
|
||||
|
||||
| Baustein | Zweck | Pflegeregel |
|
||||
|---|---|---|
|
||||
| `STATUS.md` | Was ist gerade los, wer arbeitet woran, offene Baustellen | bei jedem Merge/Übergabe |
|
||||
| `modulkarte.md` | gezieltes Lesen statt Vollzugriff; Kennzeichnung großer Dateien | bei neuen/verschobenen Modulen |
|
||||
| `schnittstellen.md` | die impliziten Verträge explizit | vor jeder Änderung daran |
|
||||
| `fallstricke.md` | nicht ableitbares Wissen mit Datum und Quelle | sobald etwas Zeit gekostet hat |
|
||||
| `glossar.md`, `externe_dienste.md` | Fachbegriffe, URLs, Abhängigkeiten | nach Bedarf |
|
||||
| `entscheidungen/NNNN-*.md` | eine Entscheidung je Datei: Kontext, Entscheidung, Folgen, Status | neue Entscheidung = neue Datei; alte nie ändern, nur ersetzen |
|
||||
| Skills | wiederkehrende Abläufe: Aufgabe bearbeiten, Tests, Übergabe, neues Fachplugin, Release | wenn sich ein Ablauf ändert |
|
||||
| `testbaseline.json` | Liste bekannter roter Tests | nur mit Absprache (`--update-baseline`) |
|
||||
|
||||
### Shared Memory: was wohin
|
||||
|
||||
| Art von Wissen | Beispiel | Ort |
|
||||
|---|---|---|
|
||||
| Regel, die immer gilt | „Qt nur über Wrapper“ | `AGENTS.md` |
|
||||
| Vertrag zwischen Komponenten | Prioritäten der Actions, Variablennamen | `schnittstellen.md` |
|
||||
| Stolperfalle | „Release baut Kanal-Branch, nicht Tag“ | `fallstricke.md` |
|
||||
| Warum-Entscheidung | „AGENTS.md kanonisch“ | `entscheidungen/` |
|
||||
| Ablauf | Release-Schritte | Skill |
|
||||
| Zwischenstand einer Aufgabe | „Test X ist rot, weil …“ | Übergabenotiz im PR und `STATUS.md` |
|
||||
| Persönliche Vorlieben, lokale Pfade | Editor, OSGeo-Pfad | lokales Tool-Gedächtnis, `CLAUDE.local.md` (gitignored) |
|
||||
|
||||
Die werkzeugeigenen Gedächtnisse (Claude Auto-Memory) bleiben erlaubt, gelten aber als
|
||||
**Notizzettel**. Die Regel in `AGENTS.md` und im Skill `sn-uebergabe` verpflichtet dazu, Teilbares in die Wissensbasis
|
||||
zu überführen. Das greift den Vorschlag aus Issue Basis #36 auf und ersetzt die dort vorgesehenen
|
||||
`copilot-instructions.md` je Plugin durch die werkzeugneutralen `AGENTS.md`.
|
||||
|
||||
## 6. Tokensparsamkeit: konkrete Maßnahmen
|
||||
|
||||
| Maßnahme | Wirkung | Umsetzung |
|
||||
|---|---|---|
|
||||
| Kurze `AGENTS.md`, Rest auf Abruf | jede Sitzung startet mit wenigen tausend Token statt Vollkontext | erledigt |
|
||||
| Modulkarte mit Zeilenzahlen und Warnung ab 600 Zeilen | Agent liest Abschnitte statt Dateien | erledigt; Regel in `AGENTS.md` |
|
||||
| Schnittstellen-Dokument | keine Erkundung von drei Repos, um einen Vertrag zu verstehen | erledigt |
|
||||
| Fallstricke | keine Wiederholung bekannter Irrwege | erledigt, wächst |
|
||||
| Skills statt wiederholter Erklärungen | Ablauf wird nur geladen, wenn er gebraucht wird | 5 Skills erledigt |
|
||||
| `run_tests.py` mit Zusammenfassung und Baseline | Sekunden statt Minuten; keine Rohausgaben im Kontext | erledigt |
|
||||
| Übergabenotiz je Aufgabe | nächste Sitzung beginnt bei Schritt 5 statt 1 | Vorlage + Skill erledigt |
|
||||
| Issues mit Abnahmekriterien | weniger Rückfragen, weniger Fehlversuche | Vorlage erledigt |
|
||||
| Unteragenten für breite Suchen (Claude Code) | Hauptkontext bleibt schlank | Hinweis in `CLAUDE.md` |
|
||||
| Refactoring großer Dateien | dauerhaft kleinere Lesekosten | spätere Phase |
|
||||
|
||||
Anti-Muster, die vermieden werden: ganze Dateien in den Chat kopieren; „Lies das ganze Projekt“; Testrohausgaben
|
||||
einfügen; parallele Erkundung durch mehrere Personen ohne Übergabe; Erkenntnisse nur im Chat.
|
||||
|
||||
## 7. Arbeitszyklus und Rollen
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
I[Issue mit Abnahmekriterien] --> B[Branch von unstable]
|
||||
B --> A[Agent-Auftrag mit Kontextverweisen]
|
||||
A --> U[Umsetzung in kleinen Schritten]
|
||||
U --> T[run_tests.py --baseline\n+ QGIS-Test bei UI]
|
||||
T --> P[PR mit Übergabenotiz]
|
||||
P --> R[Review durch Mensch]
|
||||
R -->|Änderungen| U
|
||||
R -->|Merge| S[STATUS.md, Wissen, Changelog]
|
||||
S --> REL[Release bei Bedarf]
|
||||
```
|
||||
|
||||
| Rolle | Aufgaben |
|
||||
|---|---|
|
||||
| **Maintainer** (je Repo eine Person, Vorschlag: bisherige Hauptentwickler) | Merge-Recht, Release, Pflege von `AGENTS.md`/Verträgen, Baseline-Änderungen |
|
||||
| **Entwickler** | Issues formulieren, Agenten steuern, PRs mit Nachweis liefern, Wissensbasis pflegen |
|
||||
| **Agent** | umsetzen, testen, dokumentieren; keine Merges, keine Tags, keine Entscheidungen über Verträge |
|
||||
| **AG QGIS** | Entscheidungen zu Verträgen, Datenmodell, Refactoring, Alternativansatz |
|
||||
|
||||
Review-Regeln: KI-erzeugter Code wird immer von einer Person gelesen; PRs unter etwa 400 geänderten Zeilen; jede
|
||||
Vertragsänderung braucht ein Issue mit Label `Schnittstelle`.
|
||||
|
||||
## 8. Werkzeugspezifika
|
||||
|
||||
**Claude Code:** im Workspace-Root starten; `/context` zeigt, ob `CLAUDE.md` und `AGENTS.md` geladen sind. Skills
|
||||
liegen nach `python scripts/setup_workspace.py --nur-skills` in `.claude/skills/`. Beim Arbeiten in einem
|
||||
Plugin-Ordner lädt Claude die dortige `CLAUDE.md` automatisch dazu. Auto-Memory ist lokal; Regel siehe Abschnitt 5.
|
||||
|
||||
**Gitea:** Labels `Bug`, `Feature`, `Diskussion` ergänzen um `Schnittstelle`, `Tests`, `Doku`, `Refactoring`.
|
||||
Issue- und PR-Vorlagen aus `docs/vorlagen/` in `.gitea/` der Repos übernehmen (optional).
|
||||
|
||||
## 9. Einführung in Phasen
|
||||
|
||||
| Phase | Inhalt | Ergebnis |
|
||||
|---|---|---|
|
||||
| **0 – Projektwissen veröffentlichen** | Repo `AG_QGIS/Projektwissen` anlegen, diesen Ordner pushen, Team liest Konzept und Leitfaden, Entscheidungen bestätigen (ADR-Status auf „angenommen“) | ab sofort gemeinsamer Kontext |
|
||||
| **1 – Repos anbinden** | per-Repo `AGENTS.md`/`CLAUDE.md` per PR in Basis, Plan41, Verfahrensgebiet, Widmung; Release-Excludes ergänzen; Metadata-Templates korrigieren | Agenten arbeiten auch in Einzelklonen korrekt |
|
||||
| **2 – Tests absichern** | Baseline committen, CI-Workflow aus Vorlage in die drei Plugin-Repos, Rot→Grün als Issues je Testdatei | Tests haben wieder Aussagekraft |
|
||||
| **3 – Verträge festschreiben** | Abhängigkeiten deklarieren, Variablennamen bereinigen (kleiner, gezielter Eingriff), Linkliste-Entscheidung, Manifest kompatibler Versionen | stabile Basis für den Umbau |
|
||||
| **4 – Refactoring planen** | Zielarchitektur (inkl. Fachschale-Idee), Scheiben, Reihenfolge, Monorepo-Frage | eigenes Konzept |
|
||||
|
||||
Phase 0 und 1 sind in wenigen Stunden erledigt; Phase 2 ist die erste echte Teamaufgabe mit Agenten und zugleich das
|
||||
Training des Arbeitszyklus.
|
||||
|
||||
## 10. Entscheidungen, die das Team treffen muss
|
||||
|
||||
1. Name und Ort des Meta-Repositories (entschieden: `AG_QGIS/Projektwissen`, ADR 0006).
|
||||
2. Bestätigung der ADRs 0001–0005 (`docs/entscheidungen/`).
|
||||
3. Maintainer je Repository.
|
||||
4. Bestätigung der ADRs 0006–0009 (Reponame, Branchmodell, Werkzeugfokus, Testpflicht).
|
||||
5. Linkliste: zentrale CSV als einzige Quelle (Empfehlung) oder XLSX im Plugin.
|
||||
6. Zeitpunkt des Refactorings (Empfehlung: nach Phase 3) und ob die Fachschale-Idee Teil des Zielbilds wird.
|
||||
@@ -0,0 +1,244 @@
|
||||
# Plan: automatisierte Tests gegen echtes QGIS
|
||||
|
||||
Stand 04.09.2026. Vorschlag zur Abstimmung im Team. Ergänzt [`../leitfaden/06_tests.md`](../leitfaden/06_tests.md)
|
||||
um die zweite Testebene. Recherchestand und Quellen am Ende.
|
||||
|
||||
## 1. Worum es geht
|
||||
|
||||
Heute laufen 332 Tests im Mock-Modus — ohne QGIS, in unter einer Sekunde. Das deckt Logik, Parsing und
|
||||
Prüfregeln ab. Es deckt **nicht** ab, was erst im Zusammenspiel mit QGIS passiert: ob ein Layer wirklich entsteht,
|
||||
ob ein WFS-Ergebnis verarbeitbar ist, ob ein Drucklayout eine Seite erzeugt, ob ein Dock sich sauber wieder
|
||||
abmeldet. Genau dort sitzen die Fehler, die heute nur durch manuelles Klicken auffallen — und die zwischen Qt5
|
||||
und Qt6 unterschiedlich ausfallen.
|
||||
|
||||
Es gibt seit Jahren einen etablierten Weg, QGIS-Plugins ohne Bildschirm und ohne menschliche Interaktion gegen
|
||||
eine echte QGIS-Instanz zu testen. Dieser Plan beschreibt, wie wir dort in vier Stufen hinkommen.
|
||||
|
||||
> **Der Mock-Modus bleibt.** Er wird nicht ersetzt. Er ist die schnelle Ebene, die bei jedem Commit läuft;
|
||||
> QGIS-Tests sind die langsame Ebene, die bei jedem Pull Request läuft. Wer die Mocks abschafft, tauscht
|
||||
> Sekunden gegen Minuten und verliert die Wrapper-Disziplin, die den Qt5/Qt6-Code zusammenhält.
|
||||
|
||||
## 2. Vorab: unsere Zielversionen stimmen nicht mehr
|
||||
|
||||
Recherchestand 04.09.2026, geprüft am QGIS-Release-Fahrplan und an den Docker-Tags:
|
||||
|
||||
| | Version | Status |
|
||||
|---|---|---|
|
||||
| LTR | **3.44** | aktueller Langzeit-Release |
|
||||
| Stable | **4.2** | wird am 25.09.2026 zum LTR |
|
||||
| 3.40 | 3.40.15 (Januar 2026) | **End of Life**, keine Updates mehr |
|
||||
|
||||
`workspace.toml` sagt `qgis_min = "3.40"`, alle `.gitea/templates/metadata.template` sagen
|
||||
`qgisMinimumVersion=3.40`. Das ist keine Testfrage, sondern eine Produktentscheidung: **Welche QGIS-Versionen
|
||||
unterstützen wir?** Sie muss vor Stufe 2 fallen, weil sie die Test-Matrix bestimmt. Vorschlag zur Diskussion:
|
||||
`3.44` als Minimum, `4.2` als zweites Matrix-Ziel, `3.40` nur solange Bestandsinstallationen es erzwingen.
|
||||
|
||||
## 3. Wie es technisch funktioniert
|
||||
|
||||
Der Kern ist `qgis.testing.start_app()` aus dem QGIS-Quellbaum. Es baut eine vollständige `QgsApplication` mit
|
||||
registrierten Datenprovidern auf, in einem temporären Profil, und ist idempotent — mehrfacher Aufruf schadet
|
||||
nicht. Plugins lädt es bewusst **nicht**; das eigene Plugin instanziiert der Test selbst.
|
||||
|
||||
```python
|
||||
import os
|
||||
if not os.getenv("DISPLAY"):
|
||||
os.environ["QT_QPA_PLATFORM"] = "offscreen" # muss VOR dem App-Start gesetzt sein
|
||||
|
||||
from qgis.testing import start_app, unittest
|
||||
from qgis.testing.mocked import get_iface
|
||||
|
||||
QGIS_APP = start_app()
|
||||
IFACE = get_iface() # echtes QMainWindow, echter QgsMapCanvas, echter LayerTree
|
||||
```
|
||||
|
||||
`get_iface()` liefert kein selbstgebautes Mock, sondern ein `QgisInterface`-Objekt mit echtem Hauptfenster,
|
||||
echtem Kartenfenster und echtem Layerbaum. Das ist deutlich realitätsnäher als alles, was wir selbst nachbauen
|
||||
könnten, und es kostet uns keine Pflege.
|
||||
|
||||
`QT_QPA_PLATFORM=offscreen` ist der Schalter, der das Ganze ohne Bildschirm laufen lässt — auf dem CI-Server
|
||||
genauso wie unter Windows.
|
||||
|
||||
## 4. Der Stufenplan
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
S1["<b>Stufe 1</b><br/>start_app() lokal<br/><i>klein</i>"]
|
||||
S2["<b>Stufe 2</b><br/>Gitea Actions<br/>Matrix 3.44 / 4.2<br/><i>mittel</i>"]
|
||||
S3["<b>Stufe 3</b><br/>pytest-qgis<br/><i>klein</i>"]
|
||||
S4["<b>Stufe 4</b><br/>Render-Vergleich<br/>Plan41<br/><i>groß</i>"]
|
||||
|
||||
S1 --> S2 --> S3 --> S4
|
||||
|
||||
style S1 fill:#e8f6ec,stroke:#2f9e44
|
||||
style S2 fill:#e6f2ff,stroke:#2b7dd9
|
||||
style S3 fill:#e6f2ff,stroke:#2b7dd9
|
||||
style S4 fill:#fff4e6,stroke:#d9822b
|
||||
```
|
||||
|
||||
### Stufe 1 — QGIS-Tests lokal, ohne CI
|
||||
|
||||
**Ziel:** Eine erste echte QGIS-Suite je Plugin, die ein Entwickler auf seinem Windows-Rechner starten kann.
|
||||
|
||||
- Neuer Ordner `tests/qgis/` je Plugin-Repo, getrennt von den Mock-Tests in `tests/`. Beide Suiten bleiben
|
||||
unabhängig lauffähig.
|
||||
- Start unter Windows über die OSGeo4W-Umgebung:
|
||||
`C:\OSGeo4W\bin\python-qgis-ltr.bat -m pytest tests\qgis -v`.
|
||||
Ein normales `python -m pytest` findet die QGIS-Bindings nicht.
|
||||
- Die vorhandenen Skripte `tests/test_qgis.bat` und `tests/start_osgeo4w_qgis.bat` werden dabei ersetzt. Sie
|
||||
enthalten heute fest verdrahtete Pfade (`D:\OSGeo`, `D:\QGISQT5`) und funktionieren nur auf einem Rechner.
|
||||
- Erste Kandidaten, weil dort der Mock-Modus am wenigsten aussagt: Layer anlegen und stylen (`sn_basis`),
|
||||
Verarbeitung einer WFS-Antwort (`sn_verfahrensgebiet`), Erzeugen eines Drucklayouts (`sn_plan41`).
|
||||
|
||||
**Aufwand:** klein. Kein neues Werkzeug, keine Infrastruktur.
|
||||
**Nutzen:** sofort, aber nur für die, die es lokal ausführen.
|
||||
|
||||
### Stufe 2 — Gitea Actions mit Docker
|
||||
|
||||
**Ziel:** Jeder Pull Request wird automatisch gegen zwei QGIS-Generationen geprüft, ohne dass jemand eine zweite
|
||||
QGIS-Version installiert.
|
||||
|
||||
Das ist der größte Hebel des ganzen Plans. Der Qt5/Qt6-Unterschied ist in `fallstricke.md` ein Dauerthema, und
|
||||
er lässt sich mit Bordmitteln kaum abdecken — hier fällt er automatisch auf.
|
||||
|
||||
Die offiziellen Images `qgis/qgis` bringen QGIS samt Bindings mit. Verifizierte Tags (Stand 04.09.2026):
|
||||
`3.44`, `3.44.14`, `4.0`, `4.2`, `4.2.2`, `ltr`, `stable`. QGIS 4 gibt es nur auf Qt6-Distributionen
|
||||
(Debian trixie, Ubuntu questing) — kein `4.x-noble`.
|
||||
|
||||
```yaml
|
||||
name: QGIS-Tests
|
||||
on:
|
||||
pull_request:
|
||||
branches: [unstable, testing, stable]
|
||||
|
||||
jobs:
|
||||
qgis:
|
||||
runs-on: ubuntu-latest # Label muss auf einen Docker-fähigen act_runner zeigen
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
qgis: ["3.44", "4.2"]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Tests in QGIS ${{ matrix.qgis }}
|
||||
run: |
|
||||
docker run --rm -v "$PWD":/src -w /src \
|
||||
-e QT_QPA_PLATFORM=offscreen \
|
||||
qgis/qgis:${{ matrix.qgis }} \
|
||||
bash -c "python3 -m venv /venv --system-site-packages \
|
||||
&& /venv/bin/pip install -q pytest \
|
||||
&& /venv/bin/pytest tests/qgis -v"
|
||||
```
|
||||
|
||||
**Zu klären vor der Umsetzung:**
|
||||
- Der vorhandene Gitea-Runner trägt das Label `alpine-latest` (siehe `docs/vorlagen/gitea_workflow_tests.yaml`).
|
||||
Für Docker-in-Docker braucht es einen Runner mit Docker-Zugriff. Das ist eine Frage an den Maintainer der
|
||||
Runner-Infrastruktur, keine Codefrage.
|
||||
- Die Fachplugins brauchen im Container auch `sn_basis`. Das Muster steckt schon in
|
||||
`docs/vorlagen/gitea_workflow_tests.yaml`: die Nachbar-Repos dazuklonen und unter ihren Paketnamen ablegen.
|
||||
|
||||
**Aufwand:** mittel, im Wesentlichen Infrastruktur.
|
||||
**Nutzen:** hoch und dauerhaft.
|
||||
|
||||
### Stufe 3 — `pytest-qgis`
|
||||
|
||||
**Ziel:** Die bekannten Stolperfallen von QGIS-Tests nicht selbst lösen müssen.
|
||||
|
||||
`pytest-qgis` (Version 4.1.1, August 2026, Python ≥ 3.10, QGIS ≥ 3.34, Qt6-tauglich seit 3.0.0) bringt mit:
|
||||
|
||||
| Was | Warum wichtig |
|
||||
|---|---|
|
||||
| Fixture `qgis_app` | startet die App einmal je Sitzung |
|
||||
| Fixture `qgis_iface` | patcht `qgis.utils.iface` automatisch — sonst ist es `None` |
|
||||
| Fixture `qgis_new_project` | leeres Projekt je Test; sonst wirken Layer aus Test A in Test B nach |
|
||||
| automatisches Layer-Aufräumen | verhindert die bekannten Abstürze beim Beenden |
|
||||
| Fixture `qgis_processing` | Processing-Registry initialisiert |
|
||||
| `--qgis_disable_exit` | Notausgang, falls es beim Beenden doch kracht |
|
||||
|
||||
**Aufwand:** klein — etwa eine halbe Stunde plus Umstellung der bis dahin geschriebenen Tests.
|
||||
**Empfehlung:** erst nach Stufe 2, damit die Umstellung einmal an einer laufenden CI verifiziert wird.
|
||||
|
||||
### Stufe 4 — Render-Vergleich für Plan41
|
||||
|
||||
**Ziel:** Erkennen, wenn sich das Aussehen eines Plans ungewollt ändert.
|
||||
|
||||
Für ein Plugin, dessen Zweck das Zeichnen von Plänen nach § 41 FlurbG ist, ist das fachlich der wertvollste
|
||||
Testtyp — und der teuerste. Mit einem verifizierten Fallstrick:
|
||||
|
||||
> `QgisTestCase.render_layout_check()` und `render_map_settings_check()` suchen Referenzbilder über
|
||||
> `setControlPathPrefix()` in einem zur Compile-Zeit einkompilierten Pfad des QGIS-Quellbaums. In einer
|
||||
> installierten QGIS-Version existiert dieser Pfad nicht. Die bequemen Helfer sind für Plugins **nicht** nutzbar.
|
||||
|
||||
Gangbar sind stattdessen:
|
||||
- `QgsLayoutExporter.renderPageToImage()` und ein eigener, toleranter Bildvergleich, oder
|
||||
- `QgsRenderChecker` direkt, das als einziges `setControlImagePath()` anbietet.
|
||||
|
||||
**Bedingungen, ohne die es nur Ärger macht:**
|
||||
- Referenzbilder **im CI-Container** erzeugen, nie auf einem Entwickler-Windows. Schriftrendering und
|
||||
Antialiasing unterscheiden sich zwischen den Plattformen und erzeugen sonst dauerhaft rote Tests.
|
||||
- Toleranz einplanen (Farbabweichung und Anteil abweichender Pixel), nicht auf Byte-Gleichheit prüfen.
|
||||
- Mit **zwei bis drei** Referenzplänen anfangen, nicht mit fünfzig.
|
||||
|
||||
**Aufwand:** groß. Erst angehen, wenn Stufen 1–3 stabil laufen.
|
||||
|
||||
## 5. Was wir bewusst nicht machen
|
||||
|
||||
| Ansatz | Warum nicht |
|
||||
|---|---|
|
||||
| YAML-Testframework für Processing-Algorithmen | An `nose2`, `utilities.py` und CTest im QGIS-Quellbaum gebunden. Für externe Plugins nur mit Portierungsaufwand nutzbar. Stattdessen `processing.run(...)` und `assertLayersEqual`. |
|
||||
| `qgis_setup.sh` / `qgis_testrunner.sh` (Plugin im echten QGIS laden) | Kein pytest-Reporting; Erfolg wird über String-Suche in der Standardausgabe erkannt. Nur nötig, wenn der Plugin-Ladevorgang selbst geprüft werden soll. |
|
||||
| `pytest-xdist` (Parallelisierung) | Bei unserer Teamgröße und Suite-Laufzeit unnötige Komplexität. |
|
||||
| Eigenes Docker-Image bauen | `qgis/qgis` plus `pip install` im Job reicht. |
|
||||
| `pb_tool` | Deployment-Helfer, kein Testwerkzeug. Lokal Geschmackssache. |
|
||||
|
||||
## 6. Bekannte Fallstricke
|
||||
|
||||
Wandern bei der Umsetzung nach `docs/wissen/fallstricke.md`:
|
||||
|
||||
- **`QT_QPA_PLATFORM` muss vor dem Erzeugen der `QgsApplication` gesetzt sein.** Danach wirkt es nicht mehr.
|
||||
- **`qgis.utils.iface` ist nach `start_app()` `None`.** Entweder `pytest-qgis` verwenden oder selbst
|
||||
`qgis.utils.iface = get_iface()` setzen — und darauf achten, keine Module zu früh zu importieren.
|
||||
- **`QgsProject.instance()` ist global.** Ohne Aufräumen zwischen den Tests beeinflussen sie sich gegenseitig.
|
||||
- **Abstürze beim Beenden** sind ein bekanntes QGIS-Thema, wenn Layer- oder Canvas-Objekte noch referenziert
|
||||
werden. Gegenmittel: `pytest-qgis` oder `--qgis_disable_exit`.
|
||||
- **`offscreen` hat keinen Fenstermanager.** Fokus, `activeWindow()` und Drag-and-drop verhalten sich anders.
|
||||
Für Widget-Tests notfalls `xvfb-run -a` davorsetzen (nur Linux).
|
||||
- **Modale Dialoge blockieren den Test.** Reihenfolge der Gegenmittel: den modalen Aufruf per `monkeypatch`
|
||||
ersetzen; `open()` statt `exec()` verwenden; erst als letztes `QTimer.singleShot` mit einem Klick-Handler.
|
||||
- **Locale.** Die Container laufen mit `LANG=C.UTF-8`, Entwickler-Windows meist auf Deutsch. Tests, die
|
||||
formatierte Zahlen oder Datumsangaben vergleichen, brechen sonst nur an einem der beiden Orte.
|
||||
- **QGIS 4 / PyQt6.** `exec_()` gibt es nicht mehr, Enum-Kurzformen (`Qt.UserRole`) auch nicht. Das trifft
|
||||
Testcode genauso wie Produktivcode. Für den Umstieg gibt es das Prüfwerkzeug `pyqgis4-checker`.
|
||||
|
||||
## 7. Vorschlag zur Reihenfolge
|
||||
|
||||
1. **Entscheidung zu den Zielversionen** (Abschnitt 2) — blockiert Stufe 2, eigenes Issue.
|
||||
2. **Stufe 1** an einem Plugin als Machbarkeitsnachweis, am besten `sn_basis` (keine Abhängigkeiten).
|
||||
3. **Runner-Frage klären** — hat der Gitea-Runner Docker?
|
||||
4. **Stufe 2** für `sn_basis`, dann für die Fachplugins.
|
||||
5. **Stufe 3**, sobald mehr als eine Handvoll QGIS-Tests existiert.
|
||||
6. **Stufe 4** als eigenes Vorhaben mit eigenem Konzept.
|
||||
|
||||
Die Stufen 1 bis 3 sind zusammen etwa der Aufwand, den heute ein größeres Fach-Issue kostet. Stufe 4 ist ein
|
||||
eigenes Vorhaben.
|
||||
|
||||
## Quellen
|
||||
|
||||
Recherchestand 04.09.2026.
|
||||
|
||||
- `qgis.testing` (`start_app`, `QgisTestCase`): https://github.com/qgis/QGIS/blob/master/python/testing/__init__.py
|
||||
- `qgis.testing.mocked.get_iface()`: https://github.com/qgis/QGIS/blob/master/python/testing/mocked.py
|
||||
- Docker-Images und Test-Helfer: https://github.com/qgis/QGIS/blob/master/.docker/README.md
|
||||
- Tag-Liste: https://hub.docker.com/r/qgis/qgis/tags
|
||||
- Release-Fahrplan (LTR-Termine): https://qgis.org/resources/roadmap/
|
||||
- Unit-Testing und Render-Checks: https://docs.qgis.org/testing/en/docs/developers_guide/unittesting.html
|
||||
- `QgsRenderChecker` vs. `QgsMultiRenderChecker`: https://github.com/qgis/QGIS/blob/master/src/core/qgsrenderchecker.h
|
||||
- `pytest-qgis`: https://github.com/GispoCoding/pytest-qgis · https://pypi.org/project/pytest-qgis/
|
||||
- Modale Dialoge im Test: https://pytest-qt.readthedocs.io/en/latest/note_dialogs.html
|
||||
- Migration zu Qt6/QGIS 4: https://plugins.qgis.org/docs/migrate-qgis4/ · https://github.com/qgis/pyqgis4-checker
|
||||
- Gitea Actions, Runner-Labels: https://docs.gitea.com/usage/actions/act-runner/
|
||||
|
||||
Vorbilder mit echter QGIS-Testsuite, als Muster zum Abschauen:
|
||||
|
||||
- `3liz/lizmap-plugin` — Matrix über drei QGIS-Versionen, eigener `start_app()`-Ersatz, dem hiesigen Setup am nächsten
|
||||
- `opengisch/qfieldsync` — Dialog-Tests gegen echtes QGIS, Matrix inklusive QGIS 4
|
||||
- `opengisch/qgis_plugins_test_demo` — kleines Referenz-Repo, unter anderem ein Drucklayout-Test
|
||||
@@ -0,0 +1,125 @@
|
||||
# Repository „Projektwissen" auf Gitea einrichten
|
||||
|
||||
Prüfliste für den Maintainer. Stand 04.09.2026. Einmalig auszuführen; danach ist dieses Dokument nur noch
|
||||
Nachweis, was wie eingestellt wurde.
|
||||
|
||||
Alles hier ist **Menschenarbeit**. Ein KI-Agent kann die Texte vorbereiten, aber weder Repositories anlegen noch
|
||||
pushen noch Schutzregeln setzen (ADR 0007).
|
||||
|
||||
## 1. Repository anlegen
|
||||
|
||||
Gitea → Organisation `AG_QGIS` → **Neues Repository**.
|
||||
|
||||
| Einstellung | Wert |
|
||||
|---|---|
|
||||
| Name | `Projektwissen` |
|
||||
| Beschreibung | Gemeinsame Wissensbasis, Regeln und Werkzeuge der QGIS-Plugin-Entwicklung LNO Sachsen |
|
||||
| Sichtbarkeit | öffentlich (wie die übrigen Repos der Organisation) |
|
||||
| `.gitignore`, Lizenz, README | **nicht** anlegen lassen — bringt der erste Push mit |
|
||||
| Issues | an |
|
||||
| Wiki | aus — die Doku liegt versioniert im Repo (ADR-Begründung in `docs/wissen/externe_dienste.md`) |
|
||||
| Standard-Branch | wird beim ersten Push gesetzt |
|
||||
|
||||
## 2. Ersten Stand pushen
|
||||
|
||||
Der Ordner `AG_QGIS/` ist bereits als Git-Repository vorbereitet, mit einem Commit, der genau die Dateien
|
||||
dieses Repositories enthält (die Plugin-Klone sind per `.gitignore` ausgenommen).
|
||||
|
||||
```bash
|
||||
cd <…>/AG_QGIS
|
||||
git status # erwartet: nichts Unversioniertes außer den Plugin-Klonen
|
||||
git log --oneline # erwartet: der vorbereitete erste Commit
|
||||
git remote add origin https://entwicklung.flurneuordnung-sachsen.de/AG_QGIS/Projektwissen.git
|
||||
git push -u origin unstable
|
||||
```
|
||||
|
||||
Wir beginnen bewusst auf `unstable`, damit auch dieses Repository dem Kanal-Modell aus
|
||||
[`../leitfaden/05_git_arbeitsweise.md`](../leitfaden/05_git_arbeitsweise.md) folgt.
|
||||
|
||||
**Vor dem Push prüfen:**
|
||||
|
||||
- [ ] **Autor des Commits stimmt.** `git log -1 --format='%an <%ae>'` – der vorbereitete Commit trägt die
|
||||
Identität, die auf dem vorbereitenden Rechner in `git config --global` eingetragen war. Ist das nicht die
|
||||
dienstliche Adresse, vor dem Push korrigieren:
|
||||
```bash
|
||||
git config user.name "Vorname Nachname"
|
||||
git config user.email "dienstliche@adresse"
|
||||
git commit --amend --reset-author --no-edit
|
||||
```
|
||||
- [ ] `git status` zeigt keine Plugin-Ordner (`Plugin_SN_*`, `Repository`, `Linkliste`, `Defaults`)
|
||||
- [ ] `entwuerfe/` ist nicht enthalten
|
||||
- [ ] keine Zugangsdaten, keine Tokens, keine echten Verfahrensdaten
|
||||
- [ ] `python scripts/run_tests.py` läuft
|
||||
- [ ] `python scripts/testkatalog.py --pruefen` meldet „aktuell"
|
||||
|
||||
## 3. Branches anlegen
|
||||
|
||||
```bash
|
||||
git switch -c testing unstable && git push -u origin testing
|
||||
git switch -c stable unstable && git push -u origin stable
|
||||
git switch unstable
|
||||
```
|
||||
|
||||
Danach in Gitea unter **Einstellungen → Allgemein** den Standard-Branch auf `unstable` setzen — dort landet die
|
||||
tägliche Arbeit.
|
||||
|
||||
## 4. Branch-Schutz setzen
|
||||
|
||||
**Einstellungen → Branches** (englisch: *Settings → Branches*), dort eine neue Branch-Schutzregel anlegen — je
|
||||
einmal für `stable`, `testing` und `unstable`:
|
||||
|
||||
| Einstellung | Wert |
|
||||
|---|---|
|
||||
| Branch-Name | `stable` / `testing` / `unstable` |
|
||||
| Push deaktivieren | an |
|
||||
| Merge-Freigabe erforderlich | mindestens 1 |
|
||||
| Veraltete Freigaben verwerfen | an |
|
||||
| Statusprüfungen erforderlich | erst aktivieren, wenn CI läuft (Abschnitt 6) |
|
||||
|
||||
## 5. Dasselbe für die Plugin-Repositories
|
||||
|
||||
Dort fehlt bisher der stabile Kanal. Je Repo (`Plugin_SN_Basis`, `Plugin_SN_Plan41`,
|
||||
`Plugin_SN_Verfahrensgebiet`, `Plugin_SN_Widmung`, `Plugin_SN_FLT`, `Plugin_SN_Topografie`,
|
||||
`Plugin_SN_Template`):
|
||||
|
||||
- [ ] `stable` anlegen — aus `testing`, wo es das gibt, sonst aus `unstable`. **Der Name muss exakt `stable`
|
||||
lauten**, weil der Release-Workflow `git checkout "$CHANNEL"` ausführt (ADR 0007).
|
||||
- [ ] Branch-Schutz für `stable`, `testing`, `unstable` wie in Abschnitt 4
|
||||
- [ ] `oldstable` prüfen (`Plugin_SN_Basis`, `Plugin_SN_Plan41`): enthält er noch etwas Nötiges? Sonst löschen.
|
||||
- [ ] Verwaiste Branches prüfen und löschen: `feature/Drucklayout-überarbeiten`, `print_layout` (Basis),
|
||||
`bug/crash_on_exit` (Plan41), `alkis-nur-laden-stop`, `fix-fbschl-feldname` (Verfahrensgebiet)
|
||||
|
||||
Erst danach funktioniert ein Release auf dem stabilen Kanal (Tag ohne Suffix).
|
||||
|
||||
## 6. Optional: Tests in CI
|
||||
|
||||
Vorlage: [`../vorlagen/gitea_workflow_tests.yaml`](../vorlagen/gitea_workflow_tests.yaml) → als
|
||||
`.gitea/workflows/tests.yaml` in `Plugin_SN_Basis`, `Plugin_SN_Plan41` und `Plugin_SN_Verfahrensgebiet`.
|
||||
Sie klont das Projektwissen und die Nachbar-Repos und führt den Baseline-Lauf aus.
|
||||
|
||||
Voraussetzung: ein Gitea-Runner mit dem Label `alpine-latest` (wie beim Release-Workflow). Läuft das, in
|
||||
Abschnitt 4 „Statusprüfungen erforderlich" aktivieren.
|
||||
|
||||
Der Ausbau zu Tests gegen echtes QGIS steht in
|
||||
[`07_qgis_tests_plan.md`](07_qgis_tests_plan.md) und braucht einen Runner mit Docker.
|
||||
|
||||
## 7. Team informieren
|
||||
|
||||
- [ ] Link zum Repository und zur [`README.md`](../../README.md) verteilen
|
||||
- [ ] Auf die Umstellung hinweisen: der Klon-Befehl heißt jetzt
|
||||
`git clone .../Projektwissen.git AG_QGIS` (Ordnername unverändert)
|
||||
- [ ] Um Durchsicht der Entscheidungen 0006–0009 bitten; nach Zustimmung den Status aller ADR von
|
||||
„vorgeschlagen" auf „angenommen" setzen
|
||||
- [ ] Jede Person trägt sich in `docs/status/STATUS.md` unter „Wer arbeitet woran" ein
|
||||
|
||||
## 8. Nach der Einrichtung zu klären
|
||||
|
||||
Diese Punkte liegen als Entwürfe bzw. offene Baustellen in `docs/status/STATUS.md`:
|
||||
|
||||
1. **QGIS-Mindestversion.** Steht auf 3.40, das seit Januar 2026 End of Life ist
|
||||
([`07_qgis_tests_plan.md`](07_qgis_tests_plan.md) Abschnitt 2). Betrifft `workspace.toml` und alle
|
||||
`.gitea/templates/metadata.template`.
|
||||
2. **`.vscode/` aus den Plugin-Repos entfernen.** Liegt in vier Repos mit fest verdrahteten Pfaden eines
|
||||
einzelnen Entwicklers. Je ein kleiner Pull Request.
|
||||
3. **Release-Excludes ergänzen** (`Repository:hidden/workflows`): `AGENTS.md`, `CLAUDE.md`, `.agents/`,
|
||||
`.claude/`, `docs/`, `tests/`, `.vscode/`.
|
||||
Reference in New Issue
Block a user