Files
bible-app/BUILD-AND-DEPLOY.md
T
christianandClaude Opus 5.5 e7709e8756 Fix the damaged uninstaller that blocked every update from 1.4.35 on
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>
2026-10-21 21:07:41 -04:00

8.2 KiB
Raw Blame History

Bible App — build on the laptop, push to git, update the TVs remotely

This folder is the complete source tree of the Bible App as it runs on the stage TV (version in package.json). Everything the installer build needs is here. Nothing the build produces is.

The short version, once set up:

npm run release -- "What changed in this version"
git commit -am "Release <version>" && git push

That bumps the version, builds the Windows installer, uploads it to the church Gitea server, and the TV picks it up from Settings › Updates. No .exe to hand-carry.

1. What is (and is not) in this folder

Included — everything the build and the app need:

  • All sources: app.js, server.js, main.cjs, preload.cjs, init.js, the *.html pages, styles.css, every additive module (follower-*.js, tool-wheel.js, propresenter-*.js, quick-dock.js, …).
  • App data: Translations/, the *_bible.json files, nlt-data.js, interlinear/, Dictionary/ (JSON), dict/, fonts/, Notes/, Announcements/, Graphics/ (the defaults that ship inside the installer).
  • Vendored runtimes that ship inside the installer: vendor/whisper, vendor/upscale, vendor/redist, vendor/llama (the llama-server binaries — NOT the models), vendor/fontawesome.
  • Build inputs: package.json, package-lock.json, build/installer.nsh, tools/ (build-win.cjs, release.cjs, publish-update.cjs, build-full-installer.cjs, the SFX stub in tools/sfx, mock-propresenter.cjs), build-installer.bat.
  • The *.md notes at the root (small; project history worth keeping).

Left out on purpose (would only take space):

Left out Why
dist-installer/, dist/ every installer ever built; the build recreates them
node_modules/ (640 MB) npm ci recreates it exactly from package-lock.json
vendor/llama/*.gguf (8 GB) AI models; the regular installer never carries them (see §5)
.ebcache/, archive/, *.log, server-port.txt build cache, retired scratch files, runtime files
Dictionary/**/*.zip, the Strong's XML folder source archives; the app reads the converted JSON beside them

Size of this folder: about 1.2 GB, of which ~700 MB is the vendored runtimes and ~400 MB is Bible data.

2. Build machine prerequisites

The Windows installer builds on macOS as well as on Windows. electron-builder downloads its own mac build of wine for the few Windows-only steps, so the laptop produces the exact same installer the stage PC installs. Nothing has to be done on the Windows machine any more.

  • macOS (Apple Silicon or Intel), or Windows 10/11 x64.
  • Node.js 22 LTS. On macOS: brew install node@22. On Windows: nodejs.org.
  • Internet the first time: npm ci downloads Electron, and electron-builder pulls NSIS, winCodeSign and (on macOS) wine into its cache — a few hundred MB, once.
  • No code-signing certificate is used, and tools/build-win.cjs sets the environment up accordingly.

3. Build the installer

npm ci          # first time only
npm run build:win

Output: dist-installer/Bible App Setup <version>.exe (about 750 MB).

Notes:

  • npm run build:win always builds x64. Do not call npx electron-builder --win directly on an Apple Silicon Mac: electron-builder defaults to the host architecture and will quietly hand you an arm64 build that the stage PC cannot run.
  • Bump "version" in package.json before every build you intend to ship — npm run release does it for you. The TV's updater compares versions numerically and only offers a HIGHER version.
  • If WIN_CSC_LINK / CSC_LINK / CSC_KEY_PASSWORD / CSC_KEYCHAIN exist in your environment, build-win.cjs removes them for the build (electron-builder aborts on an EMPTY WIN_CSC_LINK).
  • Keep build/installerIcon.ico. On macOS electron-builder rebuilds the uninstaller in JavaScript and leaves out NSIS's icon patch; if the installer and uninstaller icons differ, the uninstaller fails NSIS's integrity check, and that version can then never be updated or uninstalled. This file makes both use one icon (NSIS's own default, so nothing looks different). build-win.cjs refuses to build without it. Versions 1.4.35–1.4.37 shipped without it; build/installer.nsh installs over their damaged uninstallers instead of running them.
  • On Windows, if the build fails while extracting winCodeSign (symlink / permission errors), run the terminal as administrator or turn on Developer Mode, delete %LOCALAPPDATA%\electron-builder\Cache\winCodeSign, and build again.

4. Update the TVs remotely (no cable, no remote desktop)

Each TV has Settings › Updates › "Update folder or address" plus "Check for updates automatically when the app starts". The TV reads <source>/latest.json, compares versions, then downloads and runs the installer. The app closes, installs, reopens; notes, graphics, annotations and settings are kept.

The church's update source is the Gitea package registry:

http://chronicles-ab.waterschurch.org:3000/api/packages/christian/generic/bible-app/latest

Paste that into Settings › Updates on every machine, once. It is also bibleApp.updateTarget in package.json, which is where the publish script gets it from.

Why the package registry and not a Gitea release? This server caps release attachments at 100 MB and does not allow .exe. The package registry takes the 750 MB installer and — importantly — serves it to anonymous readers, which is what the updater needs: it sends no credentials. The code repository stays private; only the published installer is readable without a login.

Ship a version:

npm run release -- "Faster laser, new popup cards"

That bumps the patch version, builds, uploads the installer and latest.json, and removes builds older than the previous one. To publish somewhere else instead (a network share, a folder, a web server), pass a destination — the same string you would put in Settings › Updates:

npm run release -- --dest "\\SERVER\share\bible-app" "Notes"
npm run publish-update -- "\\SERVER\share\bible-app" "Notes"    # publish an existing build

Publishing needs a Gitea token: GITEA_TOKEN, or the macOS keychain item gitea-mcp that the git tooling already uses. It is never written to disk.

The TV picks the update up at its next start (auto-check on) or when someone taps Check for Updates in Settings. It never installs without that check, so time the publish for after the service.

5. Full Setup (only for a brand-new machine)

A machine that has never had the app needs the AI model once (about 1.9 GB). That is the one-file "Bible App Full Setup", which drops the model into %ProgramData%\Bible App\llama-cache where updates never touch it. To build it, put the model at vendor/llama/model.gguf (exactly 2,019,377,696 bytes — copy it from the stage PC: C:\ProgramData\Bible App\llama-cache\model.gguf; it is gitignored, so it never enters the repo), build the regular installer, then:

node tools/build-full-installer.cjs

This builds on the laptop too: 7-Zip only packs the payload, and the Windows SFX stub is concatenated onto it byte-for-byte, so the host platform makes no difference to the result.

Machines that already have the model keep using the regular installer.

6. Git

The tree lives on the church Gitea server:

http://chronicles-ab.waterschurch.org:3000/christian/bible-app

git push and git pull work as usual; the credential is in the macOS keychain. .gitignore keeps build outputs, node_modules, models and logs out; .gitattributes stores every file byte-for-byte (several maintenance scripts match CRLF exactly).

Tag what you ship, so a rollback is a checkout rather than an archaeology exercise:

git tag v<version> && git push --tags

If your server has Git LFS enabled, the commented lines in .gitattributes move the vendor/ binaries into LFS (only do that with LFS actually on, or clones get pointer files).

7. Dev-run without building

npm ci
npm start

then open http://localhost:8080/. This runs the server on macOS and Windows alike. To run a test copy beside a live app, set BIBLE_APP_PORT=8095 and BIBLE_APP_DATA_DIR=<some scratch folder> so it never touches the church's real content.