| 1 | | − | //! Updates. The app asks g1t.sh/downloads/desktop/latest.json soon after |
| 2 | | − | //! it starts and every six hours after, downloads a newer version in the |
| 3 | | − | //! background, and then says so: a notification, and the tray's "Restart |
| 4 | | − | //! to update". Nothing is installed under someone mid-sentence; the |
| 5 | | − | //! restart, or quitting, installs it. The manifest is signed with g1t's |
| 1 | + | //! Updates. The app asks g1t.sh/downloads/desktop/latest.json as it |
| 2 | + | //! starts and every hour after, and downloads a newer version in the |
| 3 | + | //! background. One found as it starts is installed straight away, and g1t |
| 4 | + | //! opens again on it, before anyone has begun working in it: so a person |
| 5 | + | //! whose copy misbehaves only has to open g1t again to be on the newest, |
| 6 | + | //! fixed one (unless the runner is in the middle of a job, which is never |
| 7 | + | //! cut short; then it waits like any other). One found later says so, with |
| 8 | + | //! a notification and the tray's "Restart to update", since nothing is |
| 9 | + | //! installed under someone mid-sentence; the restart, or quitting, |
| 10 | + | //! installs it. The manifest is signed with g1t's |
| 6 | 11 | | //! release key, built into the app, and a download whose signature does |
| 7 | 12 | | //! not check out is thrown away (scripts/desktop-release.mjs). |
| 8 | 13 | | |
| ⋯ |
| 21 | 26 | | bytes: Vec<u8>, |
| 22 | 27 | | } |
| 23 | 28 | | |
| 24 | | − | const FIRST_CHECK: Duration = Duration::from_secs(15); |
| 25 | | − | const EVERY: Duration = Duration::from_secs(6 * 60 * 60); |
| 29 | + | /// The first look waits only for the window to be up. |
| 30 | + | const FIRST_CHECK: Duration = Duration::from_secs(3); |
| 31 | + | const EVERY: Duration = Duration::from_secs(60 * 60); |
| 32 | + | /// An update downloaded this soon after g1t started is installed at once: |
| 33 | + | /// nobody has begun working in it yet. |
| 34 | + | const AT_START: Duration = Duration::from_secs(2 * 60); |
| 26 | 35 | | |
| 27 | | − | /// Checks soon after the start, then every six hours, unless settings |
| 28 | | − | /// turned that off; a person can still check from the menu. |
| 36 | + | /// Checks as g1t starts, installing what it finds then, and every hour |
| 37 | + | /// after, unless settings turned that off; a person can still check from |
| 38 | + | /// the menu. |
| 29 | 39 | | pub fn schedule(app: AppHandle) { |
| 30 | 40 | | tauri::async_runtime::spawn(async move { |
| 41 | + | let started = std::time::Instant::now(); |
| 31 | 42 | | tokio::time::sleep(FIRST_CHECK).await; |
| 43 | + | let mut first = true; |
| 32 | 44 | | loop { |
| 33 | 45 | | if config::settings(&app).auto_update { |
| 34 | | − | let _ = check(&app, false).await; |
| 46 | + | let at_start = first && started.elapsed() < AT_START; |
| 47 | + | let found = look(&app, false, at_start).await; |
| 48 | + | if at_start && let Some(version) = found.ok().as_deref().and_then(|answer| answer.strip_prefix("ready:")).map(str::to_owned) { |
| 49 | + | if crate::runner::busy(&app) { |
| 50 | + | tell(&app, "g1t is ready to update", &format!("Version {version} is downloaded. Restart g1t from the tray to use it.")); |
| 51 | + | } else { |
| 52 | + | tell(&app, "Updating g1t", &format!("g1t opens again on version {version} in a moment.")); |
| 53 | + | restart(&app); |
| 54 | + | } |
| 55 | + | } |
| 35 | 56 | | } |
| 57 | + | first = false; |
| 36 | 58 | | tokio::time::sleep(EVERY).await; |
| 37 | 59 | | } |
| 38 | 60 | | }); |
| ⋯ |
| 61 | 83 | | /// the tray or the site, who is told the answer either way; the timer |
| 62 | 84 | | /// says nothing unless there is something to install. |
| 63 | 85 | | pub async fn check(app: &AppHandle, asked: bool) -> Result<String, String> { |
| 86 | + | look(app, asked, false).await |
| 87 | + | } |
| 88 | + | |
| 89 | + | /// [`check`]; `at_start` says nothing of a download, which is installed at once. |
| 90 | + | async fn look(app: &AppHandle, asked: bool, at_start: bool) -> Result<String, String> { |
| 64 | 91 | | if let Some(version) = ready(app) { |
| 65 | 92 | | if asked { |
| 66 | 93 | | tell(app, "g1t is ready to update", &format!("Version {version} is downloaded. Restart g1t to use it.")); |
| ⋯ |
| 93 | 120 | | *app.state::<config::State>().update.lock().unwrap() = Some(Ready { version: version.clone(), update, bytes }); |
| 94 | 121 | | tray::update_ready(&version); |
| 95 | 122 | | let _ = app.emit_to(settings::SETTINGS, settings::READY_EVENT, version.clone()); |
| 96 | | − | tell(app, "g1t is ready to update", &format!("Version {version} is downloaded. Restart g1t from the tray to use it.")); |
| 123 | + | if !at_start { |
| 124 | + | tell(app, "g1t is ready to update", &format!("Version {version} is downloaded. Restart g1t from the tray to use it.")); |
| 125 | + | } |
| 97 | 126 | | Ok(format!("ready:{version}")) |
| 98 | 127 | | } |
| 99 | 128 | | Err(error) => { |