bambuddy/installers/windows
maziggy ba1394db3e fix(shutdown): exec uvicorn as PID 1 in Docker, and bound the graceful-shutdown wait
Two defects, both invisible until you ask the app to stop.

Docker never shut down gracefully at all. CMD ["sh","-c","uvicorn ..."] left
the shell as PID 1 with uvicorn as its child, and dash does not forward
signals, so docker stop SIGTERMed the shell and uvicorn never heard about it.
Measured on the shipped image: the full 10s grace period, exit 137, and no
"Shutting down" line in the log. Every stop, restart and image update was a
hard kill -- no WAL checkpoint, no MQTT disconnect, no virtual-printer
teardown. `exec` makes uvicorn PID 1; the rebuilt image now stops in 1s with
exit 0 and checkpoints the WAL.

Separately, uvicorn's timeout_graceful_shutdown defaults to None -- wait
forever for in-flight requests. An MJPEG camera stream is a response that
never completes (httptools' connection shutdown() only flips keep_alive on an
in-flight cycle, it never closes the transport), so one open camera tile
pinned the process until systemd SIGKILLed at 90s. The ordering makes it
unfixable from inside the app: uvicorn fires the lifespan shutdown -- the code
that tears the streams down -- only after connections drain.

All six launchers now pass --timeout-graceful-shutdown 5: Dockerfile,
deploy/bambuddy.service, the systemd unit and launchd plist from
install/install.sh, the SpoolBuddy installer's unit, and the Windows NSSM
registration. On timeout uvicorn cancels the request tasks; the camera
generators already unwind cleanly on CancelledError.

TimeoutStopSec raised to 30s on the units and stop_grace_period: 30s added to
compose, as backstops rather than the mechanism. On Windows NSSM's default
1500ms AppStopMethodConsole was force-killing uvicorn mid-teardown; raised to
15s, with the WM_CLOSE and thread-message stages skipped (uvicorn is a console
app with neither a window nor a message loop).
2026-07-11 14:44:45 +02:00
..
service fix(shutdown): exec uvicorn as PID 1 in Docker, and bound the graceful-shutdown wait 2026-07-11 14:44:45 +02:00
vendor fix(installer): vendor nssm.exe instead of fetching at build time 2026-06-10 13:14:23 +02:00
.gitignore feat(installer): scaffold Windows installer build pipeline 2026-06-10 12:13:53 +02:00
bambuddy.ico y feat(installer): version from APP_VERSION + Bambuddy icon 2026-06-10 13:09:03 +02:00
bambuddy.iss chore(installer): add RunOnceId to UninstallRun entries 2026-06-10 13:25:05 +02:00
build.py fix(windows): bundle vcruntime140_1.dll so greenlet loads on fresh Win10 (#2474) 2026-07-07 07:50:08 +02:00
README.md fix(vp): stop uvloop from silently truncating VP FTP uploads (#1896) 2026-07-05 10:32:13 +02:00

Bambuddy Windows Installer

Builds a self-contained Windows installer (.exe) for Bambuddy: embedded Python 3.13 distribution + pre-built frontend + NSSM-supervised Windows service. No Python or Node installation required on the target machine.

Architecture

  • Install target: C:\Program Files\Bambuddy\
  • Data target: C:\ProgramData\Bambuddy\data\ (preserved on uninstall by default)
  • Logs target: C:\ProgramData\Bambuddy\logs\
  • Service: registered via NSSM, runs as LocalSystem, autostart on boot
  • Service command: python.exe -m uvicorn backend.app.main:app --host 0.0.0.0 --port 8000 --loop asyncio (--loop asyncio avoids a uvloop TLS bug that can truncate VP FTP uploads, #1896)
  • Bundled binaries: Python 3.13 embeddable, NSSM, ffmpeg static build

Browser is the UI. Start Menu shortcut opens http://localhost:8000.

Why these choices

See memory/windows-installer-decision.md for the full reasoning. Short version: PowerShell install scripts can't survive environmental drift across the Windows host fleet, so we ship a self-contained bundle that depends on nothing on the host. Inno Setup + embedded Python is the lowest-maintenance path that delivers native-app UX. No Tauri/Electron launcher in v1 — browser-as-UI matches every other Bambuddy platform.

Build prerequisites

The build runs on Windows (or in a Windows GitHub Actions runner). Cross- building from Linux is possible via Wine but not officially supported.

  • Windows 10/11 x64 (or windows-latest GitHub Actions runner)
  • Python 3.11+ (for running build.py; the embedded Python that ships in the installer is downloaded fresh by the build script)
  • Node.js 22 LTS + npm (for building the frontend bundle)
  • Inno Setup 6 (for compiling bambuddy.iss.exe)

The build script downloads everything else automatically (embedded Python, NSSM, ffmpeg).

Build steps

:: From the repo root on a Windows machine
cd installers\windows
python build.py
:: Then open bambuddy.iss in Inno Setup Compiler and click Build → Compile
:: (or invoke ISCC.exe directly:)
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" bambuddy.iss

Output: installers\windows\build\output\bambuddy-windows-setup.exe

Testing without signing

The installer can be built and run unsigned. Windows SmartScreen will show "Windows protected your PC" on first run. Click More infoRun anyway to proceed. This is expected and harmless for testing. Production builds will be signed via SignPath OSS (application in flight as of 2026-06-10) and won't show this warning after reputation accrues.

CI build

See .github/workflows/windows-installer.yml for the automated build. The workflow runs on every tag matching v* and uploads the installer as a release asset.

Known limitations / open questions

  • VP feature on Windows: the Virtual Printer needs to bind 322/990/8883 (privileged ports). Service runs as LocalSystem which can bind these ports, but the user's Windows Firewall will prompt on first VP enable. Documenting this is TBD.
  • Spoolman: explicitly NOT bundled in v1. Users who want Spoolman install it separately. Bambuddy internal-inventory mode is the default on Windows.
  • Bundle size: estimated 250350MB installed (mostly opencv + ffmpeg + matplotlib). Acceptable for a v1; can investigate slimming later if users complain.
  • Updates: v1 ships as a fresh install / uninstall + install cycle. In-place upgrade via the same installer is supported by Inno Setup but needs end-to-end testing before we promise it.