Logo in der Statuszeile, Bau an einer Stelle, Startprobe fuer die EXE
Logo Das Programmlogo steht jetzt klein neben "powered by ravokk" - dort sitzt die Kennung des Werkzeugs, nicht die des Kunden. Eine eigene Kopfzeile hat dieses Programm nicht; oben sind die Reiter. bauen.py Der Weg von der Quelle zur Setup-Datei war eine Folge von Befehlen, die jemand von Hand eintippen musste. Wer einen Schritt vergisst, liefert eine Setup-Datei mit einer alten EXE aus - und sieht es ihr nicht an. Genau das ist heute passiert. Jetzt ist es ein Befehl, die Nummer kommt aus version.py, und am Ende stehen die SHA256-Summen fuer den Release-Text. pruef_start_exe.py Prueft, was kein anderer Pruefstand prueft: ob die GEBAUTE EXE ein sichtbares Fenster zeigt. Alle uebrigen fahren das Programm aus der Quelle, dort trat der Fehler nie auf. "Der Prozess laeuft" beweist dabei nichts - er lief ja, waehrend nichts zu sehen war. Aufgeraeumt "Rechnung Campinghof.exe" ist weg; gebaute Dateien gehoeren ins Release, nicht ins Repo. Die .spec-Dateien dagegen schon: ohne sie kann niemand die EXE nachbauen, die der Updater ausliefert. Fassung 1.7.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
This commit is contained in:
parent
fec0e8449c
commit
73042b771c
20 changed files with 1445 additions and 14 deletions
109
bauen.py
Normal file
109
bauen.py
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Der ganze Weg von der Quelle zur Setup-Datei - an EINER Stelle.
|
||||
|
||||
WARUM DAS HIER STEHT
|
||||
--------------------
|
||||
Bis hierher lag der Bau in einer Folge von Befehlen, die jemand von Hand
|
||||
eintippen musste: PyInstaller fuers Programm, PyInstaller fuer den Updater,
|
||||
umbenennen, Inno Setup mit den richtigen Schaltern. Wer einen Schritt
|
||||
vergisst, liefert eine Setup-Datei aus, die eine alte EXE enthaelt - und
|
||||
niemand sieht es ihr an. Genau das ist am 06.09.2026 passiert (der Installer
|
||||
schrieb ueber eine laufende, gesperrte EXE hinweg und meldete trotzdem Erfolg).
|
||||
|
||||
Ab jetzt ist der Bau ein Befehl:
|
||||
|
||||
python bauen.py
|
||||
|
||||
WAS ER TUT
|
||||
----------
|
||||
1. Die Nummer kommt aus version.py - nicht von der Kommandozeile. So kann sie
|
||||
nicht auseinanderlaufen mit dem, was das Programm ueber sich selbst sagt.
|
||||
2. Programm und Updater werden gebaut.
|
||||
3. Die Programm-EXE bekommt die Nummer in den Namen. Der Updater erkennt daran,
|
||||
welche Fassungen installiert sind, und legt neue DANEBEN statt darueber -
|
||||
deshalb kann er auch dann austauschen, wenn Windows die alte Datei sperrt.
|
||||
4. Inno Setup baut die Setup-Datei.
|
||||
5. Zum Schluss stehen die SHA256-Summen da. Die gehoeren in den Release-Text:
|
||||
nur damit laesst sich spaeter nachweisen, dass die heruntergeladene Datei
|
||||
genau die gebaute ist.
|
||||
"""
|
||||
import hashlib
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
HIER = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, HIER)
|
||||
|
||||
import version # noqa: E402
|
||||
|
||||
# Inno Setup liegt bei ihm unter LOCALAPPDATA (Installation ohne Adminrechte).
|
||||
ISCC_ORTE = [
|
||||
os.path.join(os.environ.get("LOCALAPPDATA", ""), "Programs", "Inno Setup 6", "ISCC.exe"),
|
||||
r"C:\Program Files (x86)\Inno Setup 6\ISCC.exe",
|
||||
r"C:\Program Files\Inno Setup 6\ISCC.exe",
|
||||
]
|
||||
|
||||
|
||||
def summe(pfad):
|
||||
h = hashlib.sha256()
|
||||
with open(pfad, "rb") as f:
|
||||
for brocken in iter(lambda: f.read(1 << 20), b""):
|
||||
h.update(brocken)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def lauf(befehl, was):
|
||||
print(f"--- {was}")
|
||||
e = subprocess.run(befehl, cwd=HIER, capture_output=True, text=True,
|
||||
encoding="utf-8", errors="replace")
|
||||
if e.returncode != 0:
|
||||
print(e.stdout[-3000:])
|
||||
print(e.stderr[-3000:])
|
||||
raise SystemExit(f"{was} fehlgeschlagen (Rueckgabe {e.returncode})")
|
||||
|
||||
|
||||
def iscc():
|
||||
for p in ISCC_ORTE:
|
||||
if p and os.path.exists(p):
|
||||
return p
|
||||
raise SystemExit("ISCC.exe nicht gefunden - Inno Setup 6 fehlt")
|
||||
|
||||
|
||||
def main():
|
||||
stamm, endung = os.path.splitext(version.EXE_NAME)
|
||||
mit_nummer = f"{stamm}-{version.VERSION}{endung}"
|
||||
|
||||
# Die Bauordner werden geleert. Ein PyInstaller-Lauf ueber einen alten
|
||||
# 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"):
|
||||
shutil.rmtree(os.path.join(HIER, ordner), ignore_errors=True)
|
||||
|
||||
lauf([sys.executable, "-m", "PyInstaller", "--noconfirm", f"{stamm}.spec"],
|
||||
f"{stamm} {version.VERSION}")
|
||||
lauf([sys.executable, "-m", "PyInstaller", "--noconfirm", "Updater.spec"],
|
||||
"Updater")
|
||||
|
||||
quelle = os.path.join(HIER, "dist", version.EXE_NAME)
|
||||
ziel = os.path.join(HIER, "dist", mit_nummer)
|
||||
os.replace(quelle, ziel)
|
||||
|
||||
lauf([iscc(), f"/DVersion={version.VERSION}", f"/DExeProgramm={mit_nummer}",
|
||||
"Installer.iss"], "Setup")
|
||||
|
||||
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(f" {name:44s} {os.path.getsize(p):>10,d} B {summe(p)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue