From 7992074e99d3c31756a8c281ef4d44af02675730 Mon Sep 17 00:00:00 2001 From: jdx <216188+jdx@users.noreply.github.com> Date: Fri, 15 Nov 2024 21:54:23 -0600 Subject: [PATCH] feat: added `os` option to mise.toml --- .github/workflows/test.yml | 1 - mise.toml | 4 +- src/backend/asdf.rs | 8 +-- src/backend/mod.rs | 17 ++--- src/cli/install.rs | 1 + src/config/config_file/mise_toml.rs | 98 ++++++++++++++++++++--------- src/plugins/core/deno.rs | 2 +- src/plugins/core/go.rs | 2 +- src/toolset/mod.rs | 21 +++++-- src/toolset/tool_request.rs | 74 ++++++++++++++++------ src/toolset/tool_request_set.rs | 7 ++- src/toolset/tool_version.rs | 9 +-- 12 files changed, 169 insertions(+), 75 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4b3ebcbfa4..4d69c456f7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -174,7 +174,6 @@ jobs: - run: cargo test continue-on-error: true - run: mise install - continue-on-error: true - run: mise test-tool --all continue-on-error: true windows-e2e: diff --git a/mise.toml b/mise.toml index e804dd15ff..a2cc3cbde5 100644 --- a/mise.toml +++ b/mise.toml @@ -9,8 +9,8 @@ shellcheck = "0.10" shfmt = "3" jq = "latest" cargo-binstall = "latest" -"cargo:cargo-edit" = "latest" -"cargo:cargo-show" = "latest" +"cargo:cargo-edit" = {version = "latest", os=["linux", "macos"]} +"cargo:cargo-show" = {version = "latest", os=["linux", "macos"]} "cargo:cargo-insta" = "latest" "cargo:git-cliff" = "latest" "npm:markdownlint-cli" = "latest" diff --git a/src/backend/asdf.rs b/src/backend/asdf.rs index 953df7cd20..cbdadd60c1 100644 --- a/src/backend/asdf.rs +++ b/src/backend/asdf.rs @@ -119,7 +119,7 @@ impl AsdfBackend { fn fetch_bin_paths(&self, tv: &ToolVersion) -> Result> { let list_bin_paths = self.plugin_path.join("bin/list-bin-paths"); - let bin_paths = if matches!(tv.request, ToolRequest::System(..)) { + let bin_paths = if matches!(tv.request, ToolRequest::System{..}) { Vec::new() } else if list_bin_paths.exists() { let sm = self.script_man_for_tv(tv)?; @@ -184,9 +184,9 @@ impl AsdfBackend { let install_type = match &tv.request { ToolRequest::Version { .. } | ToolRequest::Prefix { .. } => "version", ToolRequest::Ref { .. } => "ref", - ToolRequest::Path(..) => "path", + ToolRequest::Path{ .. } => "path", ToolRequest::Sub { .. } => "sub", - ToolRequest::System(..) => { + ToolRequest::System{ .. } => { panic!("should not be called for system tool") } }; @@ -384,7 +384,7 @@ impl Backend for AsdfBackend { ts: &Toolset, tv: &ToolVersion, ) -> eyre::Result> { - if matches!(tv.request, ToolRequest::System(..)) { + if matches!(tv.request, ToolRequest::System{ .. }) { return Ok(BTreeMap::new()); } if !self.plugin.script_man.script_exists(&ExecEnv) || *env::__MISE_SCRIPT { diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 0c8546a6bd..1f5b7c7395 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -193,7 +193,7 @@ pub trait Backend: Debug + Send + Sync { .collect::>(); for dep in dep_backends { // TODO: pass the right tvr - let tvr = ToolRequest::System(dep.id().into(), ToolSource::Unknown); + let tvr = ToolRequest::System{ backend: dep.id().into(), source: ToolSource::Unknown, os: None }; deps.extend(dep.get_all_dependencies(&tvr)?); } Ok(deps.into_iter().collect()) @@ -242,7 +242,7 @@ pub trait Backend: Debug + Send + Sync { } fn is_version_installed(&self, tv: &ToolVersion, check_symlink: bool) -> bool { match tv.request { - ToolRequest::System(..) => true, + ToolRequest::System{ .. } => true, _ => { let check_path = |install_path: &Path| { let is_installed = install_path.exists(); @@ -338,7 +338,7 @@ pub trait Backend: Debug + Send + Sync { fn ensure_dependencies_installed(&self) -> eyre::Result<()> { let deps = self - .get_all_dependencies(&ToolRequest::System(self.id().into(), ToolSource::Unknown))? + .get_all_dependencies(&ToolRequest::System{ backend: self.id().into(), source: ToolSource::Unknown, os: None })? .into_iter() .collect::>(); if !deps.is_empty() { @@ -474,7 +474,7 @@ pub trait Backend: Debug + Send + Sync { } fn list_bin_paths(&self, tv: &ToolVersion) -> eyre::Result> { match tv.request { - ToolRequest::System(..) => Ok(vec![]), + ToolRequest::System{..} => Ok(vec![]), _ => Ok(vec![tv.install_path().join("bin")]), } } @@ -531,10 +531,11 @@ pub trait Backend: Debug + Send + Sync { fn dependency_toolset(&self) -> eyre::Result { let config = Config::get(); let dependencies = self - .get_all_dependencies(&ToolRequest::System( - self.name().into(), - ToolSource::Unknown, - ))? + .get_all_dependencies(&ToolRequest::System{ + backend: self.name().into(), + source: ToolSource::Unknown, + os: None, + })? .into_iter() .collect(); let mut ts: Toolset = config diff --git a/src/cli/install.rs b/src/cli/install.rs index 7db0db1e60..91aa76434b 100644 --- a/src/cli/install.rs +++ b/src/cli/install.rs @@ -110,6 +110,7 @@ impl Install { let tvr = ToolRequest::Version { backend: ta.ba.clone(), version: "latest".into(), + os: None, options: ta.opts.clone().unwrap_or(Default::default()), source: ToolSource::Argument, }; diff --git a/src/config/config_file/mise_toml.rs b/src/config/config_file/mise_toml.rs index 9199b8c2f7..757fa05155 100644 --- a/src/config/config_file/mise_toml.rs +++ b/src/config/config_file/mise_toml.rs @@ -62,6 +62,7 @@ pub struct MiseTomlToolList(Vec); #[derive(Debug, Clone)] pub struct MiseTomlTool { pub tt: ToolVersionType, + pub os: Option>, pub options: Option, } @@ -313,6 +314,7 @@ impl ConfigFile for MiseToml { .iter() .map(|(v, opts)| MiseTomlTool { tt: ToolVersionType::Version(v.clone()), + os: None, // TODO: use existing os options: if !output_empty_opts(opts) { Some(opts.clone()) } else { @@ -391,16 +393,16 @@ impl ConfigFile for MiseToml { trust_check(&self.path)?; } let version = self.parse_template(&tool.tt.to_string())?; - if let Some(mut options) = tool.options.clone() { + let mut tvr = if let Some(mut options) = tool.options.clone() { for v in options.values_mut() { *v = self.parse_template(v)?; } - let tvr = ToolRequest::new_opts(fa.clone(), &version, options, source.clone())?; - trs.add_version(tvr, &source); + ToolRequest::new_opts(fa.clone(), &version, options, source.clone())? } else { - let tvr = ToolRequest::new(fa.clone(), &version, source.clone())?; - trs.add_version(tvr, &source); - } + ToolRequest::new(fa.clone(), &version, source.clone())? + }; + tvr = tvr.with_os(tool.os.clone()); + trs.add_version(tvr, &source); } } Ok(trs) @@ -759,7 +761,7 @@ impl<'de> de::Deserialize<'de> for MiseTomlToolList { let tt: ToolVersionType = v .parse() .map_err(|e| de::Error::custom(format!("invalid tool: {e}")))?; - Ok(MiseTomlToolList(vec![MiseTomlTool { tt, options: None }])) + Ok(MiseTomlToolList(vec![MiseTomlTool { tt, os: None, options: None }])) } fn visit_seq(self, mut seq: S) -> std::result::Result @@ -773,22 +775,40 @@ impl<'de> de::Deserialize<'de> for MiseTomlToolList { Ok(MiseTomlToolList(tools)) } - fn visit_map(self, map: M) -> std::result::Result + fn visit_map(self, mut map: M) -> std::result::Result where M: de::MapAccess<'de>, { - let mut options: BTreeMap = - de::Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?; - let tt: ToolVersionType = options - .remove("version") - .or_else(|| options.remove("path").map(|p| format!("path:{p}"))) - .or_else(|| options.remove("prefix").map(|p| format!("prefix:{p}"))) - .or_else(|| options.remove("ref").map(|p| format!("ref:{p}"))) - .ok_or_else(|| de::Error::custom("missing version"))? - .parse() - .map_err(de::Error::custom)?; + let mut options: BTreeMap = Default::default(); + let mut os: Option> = None; + let mut tt = ToolVersionType::System; + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "version" => { + tt = v.as_str().unwrap().parse().map_err(de::Error::custom)?; + }, + "path" | "prefix" | "ref" => { + tt = format!("{k}:{}", v.as_str().unwrap()).parse().map_err(de::Error::custom)?; + }, + "os" => match v { + toml::Value::Array(s) => { + os = Some(s.iter().map(|v| v.as_str().unwrap().to_string()).collect()); + } + toml::Value::String(s) => { + options.insert(k, s); + } + _ => { + return Err(de::Error::custom("os must be a string or array")); + } + }, + _ => { + options.insert(k, v.to_string()); + } + } + } Ok(MiseTomlToolList(vec![MiseTomlTool { tt, + os, options: Some(options), }])) } @@ -818,25 +838,43 @@ impl<'de> de::Deserialize<'de> for MiseTomlTool { let tt: ToolVersionType = v .parse() .map_err(|e| de::Error::custom(format!("invalid tool: {e}")))?; - Ok(MiseTomlTool { tt, options: None }) + Ok(MiseTomlTool { tt, os: None, options: None }) } - fn visit_map(self, map: M) -> Result + fn visit_map(self, mut map: M) -> Result where M: de::MapAccess<'de>, { - let mut options: BTreeMap = - de::Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?; - let tt: ToolVersionType = options - .remove("version") - .or_else(|| options.remove("path").map(|p| format!("path:{p}"))) - .or_else(|| options.remove("prefix").map(|p| format!("prefix:{p}"))) - .or_else(|| options.remove("ref").map(|p| format!("ref:{p}"))) - .ok_or_else(|| de::Error::custom("missing version"))? - .parse() - .map_err(de::Error::custom)?; + let mut options: BTreeMap = Default::default(); + let mut os: Option> = None; + let mut tt = ToolVersionType::System; + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "version" => { + tt = v.as_str().unwrap().parse().map_err(de::Error::custom)?; + }, + "path" | "prefix" | "ref" => { + tt = format!("{k}:{}", v.as_str().unwrap()).parse().map_err(de::Error::custom)?; + }, + "os" => match v { + toml::Value::Array(s) => { + os = Some(s.iter().map(|v| v.as_str().unwrap().to_string()).collect()); + } + toml::Value::String(s) => { + options.insert(k, s); + } + _ => { + return Err(de::Error::custom("os must be a string or array")); + } + }, + _ => { + options.insert(k, v.to_string()); + } + } + } Ok(MiseTomlTool { tt, + os, options: Some(options), }) } diff --git a/src/plugins/core/deno.rs b/src/plugins/core/deno.rs index 0f985b6682..691c08966c 100644 --- a/src/plugins/core/deno.rs +++ b/src/plugins/core/deno.rs @@ -119,7 +119,7 @@ impl Backend for DenoPlugin { } fn list_bin_paths(&self, tv: &ToolVersion) -> Result> { - if let ToolRequest::System(..) = tv.request { + if let ToolRequest::System{ .. } = tv.request { return Ok(vec![]); } let bin_paths = vec![ diff --git a/src/plugins/core/go.rs b/src/plugins/core/go.rs index 6a8cc0baa6..87085f0532 100644 --- a/src/plugins/core/go.rs +++ b/src/plugins/core/go.rs @@ -215,7 +215,7 @@ impl Backend for GoPlugin { } fn list_bin_paths(&self, tv: &ToolVersion) -> eyre::Result> { - if let ToolRequest::System(..) = tv.request { + if let ToolRequest::System{ .. } = tv.request { return Ok(vec![]); } // goroot/bin must always be included, irrespective of MISE_GO_SET_GOROOT diff --git a/src/toolset/mod.rs b/src/toolset/mod.rs index 04177c90d9..0f1c08a5fb 100644 --- a/src/toolset/mod.rs +++ b/src/toolset/mod.rs @@ -152,8 +152,9 @@ impl Toolset { pub fn list_missing_plugins(&self) -> Vec { self.versions - .keys() - .filter(|ba| ba.is_os_supported()) + .iter() + .filter(|(_, tvl)| tvl.versions.first().map(|tv| tv.request.is_os_supported()).unwrap_or_default()) + .map(|(ba, _)| ba) .flat_map(|ba| ba.backend()) .filter(|b| b.plugin().is_some_and(|p| !p.is_installed())) .map(|p| p.id().into()) @@ -289,7 +290,14 @@ impl Toolset { pub fn list_missing_versions(&self) -> Vec { self.list_current_versions() .into_iter() - .filter(|(p, tv)| tv.ba().is_os_supported() && !p.is_version_installed(tv, true)) + .filter(|(p, tv)| { + if let Some(os) = tv.request.os() { + if !os.contains(&crate::cli::version::OS) { + return false; + } + } + tv.request.is_os_supported() && !p.is_version_installed(tv, true) + }) .map(|(_, tv)| tv) .collect() } @@ -349,6 +357,7 @@ impl Toolset { backend: p.ba().clone(), ref_: r.to_string(), ref_type: ref_type.to_string(), + os: v.request.os().clone(), options: v.request.options().clone(), source: v.request.source().clone(), }; @@ -425,12 +434,14 @@ impl Toolset { version: _version, options, source, + os, } => { out.tool_request = ToolRequest::Version { backend, options, source, version: out.bump.clone().unwrap(), + os, }; } _ => { @@ -473,7 +484,7 @@ impl Toolset { let entries = self .list_current_installed_versions() .into_par_iter() - .filter(|(_, tv)| !matches!(tv.request, ToolRequest::System(..))) + .filter(|(_, tv)| !matches!(tv.request, ToolRequest::System{ .. })) .flat_map(|(p, tv)| match p.exec_env(config, self, &tv) { Ok(env) => env.into_iter().collect(), Err(e) => { @@ -506,7 +517,7 @@ impl Toolset { pub fn list_paths(&self) -> Vec { self.list_current_installed_versions() .into_par_iter() - .filter(|(_, tv)| !matches!(tv.request, ToolRequest::System(..))) + .filter(|(_, tv)| !matches!(tv.request, ToolRequest::System{ .. })) .flat_map(|(p, tv)| { p.list_bin_paths(&tv).unwrap_or_else(|e| { warn!("Error listing bin paths for {tv}: {e:#}"); diff --git a/src/toolset/tool_request.rs b/src/toolset/tool_request.rs index 134096eff0..16530b01f4 100644 --- a/src/toolset/tool_request.rs +++ b/src/toolset/tool_request.rs @@ -19,12 +19,14 @@ pub enum ToolRequest { version: String, options: ToolVersionOptions, source: ToolSource, + os: Option>, }, Prefix { backend: BackendArg, prefix: String, options: ToolVersionOptions, source: ToolSource, + os: Option>, }, Ref { backend: BackendArg, @@ -32,15 +34,17 @@ pub enum ToolRequest { ref_type: String, options: ToolVersionOptions, source: ToolSource, + os: Option>, }, Sub { backend: BackendArg, sub: String, orig_version: String, source: ToolSource, + os: Option>, }, - Path(BackendArg, PathBuf, ToolSource), - System(BackendArg, ToolSource), + Path{backend: BackendArg, path: PathBuf, source: ToolSource, os: Option>}, + System{backend: BackendArg, source: ToolSource, os: Option>}, } impl ToolRequest { @@ -54,28 +58,32 @@ impl ToolRequest { ref_: r.to_string(), ref_type: ref_type.to_string(), options: backend.opts.clone().unwrap_or_default(), + os: None, backend, source, }, Some(("prefix", p)) => Self::Prefix { prefix: p.to_string(), options: backend.opts.clone().unwrap_or_default(), + os: None, backend, source, }, - Some(("path", p)) => Self::Path(backend, PathBuf::from(p), source), + Some(("path", p)) => Self::Path{ backend, path: PathBuf::from(p), source, os: None }, Some((p, v)) if p.starts_with("sub-") => Self::Sub { sub: p.split_once('-').unwrap().1.to_string(), orig_version: v.to_string(), + os: None, backend, source, }, None => { if s == "system" { - Self::System(backend, source) + Self::System{ backend: backend, source: source, os: None } } else { Self::Version { version: s, + os: None, options: backend.opts.clone().unwrap_or_default(), backend, source, @@ -105,20 +113,20 @@ impl ToolRequest { Self::Version { source: s, .. } | Self::Prefix { source: s, .. } | Self::Ref { source: s, .. } - | Self::Path(_, _, s) + | Self::Path{ source: s, .. } | Self::Sub { source: s, .. } - | Self::System(_, s) => *s = source, + | Self::System{ source: s, .. } => *s = source, } self.clone() } pub fn ba(&self) -> &BackendArg { match self { - Self::Version { backend: f, .. } - | Self::Prefix { backend: f, .. } - | Self::Ref { backend: f, .. } - | Self::Path(f, ..) - | Self::Sub { backend: f, .. } - | Self::System(f, ..) => f, + Self::Version { backend, .. } + | Self::Prefix { backend, .. } + | Self::Ref { backend, .. } + | Self::Path{ backend, .. } + | Self::Sub { backend, .. } + | Self::System{ backend, .. } => backend, } } pub fn backend(&self) -> Result { @@ -129,11 +137,32 @@ impl ToolRequest { Self::Version { source, .. } | Self::Prefix { source, .. } | Self::Ref { source, .. } - | Self::Path(.., source) + | Self::Path { source, .. } | Self::Sub { source, .. } - | Self::System(_, source) => source, + | Self::System { source, .. } => source, } } + pub fn os(&self) -> &Option> { + match self { + Self::Version { os, .. } + | Self::Prefix { os, .. } + | Self::Ref { os, .. } + | Self::Path { os, .. } + | Self::Sub { os, .. } + | Self::System { os, .. } => os, + } + } + pub fn with_os(mut self, os: Option>) -> Self { + match &mut self { + Self::Version { os: o, .. } + | Self::Prefix { os: o, .. } + | Self::Ref { os: o, .. } + | Self::Path { os: o, .. } + | Self::Sub { os: o, .. } + | Self::System { os: o, .. } => *o = os, + } + self + } pub fn dependencies(&self) -> eyre::Result> { let backend = self.ba().backend()?; backend.get_all_dependencies(self) @@ -145,11 +174,11 @@ impl ToolRequest { Self::Ref { ref_: r, ref_type, .. } => format!("{ref_type}:{r}"), - Self::Path(_, p, ..) => format!("path:{}", p.display()), + Self::Path{path: p, ..} => format!("path:{}", p.display()), Self::Sub { sub, orig_version, .. } => format!("sub-{}:{}", sub, orig_version), - Self::System(..) => "system".to_string(), + Self::System{..} => "system".to_string(), } } @@ -204,8 +233,8 @@ impl ToolRequest { .cloned(), Err(_) => None, }, - Self::Path(_, path, ..) => Some(path.clone()), - Self::System(..) => None, + Self::Path{ path, .. } => Some(path.clone()), + Self::System{ .. } => None, } } @@ -235,6 +264,15 @@ impl ToolRequest { pub fn resolve(&self, opts: &ResolveOptions) -> Result { ToolVersion::resolve(self.clone(), opts) } + + pub fn is_os_supported(&self) -> bool { + if let Some(os) = self.os() { + if !os.contains(&crate::cli::version::OS) { + return false; + } + } + self.ba().is_os_supported() + } } /// subtracts sub from orig and removes suffix diff --git a/src/toolset/tool_request_set.rs b/src/toolset/tool_request_set.rs index ff007e6fbf..1f807dc0b7 100644 --- a/src/toolset/tool_request_set.rs +++ b/src/toolset/tool_request_set.rs @@ -43,7 +43,12 @@ impl ToolRequestSet { self.tools .values() .flatten() - .filter(|tr| tr.ba().is_os_supported()) + .filter(|tr| tr.is_os_supported()) + .filter(|tr| if let Some(os) = tr.os() { + os.contains(&crate::cli::version::OS) + } else { + true + }) .filter(|tvr| !tvr.is_installed()) .collect() } diff --git a/src/toolset/tool_version.rs b/src/toolset/tool_version.rs index 9b9f02aee5..23d4c5c49f 100644 --- a/src/toolset/tool_version.rs +++ b/src/toolset/tool_version.rs @@ -66,7 +66,7 @@ impl ToolVersion { pub fn install_path(&self) -> PathBuf { let pathname = match &self.request { - ToolRequest::Path(_, p, ..) => p.to_string_lossy().to_string(), + ToolRequest::Path{ path: p, .. } => p.to_string_lossy().to_string(), _ => self.tv_pathname(), }; let path = self.ba().installs_path.join(pathname); @@ -121,8 +121,8 @@ impl ToolVersion { ToolRequest::Prefix { .. } => self.version.to_string(), ToolRequest::Sub { .. } => self.version.to_string(), ToolRequest::Ref { ref_: r, .. } => format!("ref-{}", r), - ToolRequest::Path(_, p, ..) => format!("path-{}", hash_to_str(p)), - ToolRequest::System(..) => "system".to_string(), + ToolRequest::Path{ path: p, .. } => format!("path-{}", hash_to_str(p)), + ToolRequest::System{ .. } => "system".to_string(), } .replace([':', '/'], "-") } @@ -226,6 +226,7 @@ impl ToolVersion { backend: tr.ba().clone(), ref_, ref_type, + os: None, options: opts.clone(), source: tr.source().clone(), }; @@ -235,7 +236,7 @@ impl ToolVersion { fn resolve_path(path: PathBuf, tr: &ToolRequest) -> Result { let path = fs::canonicalize(path)?; - let request = ToolRequest::Path(tr.ba().clone(), path, tr.source().clone()); + let request = ToolRequest::Path{ backend: tr.ba().clone(), path, source: tr.source().clone(), os: None }; let version = request.version(); Ok(Self::new(request, version)) }