From 98f09830f312f2ba32a41a9fb356bb450b069052 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Wed, 16 Sep 2026 09:52:26 +0700 Subject: [PATCH] clean up --- Cargo.lock | 3 +- crates/auto_update/Cargo.toml | 2 +- crates/auto_update/src/lib.rs | 89 +++++------------------------------ 3 files changed, 13 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 32489146..bebad170 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2754,8 +2754,7 @@ dependencies = [ [[package]] name = "gpui-updater-core" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f3ec64b674794b6b8b6fb36d40974d3506bd254212c3d5f65fcc9eac6542cb" +source = "git+https://github.com/AprilNEA/gpui-updater#a622818f581eb8663a3a19d65f18b962f665946c" dependencies = [ "minisign-verify", "semver", diff --git a/crates/auto_update/Cargo.toml b/crates/auto_update/Cargo.toml index 7a2f3ea0..e58b814f 100644 --- a/crates/auto_update/Cargo.toml +++ b/crates/auto_update/Cargo.toml @@ -12,4 +12,4 @@ serde.workspace = true serde_json.workspace = true ureq.workspace = true -gpui-updater-core = "0.1" +gpui-updater-core = { git = "https://github.com/AprilNEA/gpui-updater" } diff --git a/crates/auto_update/src/lib.rs b/crates/auto_update/src/lib.rs index 916ae1c6..ce962806 100644 --- a/crates/auto_update/src/lib.rs +++ b/crates/auto_update/src/lib.rs @@ -5,33 +5,26 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use gpui::{App, AppContext, Context, Entity, Global, SharedString, Task, Window}; use gpui_updater_core::{EngineConfig, Release, UpdateEngine, UpdateStatus, Verification, Version}; -use instant::{Duration, Instant}; +use instant::Duration; use crate::source::{AssetFilter, GiteaSource, asset_filter_for}; mod source; -/// Observable auto-update status — re-exported from -/// [`gpui_updater_core::UpdateStatus`]. pub use gpui_updater_core::UpdateStatus as AutoUpdateStatus; -/// Coop publishes its releases to its own Gitea instance rather than GitHub, so -/// release metadata is read from the Gitea API. const GITEA_API_BASE: &str = "https://git.reya.info/api/v1"; const GITEA_REPO_OWNER: &str = "reya"; const GITEA_REPO_NAME: &str = "coop"; /// Delay before the automatic check that runs on startup. const AUTO_CHECK_DELAY: Duration = Duration::from_secs(120); - /// How long a failure stays visible before the status reverts to "Up to date". const ERROR_DISPLAY_DURATION: Duration = Duration::from_secs(5); const COOP_UPDATE_EXPLANATION: &str = "COOP_UPDATE_EXPLANATION"; const COOP_BUNDLE_TYPE: &str = "COOP_BUNDLE_TYPE"; -/// Whether updates are managed by an external distribution channel -/// (Flatpak/Snap), in which case the in-app updater must not run. fn uses_managed_updates() -> bool { // The Flatpak runtime exports `FLATPAK_ID` inside the sandbox. std::env::var("FLATPAK_ID").is_ok() @@ -42,11 +35,6 @@ fn uses_managed_updates() -> bool { } /// Initialize the auto-update system. -/// -/// Skips initialization when updates are handled by an external distribution -/// channel (Flatpak/Snap) or when nothing installable is published for this -/// target. Otherwise creates the global [`AutoUpdater`] entity and schedules a -/// check for updates after a 2-minute delay. pub fn init(window: &mut Window, cx: &mut App) { if uses_managed_updates() { log::info!( @@ -56,6 +44,7 @@ pub fn init(window: &mut Window, cx: &mut App) { } let (os, arch) = (std::env::consts::OS, std::env::consts::ARCH); + let Some(filter) = asset_filter_for(os, arch) else { log::info!( "Skipping auto-update initialization: no installable release artifact is published for {os}/{arch}" @@ -81,12 +70,6 @@ struct GlobalAutoUpdater(Entity); impl Global for GlobalAutoUpdater {} -/// The global auto-updater entity. -/// -/// Wraps the blocking update engine from `gpui-updater-core` with Coop's -/// release source, verification policy and delayed auto-check. -/// -/// Retrieve the global instance via [`AutoUpdater::global`]. pub struct AutoUpdater { /// The blocking engine, driven on the background executor. engine: Arc>, @@ -97,17 +80,10 @@ pub struct AutoUpdater { pub version: Version, /// The in-flight check or download, if any. task: Option>, - /// When the last error was recorded, so we can reset to idle after 5s. - error_time: Option, } impl AutoUpdater { /// Whether auto-update is available for this installation. - /// - /// Returns `false` on managed distribution channels (Flatpak/Snap) and on - /// targets without an installable artifact (see - /// [`asset_filter_for`](crate::source::asset_filter_for)), where no - /// updater is created at all. pub fn is_available(cx: &App) -> bool { cx.try_global::().is_some() } @@ -119,12 +95,6 @@ impl AutoUpdater { } /// Retrieve the global auto updater instance. - /// - /// # Panics - /// - /// Panics when auto-update is not available for this installation. Prefer - /// [`AutoUpdater::try_global`] when the installation type is not known at - /// compile time (e.g. Flatpak/Snap). pub fn global(cx: &App) -> Entity { cx.global::().0.clone() } @@ -140,16 +110,10 @@ impl AutoUpdater { cx: &mut Context, ) -> Self { let source = GiteaSource::new(GITEA_API_BASE, GITEA_REPO_OWNER, GITEA_REPO_NAME, filter); - - // Artifacts come from our own host, but a truncated or substituted - // download would otherwise be installed silently, so require the - // published SHA-256 to match. The release workflow publishes a - // `SHA256SUMS` asset for every release. let config = EngineConfig::new(version.clone()).verification(Verification::Checksum); let engine = Arc::new(UpdateEngine::new(source, config)); - // Schedule an auto-check after a 2-minute delay (deferred to run at the - // end of the current frame so the window is fully set up). + // Schedule an auto-check after a 2-minute delay cx.defer_in(window, |_this, _window, cx| { cx.spawn(async move |this, cx| { cx.background_executor().timer(AUTO_CHECK_DELAY).await; @@ -164,17 +128,12 @@ impl AutoUpdater { available: None, version, task: None, - error_time: None, } } /// Whether nothing is happening, so the UI can hide the status line. pub fn idle(&self) -> bool { - match self.status { - UpdateStatus::Idle => true, - UpdateStatus::Errored(_) => self.error_is_stale(), - _ => false, - } + matches!(self.status, UpdateStatus::Idle) } /// Whether a verified update is installed and waiting for a restart. @@ -200,18 +159,11 @@ impl AutoUpdater { UpdateStatus::Staged(version) => { format!("Version {version} ready — restart to apply").into() } - UpdateStatus::Errored(message) => { - if self.error_is_stale() { - "Up to date".into() - } else { - format!("Update failed: {message}").into() - } - } + UpdateStatus::Errored(message) => format!("Update failed: {message}").into(), } } - /// Check the release host for a newer version, then download and install it - /// without further prompting. No-op while a check or install is in flight. + /// Check the release host for a newer version, then download and install it. pub fn check(&mut self, cx: &mut Context) { if self.status.is_busy() { return; @@ -248,10 +200,6 @@ impl AutoUpdater { } /// Download the available update, verify it, and swap it into place. - /// - /// No-op unless a newer release is available and no other operation is in - /// flight. On success the status becomes [`UpdateStatus::Staged`] and the - /// app's restart path is set to the new binary; call [`restart`](Self::restart). fn download_and_install(&mut self, cx: &mut Context) { if self.status.is_busy() { return; @@ -270,9 +218,6 @@ impl AutoUpdater { ); self.task = Some(cx.spawn(async move |this, cx| { - // The blocking download runs on a background thread and reports - // progress into shared atomics; this foreground task polls them so - // the UI updates live without a notification per 64 KiB chunk. let downloaded = Arc::new(AtomicU64::new(0)); let total = Arc::new(AtomicU64::new(0)); // 0 = unknown let done = Arc::new(AtomicBool::new(false)); @@ -324,8 +269,7 @@ impl AutoUpdater { } }; - this.update(cx, |this, cx| this.set_status(UpdateStatus::Installing, cx)) - .ok(); + let _ = this.update(cx, |this, cx| this.set_status(UpdateStatus::Installing, cx)); let installed = { let engine = engine.clone(); @@ -345,7 +289,6 @@ impl AutoUpdater { this.set_status(UpdateStatus::Staged(version), cx); } Err(error) => { - log::warn!("Update install failed: {error}"); this.set_status(UpdateStatus::Errored(error.to_string()), cx); } } @@ -355,9 +298,6 @@ impl AutoUpdater { } /// Relaunch into the staged update. - /// - /// On macOS and Linux the new files are already in place, so this is what - /// starts running them. No-op unless an update is [`staged`](Self::staged). pub fn restart(&mut self, cx: &mut Context) { if !self.staged() { log::warn!("Ignoring restart request: no update is staged"); @@ -371,23 +311,16 @@ impl AutoUpdater { self.status = status; if errored { - self.error_time = Some(Instant::now()); - // Re-render once the error has been on screen long enough to make - // way for "Up to date". cx.spawn(async move |this, cx| { cx.background_executor().timer(ERROR_DISPLAY_DURATION).await; - this.update(cx, |_this, cx| cx.notify()).ok(); + this.update(cx, |this, cx| { + this.set_status(UpdateStatus::Idle, cx); + }) + .ok(); }) .detach(); - } else { - self.error_time = None; } cx.notify(); } - - fn error_is_stale(&self) -> bool { - self.error_time - .is_some_and(|time| time.elapsed() >= ERROR_DISPLAY_DURATION) - } }