diff --git a/Cargo.lock b/Cargo.lock index 92cde8d3763638c82c346598ff10b6bc92e3ca84..04428295994b1a188fb87e74f58edf4749facb34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2428,6 +2428,15 @@ dependencies = [ "libc", ] +[[package]] +name = "bzip2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" +dependencies = [ + "libbz2-rs-sys", +] + [[package]] name = "bzip2-sys" version = "0.1.13+1.0.8" @@ -3467,6 +3476,7 @@ version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef8a506ec4b81c460798f572caead636d57d3d7e940f998160f52bd254bf2d23" dependencies = [ + "bzip2 0.6.1", "compression-core", "deflate64", "flate2", @@ -9670,6 +9680,12 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" +[[package]] +name = "libbz2-rs-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" + [[package]] name = "libc" version = "0.2.182" @@ -22348,7 +22364,7 @@ checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ "aes", "byteorder", - "bzip2", + "bzip2 0.4.4", "constant_time_eq", "crc32fast", "crossbeam-utils", diff --git a/Cargo.toml b/Cargo.toml index 699c795710f327ce76e7c3203dcb978508c5b830..59ec301cf9ae59aa94bd056bd81113eabf5192ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -491,7 +491,7 @@ ashpd = { version = "0.13", default-features = false, features = [ ] } async-channel = "2.5.0" async-compat = "0.2.1" -async-compression = { version = "0.4", features = ["gzip", "futures-io"] } +async-compression = { version = "0.4", features = ["bzip2", "gzip", "futures-io"] } async-dispatcher = "0.1" async-fs = "2.1" async-lock = "2.1" diff --git a/crates/http_client/src/github.rs b/crates/http_client/src/github.rs index e52e2f1d2555de477cd4597826bc3bd8308faf89..6d2150c8566706188b907e0c9c9ddb8e603f867b 100644 --- a/crates/http_client/src/github.rs +++ b/crates/http_client/src/github.rs @@ -144,6 +144,7 @@ pub async fn get_release_by_tag_name( #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum AssetKind { TarGz, + TarBz2, Gz, Zip, } @@ -158,6 +159,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::TarBz2 => "tar.bz2", AssetKind::Gz => "gz", AssetKind::Zip => "zip", } diff --git a/crates/http_client/src/github_download.rs b/crates/http_client/src/github_download.rs index 2ef615ff64c2b564e5c254b9c6ef21413d18bcf2..47ae2c2b36b1ab37b56ab70735c2ce018bc5e275 100644 --- a/crates/http_client/src/github_download.rs +++ b/crates/http_client/src/github_download.rs @@ -5,7 +5,7 @@ use std::{ }; use anyhow::{Context, Result}; -use async_compression::futures::bufread::GzipDecoder; +use async_compression::futures::bufread::{BzDecoder, GzipDecoder}; use futures::{AsyncRead, AsyncSeek, AsyncSeekExt, AsyncWrite, io::BufReader}; use sha2::{Digest, Sha256}; @@ -119,7 +119,7 @@ async fn extract_to_staging( fn staging_path(parent: &Path, asset_kind: AssetKind) -> Result { match asset_kind { - AssetKind::TarGz | AssetKind::Zip => { + AssetKind::TarGz | AssetKind::TarBz2 | AssetKind::Zip => { let dir = tempfile::Builder::new() .prefix(".tmp-github-download-") .tempdir_in(parent) @@ -141,7 +141,7 @@ fn staging_path(parent: &Path, asset_kind: AssetKind) -> Result { async fn cleanup_staging_path(staging_path: &Path, asset_kind: AssetKind) { match asset_kind { - AssetKind::TarGz | AssetKind::Zip => { + AssetKind::TarGz | AssetKind::TarBz2 | AssetKind::Zip => { if let Err(err) = async_fs::remove_dir_all(staging_path).await { log::warn!("failed to remove staging directory {staging_path:?}: {err:?}"); } @@ -170,6 +170,7 @@ async fn stream_response_archive( ) -> Result<()> { match asset_kind { AssetKind::TarGz => extract_tar_gz(destination_path, url, response).await?, + AssetKind::TarBz2 => extract_tar_bz2(destination_path, url, response).await?, AssetKind::Gz => extract_gz(destination_path, url, response).await?, AssetKind::Zip => { util::archive::extract_zip(destination_path, response).await?; @@ -186,6 +187,7 @@ async fn stream_file_archive( ) -> Result<()> { match asset_kind { AssetKind::TarGz => extract_tar_gz(destination_path, url, file_archive).await?, + AssetKind::TarBz2 => extract_tar_bz2(destination_path, url, file_archive).await?, AssetKind::Gz => extract_gz(destination_path, url, file_archive).await?, #[cfg(not(windows))] AssetKind::Zip => { @@ -213,6 +215,20 @@ async fn extract_tar_gz( Ok(()) } +async fn extract_tar_bz2( + destination_path: &Path, + url: &str, + from: impl AsyncRead + Unpin, +) -> Result<(), anyhow::Error> { + let decompressed_bytes = BzDecoder::new(BufReader::new(from)); + let archive = async_tar::Archive::new(decompressed_bytes); + archive + .unpack(&destination_path) + .await + .with_context(|| format!("extracting {url} to {destination_path:?}"))?; + Ok(()) +} + async fn extract_gz( destination_path: &Path, url: &str, diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index 99d9788ddb5aa7497157a0694cad01ad0f4e6bf5..031354b7e974d1a48cbfbd893cacd5b9f3abd96f 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -437,7 +437,7 @@ impl LspInstaller for TyLspAdapter { async_fs::create_dir_all(&destination_path).await?; let server_path = match Self::GITHUB_ASSET_KIND { - AssetKind::TarGz | AssetKind::Gz => destination_path + AssetKind::TarGz | AssetKind::TarBz2 | AssetKind::Gz => destination_path .join(Self::build_asset_name()?.0) .join("ty"), AssetKind::Zip => destination_path.clone().join("ty.exe"), @@ -527,7 +527,7 @@ impl LspInstaller for TyLspAdapter { let path = last.context("no cached binary")?; let path = match TyLspAdapter::GITHUB_ASSET_KIND { - AssetKind::TarGz | AssetKind::Gz => { + AssetKind::TarGz | AssetKind::TarBz2 | AssetKind::Gz => { path.join(Self::build_asset_name()?.0).join("ty") } AssetKind::Zip => path.join("ty.exe"), @@ -2508,7 +2508,7 @@ impl LspInstaller for RuffLspAdapter { } = latest_version; let destination_path = container_dir.join(format!("ruff-{name}")); let server_path = match Self::GITHUB_ASSET_KIND { - AssetKind::TarGz | AssetKind::Gz => destination_path + AssetKind::TarGz | AssetKind::TarBz2 | AssetKind::Gz => destination_path .join(Self::build_asset_name()?.0) .join("ruff"), AssetKind::Zip => destination_path.clone().join("ruff.exe"), @@ -2598,7 +2598,7 @@ impl LspInstaller for RuffLspAdapter { let path = last.context("no cached binary")?; let path = match Self::GITHUB_ASSET_KIND { - AssetKind::TarGz | AssetKind::Gz => { + AssetKind::TarGz | AssetKind::TarBz2 | AssetKind::Gz => { path.join(Self::build_asset_name()?.0).join("ruff") } AssetKind::Zip => path.join("ruff.exe"), diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index e463a6c62dd6a0625c8ee7c6d314b296b881157e..27f149691b9276087567a827bbfd4f59b96440e6 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -202,6 +202,7 @@ impl RustLspAdapter { async fn build_asset_name() -> String { let extension = match Self::GITHUB_ASSET_KIND { AssetKind::TarGz => "tar.gz", + AssetKind::TarBz2 => "tar.bz2", AssetKind::Gz => "gz", AssetKind::Zip => "zip", }; @@ -706,7 +707,7 @@ impl LspInstaller for RustLspAdapter { } = version; let destination_path = container_dir.join(format!("rust-analyzer-{name}")); let server_path = match Self::GITHUB_ASSET_KIND { - AssetKind::TarGz | AssetKind::Gz => destination_path.clone(), // Tar and gzip extract in place. + AssetKind::TarGz | AssetKind::TarBz2 | AssetKind::Gz => destination_path.clone(), // Tar and gzip extract in place. AssetKind::Zip => destination_path.clone().join("rust-analyzer.exe"), // zip contains a .exe }; @@ -1280,8 +1281,8 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option return Ok(None), }; let path = match RustLspAdapter::GITHUB_ASSET_KIND { - AssetKind::TarGz | AssetKind::Gz => path, // Tar and gzip extract in place. - AssetKind::Zip => path.join("rust-analyzer.exe"), // zip contains a .exe + AssetKind::TarGz | AssetKind::TarBz2 | AssetKind::Gz => path, // Tar and gzip extract in place. + AssetKind::Zip => path.join("rust-analyzer.exe"), // zip contains a .exe }; anyhow::Ok(Some(LanguageServerBinary { diff --git a/crates/project/src/agent_server_store.rs b/crates/project/src/agent_server_store.rs index d8978b6512fa0c437a3d07feb929132a24461d0e..f1440fccee1e37e4762f5358f5144a5f977b498a 100644 --- a/crates/project/src/agent_server_store.rs +++ b/crates/project/src/agent_server_store.rs @@ -1102,6 +1102,8 @@ impl ExternalAgentServer for LocalExtensionArchiveAgent { AssetKind::Zip } else if archive_url.ends_with(".tar.gz") || archive_url.ends_with(".tgz") { AssetKind::TarGz + } else if archive_url.ends_with(".tar.bz2") || archive_url.ends_with(".tbz2") { + AssetKind::TarBz2 } else { anyhow::bail!("unsupported archive type in URL: {}", archive_url); }; @@ -1288,6 +1290,8 @@ impl ExternalAgentServer for LocalRegistryArchiveAgent { AssetKind::Zip } else if archive_url.ends_with(".tar.gz") || archive_url.ends_with(".tgz") { AssetKind::TarGz + } else if archive_url.ends_with(".tar.bz2") || archive_url.ends_with(".tbz2") { + AssetKind::TarBz2 } else { anyhow::bail!("unsupported archive type in URL: {}", archive_url); }; diff --git a/script/licenses/zed-licenses.toml b/script/licenses/zed-licenses.toml index 572dd5c14aebcdea3544ac15b751be4c212ecf52..db14a280f2f1537c37f96f6fa180b96d54afa209 100644 --- a/script/licenses/zed-licenses.toml +++ b/script/licenses/zed-licenses.toml @@ -26,6 +26,7 @@ accepted = [ "OpenSSL", "Zlib", "BSL-1.0", + "bzip2-1.0.6", ] [procinfo.clarify]