Publish updates to the Gitea package registry, and build the installer on macOS

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>
This commit is contained in:
2026-09-24 13:17:55 -04:00
co-authored by Claude Opus 5
parent 0db5e9b07d
commit 838a87dbf3
6 changed files with 422 additions and 100 deletions
+28
View File
@@ -7823,6 +7823,34 @@ server.listen(PORT, () => {
console.error('[OSC] ❌ Failed to start OSC listener:', e && e.message || e);
console.error('[OSC] Error stack:', e.stack);
}
/*
* "Check for updates automatically when the app starts" (Settings > Updates). Until now that
* setting was stored but never acted on, so the TV only learned about a new build when someone
* opened Settings and tapped Check for Updates.
*
* This CHECKS only. The answer lands in lastResult for Settings to show; installing stays a
* deliberate tap, because installing closes and reopens the app — which must never happen by
* itself in the middle of a service. The delay keeps it clear of the llama/OSC startup rush.
*/
setTimeout(async () => {
try {
const cfg = readUpdateConfig();
if (!cfg.autoCheck || !cfg.source) return;
const man = await readUpdateManifest(cfg.source);
const latest = String(man.version || '').trim();
const current = appVersion();
const available = latest && compareVersions(latest, current) > 0;
writeUpdateConfig({ lastCheck: Date.now(), lastResult: available ? ('Update ' + latest + ' available') : 'Up to date' });
console.log(available
? `[UPDATE] Version ${latest} is available (this machine has ${current}) — install it from Settings > Updates.`
: `[UPDATE] Up to date (${current}).`);
} catch (e) {
const msg = String(e && e.message || e);
try { writeUpdateConfig({ lastCheck: Date.now(), lastResult: 'Check failed: ' + msg }); } catch (_) {}
console.warn('[UPDATE] Startup check failed:', msg);
}
}, 5000);
});
server.on('error', (error) => {