# Packaging ## Windows-native build On Windows 10/11 with Python 3.11 x64: ```powershell .\build_windows.ps1 ``` The script creates `.venv-build`, installs PyInstaller, builds `dist\BoltClam\BoltClam.exe`, zips it, and runs Inno Setup when it is installed. `-OneFile` builds a single executable instead. ## Portable cross-build (Linux/macOS) `build_portable_windows.py` downloads the official Python embeddable distribution and Windows PyQt6 wheels, copies `src/boltclam` into `app/`, adds `resources/`, and places a tiny launcher at the root. It does not run the Windows app on the build host. ```bash .venv/bin/python packaging/build_portable_windows.py ``` The build uses the checked-in `BoltClam-launcher.exe` unless `--zig` (or a `zig` on `PATH`) is available, in which case it compiles `launcher.c` from source. The prebuilt launcher is a 176,640-byte GUI-subsystem PE32+ binary: ``` SHA-256 95cdb1e0a652f0b2e3870220cf1ec79c1e2972bc56e307bcb1381b2b9591a24b ``` To rebuild it with Zig 0.14.1: ```bash zig cc -target x86_64-windows-gnu -O2 -Wl,--subsystem,windows \ packaging/launcher.c -o packaging/BoltClam-launcher.exe ``` The output portable package is written to `dist/BoltClam-1.0.0-windows-x64-portable.zip` with `dist/SHA256SUMS.txt`. It is unsigned; the release checksum is the integrity signal. ## Layout of the portable package ``` BoltClam/ BoltClam.exe GUI launcher pythonw.exe embedded Python 3.11.9 python311.dll python311.zip stdlib python311._pth app/boltclam/ app source resources/ icon, fonts, QSS rasters Lib/site-packages/ Windows PyQt6 6.11 wheels ``` Keep the whole folder together. The launcher refuses to start if `pythonw.exe` or `app/` is missing rather than showing a silent failure. If the app exits before a window appears, run `python.exe -m boltclam` from the extracted folder in a command prompt to see the import/startup error. ## Release upload The `Windows release` workflow runs `build_windows.ps1` and uploads the installer/zip to a Gitea release. On a machine without the workflow, after building: ```powershell $env:GITEA_TOKEN = "…" .\packaging\publish_release.ps1 -Tag v1.0.0 ``` `publish_release.ps1` creates (or reuses) the release and uploads `dist/BoltClam-*.exe`, `dist/BoltClam-*.zip`, and `dist/SHA256SUMS.txt`. Gitea's asset endpoint expects multipart form data, so the script uses `curl.exe -F attachment=@…`.