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

172 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
```bash
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
```bash
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:
```bash
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:
```bash
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:
```bash
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:
```bash
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
```bash
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.