feat(updates): download updates in the background, install on one click - #152
Merged
Conversation
…oast Updating was three surfaces deep: a settings toggle, a dismissible toast, and the About panel. The toggle asked a question nobody wants asked (a downloaded package costs nothing and is discarded if never used), and the toast interrupted work to say something the top-bar pill was already saying. Now: detection stays as-is on every platform, the download is unconditional wherever the build can install updates itself, and the persistent pill grows a single "Restart to install" button once the package is on disk. Clicking it is still the only thing that installs an update — nothing restarts the app on its own. - Remove `AutoUpdateConfig` and the `get/set_auto_update_settings` commands; `AppUpdateStatus`/`AppUpdateCheckResult` no longer carry settings, and `get_app_update_status` is now infallible. - Configs on disk still carry `autoUpdate.autoDownload`; the field is ignored on load rather than rejected (regression test added). - Delete `AppUpdateNotifications` (toast) and its MainLayout wiring. - `AppUpdateBadge` keeps a stable "Update available · vX" label and adds the restart action when `downloaded`, with a visible, retryable error path so a failed install can never look like a dead click.
Review of the first commit found that "Restart to install" did not actually restart: `install_app_update` fetched the manifest again before looking at the cache, so the click failed outright when offline, and if a newer release had appeared meanwhile the matching-version cache was dropped and the click silently became a multi-minute download behind a disabled "Restarting..." with no progress. That was survivable while the toast offered progress; it is not survivable now that the button is the only install surface. The updater's `Update` handle is `Clone` and carries everything `install` needs, so the package cache now keeps the handle with the bytes it produced. Applying it touches no network at all, and installs exactly the version the badge advertised. The manifest check moves into the no-cache branch, where Settings > About still downloads with progress. Also from review: - A failed install now always keeps its verified package (a fresh download used to be discarded), via one `cache_downloaded_package` helper shared with the background download so both leave the same state behind. - The mandatory-download rule is now a pure `should_download_in_background` predicate with tests that fail if a setting is ever wired back in — the property had no Rust coverage at all. - The config back-compat test asserted values identical to the defaults, so it could not fail. It now carries non-default settings and a negative control was run (adding `deny_unknown_fields` makes it fail). - The install error left the top bar: it is the button's own label plus a tooltip and an `sr-only` live region, so an arbitrarily long message can no longer stretch the bar. `.counters` shrinks and clips instead of pushing the update action and settings button off-screen.
Round-2 review found that dropping the version match from `take_downloaded` traded one silent failure for another. Reachable trace: 26.8.1 downloads and caches, a later check finds 26.8.2, its download fails, and Settings > About (whose install button does not wait for `downloaded`) then installs 26.8.1 while every surface says 26.8.2 — no error, no log line. `take_downloaded_if_current` restores the guard without giving up the offline fast path: the expected version comes from the last recorded check, which is already in memory, so the common case still touches no network. A mismatch drops the stale package and lets the install path fetch what is being offered. Also from review: - The pill's `text-overflow: ellipsis` was inert — it sat on a flex container, so a narrow window hard-clipped "v26.8.12" into the plausible-but-wrong "v26.8.". The label is now its own span that really ellipsizes, the dot no longer shrinks, and the pill (never the action button) is what gives way. - Corrected two comments that overstated the fast path: a click can still wait on `install_lock` behind an in-flight background download, which is reachable from About but not from the badge.
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.
Updates stop asking. On builds that can self-install, the package is fetched in the
background as soon as a release is detected, and the only thing left for the user is
one click that restarts into it.
What changes
and the Linux bundles with a working installer). Every setting that governed it is gone,
including the
autoUpdateconfig key.same detection logic, on every platform, including the ones that cannot self-install.
pill. That click is the only thing that ever installs. Nothing installs on its own.
verified:
Update::download()checks the signature before returning them, and theinstall applies bytes that never left process memory.
take_downloaded_if_current()compares the cached version against the version the lastcheck recorded, and drops the package on a mismatch rather than installing something
older than what the user was shown.
Config compatibility
Configs written before this change still load; the removed
autoUpdatekey is ignoredrather than rejected. Covered by
config_written_before_updates_became_mandatory_keeps_its_settings.Review status
Three review rounds. Round 1 found the mandatory network round-trip before installing a
cached package; round 2 found the stale-package install. Both are fixed here, and round 3
verified both fixes and returned
production_qualityon the simplicity, test-quality, andUX lane.
The round-3 correctness/concurrency lane did not run — both reviewers assigned to it
were out of provider quota. The round-2 correctness review passed on everything except the
stale-package finding, and that fix is covered by three unit tests
(
take_downloaded_if_current_*), but the restructured install path has not had a freshadversarial correctness pass since. Draft until it does.