diff --git a/.gitignore b/.gitignore index 034417a..21e33f4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ setup/ # die EXE nachbauen, die der Updater ausliefert. !Steuerrechnungstool.spec !Updater.spec +release/ diff --git a/bauen.py b/bauen.py index 2220498..5ef69f5 100644 --- a/bauen.py +++ b/bauen.py @@ -79,7 +79,7 @@ def main(): # Bauordner uebernimmt sonst Reste, die es in der Quelle nicht mehr gibt - # und in setup/ laege noch die Datei der VORIGEN Nummer herum. Beim # Ausliefern greift man dann leicht zur falschen. - for ordner in ("build", "dist", "setup"): + for ordner in ("build", "dist", "setup", "release"): shutil.rmtree(os.path.join(HIER, ordner), ignore_errors=True) lauf([sys.executable, "-m", "PyInstaller", "--noconfirm", f"{stamm}.spec"], @@ -94,14 +94,33 @@ def main(): lauf([iscc(), f"/DVersion={version.VERSION}", f"/DExeProgramm={mit_nummer}", "Installer.iss"], "Setup") + # ---- release/ : genau das, was ins Release gehoert --------------------- + # Der Anhang, den der Updater sucht, heisst OHNE Nummer - so steht es in + # version.EXE_NAME, und danach sucht passendes_release(). Haengt man ihn + # mit Nummer an, findet der Updater nie ein Update und meldet stumm + # "aktuell". Die Nummer kommt erst beim Ablegen dazu (updater.dateiname). + # + # Daneben die Pruefsumme als eigene Datei: ohne sie prueft der Updater nur + # die GROESSE. Eine Datei, die unterwegs verfaelscht wurde, hat aber genau + # die richtige Groesse. + freigabe = os.path.join(HIER, "release") + os.makedirs(freigabe, exist_ok=True) + shutil.copy2(ziel, os.path.join(freigabe, version.EXE_NAME)) + with open(os.path.join(freigabe, version.EXE_NAME + ".sha256"), + "w", encoding="ascii") as f: + f.write(summe(ziel) + chr(10)) + shutil.copy2(os.path.join(HIER, "dist", "Updater.exe"), + os.path.join(freigabe, "Updater.exe")) + for name in os.listdir(os.path.join(HIER, "setup")): + shutil.copy2(os.path.join(HIER, "setup", name), + os.path.join(freigabe, name)) + print() print(f"FERTIG | {stamm} {version.VERSION}") - for name in sorted(os.listdir(os.path.join(HIER, "dist"))): - p = os.path.join(HIER, "dist", name) - print(f" {name:44s} {os.path.getsize(p):>10,d} B {summe(p)}") - setup = os.path.join(HIER, "setup") - for name in sorted(os.listdir(setup)) if os.path.isdir(setup) else []: - p = os.path.join(setup, name) + print() + print(" release/ - genau diese Dateien gehoeren ans Release:") + for name in sorted(os.listdir(freigabe)): + p = os.path.join(freigabe, name) print(f" {name:44s} {os.path.getsize(p):>10,d} B {summe(p)}") diff --git a/pruef_release.py b/pruef_release.py new file mode 100644 index 0000000..0616fa8 --- /dev/null +++ b/pruef_release.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +"""Pruefstand: passt das VEROEFFENTLICHTE Release zu dem, was der Updater sucht? + +DER FALL, DER DAZU GEFUEHRT HAT (06.09.2026) +-------------------------------------------- +Das Release war angelegt, die EXE hing dran - und der Updater haette trotzdem +nie etwas gefunden. Der Anhang hiess `Rechnungstool-1.7.0.exe`; gesucht wird +aber `Rechnungstool.exe` (so steht es in version.EXE_NAME, und danach sucht +passendes_release). Die Nummer kommt erst beim ABLEGEN dazu. + +Das Tueckische daran: nichts schlaegt fehl. Der Updater meldet freundlich +"Aktuell. Kein Update noetig." und startet die alte Fassung. Ein Update, das +stumm nie ankommt, ist schlimmer als eines, das laut scheitert. + +Zweiter Fund derselben Runde: die Pruefsummen standen nur im Release-TEXT. Der +Updater liest sie aus einer angehaengten Datei `.sha256`. Ohne die prueft +er nur die Groesse - und eine unterwegs verfaelschte Datei hat genau die +richtige Groesse. + +WAS HIER GEPRUEFT WIRD +---------------------- +Gegen das echte Repository, nicht gegen einen Nachbau: + + 1. Zu der Nummer in version.py gibt es ein Release. + 2. Es haengt eine Datei mit EXAKT dem Namen aus version.EXE_NAME an. + 3. Es haengt die Pruefsumme `.sha256` an. + 4. Die Summe darin ist die der angehaengten Datei - und die stimmt mit dem + ueberein, was hier zuletzt gebaut wurde (release/). + 5. passendes_release() findet dieses Release auch wirklich. + +Ohne Netz wird uebersprungen statt rot gemeldet: ein fehlender Anschluss ist +kein Fehler des Programms. +""" +import os +import sys + +HIER = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, HIER) + +import updater # noqa: E402 +import version # noqa: E402 + +fehler = [] + + +def pruefe(bedingung, text): + zeile = (" OK | " if bedingung else "FEHLER | ") + text + kod = sys.stdout.encoding or "utf-8" + print(zeile.encode(kod, "replace").decode(kod)) + if not bedingung: + fehler.append(text) + + +try: + releases = updater.releases_holen(version.REPOSITORY) +except Exception as e: # noqa: BLE001 - ohne Netz ist hier nichts zu pruefen + print(f"UEBERSPRUNGEN | Repository nicht erreichbar ({e})") + sys.exit(0) + +marke = "v" + version.VERSION +dieses = next((r for r in releases if (r.get("tag_name") or "") == marke), None) +pruefe(dieses is not None, f"zu Fassung {version.VERSION} gibt es ein Release ({marke})") +if dieses is None: + print("ROT | ohne Release ist der Rest nicht pruefbar") + sys.exit(1) + +anhaenge = {a.get("name"): a for a in dieses.get("assets") or []} +print(f" (Anhaenge: {', '.join(sorted(anhaenge)) or 'keine'})") + +pruefe(version.EXE_NAME in anhaenge, + f"der Anhang heisst genau {version.EXE_NAME} - danach sucht der Updater") +pruefe(version.EXE_NAME + ".sha256" in anhaenge, + f"die Pruefsumme haengt als {version.EXE_NAME}.sha256 dran") + +# ---- Die Summe muss zur Datei passen, und zwar zu der gebauten ------------ +gebaut = os.path.join(HIER, "release", version.EXE_NAME) +url_summe = updater.summe_aus_release(dieses, version.EXE_NAME) +if url_summe: + veroeffentlicht = updater.text_holen(url_summe).strip().split()[0] + if os.path.exists(gebaut): + pruefe(veroeffentlicht == updater.sha256(gebaut), + "die veroeffentlichte Summe ist die der zuletzt gebauten Datei") + else: + print(f" (release/{version.EXE_NAME} fehlt - Summe nicht " + f"gegenpruefbar, vorher: python bauen.py)") + pruefe(len(veroeffentlicht) == 64 and all(c in "0123456789abcdef" for c in veroeffentlicht), + f"und sie sieht aus wie eine SHA256 ({veroeffentlicht[:16]}...)") + +# ---- Und findet der Updater das Release ueberhaupt? ----------------------- +treffer = updater.passendes_release(releases, version.EXE_NAME) +pruefe(treffer is not None, "passendes_release findet ueberhaupt ein Release") +if treffer: + pruefe((treffer[0].get("tag_name") or "") == marke, + f"und zwar dieses hier ({treffer[0].get('tag_name')})") + +if fehler: + print(f"ROT | {len(fehler)} Punkt(e) nicht erfuellt") + sys.exit(1) +print("GRUEN | Das Release ist so, wie der Updater es erwartet")