# Windows installer Builds a single self-contained `.exe` that installs ShopDB-Flask on an **air-gapped** Windows Server. Nothing here ever touches the network at install time: Python, the wheels, the SPA and (optionally) MySQL all ship inside it. Lives with the application on purpose. The installer depends on app internals - `flask plugin` verbs, `site-profile.json`, `MOUNT_PATH`, the plugin registry - so a separate repo would drift out of step with the thing it installs. ## Files | File | What it is | |---|---| | `shopdb-preflight.ps1` | Stage 1. Read-only. Changes nothing, reports what this server is missing. | | `shopdb-install.ps1` | Stages 0 and 2-5 plus `uninstall`. All the actual work. | | `shopdb-admin.ps1` | Operator console installed alongside the app: status, restart, logs, backup, plugins. | | `ShopDBFlask.iss` | Inno Setup wizard. A thin wrapper - it collects input and runs the stages. | | `build-installer.sh` | Stages the bundle from a site profile. | | `make-branding.py` | Generates wizard artwork and the icon from `frontend/public/*.svg`. | | `*.bmp`, `shopdb.ico` | Generated artwork, committed so a Windows build box needs no Python. | ## Building ```bash # 1. Stage the bundle for a site's plugin set. ./build-installer.sh ../../site-profile.example.json # 2. Add the pieces that cannot be built on Linux: # bundle/wheels/ ~47 cp314 win_amd64 wheels, built ON Windows: # pip download -r requirements.txt -d wheels --only-binary=:all: # bundle/python/ python-3.14.x-amd64.exe # bundle/httpplatformhandler/ httpPlatformHandler_amd64.msi # bundle/mysql/ mysql-8.0.x-winx64.msi (bundled-database option only) # 3. Compile on Windows. iscc ShopDBFlask.iss ``` The wheelhouse is **cp314-locked**. A different Python minor version means a different wheelhouse; the installer will not use a Python it did not install. ## Deployment methods Chosen in the wizard, and the bundle carries a SPA build for each because Vite compiles the base path in - it cannot be switched at install time. - **Its own site** on a port (default 8090). - **Subpath** under an existing site, e.g. `http:///shopdb/`. Needs no new DNS record. Three things must agree - the IIS application alias, `MOUNT_PATH` in `.env`, and the SPA's build-time base - so the alias is fixed per bundle (`SUBPATH_ALIAS`, default `shopdb`) and the installer refuses if the bundle's build does not match what was asked for. Switching between methods removes the other one's IIS artifact and reconciles `MOUNT_PATH` and `CORS_ORIGINS`, so a server never ends up with both. ## Upgrades Run a newer installer over an existing install. It: - backs the database up first, **verifies** the dump is complete, and refuses to migrate if it cannot; - restores from that backup if migrations fail, and reports honestly that DDL the failed migration committed cannot be undone; - refuses to run a bundle older than what is installed; - keeps `.env` unless new credentials are supplied, and copies it aside first; - stops the app pool before replacing files, then starts it again. Whether a run is an upgrade is decided by probing the **target database**, not by whether the app directory exists - a rebuilt server pointed at an existing database is an upgrade, and treating it as fresh would drop tables. ## Testing notes Verified end to end on Windows Server 2025 against both a bundled MySQL 8.0 and an existing MySQL 5.6: fresh install, upgrade, re-run idempotency, failure and rollback, uninstall, and both deployment methods including switching between them. **Not yet verified:** a fully air-gapped run with the network disabled at the hypervisor, and any load in a real browser (all HTTP checks so far used curl, which sends no `Origin` header - so `CORS_ORIGINS` is untested in anger).