diff --git a/crates/core/src/user_config.rs b/crates/core/src/user_config.rs index 0eec024d0..77717be59 100644 --- a/crates/core/src/user_config.rs +++ b/crates/core/src/user_config.rs @@ -10,6 +10,14 @@ use warpgate::{HttpOptions, Id, PluginLocator}; pub const USER_CONFIG_NAME: &str = "config.toml"; +#[derive(Debug, Default, Deserialize, PartialEq, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum DetectStrategy { + #[default] + FirstAvailable, + PreferPrototools, +} + #[derive(Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "kebab-case")] pub enum PinType { @@ -22,6 +30,7 @@ pub enum PinType { pub struct UserConfig { pub auto_clean: bool, pub auto_install: bool, + pub detect_strategy: DetectStrategy, pub node_intercept_globals: bool, pub pin_latest: Option, pub http: HttpOptions, @@ -93,6 +102,7 @@ impl Default for UserConfig { Self { auto_clean: from_var("PROTO_AUTO_CLEAN", false), auto_install: from_var("PROTO_AUTO_INSTALL", false), + detect_strategy: DetectStrategy::default(), http: HttpOptions::default(), node_intercept_globals: from_var("PROTO_NODE_INTERCEPT_GLOBALS", true), pin_latest: None, diff --git a/crates/core/src/version_detector.rs b/crates/core/src/version_detector.rs index db493cbc7..ec4697054 100644 --- a/crates/core/src/version_detector.rs +++ b/crates/core/src/version_detector.rs @@ -1,10 +1,117 @@ use crate::error::ProtoError; use crate::tool::Tool; use crate::tools_config::ToolsConfig; +use crate::user_config::DetectStrategy; use std::{env, path::Path}; use tracing::{debug, trace}; use version_spec::*; +async fn detect_version_first_available( + tool: &Tool, + start_dir: &Path, + end_dir: &Path, +) -> miette::Result> { + let mut current_dir: Option<&Path> = Some(&start_dir); + + while let Some(dir) = current_dir { + trace!( + tool = tool.id.as_str(), + dir = ?dir, + "Checking directory", + ); + + let config = ToolsConfig::load_from(dir)?; + + if let Some(version) = config.tools.get(&tool.id) { + debug!( + tool = tool.id.as_str(), + version = version.to_string(), + file = ?config.path, + "Detected version from .prototools file", + ); + + return Ok(Some(version.to_owned())); + } + + if let Some(version) = tool.detect_version_from(dir).await? { + debug!( + tool = tool.id.as_str(), + version = version.to_string(), + "Detected version from tool's ecosystem" + ); + + return Ok(Some(version)); + } + + if dir == end_dir { + break; + } + + current_dir = dir.parent(); + } + + Ok(None) +} + +async fn detect_version_prefer_prototools( + tool: &Tool, + start_dir: &Path, + end_dir: &Path, +) -> miette::Result> { + let mut config_version = None; + let mut config_path = None; + let mut ecosystem_version = None; + let mut current_dir: Option<&Path> = Some(&start_dir); + + while let Some(dir) = current_dir { + trace!( + tool = tool.id.as_str(), + dir = ?dir, + "Checking directory", + ); + + if config_version.is_none() { + let mut config = ToolsConfig::load_from(dir)?; + + config_version = config.tools.remove(&tool.id); + config_path = Some(config.path); + } + + if ecosystem_version.is_none() { + ecosystem_version = tool.detect_version_from(dir).await?; + } + + if dir == end_dir { + break; + } + + current_dir = dir.parent(); + } + + if let Some(version) = config_version { + debug!( + tool = tool.id.as_str(), + version = version.to_string(), + file = ?config_path.unwrap(), + "Detected version from .prototools file", + ); + + return Ok(Some(version.to_owned())); + } + + if let Some(version) = ecosystem_version { + debug!( + tool = tool.id.as_str(), + version = version.to_string(), + "Detected version from tool's ecosystem" + ); + + return Ok(Some(version)); + } + + Ok(None) +} + pub async fn detect_version( tool: &Tool, forced_version: Option, @@ -45,46 +152,18 @@ pub async fn detect_version( // Traverse upwards and attempt to detect a local version if let Ok(working_dir) = env::current_dir() { - let mut current_dir: Option<&Path> = Some(&working_dir); - - while let Some(dir) = current_dir { - trace!( - tool = tool.id.as_str(), - dir = ?dir, - "Checking directory", - ); - - // Detect from our config file - let config = ToolsConfig::load_from(dir)?; - - if let Some(local_version) = config.tools.get(&tool.id) { - debug!( - tool = tool.id.as_str(), - version = local_version.to_string(), - file = ?config.path, - "Detected version from .prototools file", - ); - - return Ok(local_version.to_owned()); + let user_config = tool.proto.load_user_config()?; + let detected_version = match user_config.detect_strategy { + DetectStrategy::FirstAvailable => { + detect_version_first_available(tool, &working_dir, &tool.proto.home).await? } - - // Detect using the tool - if let Some(detected_version) = tool.detect_version_from(dir).await? { - debug!( - tool = tool.id.as_str(), - version = detected_version.to_string(), - "Detected version from tool's ecosystem" - ); - - return Ok(detected_version); - } - - // Don't traverse passed the home directory - if dir == tool.proto.home { - break; + DetectStrategy::PreferPrototools => { + detect_version_prefer_prototools(tool, &working_dir, &tool.proto.home).await? } + }; - current_dir = dir.parent(); + if let Some(version) = detected_version { + return Ok(version); } }