Repository navigation
feat(updates): self-update the packaged app from GitHub releases - #1122
Merged
Merged
Conversation
…all path UpdateService owns the safety core (test-first, injected transport): never auto-installs, rate-limited checks with persisted clock, OS notifications, dev-build no-op. The shutdown gate gains an update exit — a pending update REPLACES the final app.quit with quitAndInstall, per consulted review: calling it after a plain quit races process exit and silently drops the update, and MacUpdater latches quitAndInstallCalled forever, so Keep-Editing resets only the intent. A watchdog restores retryability instead of force-exiting. electron-builder publish config embeds app-update.yml (the previously missing wire); File-menu item forces a check or applies the ready update. Consulted risk recorded: an update restart can skew the bundled tmux client against a surviving tmux server; recovery terminals may need a fresh server after updating. Fixes #1120
A local npm regeneration dropped 27 other-platform esbuild optional entries, which npm ci on Linux (minimum-node fixture gate) needs; the hand merge keeps main's complete lock and adds only the updater subtree. Refs #1120
… stalled install Two consulted-review findings: a rejected lastCheck write would become an unhandledRejection incident on a full disk, and the watchdog's retry copy promised an in-process restart the committed-quit gate cannot deliver (a second quit exits without consulting pending()). Refs #1120
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Released apps update only by manually downloading dmg files; today's extension capabilities reached main while the released app (v0.1.0) predates them, and an extension install failed with an accurate-but-stale "unknown capability" error. The release pipeline already produces electron-updater's complete feed (latest-mac.yml + blockmaps on every release) — the app just never read it. Fixes #1120.
Implementation
src/main/updates/UpdateService.ts— the safety core, test-first with an injected transport: never auto-installs (autoInstallOnAppQuitforced false at construction AND every check), rate-limited checks (4h, persisted viaupdateCheckStore's atomic write), dev/unsigned builds no-op with one log line, OS-notification surface with actionable error mapping (offline / rate-limit / signature).sessionShutdownGate.ts, threaded viaapplicationShutdown.ts): when drain resolves with a pending update,install()REPLACES the finalapp.quit()—quitAndInstall()re-enters will-quit (whereshutdownCompleteadmits it andonQuitAllowedruns); calling it after a plain quit races process exit and silently drops the update. Pinned by tests.setWindowCloseVetoedObserver); the staged update survives for retry — important because MacUpdater latchesquitAndInstallCalledforever.app.exit()(races ShipIt).index.ts): realautoUpdaterfrom electron-updater 6.6.2 (production dep), T+3min first check + 4h interval + wake re-check (packaged only, unref'd timers), File-menu "Check for Updates…" (dual duty: force check / apply ready update).electron-builder.yml:publish: github— embedsresources/app-update.yml, the previously missing wire. Verified embedded in a locally built .app (full dmg target; note--dirbuilds skip it by design). Release workflows already uploadrelease/*.yml+ blockmaps.Consulted decisions (Claude agents, recorded per user request)
quitAndInstalluntil after drain because of the one-shot latch; (c)isSilent/isForceRunAfterare no-ops on macOS; (d) neverapp.relaunch()(Squirrel owns relaunch; double instance fights the single-instance lock); (e) tmux skew risk: an update restart is the one restart guaranteed to skew the bundled tmux client against a surviving tmux server (terminal recovery); recovery terminals may need a fresh server after updating — disclosed to users in the ready-notification copy rather than killing sessions.Verification
src/main/updates/+sessionShutdownGate(no-auto-install invariant, gate-installs-instead-of-quits ordering, veto reset, watchdog, dev no-op, rate limit, error mapping)tsc -bclean for touched projectsapp-update.ymlembed proven in a locally packaged .app; unsigned local builds still succeed (package-mac policy untouched)Known limits (honest)