Die Runde vom 07.09.2026 hat neben den zwei echten Fehlern eine Reihe Wiederholungen und stumme Stellen gemeldet. Abgearbeitet in Etappen, jede fuer sich pruefbar - nach jeder liefen beide Pruefketten gruen. * Menueleiste: der Wechsel von einem Titel zum anderen brauchte ZWEI Klicks. Der grab_set der offenen Liste schluckt den ersten, die <Button-1>-Bindung des Titels feuert gar nicht. Das native tk.Menu, das diese Leiste ersetzt hat, wechselte schon beim blossen Ueberfahren - zwei Klicks waren also eine Verschlechterung gegenueber dem, was vorher da war. Die Klappliste wird ausserdem jetzt wie jede andere Position ueber theme.auf_bildschirm() begrenzt; am unteren Fensterrand lief sie vorher hinaus. Neu: pruef_menueleiste.py misst beides ueber echte Klickereignisse. * %LOCALAPPDATA%/ravokk wurde an drei Stellen einzeln ausgerechnet. Jetzt fragen alle gemeinsam.standard_ordner(). Laufen die je auseinander, fuehren die beiden Programme zwei Nummernbuecher - und das faellt erst auf, wenn eine Rechnungsnummer zum zweiten Mal vergeben ist (§ 14 Abs. 4 Nr. 4 UStG). * pruefe() stand 35-mal im Quelltext, in sechs Fassungen -> pruefhelfer.py. Die zwei Ausreisser sind mitgezogen: pruef_storno_verrechnet.py zaehlte in einer Zahl und konnte am Ende nicht sagen, WAS fehlschlug, pruef_bilder.py im Rechnungstool schrieb ein eigenes Format. * dlg_darstellung() und die drei Handgriffe des Umschaltens standen in beiden app.py fast wortgleich -> ThemeMixin in theme.py. Beide trugen inzwischen denselben langen Kommentar zu demselben Fehler; das war der Beweis, dass es eine Sache ist. Programmspezifisch bleiben der Hinweistext im Dialog (R31) und die Frage, ob die Einstellungen ausdruecklich gespeichert werden muessen. * pruef_gemeinsam_automatisch.py sagte im Text "genau ein Buch" und prueft ">= 1" mit einem any() - eine Pruefung, die nicht rot werden kann. Jetzt == 1, und der Dateiname wird mitgeprueft. Dazu neu: die neun Dateien, die im Kopf zusagen, sie laegen in beiden Programmen gleich, werden byteweise verglichen. Bei hinweis.py stimmte die Zusage seit dem 06.09. nicht mehr - gleicher Inhalt, CRLF gegen LF. * .gitattributes, damit die Zeilenenden nicht von der Maschine abhaengen. Ohne das meldet genau diese Pruefung nach einem frischen Checkout einen Unterschied, den es im Repository gar nicht gibt. * Die stummen "except OSError" in api.py, updater.py und einzelinstanz.py sagen jetzt, warum sie schweigen duerfen. Die stummen tk.TclError bleiben: dort wird ein Widget angefasst, das gerade zerstoert wurde, und eine Meldung waere Rauschen. * Tote Parameter und Variablen aus dem Umbau des Vortags: symbol_setzen(dunkel=), kachel(grund=, radius=), mit_trennlinie, sechs x/y-Berechnungen, die von theme.mittig() sofort ueberschrieben wurden, vier lokale "import ctypes". 39 Pruefstaende, 0 rot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
98 lines
4.3 KiB
Python
98 lines
4.3 KiB
Python
# -*- 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 `<exe>.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 `<exe>.sha256` an.
|
|
4. Die Summe darin ist die der angehaengten Datei - und die stimmt mit dem
|
|
ueberein, was hier zuletzt gebaut wurde (release/<exe>).
|
|
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
|
|
from pruefhelfer import fehler, pruefe
|
|
|
|
|
|
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)
|
|
|
|
roh = dieses.get("assets") or []
|
|
anhaenge = {a.get("name"): a for a in roh}
|
|
# Doppelte Namen: Forgejo laesst zwei Anhaenge mit demselben Namen zu. Beim
|
|
# Ersetzen einer Datei blieb die alte einmal stehen (07.09.2026), und welche
|
|
# von beiden der Updater dann laedt, ist Glueckssache - unter Umstaenden die
|
|
# alte, mit der falschen Pruefsumme daneben.
|
|
doppelt = sorted({a["name"] for a in roh
|
|
if sum(1 for b in roh if b["name"] == a["name"]) > 1})
|
|
pruefe(not doppelt, f"kein Anhang haengt doppelt am Release ({doppelt})")
|
|
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")
|