From bea9f9ab5b40230b3817cee8be5dec4fc208740a Mon Sep 17 00:00:00 2001 From: Teakowa <27560638+Teakowa@users.noreply.github.com> Date: Fri, 4 Sep 2026 04:30:57 +0800 Subject: [PATCH] fix(provider): match opy release tarballs Refs #244 --- Cargo.lock | 76 ------------- Cargo.toml | 1 - crates/wright-driver/Cargo.toml | 1 - crates/wright-driver/src/opy_provider.rs | 134 ++--------------------- 4 files changed, 12 insertions(+), 200 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17bd714..ae8cf50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,15 +87,6 @@ version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" -dependencies = [ - "derive_arbitrary", -] - [[package]] name = "atomic-waker" version = "1.1.2" @@ -319,12 +310,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-utils" -version = "0.8.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" - [[package]] name = "crypto-common" version = "0.1.7" @@ -358,17 +343,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "derive_arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - [[package]] name = "digest" version = "0.10.7" @@ -1635,26 +1609,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "thiserror" -version = "2.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.3", -] - [[package]] name = "time" version = "0.3.45" @@ -2206,7 +2160,6 @@ dependencies = [ "wright-opy", "wright-ostw", "wright-transform", - "zip", ] [[package]] @@ -2407,23 +2360,6 @@ dependencies = [ "syn 3.0.3", ] -[[package]] -name = "zip" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" -dependencies = [ - "arbitrary", - "crc32fast", - "crossbeam-utils", - "displaydoc", - "flate2", - "indexmap", - "memchr", - "thiserror", - "zopfli", -] - [[package]] name = "zlib-rs" version = "0.6.7" @@ -2435,15 +2371,3 @@ name = "zmij" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" - -[[package]] -name = "zopfli" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" -dependencies = [ - "bumpalo", - "crc32fast", - "log", - "simd-adler32", -] diff --git a/Cargo.toml b/Cargo.toml index 33f29b2..6bf73b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,6 @@ sha2 = "0.10" flate2 = "1" tar = "0.4" ureq = "2" -zip = { version = "2", default-features = false, features = ["deflate"] } wright-analyzer = { path = "crates/wright-analyzer" } wright-core = { path = "crates/wright-core" } wright-driver = { path = "crates/wright-driver" } diff --git a/crates/wright-driver/Cargo.toml b/crates/wright-driver/Cargo.toml index e07dfac..f83e599 100644 --- a/crates/wright-driver/Cargo.toml +++ b/crates/wright-driver/Cargo.toml @@ -17,7 +17,6 @@ sha2.workspace = true flate2.workspace = true tar.workspace = true ureq.workspace = true -zip.workspace = true wright-analyzer.workspace = true wright-core.workspace = true wright-ir.workspace = true diff --git a/crates/wright-driver/src/opy_provider.rs b/crates/wright-driver/src/opy_provider.rs index 34d2a19..ae18ec8 100644 --- a/crates/wright-driver/src/opy_provider.rs +++ b/crates/wright-driver/src/opy_provider.rs @@ -12,11 +12,11 @@ use std::time::Duration; use flate2::read::GzDecoder; use sha2::{Digest, Sha256}; -use zip::ZipArchive; const DEFAULT_API_URL: &str = "https://api.github.com/repos/wrightkit/opy-rs/releases/latest"; const DEFAULT_BASE_URL: &str = "https://github.com/wrightkit/opy-rs/releases/download"; const MAX_DOWNLOAD_BYTES: u64 = 128 * 1024 * 1024; +const PROVIDER_ARCHIVE_EXTENSION: &str = "tar.gz"; /// The LPP language id served by the first-party OPY provider. pub const OPY_LANGUAGE_ID: &str = "opy"; @@ -193,10 +193,7 @@ impl OpyProviderResolver { Some(version) => normalize_version(version)?, None => self.fetch_latest_version()?, }; - let archive_name = format!( - "opy-provider-{version}-{target}.{}", - archive_extension(target) - ); + let archive_name = format!("opy-provider-{version}-{target}.{PROVIDER_ARCHIVE_EXTENSION}"); let archive_url = format!( "{}/v{version}/{archive_name}", self.base_url.trim_end_matches('/') @@ -278,7 +275,7 @@ impl OpyProviderResolver { staging.display() )) })?; - extract_provider(archive, &staging, version, target)?; + extract_provider(archive, &staging, target)?; std::fs::create_dir_all(final_dir.parent().expect("provider target has a parent")) .map_err(|error| { OpyProviderError::install(format!( @@ -568,14 +565,9 @@ fn verify_checksum( fn extract_provider( archive: &[u8], destination: &Path, - version: &str, target: &str, ) -> Result<(), OpyProviderError> { - let expected_root = format!("opy-provider-{version}-{target}"); let binary = provider_binary(target); - if archive_extension(target) == "zip" { - return extract_provider_zip(archive, destination, &expected_root, binary); - } let decoder = GzDecoder::new(archive); let mut archive = tar::Archive::new(decoder); let mut found = false; @@ -589,93 +581,12 @@ fn extract_provider( let path = entry.path().map_err(|error| { OpyProviderError::install(format!("cannot inspect OPY provider archive path: {error}")) })?; - let components: Vec<_> = path.components().collect(); - let expected_path = Path::new(&expected_root).join(binary); - let is_root = components.len() == 1 - && components[0] == std::path::Component::Normal(expected_root.as_ref()); - if path == expected_path { - if !entry.header().entry_type().is_file() { - return Err(OpyProviderError::install( - "OPY provider archive executable is not a regular file", - )); - } - let output = destination.join(binary); - let mut file = std::fs::File::create(&output).map_err(|error| { - OpyProviderError::install(format!( - "cannot create staged OPY provider '{}': {error}", - output.display() - )) - })?; - std::io::copy(&mut entry, &mut file).map_err(|error| { - OpyProviderError::install(format!( - "cannot unpack staged OPY provider '{}': {error}", - output.display() - )) - })?; - file.sync_all().map_err(|error| { - OpyProviderError::install(format!( - "cannot persist staged OPY provider '{}': {error}", - output.display() - )) - })?; - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - let mode = entry.header().mode().unwrap_or(0o755) | 0o111; - std::fs::set_permissions(&output, std::fs::Permissions::from_mode(mode)).map_err( - |error| { - OpyProviderError::install(format!( - "cannot make staged OPY provider executable '{}': {error}", - output.display() - )) - }, - )?; - } - found = true; - } else if !is_root { + if path != Path::new(binary) { return Err(OpyProviderError::install( "OPY provider archive contains an unexpected path", )); } - } - if !found || !is_executable(&destination.join(binary)) { - return Err(OpyProviderError::install( - "OPY provider archive does not contain an executable provider", - )); - } - Ok(()) -} - -fn extract_provider_zip( - archive: &[u8], - destination: &Path, - expected_root: &str, - binary: &str, -) -> Result<(), OpyProviderError> { - let mut archive = ZipArchive::new(std::io::Cursor::new(archive)).map_err(|error| { - OpyProviderError::install(format!("cannot read OPY provider archive: {error}")) - })?; - let expected_path = format!("{expected_root}/{binary}"); - let mut found = false; - for index in 0..archive.len() { - let mut entry = archive.by_index(index).map_err(|error| { - OpyProviderError::install(format!("cannot read OPY provider archive entry: {error}")) - })?; - let name = entry.name(); - if name == expected_root || name == format!("{expected_root}/") { - if !entry.is_dir() { - return Err(OpyProviderError::install( - "OPY provider archive root is not a directory", - )); - } - continue; - } - if name != expected_path { - return Err(OpyProviderError::install( - "OPY provider archive contains an unexpected path", - )); - } - if entry.is_dir() { + if !entry.header().entry_type().is_file() { return Err(OpyProviderError::install( "OPY provider archive executable is not a regular file", )); @@ -702,7 +613,8 @@ fn extract_provider_zip( #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; - std::fs::set_permissions(&output, std::fs::Permissions::from_mode(0o755)).map_err( + let mode = entry.header().mode().unwrap_or(0o755) | 0o111; + std::fs::set_permissions(&output, std::fs::Permissions::from_mode(mode)).map_err( |error| { OpyProviderError::install(format!( "cannot make staged OPY provider executable '{}': {error}", @@ -729,18 +641,9 @@ fn provider_binary(target: &str) -> &'static str { } } -fn archive_extension(target: &str) -> &'static str { - if target == "x86_64-pc-windows-msvc" { - "zip" - } else { - "tar.gz" - } -} - #[cfg(test)] mod tests { use super::*; - use std::io::Cursor; use std::net::{TcpListener, TcpStream}; use std::sync::{ Arc, @@ -757,27 +660,14 @@ mod tests { root } - fn archive(version: &str, target: &str, body: &[u8]) -> Vec { - let root = format!("opy-provider-{version}-{target}"); + fn archive(_version: &str, target: &str, body: &[u8]) -> Vec { let binary = provider_binary(target); - if archive_extension(target) == "zip" { - let mut writer = zip::ZipWriter::new(Cursor::new(Vec::new())); - let options = zip::write::SimpleFileOptions::default().unix_permissions(0o755); - writer.add_directory(format!("{root}/"), options).unwrap(); - writer - .start_file(format!("{root}/{binary}"), options) - .unwrap(); - writer.write_all(body).unwrap(); - return writer.finish().unwrap().into_inner(); - } let mut builder = tar::Builder::new(Vec::new()); let mut header = tar::Header::new_gnu(); header.set_size(body.len() as u64); header.set_mode(0o755); header.set_cksum(); - builder - .append_data(&mut header, format!("{root}/{binary}"), body) - .unwrap(); + builder.append_data(&mut header, binary, body).unwrap(); let mut encoder = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::default()); encoder.write_all(&builder.into_inner().unwrap()).unwrap(); encoder.finish().unwrap() @@ -874,16 +764,16 @@ mod tests { } #[test] - fn windows_target_uses_exe_and_zip_archive() { + fn windows_target_uses_exe_and_tar_gz_archive() { let target = target_for("windows", "x86_64").unwrap(); assert_eq!(target, "x86_64-pc-windows-msvc"); assert_eq!(provider_binary(&target), "opy-provider.exe"); - assert_eq!(archive_extension(&target), "zip"); + assert_eq!(PROVIDER_ARCHIVE_EXTENSION, "tar.gz"); let root = test_root("windows"); let version = "1.0.0"; let bytes = archive(version, &target, b"windows-provider"); - let checksum = format!("{} opy-provider-{version}-{target}.zip\n", hex(&bytes)); + let checksum = format!("{} opy-provider-{version}-{target}.tar.gz\n", hex(&bytes)); let (base_url, requests, server) = test_server( format!(r#"{{"tag_name":"v{version}"}}"#).into_bytes(), bytes,