Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 120 additions & 17 deletions src/backend/aqua.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::cli::args::BackendArg;
use crate::cli::version::{ARCH, OS};
use crate::config::Settings;
use crate::file::{TarFormat, TarOptions};
use crate::http::HTTP;
use crate::http::{HTTP, HTTP_FETCH};
use crate::install_context::InstallContext;
use crate::lockfile::{PlatformInfo, ProvenanceType};
use crate::path::{Path, PathBuf, PathExt};
Expand All @@ -29,6 +29,7 @@ use eyre::{ContextCompat, Result, WrapErr, bail, eyre};
use indexmap::IndexSet;
use itertools::Itertools;
use regex::Regex;
use reqwest::StatusCode;
use std::borrow::Cow;
use std::fmt::Debug;
use std::{
Expand Down Expand Up @@ -265,6 +266,11 @@ impl Backend for AquaBackend {
.package_with_version(&self.id, &[&tag])
.await
.unwrap_or_default();
if check_pkg.r#type == AquaPackageType::Http
&& !Self::http_package_version_available(&check_pkg, &tag).await
{
continue;
}
if !check_pkg.no_asset && check_pkg.error_message.is_none() {
let release_url = format!(
"https://github.com/{}/{}/releases/tag/{}",
Expand Down Expand Up @@ -1985,13 +1991,7 @@ impl AquaBackend {
let name_str: &str = name.as_ref();
install_path.join(name_str)
})
.map(|path| {
if cfg!(windows) && pkg.complete_windows_ext {
path.with_extension("exe")
} else {
path
}
})
.map(|path| complete_windows_ext(path, pkg.complete_windows_ext, os()))
.collect();
let first_bin_path = bin_paths
.first()
Expand Down Expand Up @@ -2104,9 +2104,7 @@ impl AquaBackend {
.unwrap_or(&pkg.repo_name);

let mut path = tv.install_path().join(fallback_name);
if cfg!(windows) && pkg.complete_windows_ext {
path = path.with_extension("exe");
}
path = complete_windows_ext(path, pkg.complete_windows_ext, os());

return Ok(vec![(path.clone(), path)]);
}
Expand All @@ -2115,8 +2113,9 @@ impl AquaBackend {
.files
.iter()
.map(|f| {
let srcs = if let Some(prefix) = &pkg.version_prefix {
vec![f.src(pkg, &format!("{}{}", prefix, tv.version), os(), arch())?]
let version = version_with_prefix(&tv.version, pkg.version_prefix.as_deref());
let srcs = if pkg.version_prefix.is_some() {
vec![f.src(pkg, version.as_ref(), os(), arch())?]
} else {
vec![
f.src(pkg, &tv.version, os(), arch())?,
Expand All @@ -2129,10 +2128,8 @@ impl AquaBackend {
.map(|src| tv.install_path().join(src))
.map(|mut src| {
let mut dst = src.parent().unwrap().join(f.name.as_str());
if cfg!(windows) && pkg.complete_windows_ext {
src = src.with_extension("exe");
dst = dst.with_extension("exe");
}
src = complete_windows_ext(src, pkg.complete_windows_ext, os());
dst = complete_windows_dst_ext(&src, dst, pkg.complete_windows_ext, os());
(src, dst)
}))
})
Expand All @@ -2143,6 +2140,28 @@ impl AquaBackend {
.collect();
Ok(files)
}

async fn http_package_version_available(pkg: &AquaPackage, v: &str) -> bool {
let url = match pkg.url(v, os(), arch()) {
Ok(url) => url,
Err(e) => {
debug!("failed to render aqua HTTP URL for {v}: {e}");
return false;
}
};

match HTTP_FETCH.head_status(&url).await {
Ok(status) if http_status_allows_aqua_version(status) => true,
Ok(status) => {
debug!("skipping aqua HTTP version {v}: {url} returned {status}");
false
}
Err(e) => {
debug!("could not verify aqua HTTP version {v} at {url}, keeping it: {e}");
true
}
}
}
}

fn unescape_regex_literal(pattern: &str) -> Cow<'_, str> {
Expand Down Expand Up @@ -2214,6 +2233,38 @@ fn toml_value_kind(value: &toml::Value) -> &'static str {
}
}

fn version_with_prefix<'a>(version: &'a str, version_prefix: Option<&str>) -> Cow<'a, str> {
if let Some(prefix) = version_prefix
&& !version.starts_with(prefix)
{
Cow::Owned(format!("{prefix}{version}"))
} else {
Cow::Borrowed(version)
}
}

fn complete_windows_ext(path: PathBuf, complete: bool, target_os: &str) -> PathBuf {
if target_os == "windows" && complete && path.extension().is_none() {
path.with_extension("exe")
} else {
path
}
}

fn complete_windows_dst_ext(src: &Path, dst: PathBuf, complete: bool, target_os: &str) -> PathBuf {
if target_os != "windows" || !complete || dst.extension().is_some() {
return dst;
}
match src.extension() {
Some(ext) => dst.with_extension(ext),
None => dst.with_extension("exe"),
}
}

fn http_status_allows_aqua_version(status: StatusCode) -> bool {
status.is_success() || status == StatusCode::METHOD_NOT_ALLOWED
}

/// Returns install-time-only option keys for the Aqua backend.
///
/// Aqua registry vars may be provided either as a nested `vars` table or as
Expand Down Expand Up @@ -2241,6 +2292,58 @@ mod tests {
}
}

#[test]
fn test_version_with_prefix_does_not_double_prefix() {
assert_eq!(version_with_prefix("1.0.0", Some("tool-")), "tool-1.0.0");
assert_eq!(
version_with_prefix("tool-1.0.0", Some("tool-")),
"tool-1.0.0"
);
}

#[test]
fn test_complete_windows_ext_preserves_existing_extension() {
assert_eq!(
complete_windows_ext(PathBuf::from("bat/arq.bat"), true, "windows"),
PathBuf::from("bat/arq.bat")
);
assert_eq!(
complete_windows_ext(PathBuf::from("bin/tool"), true, "windows"),
PathBuf::from("bin/tool.exe")
);
}

#[test]
fn test_complete_windows_dst_ext_uses_source_extension() {
assert_eq!(
complete_windows_dst_ext(
Path::new("bat/arq.bat"),
PathBuf::from("bat/arq"),
true,
"windows",
),
PathBuf::from("bat/arq.bat")
);
assert_eq!(
complete_windows_dst_ext(
Path::new("bin/tool"),
PathBuf::from("bin/tool"),
true,
"windows"
),
PathBuf::from("bin/tool.exe")
);
}

#[test]
fn test_http_status_allows_aqua_version() {
assert!(http_status_allows_aqua_version(StatusCode::OK));
assert!(http_status_allows_aqua_version(
StatusCode::METHOD_NOT_ALLOWED
));
assert!(!http_status_allows_aqua_version(StatusCode::NOT_FOUND));
}

#[test]
fn test_apply_var_options_prefers_nested_vars() {
let mut pkg = AquaPackage::default();
Expand Down
20 changes: 20 additions & 0 deletions src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,26 @@ impl Client {
self.head_async_with_headers(url, &headers).await
}

pub async fn head_status<U: IntoUrl>(&self, url: U) -> Result<StatusCode> {
ensure!(!Settings::get().offline(), "offline mode is enabled");
let mut url = url.into_url().unwrap();
apply_url_replacements(&mut url);
let mut headers = host_auth_headers(&url);
headers.extend(netrc_headers(&url));
debug!("HEAD {}", &url);
let resp = self
.reqwest
.request(Method::HEAD, url.clone())
.headers(headers)
.send()
.await?;
if *env::MISE_LOG_HTTP {
eprintln!("HEAD {url} {}", resp.status());
}
debug!("HEAD {url} {}", resp.status());
Ok(resp.status())
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated
Comment thread
risu729 marked this conversation as resolved.
Outdated

pub async fn head_async_with_headers<U: IntoUrl>(
&self,
url: U,
Expand Down
Loading