App menu: "Check for updates" is a real check, once, now, and says what came of it calmly
Host: `updater::check_now` runs the same check the timer runs (the same updater endpoint, nothing else) for the app's shell-only `update_check`. It never queues or retries: while a check is in flight (the timer's or an earlier request's), or within 10 seconds of the last request, it only answers with the current status (`update::may_check_now`, tested). The timer's loop uses the same single-flight guard. Frame: the menu's Updates row is that check (`updateRow`, tested): "Checking…", "Up to date · <version>", "Downloading <version>…", "Restart to update" when an update is ready (the row then installs it), and "Could not check" with a plain sentence as its tooltip when the server does not answer. The updater's error text is never shown there, and a failure is only mentioned when the owner asked for the check in this menu; the timer's own failures stay quiet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c7437ac504
commit
2f126659f9
6 changed files with 162 additions and 9 deletions
|
|
@ -14,6 +14,16 @@ pub const READY_DEADLINE: Duration = Duration::from_secs(30);
|
|||
/// Launches of an updated version without `app_ready` before automatic apply is disabled.
|
||||
pub const MAX_FAILED_BOOTS: u32 = 2;
|
||||
pub const PENDING_FILE: &str = "update-pending.json";
|
||||
/// Least time between two checks the owner asks for ("Check for updates"): a row clicked again
|
||||
/// and again asks the server once.
|
||||
pub const MANUAL_CHECK_GAP: Duration = Duration::from_secs(10);
|
||||
|
||||
/// Whether a check the owner asked for may start: never while one is in flight (the timer's or
|
||||
/// an earlier request's), and not within [`MANUAL_CHECK_GAP`] of the last request. A refused
|
||||
/// request answers with the current status; nothing is queued and nothing retries.
|
||||
pub fn may_check_now(in_flight: bool, since_last_request: Option<Duration>) -> bool {
|
||||
!in_flight && since_last_request.is_none_or(|elapsed| elapsed >= MANUAL_CHECK_GAP)
|
||||
}
|
||||
|
||||
/// Wait before the next check: 30 min +/- 5 min, from any 64-bit entropy value.
|
||||
pub fn next_check_delay(entropy: u64) -> Duration {
|
||||
|
|
@ -76,6 +86,16 @@ pub fn on_boot(pending: Option<PendingUpdate>, current_version: &str) -> BootDec
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn a_requested_check_runs_once_and_never_piles_up() {
|
||||
assert!(may_check_now(false, None), "the first request checks");
|
||||
assert!(!may_check_now(true, None), "not while the timer's check is in flight");
|
||||
assert!(!may_check_now(true, Some(Duration::from_secs(3600))));
|
||||
assert!(!may_check_now(false, Some(Duration::from_secs(2))), "a row clicked again asks the server once");
|
||||
assert!(may_check_now(false, Some(MANUAL_CHECK_GAP)));
|
||||
assert!(may_check_now(false, Some(Duration::from_secs(60))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delay_stays_within_jitter() {
|
||||
for entropy in [0, 1, 300, 600, 601, u64::MAX] {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue