fix: Versionskennung an statischen Dateien gegen veraltete Browser-Caches (v0.13.1)

Gecachte style.css ohne Tortenfarben ließ alle Segmente schwarz erscheinen.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-29 17:54:55 +02:00
co-authored by Claude Opus 5.5
parent 23557db0fb
commit 60cd3c4fea
6 changed files with 29 additions and 10 deletions
+4 -2
View File
@@ -414,8 +414,10 @@ markiert und sortiert stattdessen serverseitig über alle Seiten
**`VERSION`-Datei als Single Source.** `finance/VERSION` (ein Einzeiler, z.B.
`0.4.0`) ist die einzige Versionsquelle: `app/version.py::get_version()`
liest sie (Fallback `"0.0.0-dev"`), gespeist in den Jinja2-Footer
(`templates.env.globals["app_version"]` in `routers/gui.py`) und
`GET /api/version` (`main.py`). `Containerfile` kopiert `VERSION` ins Image;
(`templates.env.globals["app_version"]` in `routers/gui.py`), als
Cache-Busting-Parameter an allen statischen Dateien (`/static/style.css?v=…`
usw. in `base.html`/`login.html` — ohne ihn behalten Browser alte CSS/JS
über ein Release hinaus) und `GET /api/version` (`main.py`). `Containerfile` kopiert `VERSION` ins Image;
`create_pod_finance.sh` liest dieselbe Datei für den Image-Tag
(`API_IMAGE="localhost/finance-api:$(cat "$FINANCE_DIR/VERSION")"`). Ein
Release ist somit: `VERSION` hochzählen → Image bauen → Redeploy.
+1 -1
View File
@@ -1 +1 @@
0.13.0
0.13.1
+4 -4
View File
@@ -4,10 +4,10 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}Finanzberatung{% endblock %}</title>
<link rel="stylesheet" href="/static/style.css">
<script src="/static/htmx.min.js"></script>
<script src="/static/sort.js"></script>
<script src="/static/doppelzaehlung.js"></script>
<link rel="stylesheet" href="/static/style.css?v={{ app_version }}">
<script src="/static/htmx.min.js?v={{ app_version }}"></script>
<script src="/static/sort.js?v={{ app_version }}"></script>
<script src="/static/doppelzaehlung.js?v={{ app_version }}"></script>
</head>
<body>
<nav>
+1 -1
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Anmelden – Finanzberatung</title>
<link rel="stylesheet" href="/static/style.css">
<link rel="stylesheet" href="/static/style.css?v={{ app_version }}">
</head>
<body>
<h1>Finanzberatung</h1>
+17 -1
View File
@@ -599,7 +599,8 @@ def test_planungs_refresh_fehler_bricht_seiten_nicht(client, monkeypatch):
def test_base_hat_doppelzaehlungs_dialog(client):
client.post("/login", data={"username": "admin", "password": "geheim"})
html = client.get("/").text
assert '<script src="/static/doppelzaehlung.js"></script>' in html
from app.version import get_version
assert f'<script src="/static/doppelzaehlung.js?v={get_version()}"></script>' in html
assert '<dialog id="doppelzaehlung-dialog">' in html
assert 'id="dz-abbrechen" autofocus' in html
assert ">Trotzdem speichern</button>" in html
@@ -637,3 +638,18 @@ def test_vorschlag_formular_nutzt_json_form_post(client):
from pathlib import Path
src = Path("app/templates/planning.html").read_text(encoding="utf-8")
assert re.search(r'class="inline-form" hx-ext="json-form" hx-post="/api/recurring"', src)
def test_statische_dateien_mit_versionskennung(client):
# Cache-Busting: jede neue VERSION erzwingt frische CSS/JS im Browser,
# sonst zeigen gecachte Altstände z. B. schwarze Tortensegmente.
from app.version import get_version
v = get_version()
login = client.get("/login").text
assert f'href="/static/style.css?v={v}"' in login
assert re.search(r'/static/[^"?]+"', login) is None
client.post("/login", data={"username": "admin", "password": "geheim"})
html = client.get("/").text
for datei in ("style.css", "htmx.min.js", "sort.js", "doppelzaehlung.js"):
assert f'/static/{datei}?v={v}"' in html
assert re.search(r'/static/[^"?]+"', html) is None
+2 -1
View File
@@ -17,7 +17,8 @@ def _follow_ids(html):
def test_base_bindet_sort_js_ein(client):
_login(client)
assert '<script src="/static/sort.js"></script>' in client.get("/").text
from app.version import get_version
assert f'<script src="/static/sort.js?v={get_version()}"></script>' in client.get("/").text
def test_planung_folgezeilen_markiert(client, db):