diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index a45eb3a05b..61154cb504 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -432,10 +432,11 @@ impl AutoUpdater { cx.notify(); } - pub async fn get_latest_remote_server_release( + pub async fn download_remote_server_release( os: &str, arch: &str, - mut release_channel: ReleaseChannel, + release_channel: ReleaseChannel, + version: Option, cx: &mut AsyncAppContext, ) -> Result { let this = cx.update(|cx| { @@ -445,15 +446,12 @@ impl AutoUpdater { .ok_or_else(|| anyhow!("auto-update not initialized")) })??; - if release_channel == ReleaseChannel::Dev { - release_channel = ReleaseChannel::Nightly; - } - - let release = Self::get_latest_release( + let release = Self::get_release( &this, "zed-remote-server", os, arch, + version, Some(release_channel), cx, ) @@ -468,17 +466,21 @@ impl AutoUpdater { let client = this.read_with(cx, |this, _| this.http_client.clone())?; if smol::fs::metadata(&version_path).await.is_err() { - log::info!("downloading zed-remote-server {os} {arch}"); + log::info!( + "downloading zed-remote-server {os} {arch} version {}", + release.version + ); download_remote_server_binary(&version_path, release, client, cx).await?; } Ok(version_path) } - pub async fn get_latest_remote_server_release_url( + pub async fn get_remote_server_release_url( os: &str, arch: &str, - mut release_channel: ReleaseChannel, + release_channel: ReleaseChannel, + version: Option, cx: &mut AsyncAppContext, ) -> Result<(String, String)> { let this = cx.update(|cx| { @@ -488,15 +490,12 @@ impl AutoUpdater { .ok_or_else(|| anyhow!("auto-update not initialized")) })??; - if release_channel == ReleaseChannel::Dev { - release_channel = ReleaseChannel::Nightly; - } - - let release = Self::get_latest_release( + let release = Self::get_release( &this, "zed-remote-server", os, arch, + version, Some(release_channel), cx, ) @@ -508,6 +507,56 @@ impl AutoUpdater { Ok((release.url, body)) } + async fn get_release( + this: &Model, + asset: &str, + os: &str, + arch: &str, + version: Option, + release_channel: Option, + cx: &mut AsyncAppContext, + ) -> Result { + let client = this.read_with(cx, |this, _| this.http_client.clone())?; + + if let Some(version) = version { + let channel = release_channel.map(|c| c.dev_name()).unwrap_or("stable"); + + let url = format!("/api/releases/{channel}/{version}/{asset}-{os}-{arch}.gz?update=1",); + + Ok(JsonRelease { + version: version.to_string(), + url: client.build_url(&url), + }) + } else { + let mut url_string = client.build_url(&format!( + "/api/releases/latest?asset={}&os={}&arch={}", + asset, os, arch + )); + if let Some(param) = release_channel.and_then(|c| c.release_query_param()) { + url_string += "&"; + url_string += param; + } + + let mut response = client.get(&url_string, Default::default(), true).await?; + let mut body = Vec::new(); + response.body_mut().read_to_end(&mut body).await?; + + if !response.status().is_success() { + return Err(anyhow!( + "failed to fetch release: {:?}", + String::from_utf8_lossy(&body), + )); + } + + serde_json::from_slice(body.as_slice()).with_context(|| { + format!( + "error deserializing release {:?}", + String::from_utf8_lossy(&body), + ) + }) + } + } + async fn get_latest_release( this: &Model, asset: &str, @@ -516,38 +565,7 @@ impl AutoUpdater { release_channel: Option, cx: &mut AsyncAppContext, ) -> Result { - let client = this.read_with(cx, |this, _| this.http_client.clone())?; - let mut url_string = client.build_url(&format!( - "/api/releases/latest?asset={}&os={}&arch={}", - asset, os, arch - )); - if let Some(param) = release_channel.and_then(|c| c.release_query_param()) { - url_string += "&"; - url_string += param; - } - - let mut response = client.get(&url_string, Default::default(), true).await?; - - let mut body = Vec::new(); - response - .body_mut() - .read_to_end(&mut body) - .await - .context("error reading release")?; - - if !response.status().is_success() { - Err(anyhow!( - "failed to fetch release: {:?}", - String::from_utf8_lossy(&body), - ))?; - } - - serde_json::from_slice(body.as_slice()).with_context(|| { - format!( - "error deserializing release {:?}", - String::from_utf8_lossy(&body), - ) - }) + Self::get_release(this, asset, os, arch, None, release_channel, cx).await } async fn update(this: Model, mut cx: AsyncAppContext) -> Result<()> { diff --git a/crates/recent_projects/src/ssh_connections.rs b/crates/recent_projects/src/ssh_connections.rs index 0718c4f983..47e4c91dbd 100644 --- a/crates/recent_projects/src/ssh_connections.rs +++ b/crates/recent_projects/src/ssh_connections.rs @@ -517,17 +517,31 @@ impl SshClientDelegate { } } + // For nightly channel, always get latest + let current_version = if release_channel == ReleaseChannel::Nightly { + None + } else { + Some(version) + }; + + self.update_status( + Some(&format!("Checking remote server release {}", version)), + cx, + ); + if download_binary_on_host { - let (request_url, request_body) = AutoUpdater::get_latest_remote_server_release_url( + let (request_url, request_body) = AutoUpdater::get_remote_server_release_url( platform.os, platform.arch, release_channel, + current_version, cx, ) .await .map_err(|e| { anyhow!( - "Failed to get remote server binary download url (os: {}, arch: {}): {}", + "Failed to get remote server binary download url (version: {}, os: {}, arch: {}): {}", + version, platform.os, platform.arch, e @@ -542,17 +556,18 @@ impl SshClientDelegate { version, )) } else { - self.update_status(Some("Checking for latest version of remote server"), cx); - let binary_path = AutoUpdater::get_latest_remote_server_release( + let binary_path = AutoUpdater::download_remote_server_release( platform.os, platform.arch, release_channel, + current_version, cx, ) .await .map_err(|e| { anyhow!( - "Failed to download remote server binary (os: {}, arch: {}): {}", + "Failed to download remote server binary (version: {}, os: {}, arch: {}): {}", + version, platform.os, platform.arch, e diff --git a/crates/remote/src/ssh_session.rs b/crates/remote/src/ssh_session.rs index 8e0c345f74..656560f0b6 100644 --- a/crates/remote/src/ssh_session.rs +++ b/crates/remote/src/ssh_session.rs @@ -1707,21 +1707,32 @@ impl SshRemoteConnection { let (binary, version) = delegate.get_server_binary(platform, cx).await??; - let mut server_binary_exists = false; - if !server_binary_exists && cfg!(not(debug_assertions)) { + let mut remote_version = None; + if cfg!(not(debug_assertions)) { if let Ok(installed_version) = run_cmd(self.socket.ssh_command(dst_path).arg("version")).await { - if installed_version.trim() == version.to_string() { - server_binary_exists = true; + if let Ok(version) = installed_version.trim().parse::() { + remote_version = Some(version); + } else { + log::warn!("failed to parse version of remote server: {installed_version:?}",); } - log::info!("checked remote server binary for version. latest version: {}. remote server version: {}", version.to_string(), installed_version.trim()); } - } - if server_binary_exists { - log::info!("remote development server already present",); - return Ok(()); + if let Some(remote_version) = remote_version { + if remote_version == version { + log::info!("remote development server present and matching client version"); + return Ok(()); + } else if remote_version > version { + let error = anyhow!("The version of the remote server ({}) is newer than the Zed version ({}). Please update Zed.", remote_version, version); + return Err(error); + } else { + log::info!( + "remote development server has older version: {}. updating...", + remote_version + ); + } + } } match binary {