From 5ad8c315a5add1964d9e9481b5e908b3a63db6fc Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:54:19 +0100 Subject: [PATCH] feat: take remote tags into account when excluding based on `images.exclude` from the config (#148) --- src/check.rs | 23 +++++++++++++++++++++-- src/registry.rs | 18 ++++++++++++++++++ src/structs/image.rs | 12 ++++++++++-- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/check.rs b/src/check.rs index 27e188e..7eef206 100644 --- a/src/check.rs +++ b/src/check.rs @@ -7,7 +7,10 @@ use crate::{ http::Client, registry::{check_auth, get_token}, structs::{image::Image, update::Update}, - utils::request::{get_response_body, parse_json}, + utils::{ + reference::split, + request::{get_response_body, parse_json}, + }, Context, }; @@ -79,6 +82,21 @@ async fn get_remote_updates(ctx: &Context, client: &Client, refresh: bool) -> Ve remote_images } +/// Returns a list of excluded tag prefixes for the given image. +fn get_excluded_tags(image: &Image, ctx: &Context) -> Vec { + let image_name = image.reference.split(':').next().unwrap(); + ctx.config + .images + .exclude + .iter() + .filter(|item| item.starts_with(image_name)) + .filter_map(|excluded| { + let tag = split(excluded).2; + (tag != "latest").then_some(tag) + }) + .collect() +} + /// Returns a list of updates for all images passed in. pub async fn get_updates( references: &Option>, // If a user requested _specific_ references to be checked, this will have a value @@ -200,8 +218,9 @@ pub async fn get_updates( .iter() .any(|item| image.reference.starts_with(item)); if !is_ignored { + let excluded_tags = get_excluded_tags(image, ctx); let token = tokens.get(image.parts.registry.as_str()).unwrap(); - let future = image.check(token.as_deref(), ctx, &client); + let future = image.check(token.as_deref(), ctx, &client, excluded_tags); handles.push(future); } } diff --git a/src/registry.rs b/src/registry.rs index c1b352f..a1d74be 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -122,6 +122,7 @@ pub async fn get_latest_tag( token: Option<&str>, ctx: &Context, client: &Client, + excluded_tags: Vec, ) -> Image { ctx.logger .debug(format!("Checking for tag update to {}", image.reference)); @@ -153,6 +154,7 @@ pub async fn get_latest_tag( &image.version_info.as_ref().unwrap().format_str, ctx, client, + &excluded_tags, ) .await { @@ -211,6 +213,20 @@ pub async fn get_latest_tag( } } +/// Checks if a tag matches any of the excluded tag prefixes. +fn is_excluded_tag(tag: &str, excluded_tags: &[String], ctx: &Context) -> bool { + for excluded in excluded_tags { + if tag.starts_with(excluded) { + ctx.logger.debug(format!( + "Ignoring tag \"{}\" as it matches excluded prefix \"{}\"", + tag, excluded + )); + return true; + } + } + false +} + pub async fn get_extra_tags( url: &str, headers: &[(&str, Option<&str>)], @@ -218,6 +234,7 @@ pub async fn get_extra_tags( format_str: &str, ctx: &Context, client: &Client, + excluded_tags: &[String], ) -> Result<(Vec, Option), String> { let response = client.get(url, headers, false).await; @@ -232,6 +249,7 @@ pub async fn get_extra_tags( .as_array() .unwrap() .iter() + .filter(|tag| !is_excluded_tag(tag.as_str().unwrap(), excluded_tags, ctx)) .filter_map(|tag| Version::from_tag(tag.as_str().unwrap())) .filter(|(tag, format_string)| match (base.minor, tag.minor) { (Some(_), Some(_)) | (None, None) => { diff --git a/src/structs/image.rs b/src/structs/image.rs index 17a19b7..a180bfa 100644 --- a/src/structs/image.rs +++ b/src/structs/image.rs @@ -228,9 +228,17 @@ impl Image { } /// Checks if the image has an update - pub async fn check(&self, token: Option<&str>, ctx: &Context, client: &Client) -> Self { + pub async fn check( + &self, + token: Option<&str>, + ctx: &Context, + client: &Client, + excluded_tags: Vec, + ) -> Self { match &self.version_info { - Some(data) => get_latest_tag(self, &data.current_tag, token, ctx, client).await, + Some(data) => { + get_latest_tag(self, &data.current_tag, token, ctx, client, excluded_tags).await + } None => match self.digest_info { Some(_) => get_latest_digest(self, token, ctx, client).await, None => unreachable!(),