use semver::Version;
pub fn select_rollback_target(
current: Option<&Version>,
successful_releases: &[Version],
tags: &[Version],
) -> Option<Version> {
let candidates: &[Version] = if successful_releases.is_empty() {
tags
} else {
successful_releases
};
match current {
Some(cur) => candidates.iter().find(|v| *v < cur).cloned(),
None => candidates.get(1).cloned(),
}
}
pub fn version_from_image(image: &str) -> Option<Version> {
let tag = image.rsplit_once(':').map(|(_, t)| t)?;
if tag.contains('/') {
return None;
}
Version::parse(tag.trim_start_matches('v')).ok()
}
#[cfg(test)]
mod tests {
use super::*;
fn v(s: &str) -> Version {
Version::parse(s).unwrap()
}
#[test]
fn picks_newest_release_older_than_current() {
let releases = [v("1.4.0"), v("1.3.2"), v("1.3.1")];
let target = select_rollback_target(Some(&v("1.4.0")), &releases, &[]);
assert_eq!(target, Some(v("1.3.2")));
}
#[test]
fn skips_releases_newer_than_current() {
let releases = [v("2.0.0"), v("1.4.0"), v("1.3.2")];
let target = select_rollback_target(Some(&v("1.4.0")), &releases, &[]);
assert_eq!(target, Some(v("1.3.2")));
}
#[test]
fn without_current_uses_second_newest() {
let releases = [v("1.4.0"), v("1.3.2"), v("1.3.1")];
let target = select_rollback_target(None, &releases, &[]);
assert_eq!(target, Some(v("1.3.2")));
}
#[test]
fn falls_back_to_tags_when_no_audit_history() {
let tags = [v("0.9.0"), v("0.8.0")];
let target = select_rollback_target(Some(&v("0.9.0")), &[], &tags);
assert_eq!(target, Some(v("0.8.0")));
}
#[test]
fn none_when_no_older_candidate() {
let releases = [v("1.0.0")];
assert_eq!(
select_rollback_target(Some(&v("1.0.0")), &releases, &[]),
None
);
assert_eq!(select_rollback_target(None, &releases, &[]), None);
assert_eq!(select_rollback_target(None, &[], &[]), None);
}
#[test]
fn parses_version_from_image_tag() {
assert_eq!(
version_from_image("123.dkr.ecr.us-east-1.amazonaws.com/app:1.2.3"),
Some(v("1.2.3"))
);
assert_eq!(
version_from_image("ghcr.io/org/app:v2.0.1"),
Some(v("2.0.1"))
);
assert_eq!(version_from_image("app:latest"), None);
assert_eq!(version_from_image("app"), None);
assert_eq!(version_from_image("localhost:5000/app"), None);
}
}