From ec99042569520fc05b8ae0a8a433fddf3304d74c Mon Sep 17 00:00:00 2001 From: Kerim Atilgan Date: Mon, 2 Nov 2026 21:26:53 +0100 Subject: [PATCH] Windows-Setup (Inno Setup): Installation pro Benutzer, Installer-Test im Build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PyInstaller baut unter Windows zusätzlich die Ordner-Variante dist/Provisionsmanager/ (schnellerer Start); onefile-.exe und .zip bleiben, Linux unverändert - build/installer.iss: ohne Admin nach %LOCALAPPDATA%\Programs\Provisionsmanager, Startmenü, optional Desktop, deutsch, feste AppId, CloseApplications, Benutzerdaten in %APPDATA%\Provisionsmanager95 bleiben bei Deinstallation erhalten - build_windows.sh: Inno Setup 6.7.3 (SHA256 geprüft) unter Wine installieren, Setup bauen, still installieren, Selbsttest der installierten .exe, still deinstallieren und prüfen - Pipeline: Setup als Artefakt und am Release; README-Abschnitt Installation Co-Authored-By: Claude Opus 5.5 --- .gitea/workflows/windows-build.yml | 7 +++- README.md | 14 ++++++- build/build_windows.sh | 60 ++++++++++++++++++++++++------ build/installer.iss | 45 ++++++++++++++++++++++ build/provisionsmanager.spec | 19 +++++++++- build/release_hochladen.sh | 2 +- 6 files changed, 130 insertions(+), 17 deletions(-) create mode 100644 build/installer.iss diff --git a/.gitea/workflows/windows-build.yml b/.gitea/workflows/windows-build.yml index 7cf5c8d..220bf84 100644 --- a/.gitea/workflows/windows-build.yml +++ b/.gitea/workflows/windows-build.yml @@ -1,6 +1,6 @@ # Baut die Windows-Version (.exe, mit Windows-Python unter Wine) und die Linux-Version. # - jeder Push / PR: Tests + Programmdateien als Build-Artefakte -# - Tag "v*" (z. B. v1.0.0): zusätzlich Gitea-Release mit .exe, .zip und Linux-.tar.gz +# - Tag "v*" (z. B. v1.0.0): zusätzlich Gitea-Release mit .exe, .zip, Setup und Linux-.tar.gz name: Build on: @@ -37,6 +37,7 @@ jobs: path: | dist/Provisionsmanager95.exe dist/Provisionsmanager95-*.zip + dist/Provisionsmanager-Setup-*.exe - name: Release erstellen / ergänzen if: startsWith(github.ref, 'refs/tags/v') @@ -44,7 +45,9 @@ jobs: TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITEA_TOKEN }} API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }} TAG: ${{ github.ref_name }} - run: build/release_hochladen.sh dist/Provisionsmanager95.exe dist/Provisionsmanager95-$(tr -d '\r\n' < dist/VERSION).zip + run: | + V=$(tr -d '\r\n' < dist/VERSION) + build/release_hochladen.sh dist/Provisionsmanager95.exe dist/Provisionsmanager95-$V.zip dist/Provisionsmanager-Setup-$V.exe linux: runs-on: ubuntu-latest diff --git a/README.md b/README.md index 24db9e4..01756ba 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,16 @@ von **freenet** (CSV/Excel) und **Vodafone/Euronics** (Excel) ein, ordnet die Po Unterhändlern zu, erlaubt freie Anpassung der Auszahlung und exportiert je Händler eine fertige Excel-Abrechnung. Optik: Windows 95. +## Installation (Windows) + +`Provisionsmanager-Setup-.exe` aus dem Release herunterladen und starten – keine +Admin-Rechte nötig. Das Programm landet in `%LOCALAPPDATA%\Programs\Provisionsmanager` mit +Eintrag im Startmenü (Desktop-Verknüpfung optional) und startet schneller als die einzelne `.exe`. +Ein Update installiert man einfach drüber (ein laufendes Programm wird dafür geschlossen). +Deinstallieren über *Einstellungen ▸ Apps*; Datenbank und Einstellungen in +`%APPDATA%\Provisionsmanager95` bleiben dabei erhalten. Ohne Installation geht weiterhin +`Provisionsmanager95.exe` (bzw. das `.zip`) – einfach starten. + ## Erster Start Beim ersten Start führt ein **Einrichtungs-Assistent** durch Speicherort, Unterhändler @@ -134,7 +144,9 @@ Gitea Actions (`.gitea/workflows/windows-build.yml`) baut die `.exe` im Containe - jeder Push: alle Tests unter Windows-Python (Wine) **inklusive Oberflächen-Tests** auf einem virtuellen Bildschirm, danach `Provisionsmanager95.exe` bauen und mit `Provisionsmanager95.exe --selbsttest` die **fertige .exe** prüfen; erst dann gibt es das Artefakt -- Tag `vX.Y.Z` (passend zu `provtool/__init__.py`): zusätzlich ein Gitea-Release mit `.exe` und `.zip` +- danach das Setup (`build/installer.iss`, Inno Setup unter Wine) bauen, still installieren, + den Selbsttest in der installierten `.exe` laufen lassen und still deinstallieren +- Tag `vX.Y.Z` (passend zu `provtool/__init__.py`): zusätzlich ein Gitea-Release mit `.exe`, `.zip` und Setup Die Oberflächen-Tests (`tests/test_gui.py`) bedienen die echte Oberfläche: Dialoge öffnen, Programm schließen, während Assistent/Rückfragen/Rundgang offen sind, Alt+F4, Beträge ändern usw. diff --git a/build/build_windows.sh b/build/build_windows.sh index 52df544..999b5b3 100755 --- a/build/build_windows.sh +++ b/build/build_windows.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash # Baut die Windows-.exe mit Windows-Python unter Wine (z. B. im Container tobix/pywine:3.12). -# Ergebnis: dist/Provisionsmanager95.exe und dist/Provisionsmanager95-.zip +# Ergebnis: dist/Provisionsmanager95.exe, dist/Provisionsmanager95-.zip und +# dist/Provisionsmanager-Setup-.exe (Inno Setup, Installation pro Benutzer) set -euo pipefail cd "$(dirname "$0")/.." @@ -40,19 +41,24 @@ echo "== PyInstaller ==" $PY -m PyInstaller --noconfirm --clean --distpath dist --workpath build/tmp build/provisionsmanager.spec test -f dist/Provisionsmanager95.exe +test -f dist/Provisionsmanager/Provisionsmanager.exe # Ordner-Variante für das Setup + +selbsttest() { # $1 = zu prüfende .exe + rm -f selbsttest.txt + set +e + timeout 300 wine "$1" --selbsttest selbsttest.txt + CODE=$? + set -e + cat selbsttest.txt 2>/dev/null || echo "(keine Ergebnisdatei – die .exe ist vermutlich gar nicht gestartet)" + if [ "$CODE" -ne 0 ] || ! head -1 selbsttest.txt 2>/dev/null | tr -d '\r' | grep -qx OK; then + echo "Selbsttest von $1 FEHLGESCHLAGEN (Exit-Code $CODE)" + exit 1 + fi + echo "Selbsttest bestanden" +} echo "== Selbsttest der fertigen .exe ==" -rm -f selbsttest.txt -set +e -timeout 300 wine dist/Provisionsmanager95.exe --selbsttest selbsttest.txt -CODE=$? -set -e -cat selbsttest.txt 2>/dev/null || echo "(keine Ergebnisdatei – die .exe ist vermutlich gar nicht gestartet)" -if [ "$CODE" -ne 0 ] || ! head -1 selbsttest.txt 2>/dev/null | tr -d '\r' | grep -qx OK; then - echo "Selbsttest der .exe FEHLGESCHLAGEN (Exit-Code $CODE)" - exit 1 -fi -echo "Selbsttest bestanden" +selbsttest dist/Provisionsmanager95.exe cp README.md dist/LIESMICH.md 2>/dev/null || true ( cd dist && rm -f Provisionsmanager95-*.zip && \ python3 -c "import zipfile,sys; z=zipfile.ZipFile(sys.argv[1],'w',zipfile.ZIP_DEFLATED); [z.write(f) for f in sys.argv[2:]]; z.close()" \ @@ -60,4 +66,34 @@ cp README.md dist/LIESMICH.md 2>/dev/null || true || $PY -c "import zipfile,sys; z=zipfile.ZipFile(sys.argv[1],'w',zipfile.ZIP_DEFLATED); [z.write(f) for f in sys.argv[2:]]; z.close()" \ "Provisionsmanager95-${VERSION}.zip" Provisionsmanager95.exe LIESMICH.md ) echo "$VERSION" > dist/VERSION + +echo "== Setup (Inno Setup) ==" +INNO_VERSION=6.7.3 +INNO_SHA256=9c73c3bae7ed48d44112a0f48e66742c00090bdb5bef71d9d3c056c66e97b732 +INNO_EXE=build/tmp/innosetup-$INNO_VERSION.exe +mkdir -p build/tmp +curl -fsSL -o "$INNO_EXE" "https://github.com/jrsoftware/issrc/releases/download/is-${INNO_VERSION//./_}/innosetup-$INNO_VERSION.exe" +echo "$INNO_SHA256 $INNO_EXE" | sha256sum -c - +timeout 300 wine "$INNO_EXE" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- /CURRENTUSER '/DIR=C:\InnoSetup' +rm -f dist/Provisionsmanager-Setup-*.exe +timeout 300 wine 'C:\InnoSetup\ISCC.exe' /Q "/DAppVersion=$VERSION" 'build\installer.iss' +SETUP="dist/Provisionsmanager-Setup-$VERSION.exe" +test -f "$SETUP" + +echo "== Installer-Test (still installieren, Selbsttest, still deinstallieren) ==" +winordner() { winepath -u "$(wine cmd /c echo "%$1%" | tr -d '\r')"; } # z. B. APPDATA -> /opt/wineprefix/... +DATEN="$(winordner APPDATA)/Provisionsmanager95" +ZIEL="$(winordner LOCALAPPDATA)/Programs/Provisionsmanager" +mkdir -p "$DATEN" +echo "Benutzerdaten" > "$DATEN/installer-test.txt" +timeout 300 wine "$SETUP" /VERYSILENT /SUPPRESSMSGBOXES /CURRENTUSER /NORESTART +test -f "$(winordner APPDATA)/Microsoft/Windows/Start Menu/Programs/Provisions-Manager.lnk" +selbsttest "$ZIEL/Provisionsmanager.exe" +timeout 300 wine "$ZIEL/unins000.exe" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART +# Der Deinstaller arbeitet als Kopie im Temp-Ordner weiter – auf das Ende warten +for _ in $(seq 60); do [ -e "$ZIEL" ] || break; sleep 1; done +if [ -e "$ZIEL" ]; then echo "Deinstallation FEHLGESCHLAGEN – Programmordner noch da:"; ls -la "$ZIEL"; exit 1; fi +if [ ! -f "$DATEN/installer-test.txt" ]; then echo "Deinstallation hat Benutzerdaten gelöscht!"; exit 1; fi +rm "$DATEN/installer-test.txt" +echo "Installer-Test bestanden" ls -la dist diff --git a/build/installer.iss b/build/installer.iss new file mode 100644 index 0000000..e1d4b9d --- /dev/null +++ b/build/installer.iss @@ -0,0 +1,45 @@ +; Inno-Setup-Skript: Installation pro Benutzer (ohne Admin-Rechte) aus dist/Provisionsmanager/ +; Aufruf (siehe build/build_windows.sh): ISCC.exe /DAppVersion=1.2.3 build\installer.iss +; Ergebnis: dist/Provisionsmanager-Setup-.exe +#ifndef AppVersion + #error AppVersion fehlt – ISCC mit /DAppVersion= aufrufen +#endif + +[Setup] +; AppId nie ändern: daran erkennt Windows bei Updates die bestehende Installation +AppId={{79CF85D1-16DC-4FD5-924B-B6C9AFB90309} +AppName=Provisions-Manager +AppVersion={#AppVersion} +AppPublisher=Handy-Insel Miesbach +PrivilegesRequired=lowest +DefaultDirName={localappdata}\Programs\Provisionsmanager +DefaultGroupName=Provisions-Manager +DisableProgramGroupPage=yes +UninstallDisplayIcon={app}\Provisionsmanager.exe +SetupIconFile=..\assets\app.ico +OutputDir=..\dist +OutputBaseFilename=Provisionsmanager-Setup-{#AppVersion} +CloseApplications=yes +WizardStyle=modern +; Benutzerdaten liegen in %APPDATA%\Provisionsmanager95 – das Setup fasst sie nicht an, +; der Deinstaller entfernt nur, was er selbst installiert hat. + +[Languages] +Name: "de"; MessagesFile: "compiler:Languages\German.isl" + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked + +[InstallDelete] +; Update: Bibliotheken der alten Version entfernen, damit keine veralteten Dateien liegen bleiben +Type: filesandordirs; Name: "{app}\_internal" + +[Files] +Source: "..\dist\Provisionsmanager\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs + +[Icons] +Name: "{autoprograms}\Provisions-Manager"; Filename: "{app}\Provisionsmanager.exe" +Name: "{autodesktop}\Provisions-Manager"; Filename: "{app}\Provisionsmanager.exe"; Tasks: desktopicon + +[Run] +Filename: "{app}\Provisionsmanager.exe"; Description: "{cm:LaunchProgram,Provisions-Manager}"; Flags: nowait postinstall skipifsilent diff --git a/build/provisionsmanager.spec b/build/provisionsmanager.spec index 3313f0e..40fd297 100644 --- a/build/provisionsmanager.spec +++ b/build/provisionsmanager.spec @@ -1,4 +1,5 @@ -# PyInstaller-Spezifikation – erzeugt eine einzelne Programmdatei (Windows: .exe, Linux: ELF) +# PyInstaller-Spezifikation – erzeugt eine einzelne Programmdatei (Windows: .exe, Linux: ELF), +# unter Windows zusätzlich den Ordner dist/Provisionsmanager/ (schneller Start, Inhalt des Setups) # Aufruf: pyinstaller --noconfirm build/provisionsmanager.spec import os import sys @@ -31,3 +32,19 @@ exe = EXE( strip=False, runtime_tmpdir=None, ) + +if WIN: + exe_ordner = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name="Provisionsmanager", + icon=os.path.join(ROOT, "assets", "app.ico"), + version=os.path.join(ROOT, "build", "version_info.txt"), + console=False, + upx=False, + debug=False, + strip=False, + ) + COLLECT(exe_ordner, a.binaries, a.datas, name="Provisionsmanager", upx=False, strip=False) diff --git a/build/release_hochladen.sh b/build/release_hochladen.sh index 000ff83..06ca1bb 100755 --- a/build/release_hochladen.sh +++ b/build/release_hochladen.sh @@ -11,7 +11,7 @@ release_id() { curl -sf "$API/releases/tags/$TAG" -H "Authorization: token $TOKE id=$(release_id || true) if [ -z "$id" ]; then curl -sf -X POST "$API/releases" -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \ - -d "{\"tag_name\":\"$TAG\",\"name\":\"Provisions-Manager 95 $TAG\",\"body\":\"Windows: .exe herunterladen und starten. Linux: tar.gz entpacken, Provisionsmanager95 starten (ab Debian 12 / Ubuntu 24.04).\"}" \ + -d "{\"tag_name\":\"$TAG\",\"name\":\"Provisions-Manager 95 $TAG\",\"body\":\"Windows: Setup installieren (oder die .exe direkt starten). Linux: tar.gz entpacken, Provisionsmanager95 starten (ab Debian 12 / Ubuntu 24.04).\"}" \ >/dev/null || sleep 5 # Gleichzeitig angelegt? Dann gibt es das Release jetzt. id=$(release_id) fi