diff --git a/Cargo.lock b/Cargo.lock index 049bd7d59..e03ef44f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -167,6 +167,62 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axoasset" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d492e2a60fbacf2154ee58fd4bc3dd7385a5febf10fef6145924fd3117cd920" +dependencies = [ + "camino", + "miette", + "mime", + "serde", + "serde_json", + "thiserror", + "url", + "walkdir", +] + +[[package]] +name = "axoprocess" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4de46920588aef95658797996130bacd542436aee090084646521260a74bda7d" +dependencies = [ + "miette", + "thiserror", + "tracing", +] + +[[package]] +name = "axotag" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d888fac0b73e64cbdf36a743fc5a25af5ae955c357535cb420b389bf1e1a6c54" +dependencies = [ + "miette", + "semver", + "thiserror", +] + +[[package]] +name = "axoupdater" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd70e10a815d55bcef2a2e0907b189fc6d800558b7481883ad6535d5ae7cd42" +dependencies = [ + "axoasset", + "axoprocess", + "axotag", + "camino", + "homedir", + "miette", + "reqwest", + "serde", + "temp-dir", + "thiserror", +] + [[package]] name = "axum" version = "0.6.20" @@ -319,6 +375,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + [[package]] name = "cap-fs-ext" version = "2.0.1" @@ -1518,6 +1583,20 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls 0.21.12", + "tokio", + "tokio-rustls", +] + [[package]] name = "hyper-timeout" version = "0.4.1" @@ -1982,6 +2061,7 @@ version = "0.1.1" dependencies = [ "ai_builtins", "anyhow", + "axoupdater", "buildkite-test-collector", "chrono", "clap", @@ -2265,6 +2345,29 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miette" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" +dependencies = [ + "cfg-if", + "miette-derive", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + [[package]] name = "mime" version = "0.3.17" @@ -3050,6 +3153,7 @@ dependencies = [ "http", "http-body", "hyper", + "hyper-rustls", "hyper-tls", "ipnet", "js-sys", @@ -3059,6 +3163,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls 0.21.12", "rustls-pemfile", "serde", "serde_json", @@ -3067,6 +3172,7 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", + "tokio-rustls", "tokio-util", "tower-service", "url", @@ -3074,6 +3180,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", + "webpki-roots 0.25.4", "winreg", ] @@ -3141,6 +3248,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + [[package]] name = "rustls" version = "0.22.2" @@ -3150,7 +3269,7 @@ dependencies = [ "log", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.102.2", "subtle", "zeroize", ] @@ -3170,6 +3289,16 @@ version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustls-webpki" version = "0.102.2" @@ -3223,6 +3352,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -3538,6 +3677,12 @@ version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +[[package]] +name = "temp-dir" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f227968ec00f0e5322f9b8173c7a0cbcff6181a0a5b28e9892491c286277231" + [[package]] name = "tempfile" version = "3.10.1" @@ -3567,18 +3712,18 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", @@ -3660,6 +3805,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.14" @@ -4177,13 +4332,13 @@ dependencies = [ "flate2", "log", "once_cell", - "rustls", + "rustls 0.22.2", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.102.2", "serde", "serde_json", "url", - "webpki-roots", + "webpki-roots 0.26.1", ] [[package]] @@ -4872,6 +5027,12 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "webpki-roots" version = "0.26.1" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 5384485f1..e1c8a674e 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -58,6 +58,9 @@ clap-markdown = { git = "https://github.com/getgrit/clap-markdown", rev = "4568d flate2 = { version = "1.0.17", features = [ "rust_backend", ], default-features = false } +axoupdater = { version = "0.6.2", default-features = false, features = [ + "github_releases" +] } opentelemetry-otlp = { version = "0.14.0", optional = true, features = [ "http-proto", diff --git a/crates/cli/src/updater.rs b/crates/cli/src/updater.rs index a910dd5b7..0ccb80fc4 100644 --- a/crates/cli/src/updater.rs +++ b/crates/cli/src/updater.rs @@ -1,5 +1,6 @@ use anyhow::bail; use anyhow::{Context, Result}; +use axoupdater::{AxoUpdater, ReleaseSource, ReleaseSourceType, Version}; use chrono::{DateTime, NaiveDateTime, Utc}; use colored::Colorize; use futures_util::StreamExt; @@ -300,18 +301,16 @@ impl Updater { let app_string = app.get_base_name(); let current_binary = self.binaries.get(&app_string).cloned(); tasks.push(tokio::spawn(async move { - let (_, info_url) = match get_release_url(app, None, None).await { - Ok(urls) => urls, - Err(_) => return, - }; - let manifest = match fetch_manifest(&info_url, app).await { - Ok(manifest) => manifest, - Err(_) => return, - }; - if let Some(current_manifest) = current_binary { - if manifest.release != current_manifest.release && manifest.release.is_some() { + { + let (result, version) = match app { + SupportedApp::Marzano | SupportedApp::Gouda => { + check_release_axo(app, ¤t_binary).await.unwrap() + } + _ => check_release_internal(app, ¤t_binary).await.unwrap(), + }; + if result { let mut found = found_updates.lock().await; - found.push((app, manifest.version.unwrap())); + found.push((app, version.unwrap())); } } })); @@ -334,7 +333,51 @@ impl Updater { Ok(!found_updates.is_empty()) } - pub async fn install_latest( + pub async fn install_latest_axo(&mut self, app: SupportedApp) -> Result<()> { + let mut updater = AxoUpdater::new_for(&app.get_base_name()); + // Disable axo installers' verbose output with info on + // where the tool is installed + updater.disable_installer_output(); + + // Set "always update" to match install_latest_internal, + // and because this is preceded by an "is this outdated" check. + updater.always_update(true); + + // This can be autodetected if grit was itself installed via an axo + // installer in the past, but specifying this source manually is + // necessary if no cargo-dist-style install receipt exists. + updater.set_release_source(ReleaseSource { + release_type: ReleaseSourceType::GitHub, + owner: "getgrit".to_owned(), + name: "gritql".to_owned(), + app_name: app.get_base_name(), + }); + updater.set_install_dir(&self.install_path.to_string_lossy()); + match updater.run().await { + Ok(result) => { + if let Some(outcome) = result { + self.set_app_version( + app, + outcome.new_version.to_string(), + outcome.new_version_tag, + )?; + self.dump().await?; + // This path is primarily hit if no releases exist, or + // if `always_update(false)` is set and there isn't a newer + // version available. + } else { + info!("New version of {app} not installed"); + } + } + Err(e) => { + return Err(e.into()); + } + } + + Ok(()) + } + + pub async fn install_latest_internal( &mut self, app: SupportedApp, os: Option<&str>, @@ -361,6 +404,18 @@ impl Updater { Ok(()) } + pub async fn install_latest( + &mut self, + app: SupportedApp, + os: Option<&str>, + arch: Option<&str>, + ) -> Result<()> { + match app { + SupportedApp::Marzano | SupportedApp::Gouda => self.install_latest_axo(app).await, + _ => self.install_latest_internal(app, os, arch).await, + } + } + pub fn get_context(&self) -> Result { let auth = self.get_auth(); let context = if let Some(auth) = auth { @@ -681,6 +736,49 @@ async fn get_release_url( )) } +pub async fn check_release_internal( + app: SupportedApp, + current_binary: &Option, +) -> Result<(bool, Option)> { + let (_, info_url) = match get_release_url(app, None, None).await { + Ok(urls) => urls, + Err(_) => return Ok((false, None)), + }; + let manifest = match fetch_manifest(&info_url, app).await { + Ok(manifest) => manifest, + Err(_) => return Ok((false, None)), + }; + if let Some(current_manifest) = current_binary { + let result = manifest.release != current_manifest.release && manifest.release.is_some(); + Ok((result, manifest.version)) + } else { + Ok((false, None)) + } +} + +pub async fn check_release_axo( + app: SupportedApp, + current_binary: &Option, +) -> Result<(bool, Option)> { + let mut updater = AxoUpdater::new_for(&app.get_base_name()); + // Avoids a look up to cargo-dist's install receipt, which may not exist yet; + // we want to fetch this data from the current manifest if at all possible + if let Some(current_manifest) = current_binary { + if let Some(current) = ¤t_manifest.version { + updater.set_current_version(Version::parse(current)?)?; + } + } + updater.set_release_source(ReleaseSource { + release_type: ReleaseSourceType::GitHub, + owner: "getgrit".to_owned(), + name: "gritql".to_owned(), + app_name: app.get_base_name(), + }); + let update_needed = updater.is_update_needed().await?; + let new_version = updater.query_new_version().await?.map(|v| v.to_string()); + Ok((update_needed, new_version)) +} + async fn fetch_manifest(relative_url: &str, app: SupportedApp) -> Result { let client = reqwest::Client::builder().build()?; let url = format!("{}{}", KEYGEN_API, relative_url);