On the TV the update stopped at "Bible App cannot be closed", and uninstalling
from Windows Settings showed "Installer integrity check has failed". The cause is
the uninstaller, not anything running.
An update runs the PREVIOUS version's uninstaller first. Versions 1.4.35-1.4.37
were built on macOS, where electron-builder can't run NSIS to write the uninstaller
and rebuilds it in JavaScript (UninstallerReader) from the installer's stub — without
NSIS's icon patch. makensis computes the uninstaller's CRC over the stub WITH the
uninstaller icon in it, so whenever the two icons differ the result fails NSIS's
integrity check. With no app icon configured, NSIS uses its two different default
icons, so every Mac-built uninstaller was damaged. Verified with a reimplementation
of NSIS's loadHeaders() check: the shipped installers pass; the captured uninstaller
failed (0x4235c8f5 vs stored 0x90f7de2c) and passes with this fix (0xb37e533c).
- build/installerIcon.ico: NSIS's own default install icon, so electron-builder uses
ONE icon for installer and uninstaller (nothing looks different; the app's icon is
unchanged). tools/build-win.cjs refuses to build without it.
- build/installer.nsh: for installs of 1.4.35-1.4.37 only, clear the old
UninstallString so the damaged uninstaller is not run; the new files are copied over
the old ones and a fresh, valid uninstaller is written. Every other version keeps
the normal uninstall-then-install path.
- BUILD-AND-DEPLOY.md: why the icon file must stay.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dev machine is now a laptop, so the release path had to stop assuming Windows
and a reachable network share.
- tools/build-win.cjs: build the Windows x64 installer from macOS or Windows.
electron-builder fetches its own mac wine build, so the only real trap is
architecture: a plain `electron-builder --win` on Apple Silicon silently
produces an arm64 installer the stage PC cannot run. This pins --x64 and
strips the CSC_* signing vars (an EMPTY WIN_CSC_LINK aborts the build).
- tools/publish-update.cjs: an http(s) destination now uploads to a Gitea
generic package registry. Not a Gitea release: this server caps release
attachments at 100 MB and disallows .exe, while the package registry takes the
750 MB installer and serves it anonymously — which the updater requires, since
it sends no credentials. Installer first, manifest last, so a TV checking
mid-publish never sees a manifest naming a file that is not there yet.
- tools/release.cjs: bump, build and publish in one command.
- server.js: honour "check for updates automatically when the app starts". The
setting was stored but never acted on. It checks only — installing restarts
the app, which must stay a deliberate tap.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>