From 3dd223447455ada85e7be112690e54987a316db8 Mon Sep 17 00:00:00 2001 From: Miles Johnson Date: Thu, 7 Sep 2023 19:55:08 -0700 Subject: [PATCH 1/2] Add canary flag. --- crates/cli/src/commands/install.rs | 11 +++++++++-- crates/cli/src/commands/install_all.rs | 1 + crates/cli/src/commands/run.rs | 11 ++++++----- crates/core/src/tool.rs | 4 +++- crates/core/src/tool_manifest.rs | 4 ++-- crates/core/src/version.rs | 8 ++++++++ crates/core/src/version_resolver.rs | 3 +++ crates/pdk-api/src/error.rs | 3 +++ 8 files changed, 35 insertions(+), 10 deletions(-) diff --git a/crates/cli/src/commands/install.rs b/crates/cli/src/commands/install.rs index 84e361eba..d65b1e1b7 100644 --- a/crates/cli/src/commands/install.rs +++ b/crates/cli/src/commands/install.rs @@ -17,6 +17,9 @@ pub struct InstallArgs { #[arg(default_value = "latest", help = "Version or alias of tool")] pub spec: Option, + #[arg(long, help = "Install a canary (next, nightly, etc) version")] + pub canary: bool, + #[arg(long, help = "Pin version as the global default")] pub pin: bool, @@ -26,10 +29,14 @@ pub struct InstallArgs { } pub async fn internal_install(args: InstallArgs) -> SystemResult { - let version = args.spec.clone().unwrap_or_default(); let mut tool = load_tool(&args.id).await?; + let version = if args.canary { + UnresolvedVersionSpec::Canary + } else { + args.spec.clone().unwrap_or_default() + }; - if tool.is_setup(&version).await? { + if !args.canary && tool.is_setup(&version).await? { info!( "{} has already been installed at {}", tool.get_name(), diff --git a/crates/cli/src/commands/install_all.rs b/crates/cli/src/commands/install_all.rs index b883bd7e1..304f5cb9b 100644 --- a/crates/cli/src/commands/install_all.rs +++ b/crates/cli/src/commands/install_all.rs @@ -62,6 +62,7 @@ pub async fn install_all() { for (id, version) in config.tools { futures.push(internal_install(InstallArgs { + canary: false, id, pin: false, passthrough: vec![], diff --git a/crates/cli/src/commands/run.rs b/crates/cli/src/commands/run.rs index 812600c0b..5a4725c20 100644 --- a/crates/cli/src/commands/run.rs +++ b/crates/cli/src/commands/run.rs @@ -49,11 +49,14 @@ pub async fn run(args: ArgsRef) -> SystemResult { // Install the tool debug!("Auto-install setting is configured, attempting to install"); + let resolved_version = tool.get_resolved_version(); + internal_install(InstallArgs { + canary: resolved_version.is_canary(), id: args.id.clone(), - spec: Some(tool.get_resolved_version().to_unresolved_spec()), pin: false, passthrough: vec![], + spec: Some(resolved_version.to_unresolved_spec()), }) .await?; @@ -61,11 +64,9 @@ pub async fn run(args: ArgsRef) -> SystemResult { tool.locate_bins().await?; } - let resolved_version = tool.get_resolved_version(); - // Update the last used timestamp if env::var("PROTO_SKIP_USED_AT").is_err() { - tool.manifest.track_used_at(&resolved_version); + tool.manifest.track_used_at(tool.get_resolved_version()); // Ignore errors in case of race conditions... // this timestamp isn't *super* important @@ -126,7 +127,7 @@ pub async fn run(args: ArgsRef) -> SystemResult { .args(&args.passthrough) .env( format!("{}_VERSION", tool.get_env_var_prefix()), - resolved_version.to_string(), + tool.get_resolved_version().to_string(), ) .env( format!("{}_BIN", tool.get_env_var_prefix()), diff --git a/crates/core/src/tool.rs b/crates/core/src/tool.rs index d020c01ce..e73a771ec 100644 --- a/crates/core/src/tool.rs +++ b/crates/core/src/tool.rs @@ -1118,7 +1118,9 @@ impl Tool { fn is_installed(&self) -> bool { let dir = self.get_tool_dir(); - self.version.as_ref().is_some_and(|v| !v.is_latest()) + self.version + .as_ref() + .is_some_and(|v| !v.is_latest() && !v.is_canary()) && dir.exists() && !fs::is_dir_locked(dir) } diff --git a/crates/core/src/tool_manifest.rs b/crates/core/src/tool_manifest.rs index b954e7d3b..c576dad5a 100644 --- a/crates/core/src/tool_manifest.rs +++ b/crates/core/src/tool_manifest.rs @@ -124,9 +124,9 @@ impl ToolManifest { Ok(()) } - pub fn track_used_at(&mut self, version: &VersionSpec) { + pub fn track_used_at(&mut self, version: VersionSpec) { self.versions - .entry(version.to_owned()) + .entry(version) .and_modify(|v| { v.last_used_at = Some(now()); }) diff --git a/crates/core/src/version.rs b/crates/core/src/version.rs index 7abb7f279..eaa3c4423 100644 --- a/crates/core/src/version.rs +++ b/crates/core/src/version.rs @@ -11,6 +11,7 @@ use std::str::FromStr; #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(untagged, into = "String", try_from = "String")] pub enum UnresolvedVersionSpec { + Canary, Alias(String), Req(VersionReq), ReqAny(Vec), @@ -24,6 +25,7 @@ impl UnresolvedVersionSpec { pub fn to_spec(&self) -> VersionSpec { match self { + UnresolvedVersionSpec::Canary => VersionSpec::Alias("canary".to_owned()), UnresolvedVersionSpec::Alias(alias) => VersionSpec::Alias(alias.to_owned()), UnresolvedVersionSpec::Version(version) => VersionSpec::Version(version.to_owned()), _ => unreachable!(), @@ -43,6 +45,10 @@ impl FromStr for UnresolvedVersionSpec { fn from_str(value: &str) -> Result { let value = remove_space_after_gtlt(remove_v_prefix(value.trim().replace(".*", ""))); + if value == "canary" { + return Ok(UnresolvedVersionSpec::Canary); + } + if is_alias_name(&value) { return Ok(UnresolvedVersionSpec::Alias(value)); } @@ -115,6 +121,7 @@ impl Into for UnresolvedVersionSpec { impl Display for UnresolvedVersionSpec { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + Self::Canary => write!(f, "canary"), Self::Alias(alias) => write!(f, "{}", alias), Self::Req(req) => write!(f, "{}", req), Self::ReqAny(reqs) => write!( @@ -133,6 +140,7 @@ impl Display for UnresolvedVersionSpec { impl PartialEq for UnresolvedVersionSpec { fn eq(&self, other: &VersionSpec) -> bool { match (self, other) { + (Self::Canary, VersionSpec::Alias(a)) => a == "canary", (Self::Alias(a1), VersionSpec::Alias(a2)) => a1 == a2, (Self::Version(v1), VersionSpec::Version(v2)) => v1 == v2, _ => false, diff --git a/crates/core/src/version_resolver.rs b/crates/core/src/version_resolver.rs index 1d903bffd..c283d3fbd 100644 --- a/crates/core/src/version_resolver.rs +++ b/crates/core/src/version_resolver.rs @@ -95,6 +95,9 @@ pub fn resolve_version( }; match &candidate { + UnresolvedVersionSpec::Canary => { + return Ok(VersionSpec::Alias("canary".into())); + } UnresolvedVersionSpec::Alias(alias) => { let mut alias_value = None; diff --git a/crates/pdk-api/src/error.rs b/crates/pdk-api/src/error.rs index 74cd56be1..fb34d5387 100644 --- a/crates/pdk-api/src/error.rs +++ b/crates/pdk-api/src/error.rs @@ -8,6 +8,9 @@ pub enum PluginError { #[error("Unable to install {tool}, unsupported architecture {arch}.")] UnsupportedArch { tool: String, arch: String }, + #[error("{tool} does not support canary versions or installations.")] + UnsupportedCanary { tool: String }, + #[error("Unable to install {tool}, unsupported OS {os}.")] UnsupportedOS { tool: String, os: String }, From 899160439101f1010fa96d46ea5ca75ac79a9d42 Mon Sep 17 00:00:00 2001 From: Miles Johnson Date: Fri, 8 Sep 2023 11:24:35 -0700 Subject: [PATCH 2/2] More work. --- crates/core/src/tool.rs | 30 ++++++++++++++++++++---------- crates/pdk-api/src/error.rs | 2 +- crates/pdk/src/helpers.rs | 12 ++++++++++++ crates/pdk/src/macros.rs | 20 ++++++++++++++++---- crates/schema-plugin/src/schema.rs | 2 ++ 5 files changed, 51 insertions(+), 15 deletions(-) diff --git a/crates/core/src/tool.rs b/crates/core/src/tool.rs index e73a771ec..91f1f7157 100644 --- a/crates/core/src/tool.rs +++ b/crates/core/src/tool.rs @@ -372,11 +372,11 @@ impl Tool { if cache_path.exists() && (is_cache_enabled() || is_offline()) { let metadata = fs::metadata(&cache_path)?; - // If offline, always use the cache, otherwise only within the last 24 hours + // If offline, always use the cache, otherwise only within the last 12 hours let read_cache = if is_offline() { true } else if let Ok(modified_time) = metadata.modified().or_else(|_| metadata.created()) { - modified_time > SystemTime::now() - Duration::from_secs(60 * 60 * 24) + modified_time > SystemTime::now() - Duration::from_secs(60 * 60 * 12) } else { false }; @@ -423,11 +423,27 @@ impl Tool { return Ok(()); } + debug!( + tool = self.id.as_str(), + initial_version = initial_version.to_string(), + "Resolving a semantic version or alias", + ); + // If offline but we have a fully qualified semantic version, - // exit early and assume the version is legitimate! - if is_offline() && matches!(initial_version, UnresolvedVersionSpec::Version(_)) { + // exit early and assume the version is legitimate! Additionally, + // canary is a special type that we can simply just use. + if is_offline() && matches!(initial_version, UnresolvedVersionSpec::Version(_)) + || matches!(initial_version, UnresolvedVersionSpec::Canary) + { let version = initial_version.to_spec(); + debug!( + tool = self.id.as_str(), + version = version.to_string(), + "Resolved to {}", + version + ); + self.on_resolved_version .emit(ResolvedVersionEvent { candidate: initial_version.to_owned(), @@ -440,12 +456,6 @@ impl Tool { return Ok(()); } - debug!( - tool = self.id.as_str(), - initial_version = initial_version.to_string(), - "Resolving a semantic version", - ); - let resolver = self.load_version_resolver(initial_version).await?; let mut version = VersionSpec::default(); let mut resolved = false; diff --git a/crates/pdk-api/src/error.rs b/crates/pdk-api/src/error.rs index fb34d5387..c3cba1e15 100644 --- a/crates/pdk-api/src/error.rs +++ b/crates/pdk-api/src/error.rs @@ -8,7 +8,7 @@ pub enum PluginError { #[error("Unable to install {tool}, unsupported architecture {arch}.")] UnsupportedArch { tool: String, arch: String }, - #[error("{tool} does not support canary versions or installations.")] + #[error("{tool} does not support canary/nightly versions.")] UnsupportedCanary { tool: String }, #[error("Unable to install {tool}, unsupported OS {os}.")] diff --git a/crates/pdk/src/helpers.rs b/crates/pdk/src/helpers.rs index cba2d0964..b8de01519 100644 --- a/crates/pdk/src/helpers.rs +++ b/crates/pdk/src/helpers.rs @@ -48,6 +48,18 @@ where fetch(HttpRequest::new(url.as_ref()), None, false) } +/// Fetch the provided URL and return the text response. +pub fn fetch_url_text(url: U) -> anyhow::Result +where + U: AsRef, +{ + let req = HttpRequest::new(url.as_ref()); + let res = request::(&req, None) + .map_err(|e| anyhow::anyhow!("Failed to make request to {}: {e}", req.url))?; + + String::from_bytes(res.body()) +} + /// Fetch the provided URL, deserialize the response as JSON, /// and cache the response in memory for the duration of the WASM instance. pub fn fetch_url_with_cache(url: U) -> anyhow::Result diff --git a/crates/pdk/src/macros.rs b/crates/pdk/src/macros.rs index 4e0007662..5a1fe75e1 100644 --- a/crates/pdk/src/macros.rs +++ b/crates/pdk/src/macros.rs @@ -57,13 +57,25 @@ macro_rules! host_log { #[macro_export] macro_rules! err { - ($msg:expr) => { - Err(WithReturnCode::new(PluginError::Message($msg).into(), 1)) + ($msg:literal) => { + Err(WithReturnCode::new( + PluginError::Message($msg.into()).into(), + 1, + )) }; - ($msg:expr, $code:expr) => { + ($msg:literal, $($arg:tt)*) => { Err(WithReturnCode::new( - PluginError::Message($msg).into(), + PluginError::Message(format!($msg, $($arg)*)).into(), + 1, + )) + }; + ($msg:literal, $code:expr) => { + Err(WithReturnCode::new( + PluginError::Message($msg.into()).into(), $code, )) }; + ($msg:expr) => { + Err(WithReturnCode::new($msg, 1)) + }; } diff --git a/crates/schema-plugin/src/schema.rs b/crates/schema-plugin/src/schema.rs index ce34f4ca8..7bfa11944 100644 --- a/crates/schema-plugin/src/schema.rs +++ b/crates/schema-plugin/src/schema.rs @@ -21,7 +21,9 @@ pub struct DetectSchema { pub struct InstallSchema { pub arch: HashMap, pub checksum_url: Option, + pub checksum_url_canary: Option, pub download_url: String, + pub download_url_canary: Option, } #[derive(Debug, Default, Deserialize)]