- 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 <noreply@anthropic.com>
100 lines
4.8 KiB
Bash
Executable File
100 lines
4.8 KiB
Bash
Executable File
#!/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, dist/Provisionsmanager95-<version>.zip und
|
||
# dist/Provisionsmanager-Setup-<version>.exe (Inno Setup, Installation pro Benutzer)
|
||
set -euo pipefail
|
||
cd "$(dirname "$0")/.."
|
||
|
||
export WINEDEBUG=-all
|
||
export LANG=C.UTF-8 LC_ALL=C.UTF-8 # sonst kann Wine keine Umlaute in Dateinamen abbilden
|
||
export WINEPREFIX="${WINEPREFIX:-/opt/wineprefix}"
|
||
PY="wine python"
|
||
|
||
# Ein virtueller Bildschirm für den GANZEN Build – vor dem ersten Wine-Aufruf starten!
|
||
# Wine bindet seinen Hintergrundprozess (wineserver/Desktop) an das DISPLAY des ersten Aufrufs.
|
||
# Läuft z. B. „pip install“ ohne Bildschirm und die GUI-Tests später mit, scheitert Tk unter
|
||
# Wine mit „Failed to create the menu window“.
|
||
wineserver -k 2>/dev/null || true
|
||
ANZEIGE=":$((90 + RANDOM % 9))"
|
||
Xvfb "$ANZEIGE" -screen 0 1280x800x24 -nolisten tcp >/dev/null 2>&1 &
|
||
XVFB_PID=$!
|
||
trap 'wineserver -k 2>/dev/null || true; kill $XVFB_PID 2>/dev/null || true' EXIT
|
||
export DISPLAY="$ANZEIGE"
|
||
sleep 1
|
||
|
||
$PY --version
|
||
$PY -m pip install --disable-pip-version-check -q -r requirements-dev.txt
|
||
|
||
echo "== Tests inkl. Oberfläche (Windows-Python unter Wine) =="
|
||
# PROVTOOL_GUI_TESTS=1: fehlt der Bildschirm, ist das ein Fehler (nicht „übersprungen“)
|
||
# -v: im Log ist live sichtbar, welcher Test läuft; timeout: Obergrenze für alle Tests zusammen
|
||
PROVTOOL_GUI_TESTS=1 timeout 900 $PY -m pytest -v -p no:cacheprovider
|
||
|
||
echo "== Maustest (echte Maus: Fenster ziehen, Rand ziehen, maximiert ziehen) =="
|
||
PY="$PY" timeout 300 bash build/maustest.sh
|
||
|
||
echo "== Version =="
|
||
VERSION=$($PY build/version_info.py | tr -d '\r')
|
||
echo "Version $VERSION"
|
||
|
||
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 =="
|
||
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()" \
|
||
"Provisionsmanager95-${VERSION}.zip" Provisionsmanager95.exe LIESMICH.md 2>/dev/null \
|
||
|| $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
|