From 6f96ec64b71f79af2989b4ea30ffdd41ffa1e051 Mon Sep 17 00:00:00 2001 From: brianheineman Date: Sat, 25 May 2024 09:18:29 -0600 Subject: [PATCH 1/2] skip `sha` (`sha1`, `sha256`, `sha512`) checksum files when determining the release asset --- CHANGELOG.md | 1 + src/update.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 908a1fa..82cde65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [unreleased] ### Added ### Changed +- skip `sha` (`sha1`, `sha256`, `sha512`) checksum files when determining the release asset ### Removed ## [0.40.0] diff --git a/src/update.rs b/src/update.rs index 3d0cf17..54a768d 100644 --- a/src/update.rs +++ b/src/update.rs @@ -64,6 +64,12 @@ impl Release { self.assets .iter() .find(|asset| { + // Skip .sha(1|256|512) checksum files + let extension = asset.name.split('.').last().unwrap_or_default(); + if extension.starts_with("sha") { + return false; + } + asset.name.contains(target) || (asset.name.contains(OS) && asset.name.contains(ARCH)) && if let Some(i) = identifier { From 8434444ac720d10c40eb638acbc83e1c0fb5d364 Mon Sep 17 00:00:00 2001 From: brianheineman Date: Thu, 4 Jul 2024 11:36:09 -0600 Subject: [PATCH 2/2] feat: add UpdateBuilder::asset_match_fn to enable custom logic for release asset matching --- CHANGELOG.md | 2 +- src/backends/gitea.rs | 17 +++++++++++++++++ src/backends/github.rs | 17 +++++++++++++++++ src/backends/gitlab.rs | 17 +++++++++++++++++ src/backends/s3.rs | 9 +++++++++ src/update.rs | 24 +++++++++++++----------- 6 files changed, 74 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82cde65..ccf5176 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,8 @@ ## [unreleased] ### Added +- add `UpdateBuilder::asset_match_fn` to enable custom logic for release asset matching ### Changed -- skip `sha` (`sha1`, `sha256`, `sha512`) checksum files when determining the release asset ### Removed ## [0.40.0] diff --git a/src/backends/gitea.rs b/src/backends/gitea.rs index ef16a5d..46c63bd 100644 --- a/src/backends/gitea.rs +++ b/src/backends/gitea.rs @@ -13,6 +13,7 @@ use crate::{ update::{Release, ReleaseAsset, ReleaseUpdate}, DEFAULT_PROGRESS_CHARS, DEFAULT_PROGRESS_TEMPLATE, }; +use crate::update::AssetMatchFn; impl ReleaseAsset { /// Parse a release-asset json object @@ -230,6 +231,7 @@ pub struct UpdateBuilder { repo_owner: Option, repo_name: Option, target: Option, + asset_match_fn: Option, bin_name: Option, bin_install_path: Option, bin_path_in_archive: Option, @@ -294,6 +296,14 @@ impl UpdateBuilder { self } + /// Set the function to match assets + /// + /// If unspecified, the first asset matching the target will be chosen + pub fn asset_match_fn(&mut self, asset_match_fn: AssetMatchFn) -> &mut Self { + self.asset_match_fn = Some(asset_match_fn); + self + } + /// Set the exe's name. Also sets `bin_path_in_archive` if it hasn't already been set. /// /// This method will append the platform specific executable file suffix @@ -439,6 +449,7 @@ impl UpdateBuilder { .as_ref() .map(|t| t.to_owned()) .unwrap_or_else(|| get_target().to_owned()), + asset_match_fn: self.asset_match_fn.clone(), bin_name: if let Some(ref name) = self.bin_name { name.to_owned() } else { @@ -476,6 +487,7 @@ pub struct Update { repo_name: String, target: String, current_version: String, + asset_match_fn: Option, target_version: Option, bin_name: String, bin_install_path: PathBuf, @@ -553,6 +565,10 @@ impl ReleaseUpdate for Update { self.target_version.clone() } + fn asset_match_fn(&self) -> Option { + self.asset_match_fn.clone() + } + fn bin_name(&self) -> String { self.bin_name.clone() } @@ -606,6 +622,7 @@ impl Default for UpdateBuilder { repo_owner: None, repo_name: None, target: None, + asset_match_fn: None, bin_name: None, bin_install_path: None, bin_path_in_archive: None, diff --git a/src/backends/github.rs b/src/backends/github.rs index ca05e20..5106bdc 100644 --- a/src/backends/github.rs +++ b/src/backends/github.rs @@ -14,6 +14,7 @@ use crate::{ update::{Release, ReleaseAsset, ReleaseUpdate}, DEFAULT_PROGRESS_CHARS, DEFAULT_PROGRESS_TEMPLATE, }; +use crate::update::AssetMatchFn; impl ReleaseAsset { /// Parse a release-asset json object @@ -232,6 +233,7 @@ pub struct UpdateBuilder { repo_name: Option, target: Option, identifier: Option, + asset_match_fn: Option, bin_name: Option, bin_install_path: Option, bin_path_in_archive: Option, @@ -307,6 +309,14 @@ impl UpdateBuilder { self } + /// Set the function to match assets + /// + /// If unspecified, the first asset matching the target will be chosen + pub fn asset_match_fn(&mut self, asset_match_fn: AssetMatchFn) -> &mut Self { + self.asset_match_fn = Some(asset_match_fn); + self + } + /// Set the exe's name. Also sets `bin_path_in_archive` if it hasn't already been set. /// /// This method will append the platform specific executable file suffix @@ -448,6 +458,7 @@ impl UpdateBuilder { .map(|t| t.to_owned()) .unwrap_or_else(|| get_target().to_owned()), identifier: self.identifier.clone(), + asset_match_fn: self.asset_match_fn.clone(), bin_name: if let Some(ref name) = self.bin_name { name.to_owned() } else { @@ -485,6 +496,7 @@ pub struct Update { repo_name: String, target: String, identifier: Option, + asset_match_fn: Option, current_version: String, target_version: Option, bin_name: String, @@ -577,6 +589,10 @@ impl ReleaseUpdate for Update { self.identifier.clone() } + fn asset_match_fn(&self) -> Option { + self.asset_match_fn.clone() + } + fn bin_name(&self) -> String { self.bin_name.clone() } @@ -630,6 +646,7 @@ impl Default for UpdateBuilder { repo_name: None, target: None, identifier: None, + asset_match_fn: None, bin_name: None, bin_install_path: None, bin_path_in_archive: None, diff --git a/src/backends/gitlab.rs b/src/backends/gitlab.rs index 4dbc2f0..fc53052 100644 --- a/src/backends/gitlab.rs +++ b/src/backends/gitlab.rs @@ -13,6 +13,7 @@ use crate::{ update::{Release, ReleaseAsset, ReleaseUpdate}, DEFAULT_PROGRESS_CHARS, DEFAULT_PROGRESS_TEMPLATE, }; +use crate::update::AssetMatchFn; impl ReleaseAsset { /// Parse a release-asset json object @@ -227,6 +228,7 @@ pub struct UpdateBuilder { repo_owner: Option, repo_name: Option, target: Option, + asset_match_fn: Option, bin_name: Option, bin_install_path: Option, bin_path_in_archive: Option, @@ -291,6 +293,14 @@ impl UpdateBuilder { self } + /// Set the function to match assets + /// + /// If unspecified, the first asset matching the target will be chosen + pub fn asset_match_fn(&mut self, asset_match_fn: AssetMatchFn) -> &mut Self { + self.asset_match_fn = Some(asset_match_fn); + self + } + /// Set the exe's name. Also sets `bin_path_in_archive` if it hasn't already been set. /// /// This method will append the platform specific executable file suffix @@ -432,6 +442,7 @@ impl UpdateBuilder { .as_ref() .map(|t| t.to_owned()) .unwrap_or_else(|| get_target().to_owned()), + asset_match_fn: self.asset_match_fn.clone(), bin_name: if let Some(ref name) = self.bin_name { name.to_owned() } else { @@ -470,6 +481,7 @@ pub struct Update { target: String, current_version: String, target_version: Option, + asset_match_fn: Option, bin_name: String, bin_install_path: PathBuf, bin_path_in_archive: String, @@ -551,6 +563,10 @@ impl ReleaseUpdate for Update { self.target_version.clone() } + fn asset_match_fn(&self) -> Option { + self.asset_match_fn.clone() + } + fn bin_name(&self) -> String { self.bin_name.clone() } @@ -604,6 +620,7 @@ impl Default for UpdateBuilder { repo_owner: None, repo_name: None, target: None, + asset_match_fn: None, bin_name: None, bin_install_path: None, bin_path_in_archive: None, diff --git a/src/backends/s3.rs b/src/backends/s3.rs index 9951b99..d284009 100644 --- a/src/backends/s3.rs +++ b/src/backends/s3.rs @@ -14,6 +14,7 @@ use regex::Regex; use std::cmp::Ordering; use std::env::{self, consts::EXE_SUFFIX}; use std::path::{Path, PathBuf}; +use crate::update::AssetMatchFn; /// Maximum number of items to retrieve from S3 API const MAX_KEYS: u8 = 100; @@ -142,6 +143,7 @@ pub struct UpdateBuilder { asset_prefix: Option, target: Option, region: Option, + asset_match_fn: Option, bin_name: Option, bin_install_path: Option, bin_path_in_archive: Option, @@ -165,6 +167,7 @@ impl Default for UpdateBuilder { asset_prefix: None, target: None, region: None, + asset_match_fn: None, bin_name: None, bin_install_path: None, bin_path_in_archive: None, @@ -367,6 +370,7 @@ impl UpdateBuilder { .as_ref() .map(|t| t.to_owned()) .unwrap_or_else(|| get_target().to_owned()), + asset_match_fn: self.asset_match_fn.clone(), bin_name: if let Some(ref name) = self.bin_name { name.to_owned() } else { @@ -406,6 +410,7 @@ pub struct Update { region: Option, current_version: String, target_version: Option, + asset_match_fn: Option, bin_name: String, bin_install_path: PathBuf, bin_path_in_archive: String, @@ -486,6 +491,10 @@ impl ReleaseUpdate for Update { self.target_version.clone() } + fn asset_match_fn(&self) -> Option { + self.asset_match_fn.clone() + } + fn bin_name(&self) -> String { self.bin_name.clone() } diff --git a/src/update.rs b/src/update.rs index 54a768d..039fa45 100644 --- a/src/update.rs +++ b/src/update.rs @@ -64,12 +64,6 @@ impl Release { self.assets .iter() .find(|asset| { - // Skip .sha(1|256|512) checksum files - let extension = asset.name.split('.').last().unwrap_or_default(); - if extension.starts_with("sha") { - return false; - } - asset.name.contains(target) || (asset.name.contains(OS) && asset.name.contains(ARCH)) && if let Some(i) = identifier { @@ -82,6 +76,9 @@ impl Release { } } +/// Function type for determining the asset among multiple matches +pub type AssetMatchFn = fn(&Release, &str, Option<&str>) -> Option; + /// Updates to a specified or latest release pub trait ReleaseUpdate { /// Fetch details of the latest release from the backend @@ -104,6 +101,9 @@ pub trait ReleaseUpdate { None } + /// Optional function for determining the asset among multiple matches + fn asset_match_fn(&self) -> Option; + /// Name of the binary being updated fn bin_name(&self) -> String; @@ -212,11 +212,13 @@ pub trait ReleaseUpdate { } }; - let target_asset = release - .asset_for(&target, self.identifier().as_deref()) - .ok_or_else(|| { - format_err!(Error::Release, "No asset found for target: `{}`", target) - })?; + let target_asset = if let Some(asset_match_fn) = self.asset_match_fn() { + asset_match_fn(&release, &target, self.identifier().as_deref()) + } else { + release.asset_for(&target, self.identifier().as_deref()) + }.ok_or_else(|| { + format_err!(Error::Release, "No asset found for target: `{}`", target) + })?; let prompt_confirmation = !self.no_confirm(); if self.show_output() || prompt_confirmation {