From d5963ce229c92c48476398326be6db92666140a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Misty=20De=20M=C3=A9o?= Date: Tue, 30 Apr 2024 15:51:56 -0700 Subject: [PATCH] fix(updater): cross-platform compatibility, robustness enhancements (#293) --- Cargo.lock | 16 ++++++++ crates/cli/Cargo.toml | 3 +- crates/cli/src/updater.rs | 81 +++++++++++++++++++-------------------- 3 files changed, 57 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b7ba2784..79a8b28ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1950,6 +1950,7 @@ dependencies = [ "dialoguer", "env_logger", "flate2", + "futures-util", "git2", "grit-util", "grit_cache", @@ -2967,10 +2968,12 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "winreg", ] @@ -4318,6 +4321,19 @@ dependencies = [ "leb128", ] +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmparser" version = "0.118.2" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index f1ea04365..fc815f233 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -26,7 +26,8 @@ serde_json = { version = "1.0.96" } uuid = { version = "1.1", features = ["v4", "serde"] } tokio = { version = "1", features = ["full"] } chrono = { version = "0.4.26", features = ["serde"] } -reqwest = { version = "0.11", features = ["json"] } +reqwest = { version = "0.11", features = ["json", "stream"] } +futures-util = "0.3.30" lazy_static = { version = "1.4.0" } indicatif-log-bridge = { version = "0.2.1" } colored = { version = "2.0.4" } diff --git a/crates/cli/src/updater.rs b/crates/cli/src/updater.rs index 5b21b9abc..171c31ccd 100644 --- a/crates/cli/src/updater.rs +++ b/crates/cli/src/updater.rs @@ -2,6 +2,7 @@ use anyhow::bail; use anyhow::{Context, Result}; use chrono::{DateTime, NaiveDateTime, Utc}; use colored::Colorize; +use futures_util::StreamExt; use log::info; use marzano_auth::info::AuthInfo; use marzano_gritmodule::config::REPO_CONFIG_DIR_NAME; @@ -359,7 +360,10 @@ impl Updater { let (downloaded, manifest) = tokio::try_join!(downloader, manifest_fetcher)?; // Unzip the artifact - self.unpack_artifact(app, downloaded).await?; + self.unpack_artifact(app, downloaded.to_owned()).await?; + + // Clean up the temp artifact + async_fs::remove_file(&downloaded).await?; self.set_app_version(app, manifest.version.unwrap(), manifest.release.unwrap())?; self.dump().await?; @@ -444,17 +448,17 @@ impl Updater { async fn download_artifact(&self, app: SupportedApp, artifact_url: String) -> Result { let target_path = self.bin_path.join(format!("{}-temp", app.get_bin_name())); - // Download via curl - let cmd = AsyncCommand::new("curl") - .arg("-L") - .arg("-o") - .arg(&target_path) - .arg(&artifact_url) - .output() - .await?; - - if !cmd.status.success() { - bail!("Failed to download artifact: {:?}", cmd); + match reqwest::get(&artifact_url).await { + Ok(response) => { + let mut file = async_fs::File::create(&target_path).await?; + let mut bytes_stream = response.bytes_stream(); + while let Some(chunk) = bytes_stream.next().await { + tokio::io::copy(&mut chunk?.as_ref(), &mut file).await?; + } + } + Err(e) => { + bail!("Failed to download artifact: {:?}", e); + } } Ok(target_path) @@ -484,42 +488,35 @@ impl Updater { } let target_path = self.get_app_bin(&app)?; - let output = AsyncCommand::new("mv") - .arg(format!("{}/{}", unpacked_dir.display(), app.get_bin_name())) - .arg(&target_path) - .output() - .await?; - - if !output.status.success() { - let fallback_output = AsyncCommand::new("mv") - .arg(format!( - "{}/{}", - unpacked_dir.display(), - app.get_fallback_bin_name() - )) - .arg(&target_path) - .output() - .await?; - if !fallback_output.status.success() { - bail!("Failed to move files: {:?}", output); + if async_fs::rename(unpacked_dir.join(app.get_bin_name()), &target_path) + .await + .is_err() + { + if let Err(e) = + async_fs::rename(unpacked_dir.join(app.get_fallback_bin_name()), &target_path).await + { + bail!("Failed to move files: {:?}", e); } } // Make the file executable - let output = AsyncCommand::new("chmod") - .arg("+x") - .arg(&target_path) - .output() - .await?; + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + + let target_file = std::fs::File::open(&target_path)?; + let mut perms = target_file.metadata()?.permissions(); + perms.set_mode(0o744); + if let Err(e) = target_file.set_permissions(perms) { + bail!( + "Failed to make {} executable: {:?}", + target_path.display(), + e + ); + } - if !output.status.success() { - bail!( - "Failed to make {} executable: {:?}", - target_path.display(), - output - ); + info!("Successfully made {} executable", target_path.display()); } - info!("Successfully made {} executable", target_path.display()); async_fs::remove_dir_all(&unpacked_dir).await?;