Fix Linux rust-analyzer downloads in Preview (#20718)

Kirill Bulatov created

Follow-up of https://github.com/zed-industries/zed/pull/20408

Release Notes:

- (Preview) Fixed broken rust-analyzer downloads

Change summary

crates/http_client/src/github.rs   |  2 +
crates/languages/src/rust.rs       | 36 +++++++++++++++++++++++++------
crates/languages/src/typescript.rs | 27 +++++++++++++++++++++--
3 files changed, 55 insertions(+), 10 deletions(-)

Detailed changes

crates/http_client/src/github.rs 🔗

@@ -121,6 +121,7 @@ pub async fn get_release_by_tag_name(
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
 pub enum AssetKind {
     TarGz,
+    Gz,
     Zip,
 }
 
@@ -134,6 +135,7 @@ pub fn build_asset_url(repo_name_with_owner: &str, tag: &str, kind: AssetKind) -
         "{tag}.{extension}",
         extension = match kind {
             AssetKind::TarGz => "tar.gz",
+            AssetKind::Gz => "gz",
             AssetKind::Zip => "zip",
         }
     );

crates/languages/src/rust.rs 🔗

@@ -26,13 +26,13 @@ pub struct RustLspAdapter;
 
 #[cfg(target_os = "macos")]
 impl RustLspAdapter {
-    const GITHUB_ASSET_KIND: AssetKind = AssetKind::TarGz;
+    const GITHUB_ASSET_KIND: AssetKind = AssetKind::Gz;
     const ARCH_SERVER_NAME: &str = "apple-darwin";
 }
 
 #[cfg(target_os = "linux")]
 impl RustLspAdapter {
-    const GITHUB_ASSET_KIND: AssetKind = AssetKind::TarGz;
+    const GITHUB_ASSET_KIND: AssetKind = AssetKind::Gz;
     const ARCH_SERVER_NAME: &str = "unknown-linux-gnu";
 }
 
@@ -47,7 +47,8 @@ impl RustLspAdapter {
 
     fn build_asset_name() -> String {
         let extension = match Self::GITHUB_ASSET_KIND {
-            AssetKind::TarGz => "gz", // Nb: rust-analyzer releases use .gz not .tar.gz
+            AssetKind::TarGz => "tar.gz",
+            AssetKind::Gz => "gz",
             AssetKind::Zip => "zip",
         };
 
@@ -134,7 +135,7 @@ impl LspAdapter for RustLspAdapter {
         let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
         let destination_path = container_dir.join(format!("rust-analyzer-{}", version.name));
         let server_path = match Self::GITHUB_ASSET_KIND {
-            AssetKind::TarGz => destination_path.clone(), // Tar extracts in place.
+            AssetKind::TarGz | AssetKind::Gz => destination_path.clone(), // Tar and gzip extract in place.
             AssetKind::Zip => destination_path.clone().join("rust-analyzer.exe"), // zip contains a .exe
         };
 
@@ -145,19 +146,40 @@ impl LspAdapter for RustLspAdapter {
                 .http_client()
                 .get(&version.url, Default::default(), true)
                 .await
-                .map_err(|err| anyhow!("error downloading release: {}", err))?;
+                .with_context(|| format!("downloading release from {}", version.url))?;
             match Self::GITHUB_ASSET_KIND {
                 AssetKind::TarGz => {
                     let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
                     let archive = async_tar::Archive::new(decompressed_bytes);
-                    archive.unpack(&destination_path).await?;
+                    archive.unpack(&destination_path).await.with_context(|| {
+                        format!("extracting {} to {:?}", version.url, destination_path)
+                    })?;
+                }
+                AssetKind::Gz => {
+                    let mut decompressed_bytes =
+                        GzipDecoder::new(BufReader::new(response.body_mut()));
+                    let mut file =
+                        fs::File::create(&destination_path).await.with_context(|| {
+                            format!(
+                                "creating a file {:?} for a download from {}",
+                                destination_path, version.url,
+                            )
+                        })?;
+                    futures::io::copy(&mut decompressed_bytes, &mut file)
+                        .await
+                        .with_context(|| {
+                            format!("extracting {} to {:?}", version.url, destination_path)
+                        })?;
                 }
                 AssetKind::Zip => {
                     node_runtime::extract_zip(
                         &destination_path,
                         BufReader::new(response.body_mut()),
                     )
-                    .await?;
+                    .await
+                    .with_context(|| {
+                        format!("unzipping {} to {:?}", version.url, destination_path)
+                    })?;
                 }
             };
 

crates/languages/src/typescript.rs 🔗

@@ -1,4 +1,4 @@
-use anyhow::{anyhow, Result};
+use anyhow::{anyhow, Context as _, Result};
 use async_compression::futures::bufread::GzipDecoder;
 use async_tar::Archive;
 use async_trait::async_trait;
@@ -445,14 +445,35 @@ impl LspAdapter for EsLintLspAdapter {
                 AssetKind::TarGz => {
                     let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
                     let archive = Archive::new(decompressed_bytes);
-                    archive.unpack(&destination_path).await?;
+                    archive.unpack(&destination_path).await.with_context(|| {
+                        format!("extracting {} to {:?}", version.url, destination_path)
+                    })?;
+                }
+                AssetKind::Gz => {
+                    let mut decompressed_bytes =
+                        GzipDecoder::new(BufReader::new(response.body_mut()));
+                    let mut file =
+                        fs::File::create(&destination_path).await.with_context(|| {
+                            format!(
+                                "creating a file {:?} for a download from {}",
+                                destination_path, version.url,
+                            )
+                        })?;
+                    futures::io::copy(&mut decompressed_bytes, &mut file)
+                        .await
+                        .with_context(|| {
+                            format!("extracting {} to {:?}", version.url, destination_path)
+                        })?;
                 }
                 AssetKind::Zip => {
                     node_runtime::extract_zip(
                         &destination_path,
                         BufReader::new(response.body_mut()),
                     )
-                    .await?;
+                    .await
+                    .with_context(|| {
+                        format!("unzipping {} to {:?}", version.url, destination_path)
+                    })?;
                 }
             }