Add in-app update checker and one-click updater

Settings now shows when a newer version is available, with the real
version number read from the remote's package.json (via the git
binary already required for this deployment model, so it works
against any remote, not just GitHub), and an "Uppdatera nu" button
that stops the server, pulls, reinstalls dependencies and restarts
automatically - warning explicitly if a race is currently active.
The existing app-version poller already reloads the page once a
restart is detected, so no new client-side polling was needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
larssand
2026-09-09 22:05:13 +02:00
parent 2bbbd6e995
commit 4c463485e0
9 changed files with 335 additions and 8 deletions

View File

@@ -165,6 +165,16 @@ Fixed 2026-09-09, reported while exporting an old race from the new Results arch
Verified 2026-09-09: a new integration test (`tests/server_persistence.integration.test.js`) exports a PDF with a Swedish title and a table row containing å/ä/ö/Å/Ä/Ö, asserts the font declares WinAnsiEncoding, asserts the correct single-byte WinAnsi sequence is present in the raw PDF bytes, and explicitly asserts the old UTF-8 byte sequence is *not* present - confirmed this test genuinely fails against the pre-fix code (not a tautology) by re-running it with the fix reverted.
## In-app update checker and one-click updater
Implemented 2026-09-09, requested by the user: an easy way to see a new version is available in the GUI, and an easy way to update for someone not comfortable with `git pull` on their own.
- [x] Show whether a newer version is available, with the actual version number. `GET /api/update-status` shells out to the git binary already required for this deployment model (`git fetch`, `git rev-parse`, `git show origin/<branch>:package.json`) rather than any git-hosting-specific API, so it works the same against GitHub, GitLab or a self-hosted remote. Result is cached for 10 minutes (`force=1` bypasses it) so opening Settings repeatedly doesn't hammer the remote. Settings now shows the installed version, and - only when an update is genuinely available - the new version number read from the remote's `package.json`, not just a commit hash.
- [x] One-click update, no git knowledge required. An "Uppdatera nu"/"Update now" button `POST`s to `/api/update/run`, which spawns the existing `scripts/serverctl.js` with a new `update` command: stop -> `git pull --ff-only` (refuses and restarts on the code already installed rather than merging/rewriting history if the local branch has diverged - a trackside laptop is not a place to leave a merge conflict for someone to resolve) -> `npm install` (warns and continues on failure rather than leaving the server down) -> start, every step logged to `logs/update.log`. The confirmation dialog before starting adds an explicit extra warning if a race/session is currently active, since the update stops the server.
- [x] Auto-reload once the server is back. Deliberately built no new client-side polling for this: the app already re-polls `/api/app-version` every 5 seconds and reloads the page once it detects the running server's process identity changed (`revision:updatedAt`) - exactly what a restart-with-new-code produces - so the existing mechanism picks this up for free.
Verified 2026-09-09: a new integration test (`tests/server_persistence.integration.test.js`) builds a throwaway local bare git repo plus two clones (standing in for "this installation" and "somewhere a newer commit was pushed"), confirms `available: false` initially, pushes a version bump from the second clone, confirms `available: true` with the correct `remoteVersion` read from its `package.json`, and confirms the 10-minute cache is respected until `force=1`. A full headless-browser run went further than any test this session before it: cloned the real project into a throwaway git working copy (own bare "remote", own temporary SQLite data dir - the real installation and its git history were never touched), started the real server from that clone, pushed a version bump to the throwaway remote from a second clone, then in a real browser opened Settings, saw "Version 9.9.9 finns tillgänglig", clicked "Uppdatera nu", accepted the confirmation dialog, and confirmed end-to-end: the server actually stopped, `git pull --ff-only` fast-forwarded, `npm install` ran, the server came back up on the same port serving the new code, and the open browser tab auto-reloaded on its own and displayed the new version - with zero uncaught JS exceptions throughout.
## Documentation maintenance
- [ ] Update the architecture's module/dependency inventory as modules change.