Make build_and_deploy.ps1 self-installing and fail loudly
pip install of pyinstaller/pystray/pillow was a separate manual step the README only mentioned in prose, so the script silently died on missing packages -- especially bad on Explorer double-click, where the window closes before any error is visible. Consolidate all dependencies into requirements.txt (also used by README's plain "pip install -r" flow), have the script install it itself, wrap the whole build in try/catch with exit-code checks after every native call, and pause on both success and failure unless -NoPause is passed. Also move the build output from %LOCALAPPDATA% into dist/ next to the script, so it's easy to find and matches the already-gitignored dist/ entry.
This commit is contained in:
parent
540ce5b1eb
commit
13c3745479
3 changed files with 96 additions and 31 deletions
24
README.md
24
README.md
|
|
@ -37,15 +37,18 @@ Windows-Maschine hartkodiert (`versapad_data.CONFIG_PATHS`,
|
||||||
## Voraussetzungen
|
## Voraussetzungen
|
||||||
|
|
||||||
- Python 3.11 oder neuer
|
- Python 3.11 oder neuer
|
||||||
- Pakete: `pyserial` (Board-Kommunikation), `pystray` + `pillow`
|
- Alle Pakete stehen in `requirements.txt` (`pyserial` fürs Board,
|
||||||
(Tray-Icon/Desktop-App), optional `mcp` (nur für den MCP-Server)
|
`pystray` + `pillow` fürs Tray-Icon/Desktop-Fenster, `mcp` für den
|
||||||
|
MCP-Server, `pyinstaller` fürs `.exe`-Bauen):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install pyserial pystray pillow mcp
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
Für den Browser-Modus (`server.py`) reicht die Python-Standardbibliothek —
|
Für den Browser-Modus (`server.py`) reicht die Python-Standardbibliothek —
|
||||||
keine zusätzlichen Pakete nötig.
|
keine zusätzlichen Pakete nötig. `build_and_deploy.ps1` installiert
|
||||||
|
`requirements.txt` beim Bauen automatisch selbst — ein manuelles
|
||||||
|
`pip install` vorher ist dafür nicht nötig.
|
||||||
|
|
||||||
## Starten
|
## Starten
|
||||||
|
|
||||||
|
|
@ -65,8 +68,15 @@ Maschine/Python-Version unterschiedlich). Selbst bauen:
|
||||||
.\build_and_deploy.ps1
|
.\build_and_deploy.ps1
|
||||||
```
|
```
|
||||||
|
|
||||||
Das Skript baut mit PyInstaller (`--onedir --windowed`, eigenes Icon) und
|
Das Skript installiert/aktualisiert selbst alle nötigen Pakete aus
|
||||||
kopiert das Ergebnis nach `%LOCALAPPDATA%\VersaPadViewer\VersaPadViewer.exe`.
|
`requirements.txt` (kein manuelles `pip install` vorher nötig), baut dann
|
||||||
|
mit PyInstaller (`--onedir --windowed`, eigenes Icon) und kopiert das
|
||||||
|
Ergebnis nach `dist\VersaPadViewer\VersaPadViewer.exe` im Projektordner.
|
||||||
|
Bricht ein Schritt ab (fehlendes Python, PyInstaller-Fehler, ...), zeigt das
|
||||||
|
Skript eine klare Fehlermeldung und wartet auf einen Tastendruck, statt sich
|
||||||
|
bei Doppelklick im Explorer kommentarlos zu schließen (`-NoPause`
|
||||||
|
unterdrückt das für automatisierte Aufrufe/CI).
|
||||||
|
|
||||||
**Wichtig:** Sowohl Bauen als auch Ausführen müssen auf einem lokalen
|
**Wichtig:** Sowohl Bauen als auch Ausführen müssen auf einem lokalen
|
||||||
Laufwerk passieren — von einem Netzlaufwerk (SMB-Share) aus scheitert
|
Laufwerk passieren — von einem Netzlaufwerk (SMB-Share) aus scheitert
|
||||||
PyInstaller beim Bauen (Pfadlängen-Problem mit Tcl/Tk-Zeitzonendaten) und
|
PyInstaller beim Bauen (Pfadlängen-Problem mit Tcl/Tk-Zeitzonendaten) und
|
||||||
|
|
@ -75,8 +85,6 @@ das Nachladen der Bundle-DLLs von einem Netzwerkpfad, ohne jede
|
||||||
Fehlermeldung). `build_and_deploy.ps1` kopiert den Quellcode deshalb
|
Fehlermeldung). `build_and_deploy.ps1` kopiert den Quellcode deshalb
|
||||||
automatisch zuerst nach `%TEMP%` und baut nur dort.
|
automatisch zuerst nach `%TEMP%` und baut nur dort.
|
||||||
|
|
||||||
Voraussetzung: `pip install pyinstaller` zusätzlich zu den obigen Paketen.
|
|
||||||
|
|
||||||
## Aufbau
|
## Aufbau
|
||||||
|
|
||||||
| Datei | Zweck |
|
| Datei | Zweck |
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# Baut VersaPadViewer.exe (PyInstaller --onedir) und kopiert das Ergebnis
|
# Baut VersaPadViewer.exe (PyInstaller --onedir) und kopiert das Ergebnis
|
||||||
# nach lokal (C:\Users\<user>\AppData\Local\VersaPadViewer).
|
# nach $projectDir\dist\VersaPadViewer.
|
||||||
#
|
#
|
||||||
# WICHTIG: Sowohl Bauen als auch Laufen muessen lokal passieren, NICHT auf
|
# WICHTIG: Sowohl Bauen als auch Laufen muessen lokal passieren, NICHT auf
|
||||||
# dem Netzlaufwerk (Z:\Git\...):
|
# dem Netzlaufwerk (Z:\Git\...):
|
||||||
|
|
@ -12,35 +12,87 @@
|
||||||
# SMB-Share, kombiniert mit dem eh schon langen Projektpfad.
|
# SMB-Share, kombiniert mit dem eh schon langen Projektpfad.
|
||||||
#
|
#
|
||||||
# Deshalb: Quellcode zuerst nach lokal (%TEMP%) kopieren, dort bauen, danach
|
# Deshalb: Quellcode zuerst nach lokal (%TEMP%) kopieren, dort bauen, danach
|
||||||
# das fertige Bundle nach %LOCALAPPDATA% kopieren. Z: wird nur zum Lesen der
|
# das fertige Bundle nach $projectDir\dist kopieren. Z: wird nur zum Lesen
|
||||||
# Quelldateien angefasst.
|
# der Quelldateien angefasst.
|
||||||
|
#
|
||||||
|
# Fehlerverhalten: Bei Doppelklick im Explorer schliesst sich das Fenster
|
||||||
|
# sofort nach Skriptende -- ohne Pause waeren Fehlermeldungen unsichtbar
|
||||||
|
# ("stirbt ohne jede Fehlermeldung"). Deshalb: alles in try/catch, im
|
||||||
|
# Fehlerfall UND am Ende eine Pause, ausser bei -NoPause (fuer CI/Automation).
|
||||||
|
|
||||||
|
param(
|
||||||
|
[switch]$NoPause
|
||||||
|
)
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
$projectDir = $PSScriptRoot
|
$projectDir = $PSScriptRoot
|
||||||
$buildSrc = "$env:TEMP\versapad_build_src"
|
$buildSrc = "$env:TEMP\versapad_build_src"
|
||||||
$localDir = "$env:LOCALAPPDATA\VersaPadViewer"
|
$localDir = "$projectDir\dist\VersaPadViewer"
|
||||||
|
$requirementsFile = "$projectDir\requirements.txt"
|
||||||
|
|
||||||
if (Test-Path $buildSrc) {
|
function Assert-LastExitCode([string]$step) {
|
||||||
Remove-Item $buildSrc -Recurse -Force
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "$step ist fehlgeschlagen (Exit-Code $LASTEXITCODE) -- Ausgabe oben pruefen."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Wait-ForKeyIfInteractive {
|
||||||
|
if ($NoPause) { return }
|
||||||
|
try {
|
||||||
|
Read-Host "Taste druecken zum Schliessen"
|
||||||
|
} catch {
|
||||||
|
# z.B. nicht-interaktiver Aufruf (stdin nicht verfuegbar) -- einfach ignorieren
|
||||||
|
}
|
||||||
}
|
}
|
||||||
New-Item -ItemType Directory -Path $buildSrc | Out-Null
|
|
||||||
Copy-Item "$projectDir\*.py" -Destination $buildSrc
|
|
||||||
Copy-Item "$projectDir\icon.ico" -Destination $buildSrc
|
|
||||||
Copy-Item "$projectDir\icon.png" -Destination $buildSrc
|
|
||||||
|
|
||||||
Push-Location $buildSrc
|
|
||||||
try {
|
try {
|
||||||
py -m PyInstaller --windowed --onedir --name VersaPadViewer `
|
Write-Host "Pruefe Python-Installation..."
|
||||||
--icon icon.ico --add-data "icon.png;." --add-data "icon.ico;." `
|
py --version
|
||||||
desktop_viewer.py --noconfirm
|
Assert-LastExitCode "'py --version' (ist Python installiert und im PATH?)"
|
||||||
} finally {
|
|
||||||
Pop-Location
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Test-Path $localDir) {
|
if (-not (Test-Path $requirementsFile)) {
|
||||||
Remove-Item $localDir -Recurse -Force
|
throw "requirements.txt nicht gefunden unter $requirementsFile"
|
||||||
}
|
}
|
||||||
Copy-Item "$buildSrc\dist\VersaPadViewer" -Destination $localDir -Recurse
|
Write-Host "Installiere/aktualisiere benoetigte Pakete aus requirements.txt..."
|
||||||
Remove-Item $buildSrc -Recurse -Force
|
py -m pip install --quiet --disable-pip-version-check -r $requirementsFile
|
||||||
|
Assert-LastExitCode "Paketinstallation (pip install -r requirements.txt)"
|
||||||
|
|
||||||
Write-Host "Fertig: $localDir\VersaPadViewer.exe"
|
if (Test-Path $buildSrc) {
|
||||||
|
Remove-Item $buildSrc -Recurse -Force
|
||||||
|
}
|
||||||
|
New-Item -ItemType Directory -Path $buildSrc | Out-Null
|
||||||
|
Copy-Item "$projectDir\*.py" -Destination $buildSrc
|
||||||
|
Copy-Item "$projectDir\icon.ico" -Destination $buildSrc
|
||||||
|
Copy-Item "$projectDir\icon.png" -Destination $buildSrc
|
||||||
|
|
||||||
|
Push-Location $buildSrc
|
||||||
|
try {
|
||||||
|
Write-Host "Baue mit PyInstaller (--onedir --windowed)..."
|
||||||
|
py -m PyInstaller --windowed --onedir --name VersaPadViewer `
|
||||||
|
--icon icon.ico --add-data "icon.png;." --add-data "icon.ico;." `
|
||||||
|
desktop_viewer.py --noconfirm
|
||||||
|
Assert-LastExitCode "PyInstaller-Build"
|
||||||
|
} finally {
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path "$buildSrc\dist\VersaPadViewer\VersaPadViewer.exe")) {
|
||||||
|
throw "PyInstaller hat keine VersaPadViewer.exe erzeugt, obwohl der Exit-Code 0 war -- Build-Ausgabe oben pruefen."
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path $localDir) {
|
||||||
|
Remove-Item $localDir -Recurse -Force
|
||||||
|
}
|
||||||
|
Copy-Item "$buildSrc\dist\VersaPadViewer" -Destination $localDir -Recurse
|
||||||
|
Remove-Item $buildSrc -Recurse -Force
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Fertig: $localDir\VersaPadViewer.exe" -ForegroundColor Green
|
||||||
|
Wait-ForKeyIfInteractive
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "FEHLER: $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
Wait-ForKeyIfInteractive
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
|
||||||
5
requirements.txt
Normal file
5
requirements.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
pyserial
|
||||||
|
pystray
|
||||||
|
pillow
|
||||||
|
mcp
|
||||||
|
pyinstaller
|
||||||
Loading…
Add table
Add a link
Reference in a new issue