Neustart-Erkennung: Zeilenenden zaehlen nicht als Aenderung

Am echten Update-Weg gemessen: die installierte Fassung lag mit CRLF auf der
Platte, das Gitea-Archiv liefert LF. Drei .py-Dateien galten dadurch als
geaendert, obwohl sie nach Entfernen der CR byteweise identisch waren - die
Erkennung verlangte einen Neustart fuer einen rein kosmetischen Unterschied.

Textdateien werden jetzt vor dem Hashen auf LF normalisiert. Binaerdateien
(Schriften, Bilder, das OTA-Zip) bleiben roh, dort waere 0x0D0A echter Inhalt.
Drei neue Testfaelle: reiner Zeilenenden-Unterschied, echte Aenderung trotz
verschiedener Zeilenenden, Binaerdatei. 26 Tests der Suite gruen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-05 13:51:02 +02:00
parent ecea8a0c22
commit 7c96bc8304
3 changed files with 61 additions and 6 deletions
@@ -217,6 +217,37 @@ def version_von_platte() -> str | None:
return None
# Endungen, bei denen Zeilenenden nichts bedeuten.
#
# Am 05.09.2026 am echten Update-Weg gemessen: die installierte Fassung lag mit
# CRLF auf der Platte (Windows-Arbeitsstand), das Gitea-Archiv liefert LF -
# geokodierung.py, screening.py und veroeffentlichung.py galten dadurch als
# geaendert, obwohl sie nach Entfernen der CR byteweise identisch waren. Die
# Erkennung meldete "Neustart noetig" fuer einen rein kosmetischen Unterschied.
#
# Binaerdateien (Schriften, Bilder, das OTA-Zip) bleiben bewusst aussen vor:
# dort waere 0x0D0A echter Inhalt, und ein normalisierter Vergleich koennte
# einen echten Unterschied verschlucken.
TEXTENDUNGEN = (".py", ".json", ".yaml", ".yml", ".md", ".txt", ".js", ".css", ".html", ".svg")
def _dateihash(pfad: str) -> str:
"""sha256 einer Datei - bei Textdateien ueber auf LF normalisierten Inhalt."""
hasher = hashlib.sha256()
if pfad.lower().endswith(TEXTENDUNGEN):
try:
with open(pfad, "rb") as datei:
roh = datei.read()
hasher.update(roh.replace(b"\r\n", b"\n").replace(b"\r", b"\n"))
return hasher.hexdigest()
except OSError:
raise
with open(pfad, "rb") as datei:
for block in iter(lambda: datei.read(65536), b""):
hasher.update(block)
return hasher.hexdigest()
def _dateihashes(ordner: str) -> dict[str, str]:
"""Alle Dateien unter `ordner` als {relativer Pfad: sha256}.
@@ -228,11 +259,7 @@ def _dateihashes(ordner: str) -> dict[str, str]:
for name in dateien:
pfad = os.path.join(wurzel, name)
rel = os.path.relpath(pfad, ordner).replace(os.sep, "/")
hasher = hashlib.sha256()
with open(pfad, "rb") as datei:
for block in iter(lambda: datei.read(65536), b""):
hasher.update(block)
aus[rel] = hasher.hexdigest()
aus[rel] = _dateihash(pfad)
return aus
@@ -1,7 +1,7 @@
{
"domain": "audi_dashboard",
"name": "Audi Dashboard",
"version": "2026.9.5.24",
"version": "2026.9.5.25",
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
"codeowners": [