chore: Release v0.10.0 (Reverse-Proxy-Tauglichkeit)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-21 07:31:27 +02:00
parent 208c0a4df4
commit 51d85c8de6
2 changed files with 43 additions and 43 deletions

View File

@@ -61,7 +61,7 @@ systemd `--user`), Grafana OSS 12.1.0, Bash-Deployskript.
`FB_SESSION_COOKIE_SECURE`, Default `False`). Bool-Parsing via Modul-Helfer `FB_SESSION_COOKIE_SECURE`, Default `False`). Bool-Parsing via Modul-Helfer
`_env_bool(value: str) -> bool` (truthy: `1/true/yes/on`, case-insensitiv). `_env_bool(value: str) -> bool` (truthy: `1/true/yes/on`, case-insensitiv).
- [ ] **Step 1: Failing Tests schreiben** — an `finance/tests/test_config.py` - [x] **Step 1: Failing Tests schreiben** — an `finance/tests/test_config.py`
anhängen (oben `import pytest` ergänzen, falls nicht vorhanden): anhängen (oben `import pytest` ergänzen, falls nicht vorhanden):
```python ```python
@@ -105,12 +105,12 @@ def test_session_cookie_secure_parsing(monkeypatch, val, expected):
get_settings.cache_clear() get_settings.cache_clear()
``` ```
- [ ] **Step 2: Tests laufen lassen, Fehlschlag prüfen** - [x] **Step 2: Tests laufen lassen, Fehlschlag prüfen**
Run: `cd finance && .venv/bin/python -m pytest tests/test_config.py -q` Run: `cd finance && .venv/bin/python -m pytest tests/test_config.py -q`
Expected: FAIL (`AttributeError: ... 'grafana_public_url'` / `session_cookie_secure`). Expected: FAIL (`AttributeError: ... 'grafana_public_url'` / `session_cookie_secure`).
- [ ] **Step 3: Implementieren** — `finance/app/config.py`. Bool-Helfer vor - [x] **Step 3: Implementieren** — `finance/app/config.py`. Bool-Helfer vor
`get_settings` einfügen, zwei Felder ins `@dataclass Settings` und zwei `get_settings` einfügen, zwei Felder ins `@dataclass Settings` und zwei
Zeilen in den `Settings(...)`-Konstruktor: Zeilen in den `Settings(...)`-Konstruktor:
@@ -142,17 +142,17 @@ In `get_settings()` in den `return Settings(...)`-Aufruf (nach
session_cookie_secure=_env_bool(e("FB_SESSION_COOKIE_SECURE", "")), session_cookie_secure=_env_bool(e("FB_SESSION_COOKIE_SECURE", "")),
``` ```
- [ ] **Step 4: Tests grün** - [x] **Step 4: Tests grün**
Run: `cd finance && .venv/bin/python -m pytest tests/test_config.py -q` Run: `cd finance && .venv/bin/python -m pytest tests/test_config.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Volle Suite** - [x] **Step 5: Volle Suite**
Run: `cd finance && .venv/bin/python -m pytest -q` Run: `cd finance && .venv/bin/python -m pytest -q`
Expected: PASS (> 199). Expected: PASS (> 199).
- [ ] **Step 6: Fable-Gate + Commit** - [x] **Step 6: Fable-Gate + Commit**
```bash ```bash
git add finance/app/config.py finance/tests/test_config.py git add finance/app/config.py finance/tests/test_config.py
@@ -173,7 +173,7 @@ git commit -m "feat: FB_GRAFANA_PUBLIC_URL + FB_SESSION_COOKIE_SECURE in Setting
`secure`) auf: jetzt per Env konfigurierbar, Default unverändert (kein `secure`) auf: jetzt per Env konfigurierbar, Default unverändert (kein
`secure` im Lokalbetrieb). `secure` im Lokalbetrieb).
- [ ] **Step 1: Failing Tests** — an `finance/tests/test_auth.py` anhängen. - [x] **Step 1: Failing Tests** — an `finance/tests/test_auth.py` anhängen.
Sicherstellen, dass oben importiert ist: `from app.config import get_settings`. Sicherstellen, dass oben importiert ist: `from app.config import get_settings`.
Die Tests nutzen die vorhandene `client`-Fixture (setzt Passwort-Hash) und Die Tests nutzen die vorhandene `client`-Fixture (setzt Passwort-Hash) und
schalten das Flag zur Laufzeit um (Login liest `get_settings()` je Request): schalten das Flag zur Laufzeit um (Login liest `get_settings()` je Request):
@@ -197,12 +197,12 @@ def test_login_cookie_not_secure_by_default(client, monkeypatch):
assert "secure" not in r.headers["set-cookie"].lower() assert "secure" not in r.headers["set-cookie"].lower()
``` ```
- [ ] **Step 2: Fehlschlag prüfen** - [x] **Step 2: Fehlschlag prüfen**
Run: `cd finance && .venv/bin/python -m pytest tests/test_auth.py::test_login_cookie_secure_when_enabled -q` Run: `cd finance && .venv/bin/python -m pytest tests/test_auth.py::test_login_cookie_secure_when_enabled -q`
Expected: FAIL (`assert "secure" in ...`, Cookie hat noch kein secure-Flag). Expected: FAIL (`assert "secure" in ...`, Cookie hat noch kein secure-Flag).
- [ ] **Step 3: Implementieren** — in `finance/app/main.py` den - [x] **Step 3: Implementieren** — in `finance/app/main.py` den
`set_cookie`-Aufruf im Login um `secure=` ergänzen: `set_cookie`-Aufruf im Login um `secure=` ergänzen:
```python ```python
@@ -219,17 +219,17 @@ def login(username: str = Form(...), password: str = Form(...)):
return resp return resp
``` ```
- [ ] **Step 4: Tests grün** - [x] **Step 4: Tests grün**
Run: `cd finance && .venv/bin/python -m pytest tests/test_auth.py -q` Run: `cd finance && .venv/bin/python -m pytest tests/test_auth.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Volle Suite** - [x] **Step 5: Volle Suite**
Run: `cd finance && .venv/bin/python -m pytest -q` Run: `cd finance && .venv/bin/python -m pytest -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 6: Fable-Gate + Commit** - [x] **Step 6: Fable-Gate + Commit**
```bash ```bash
git add finance/app/main.py finance/tests/test_auth.py git add finance/app/main.py finance/tests/test_auth.py
@@ -254,7 +254,7 @@ git commit -m "feat: Session-Cookie secure-Flag per FB_SESSION_COOKIE_SECURE"
`http://<request-host>:8097`. Templates hängen bei Bedarf `/d/finanzen/...` `http://<request-host>:8097`. Templates hängen bei Bedarf `/d/finanzen/...`
an — mit sub-path-URL ergibt das `.../grafana/d/finanzen/...`. an — mit sub-path-URL ergibt das `.../grafana/d/finanzen/...`.
- [ ] **Step 1: Failing Tests** — an `finance/tests/test_gui.py` anhängen. Oben - [x] **Step 1: Failing Tests** — an `finance/tests/test_gui.py` anhängen. Oben
`from app.config import get_settings` ergänzen, falls nicht vorhanden: `from app.config import get_settings` ergänzen, falls nicht vorhanden:
```python ```python
@@ -287,12 +287,12 @@ def test_grafana_links_fallback_to_host_port_when_unset(client, monkeypatch):
get_settings.cache_clear() get_settings.cache_clear()
``` ```
- [ ] **Step 2: Fehlschlag prüfen** - [x] **Step 2: Fehlschlag prüfen**
Run: `cd finance && .venv/bin/python -m pytest tests/test_gui.py::test_grafana_links_use_public_url_when_set -q` Run: `cd finance && .venv/bin/python -m pytest tests/test_gui.py::test_grafana_links_use_public_url_when_set -q`
Expected: FAIL (Templates nutzen noch `<host>:8097`, `:8097` ist im Text). Expected: FAIL (Templates nutzen noch `<host>:8097`, `:8097` ist im Text).
- [ ] **Step 3a: Jinja-Global implementieren** — `finance/app/routers/gui.py`. - [x] **Step 3a: Jinja-Global implementieren** — `finance/app/routers/gui.py`.
Import ergänzen (bei den `from app...`-Imports): Import ergänzen (bei den `from app...`-Imports):
```python ```python
@@ -319,7 +319,7 @@ def grafana_public_base(request: Request) -> str:
templates.env.globals["grafana_public_base"] = grafana_public_base templates.env.globals["grafana_public_base"] = grafana_public_base
``` ```
- [ ] **Step 3b: Templates umstellen.** - [x] **Step 3b: Templates umstellen.**
`finance/app/templates/base.html` Zeile 20: `finance/app/templates/base.html` Zeile 20:
@@ -342,22 +342,22 @@ templates.env.globals["grafana_public_base"] = grafana_public_base
Kurven in <a href="{{ grafana_public_base(request) }}" target="_blank" rel="noopener">Grafana</a> ansehen. Kurven in <a href="{{ grafana_public_base(request) }}" target="_blank" rel="noopener">Grafana</a> ansehen.
``` ```
- [ ] **Step 4: Tests grün** - [x] **Step 4: Tests grün**
Run: `cd finance && .venv/bin/python -m pytest tests/test_gui.py -q` Run: `cd finance && .venv/bin/python -m pytest tests/test_gui.py -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 5: Kontrollgriff — keine hartkodierten `:8097` mehr in Templates** - [x] **Step 5: Kontrollgriff — keine hartkodierten `:8097` mehr in Templates**
Run: `grep -rn ":8097" finance/app/templates` Run: `grep -rn ":8097" finance/app/templates`
Expected: keine Treffer. Expected: keine Treffer.
- [ ] **Step 6: Volle Suite** - [x] **Step 6: Volle Suite**
Run: `cd finance && .venv/bin/python -m pytest -q` Run: `cd finance && .venv/bin/python -m pytest -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 7: Fable-Gate + Commit** - [x] **Step 7: Fable-Gate + Commit**
```bash ```bash
git add finance/app/routers/gui.py finance/app/templates/base.html \ git add finance/app/routers/gui.py finance/app/templates/base.html \
@@ -381,7 +381,7 @@ git commit -m "feat: Grafana-Links aus FB_GRAFANA_PUBLIC_URL (Sub-Pfad-tauglich)
nur an `127.0.0.1` gebunden ist und ausschließlich der lokale Traefik ihn nur an `127.0.0.1` gebunden ist und ausschließlich der lokale Traefik ihn
erreicht (dokumentiert in docs/reverse-proxy.md, Task 6). erreicht (dokumentiert in docs/reverse-proxy.md, Task 6).
- [ ] **Step 1: Failing Test** — `finance/tests/test_entrypoint.py` anlegen: - [x] **Step 1: Failing Test** — `finance/tests/test_entrypoint.py` anlegen:
```python ```python
from pathlib import Path from pathlib import Path
@@ -395,12 +395,12 @@ def test_entrypoint_enables_proxy_headers():
assert "--forwarded-allow-ips" in text assert "--forwarded-allow-ips" in text
``` ```
- [ ] **Step 2: Fehlschlag prüfen** - [x] **Step 2: Fehlschlag prüfen**
Run: `cd finance && .venv/bin/python -m pytest tests/test_entrypoint.py -q` Run: `cd finance && .venv/bin/python -m pytest tests/test_entrypoint.py -q`
Expected: FAIL (Flags fehlen noch). Expected: FAIL (Flags fehlen noch).
- [ ] **Step 3: Implementieren** — `finance/entrypoint.sh` vollständig: - [x] **Step 3: Implementieren** — `finance/entrypoint.sh` vollständig:
```sh ```sh
#!/bin/sh #!/bin/sh
@@ -416,17 +416,17 @@ exec uvicorn app.main:app --host 0.0.0.0 --port 8000 \
--proxy-headers --forwarded-allow-ips='*' --proxy-headers --forwarded-allow-ips='*'
``` ```
- [ ] **Step 4: Test grün + Shell-Syntaxcheck** - [x] **Step 4: Test grün + Shell-Syntaxcheck**
Run: `cd finance && .venv/bin/python -m pytest tests/test_entrypoint.py -q && sh -n entrypoint.sh && echo OK` Run: `cd finance && .venv/bin/python -m pytest tests/test_entrypoint.py -q && sh -n entrypoint.sh && echo OK`
Expected: PASS + `OK`. Expected: PASS + `OK`.
- [ ] **Step 5: Volle Suite** - [x] **Step 5: Volle Suite**
Run: `cd finance && .venv/bin/python -m pytest -q` Run: `cd finance && .venv/bin/python -m pytest -q`
Expected: PASS. Expected: PASS.
- [ ] **Step 6: Fable-Gate + Commit** - [x] **Step 6: Fable-Gate + Commit**
```bash ```bash
git add finance/entrypoint.sh finance/tests/test_entrypoint.py git add finance/entrypoint.sh finance/tests/test_entrypoint.py
@@ -451,7 +451,7 @@ git commit -m "feat: uvicorn Proxy-Headers fuer Reverse-Proxy-Betrieb"
`bash -n`, isolierten Test der Sub-Pfad-Extraktion und den Live-Redeploy in `bash -n`, isolierten Test der Sub-Pfad-Extraktion und den Live-Redeploy in
Task 7. Task 7.
- [ ] **Step 1: Sub-Pfad-Berechnung einfügen** — in `create_pod_finance.sh` - [x] **Step 1: Sub-Pfad-Berechnung einfügen** — in `create_pod_finance.sh`
VOR dem **API**-`podman run` (`podman run -d --name "$API_CTR_NAME" ...`) VOR dem **API**-`podman run` (`podman run -d --name "$API_CTR_NAME" ...`)
diesen Block einfügen. WICHTIG: nicht erst vor dem Grafana-Container — der diesen Block einfügen. WICHTIG: nicht erst vor dem Grafana-Container — der
API-Container läuft im Skript zuerst und verwendet bereits `${GF_SUBPATH}` API-Container läuft im Skript zuerst und verwendet bereits `${GF_SUBPATH}`
@@ -481,7 +481,7 @@ if [ -n "${FB_GRAFANA_PUBLIC_URL:-}" ]; then
fi fi
``` ```
- [ ] **Step 2: Grafana-Container um die Sub-Pfad-Args ergänzen** — im - [x] **Step 2: Grafana-Container um die Sub-Pfad-Args ergänzen** — im
`podman run -d --name "$GRAFANA_CTR_NAME" ...`-Aufruf eine Zeile `podman run -d --name "$GRAFANA_CTR_NAME" ...`-Aufruf eine Zeile
einfügen (z.B. direkt nach `-e GF_SECURITY_COOKIE_SAMESITE=lax \`): einfügen (z.B. direkt nach `-e GF_SECURITY_COOKIE_SAMESITE=lax \`):
@@ -492,7 +492,7 @@ fi
(Bei leerem Array expandiert das unter bash zu nichts — Lokalbetrieb (Bei leerem Array expandiert das unter bash zu nichts — Lokalbetrieb
unverändert.) unverändert.)
- [ ] **Step 3: API-Container um die drei Env-Durchreichungen ergänzen** — im - [x] **Step 3: API-Container um die drei Env-Durchreichungen ergänzen** — im
`podman run -d --name "$API_CTR_NAME" ...`-Aufruf nach der Zeile `podman run -d --name "$API_CTR_NAME" ...`-Aufruf nach der Zeile
`-e FB_UPLOADS_DIR=/data/uploads \` einfügen: `-e FB_UPLOADS_DIR=/data/uploads \` einfügen:
@@ -506,7 +506,7 @@ unverändert.)
zum bisherigen Config-Default; im Sub-Pfad-Modus `http://localhost:3000/grafana`, zum bisherigen Config-Default; im Sub-Pfad-Modus `http://localhost:3000/grafana`,
damit die GUI-Passwortänderung die Grafana-Admin-API unter dem Präfix trifft.) damit die GUI-Passwortänderung die Grafana-Admin-API unter dem Präfix trifft.)
- [ ] **Step 4: Grafana-Health-Check-URL sub-path-bewusst machen** — die Zeile - [x] **Step 4: Grafana-Health-Check-URL sub-path-bewusst machen** — die Zeile
```bash ```bash
CHECK_URL_GRAFANA="http://$HOST_LOCAL_IP:$GRAFANA_HOST_PORT/api/health" CHECK_URL_GRAFANA="http://$HOST_LOCAL_IP:$GRAFANA_HOST_PORT/api/health"
@@ -518,7 +518,7 @@ ersetzen durch:
CHECK_URL_GRAFANA="http://$HOST_LOCAL_IP:$GRAFANA_HOST_PORT${GF_SUBPATH}/api/health" CHECK_URL_GRAFANA="http://$HOST_LOCAL_IP:$GRAFANA_HOST_PORT${GF_SUBPATH}/api/health"
``` ```
- [ ] **Step 5: Statische Verifikation** - [x] **Step 5: Statische Verifikation**
Run: `bash -n create_pod_finance.sh && echo SYNTAX-OK` Run: `bash -n create_pod_finance.sh && echo SYNTAX-OK`
Expected: `SYNTAX-OK`. Expected: `SYNTAX-OK`.
@@ -538,7 +538,7 @@ http://host.tld/g/ -> [/g]
-> [] -> []
``` ```
- [ ] **Step 6: Fable-Gate (adversariale Skript-Review) + Commit** - [x] **Step 6: Fable-Gate (adversariale Skript-Review) + Commit**
Fable prüft insbesondere: Lokalbetrieb (leere Variable) ist echter No-Op Fable prüft insbesondere: Lokalbetrieb (leere Variable) ist echter No-Op
(leeres Array, leerer Präfix, `FB_GRAFANA_URL=http://localhost:3000`); (leeres Array, leerer Präfix, `FB_GRAFANA_URL=http://localhost:3000`);
@@ -562,7 +562,7 @@ git commit -m "feat: create_pod_finance.sh Grafana-Sub-Pfad + interne URLs sub-p
- Produces: Betriebsdoku für den Proxy-Umzug. Beispiel-Snippets sind - Produces: Betriebsdoku für den Proxy-Umzug. Beispiel-Snippets sind
Vorlagen — die eigentliche Proxy-Konfiguration macht der Nutzer. Vorlagen — die eigentliche Proxy-Konfiguration macht der Nutzer.
- [ ] **Step 1: `docs/reverse-proxy.md` anlegen** mit folgendem Inhalt: - [x] **Step 1: `docs/reverse-proxy.md` anlegen** mit folgendem Inhalt:
````markdown ````markdown
# Reverse-Proxy-Betrieb (Ausbaustufe 10) # Reverse-Proxy-Betrieb (Ausbaustufe 10)
@@ -658,7 +658,7 @@ Forwarded-Proto https` ist wichtig, damit uvicorn (mit `--proxy-headers`) das
entfernen (oder leeren) und `./create_pod_finance.sh` erneut ausführen. entfernen (oder leeren) und `./create_pod_finance.sh` erneut ausführen.
```` ````
- [ ] **Step 2: Verweis in `docs/ARCHITEKTUR.md`** — im Deployment-Abschnitt - [x] **Step 2: Verweis in `docs/ARCHITEKTUR.md`** — im Deployment-Abschnitt
(Abschnitt 3) einen Hinweis auf die neue Doku ergänzen, z.B.: (Abschnitt 3) einen Hinweis auf die neue Doku ergänzen, z.B.:
```markdown ```markdown
@@ -668,10 +668,10 @@ entfernen (oder leeren) und `./create_pod_finance.sh` erneut ausführen.
> Direktbetrieb unverändert. > Direktbetrieb unverändert.
``` ```
- [ ] **Step 3: `CLAUDE.md`** — im Abschnitt zur `.env` die zwei neuen - [x] **Step 3: `CLAUDE.md`** — im Abschnitt zur `.env` die zwei neuen
Variablen kurz erwähnen (ein Satz), inkl. Verweis auf `docs/reverse-proxy.md`. Variablen kurz erwähnen (ein Satz), inkl. Verweis auf `docs/reverse-proxy.md`.
- [ ] **Step 4: Fable-Gate (Faktencheck gegen Skript/Config/Templates) + Commit** - [x] **Step 4: Fable-Gate (Faktencheck gegen Skript/Config/Templates) + Commit**
Fable prüft: Snippets stimmen mit dem tatsächlichen Verhalten aus Task 15 Fable prüft: Snippets stimmen mit dem tatsächlichen Verhalten aus Task 15
überein (Ports 8096/8097, kein Prefix-Stripping, `serve_from_sub_path`, überein (Ports 8096/8097, kein Prefix-Stripping, `serve_from_sub_path`,
@@ -696,26 +696,26 @@ git commit -m "docs: Reverse-Proxy-Snippets (Traefik/Apache) + .env-Schalter"
- Live-Verifikation, dass der **Lokalbetrieb** (leere Proxy-Variablen) mit - Live-Verifikation, dass der **Lokalbetrieb** (leere Proxy-Variablen) mit
v0.10.0 unverändert funktioniert. v0.10.0 unverändert funktioniert.
- [ ] **Step 1: Version hochzählen** - [x] **Step 1: Version hochzählen**
`finance/VERSION`: `finance/VERSION`:
``` ```
0.10.0 0.10.0
``` ```
- [ ] **Step 2: Volle Suite** - [x] **Step 2: Volle Suite**
Run: `cd finance && .venv/bin/python -m pytest -q` Run: `cd finance && .venv/bin/python -m pytest -q`
Expected: PASS (> 199). Expected: PASS (> 199).
- [ ] **Step 3: Redeploy (Lokalbetrieb, `.env` OHNE Proxy-Variablen)** - [x] **Step 3: Redeploy (Lokalbetrieb, `.env` OHNE Proxy-Variablen)**
Run: `./create_pod_finance.sh` Run: `./create_pod_finance.sh`
Expected: Läuft durch bis „API is reachable ... (200)" und „Grafana is Expected: Läuft durch bis „API is reachable ... (200)" und „Grafana is
reachable ... (200)"; Service enabled+active. Der Health-Check spricht ohne reachable ... (200)"; Service enabled+active. Der Health-Check spricht ohne
Präfix `:8097/api/health` (weil `FB_GRAFANA_PUBLIC_URL` leer). Präfix `:8097/api/health` (weil `FB_GRAFANA_PUBLIC_URL` leer).
- [ ] **Step 4: Live-Check Lokalbetrieb unverändert** - [x] **Step 4: Live-Check Lokalbetrieb unverändert**
```bash ```bash
# Login → 303 + Cookie (Cookie OHNE Secure, da FB_SESSION_COOKIE_SECURE leer) # Login → 303 + Cookie (Cookie OHNE Secure, da FB_SESSION_COOKIE_SECURE leer)
@@ -737,12 +737,12 @@ Expected: Login `303` + `set-cookie: fb_session=...` **ohne** `Secure`;
`/api/version` → `{"version":"0.10.0"}`; Grafana-Link enthält `:8097/d/finanzen` `/api/version` → `{"version":"0.10.0"}`; Grafana-Link enthält `:8097/d/finanzen`
und **kein** `/grafana`; alle Seiten `200`; `grafana:200`. und **kein** `/grafana`; alle Seiten `200`; `grafana:200`.
- [ ] **Step 5: Plan-Haken + Ledger** — alle Task-Checkboxen dieses Plans - [x] **Step 5: Plan-Haken + Ledger** — alle Task-Checkboxen dieses Plans
setzen; in `.superpowers/sdd/progress.md` einen A10-Abschluss-Eintrag setzen; in `.superpowers/sdd/progress.md` einen A10-Abschluss-Eintrag
ergänzen (Commit-Range, Fable-Befunde je Task, Live-Check-Ergebnis; **keine** ergänzen (Commit-Range, Fable-Befunde je Task, Live-Check-Ergebnis; **keine**
echten Kontodaten/Domain-Secrets). echten Kontodaten/Domain-Secrets).
- [ ] **Step 6: Fable-Release-Gate + Commit** - [x] **Step 6: Fable-Release-Gate + Commit**
Fable verifiziert: Suite grün, Version live `0.10.0`, Lokalbetrieb im Fable verifiziert: Suite grün, Version live `0.10.0`, Lokalbetrieb im
Live-Check unverändert (Cookie ohne Secure, Grafana-Fallback `:8097`), Live-Check unverändert (Cookie ohne Secure, Grafana-Fallback `:8097`),

View File

@@ -1 +1 @@
0.9.0 0.10.0