diff --git a/e2e/backend/test_aqua b/e2e/backend/test_aqua index 60c555a553..f3c520fd25 100644 --- a/e2e/backend/test_aqua +++ b/e2e/backend/test_aqua @@ -11,6 +11,7 @@ test age@1.2.0 "age --version" "v1.2.0" test aqua:helm/helm@3.16.3 "helm version" "v3.16.3" test aqua:crate-ci/typos@1.27.3 "typos --version" "typos-cli 1.27.3" test aqua:biomejs/biome@2.0.0 "biome --version" "Version: 2.0.0" +test aqua:biomejs/biome@@biomejs/biome@2.0.0 "biome --version" "Version: 2.0.0" assert_contains "MISE_USE_VERSIONS_HOST=0 mise ls-remote aqua:sharkdp/hyperfine" "1.9.0 1.10.0" diff --git a/src/aqua/aqua_registry.rs b/src/aqua/aqua_registry.rs index 3093b42225..b6ae22d9ef 100644 --- a/src/aqua/aqua_registry.rs +++ b/src/aqua/aqua_registry.rs @@ -225,8 +225,8 @@ impl AquaRegistry { Ok(pkg) } - pub async fn package_with_version(&self, id: &str, v: &str) -> Result { - Ok(self.package(id).await?.with_version(v)) + pub async fn package_with_version(&self, id: &str, versions: &[&str]) -> Result { + Ok(self.package(id).await?.with_version(versions)) } async fn fetch_package_yaml( @@ -281,8 +281,8 @@ fn fetch_latest_repo(repo: &Git) -> Result<()> { } impl AquaPackage { - pub fn with_version(mut self, v: &str) -> AquaPackage { - self = apply_override(self.clone(), self.version_override(v)); + pub fn with_version(mut self, versions: &[&str]) -> AquaPackage { + self = apply_override(self.clone(), self.version_override(versions)); if let Some(avo) = self.overrides.clone().into_iter().find(|o| { if let (Some(goos), Some(goarch)) = (&o.goos, &o.goarch) { goos == aqua::os() && goarch == aqua::arch() @@ -299,9 +299,12 @@ impl AquaPackage { self } - fn version_override(&self, v: &str) -> &AquaPackage { - let expr = self.expr_parser(v); - let ctx = self.expr_ctx(v); + // all versions must refer to the same logical version. e.g. ["v1.2.3", "1.2.3"] + fn version_override(&self, versions: &[&str]) -> &AquaPackage { + let expressions = versions + .iter() + .map(|v| (self.expr_parser(v), self.expr_ctx(v))) + .collect_vec(); vec![self] .into_iter() .chain(self.version_overrides.iter()) @@ -309,11 +312,13 @@ impl AquaPackage { if vo.version_constraint.is_empty() { true } else { - expr.eval(&vo.version_constraint, &ctx) - .map_err(|e| debug!("error parsing {}: {e}", vo.version_constraint)) - .unwrap_or(false.into()) - .as_bool() - .unwrap() + expressions.iter().any(|(expr, ctx)| { + expr.eval(&vo.version_constraint, ctx) + .map_err(|e| debug!("error parsing {}: {e}", vo.version_constraint)) + .unwrap_or(false.into()) + .as_bool() + .unwrap() + }) } }) .unwrap_or(self) diff --git a/src/backend/aqua.rs b/src/backend/aqua.rs index b89dfccc0b..918bee8ef5 100644 --- a/src/backend/aqua.rs +++ b/src/backend/aqua.rs @@ -62,7 +62,7 @@ impl Backend for AquaBackend { let mut versions = Vec::new(); for (v, tag) in version_tags.iter() { let pkg = AQUA_REGISTRY - .package_with_version(&self.id, tag) + .package_with_version(&self.id, &[tag]) .await .unwrap_or_default(); if !pkg.no_asset && pkg.error_message.is_none() { @@ -77,24 +77,26 @@ impl Backend for AquaBackend { ctx: &InstallContext, mut tv: ToolVersion, ) -> Result { - let mut v; - let pkg; - match self + let tag = self .get_version_tags() .await? .iter() .find(|(version, _)| version == &tv.version) - { - Some((_, tag)) => { - v = tag.clone(); - pkg = AQUA_REGISTRY.package_with_version(&self.id, &v).await?; - } - None => { - v = format!("v{}", tv.version); - pkg = AQUA_REGISTRY.package_with_version(&self.id, &v).await?; - if let Some(prefix) = &pkg.version_prefix { - v = format!("{prefix}{v}"); - } + .map(|(_, tag)| tag); + let mut v = tag.cloned().unwrap_or_else(|| tv.version.clone()); + let mut v_prefixed = + (tag.is_none() && !tv.version.starts_with('v')).then(|| format!("v{v}")); + let versions = match &v_prefixed { + Some(v_prefixed) => vec![v.as_str(), v_prefixed.as_str()], + None => vec![v.as_str()], + }; + let pkg = AQUA_REGISTRY + .package_with_version(&self.id, &versions) + .await?; + if let Some(prefix) = &pkg.version_prefix { + if !v.starts_with(prefix) { + v = format!("{prefix}{v}"); + v_prefixed = v_prefixed.map(|v| format!("{prefix}{v}")); } } if pkg.no_asset { @@ -104,18 +106,17 @@ impl Backend for AquaBackend { bail!(pkg.error_message.unwrap()); } validate(&pkg)?; - let url = match self.fetch_url(&pkg, &v).await { + // try v-prefixed version first because most aqua packages use v-prefixed versions + let url = match self + .fetch_url(&pkg, v_prefixed.as_ref().unwrap_or(&v)) + .await + { Ok(url) => url, - Err(err) => { - if let Some(prefix) = &pkg.version_prefix { - v = format!("{}{}", prefix, tv.version); - } else { - v = tv.version.to_string(); - } - self.fetch_url(&pkg, &v) - .await - .map_err(|e| err.wrap_err(e))? - } + Err(err) if v_prefixed.is_some() => self + .fetch_url(&pkg, &v) + .await + .map_err(|e| err.wrap_err(e))?, + Err(err) => return Err(err), }; let filename = url.split('/').next_back().unwrap(); self.download(ctx, &tv, &url, filename).await?; @@ -142,8 +143,9 @@ impl Backend for AquaBackend { let install_path = tv.install_path(); let paths = cache .get_or_try_init_async(async || { + // TODO: align this logic with the one in `install_version_` let pkg = AQUA_REGISTRY - .package_with_version(&self.id, &tv.version) + .package_with_version(&self.id, &[&tv.version]) .await?; let srcs = self.srcs(&pkg, tv)?; @@ -232,7 +234,7 @@ impl AquaBackend { continue; } } - let pkg = pkg.clone().with_version(version); + let pkg = pkg.clone().with_version(&[version]); if let Some(prefix) = &pkg.version_prefix { if let Some(_v) = version.strip_prefix(prefix) { version = _v;