chore: Simplify opts using new ImageRef type

This commit is contained in:
Gerald Pinder 2025-05-09 15:53:59 -04:00
parent 0896907c0b
commit 00806b02e1
7 changed files with 153 additions and 78 deletions

View file

@ -84,12 +84,12 @@ impl BuildDriver for BuildahDriver {
"-f",
&*opts.containerfile,
"-t",
&*opts.image,
opts.image.to_string(),
);
trace!("{command:?}");
let status = command
.build_status(&opts.image, "Building Image")
.build_status(opts.image.to_string(), "Building Image")
.into_diagnostic()?;
if status.success() {

View file

@ -29,7 +29,7 @@ use crate::{
RunOptsVolume, TagOpts,
},
traits::{BuildDriver, DriverVersion, InspectDriver, RunDriver},
types::{ContainerId, ImageMetadata, Platform},
types::{ContainerId, ImageMetadata, ImageRef, Platform},
},
logging::CommandLogging,
signal_handler::{ContainerRuntime, ContainerSignalId, add_cid, remove_cid},
@ -207,7 +207,7 @@ impl BuildDriver for DockerDriver {
opts.platform.to_string(),
],
"-t",
&*opts.image,
opts.image.to_string(),
"-f",
if let Some(cache_from) = opts.cache_from.as_ref() => [
"--cache-from",
@ -388,7 +388,7 @@ impl BuildDriver for DockerDriver {
Self::setup()?;
let final_images = get_final_images(opts)?;
let final_images = get_final_images(opts);
let first_image = final_images.first().unwrap();
@ -418,22 +418,22 @@ fn build_tag_push_cmd(opts: &BuildTagPushOpts<'_>, first_image: &str) -> Command
format!("--builder={BLUE_BUILD}"),
"build",
".",
match (opts.image, opts.archive_path.as_deref()) {
(Some(_), None) if opts.push => [
match &opts.image {
ImageRef::Remote(_remote) if opts.push => [
"--output",
format!(
"type=image,name={first_image},push=true,compression={},oci-mediatypes=true",
opts.compression
),
],
(Some(_), None) if env::var(GITHUB_ACTIONS).is_err() => "--load",
(None, Some(archive_path)) => [
ImageRef::Remote(_remote) if env::var(GITHUB_ACTIONS).is_err() => "--load",
ImageRef::LocalTar(archive_path) => [
"--output",
format!("type=oci,dest={}", archive_path.display()),
],
_ => [],
},
for opts.image.as_ref().map_or_else(Vec::new, |image| {
for opts.image.remote_ref().map_or_else(Vec::new, |image| {
opts.tags.iter().flat_map(|tag| {
vec![
"-t".to_string(),
@ -465,9 +465,9 @@ fn build_tag_push_cmd(opts: &BuildTagPushOpts<'_>, first_image: &str) -> Command
c
}
fn get_final_images(opts: &BuildTagPushOpts<'_>) -> Result<Vec<String>> {
Ok(match (opts.image, opts.archive_path.as_deref()) {
(Some(image), None) => {
fn get_final_images(opts: &BuildTagPushOpts<'_>) -> Vec<String> {
match &opts.image {
ImageRef::Remote(image) => {
let images = if opts.tags.is_empty() {
let image = image.to_string();
string_vec![image]
@ -480,12 +480,10 @@ fn get_final_images(opts: &BuildTagPushOpts<'_>) -> Result<Vec<String>> {
images
}
(None, Some(archive_path)) => {
ImageRef::LocalTar(archive_path) => {
string_vec![archive_path.display().to_string()]
}
(Some(_), Some(_)) => bail!("Cannot use both image and archive path"),
(None, None) => bail!("Need either the image or archive path set"),
})
}
}
impl InspectDriver for DockerDriver {

View file

@ -3,7 +3,7 @@ use std::{borrow::Cow, path::Path};
use bon::Builder;
use oci_distribution::Reference;
use crate::drivers::types::Platform;
use crate::drivers::types::{ImageRef, Platform};
use super::CompressionType;
@ -11,7 +11,7 @@ use super::CompressionType;
#[derive(Debug, Clone, Builder)]
pub struct BuildOpts<'scope> {
#[builder(into)]
pub image: Cow<'scope, str>,
pub image: ImageRef<'scope>,
#[builder(default)]
pub squash: bool,
@ -65,17 +65,8 @@ pub struct PruneOpts {
#[derive(Debug, Clone, Builder)]
pub struct BuildTagPushOpts<'scope> {
/// The base image name.
///
/// NOTE: This SHOULD NOT contain the tag of the image.
///
/// NOTE: You cannot have this set with `archive_path` set.
pub image: Option<&'scope Reference>,
/// The path to the archive file.
///
/// NOTE: You cannot have this set with image set.
#[builder(into)]
pub archive_path: Option<Cow<'scope, Path>>,
pub image: ImageRef<'scope>,
/// The path to the Containerfile to build.
#[builder(into)]

View file

@ -179,13 +179,13 @@ impl BuildDriver for PodmanDriver {
"-f",
&*opts.containerfile,
"-t",
&*opts.image,
opts.image.to_string(),
".",
);
trace!("{command:?}");
let status = command
.build_status(&opts.image, "Building Image")
.build_status(opts.image.to_string(), "Building Image")
.into_diagnostic()?;
if status.success() {

View file

@ -10,7 +10,11 @@ use miette::{Context, IntoDiagnostic, Result, bail};
use oci_distribution::Reference;
use semver::VersionReq;
use crate::drivers::{Driver, functions::get_private_key, types::CiDriverType};
use crate::drivers::{
Driver,
functions::get_private_key,
types::{CiDriverType, ImageRef},
};
#[cfg(feature = "sigstore")]
use super::sigstore_driver::SigstoreDriver;
@ -122,17 +126,8 @@ pub trait BuildDriver: PrivateDriver {
fn build_tag_push(opts: &BuildTagPushOpts) -> Result<Vec<String>> {
trace!("BuildDriver::build_tag_push({opts:#?})");
let full_image = match (opts.archive_path.as_ref(), opts.image.as_ref()) {
(Some(archive_path), None) => {
format!("oci-archive:{}", archive_path.display())
}
(None, Some(image)) => image.to_string(),
(Some(_), Some(_)) => bail!("Cannot use both image and archive path"),
(None, None) => bail!("Need either the image or archive path set"),
};
let build_opts = BuildOpts::builder()
.image(&full_image)
.image(&opts.image)
.containerfile(opts.containerfile.as_ref())
.platform(opts.platform)
.squash(opts.squash)
@ -140,24 +135,25 @@ pub trait BuildDriver: PrivateDriver {
.maybe_cache_to(opts.cache_to)
.build();
info!("Building image {full_image}");
info!("Building image {}", opts.image);
Self::build(&build_opts)?;
let image_list: Vec<String> = if !opts.tags.is_empty() && opts.archive_path.is_none() {
let image = opts.image.unwrap();
let image_list: Vec<String> = match &opts.image {
ImageRef::Remote(image) if !opts.tags.is_empty() => {
debug!("Tagging all images");
let mut image_list = Vec::with_capacity(opts.tags.len());
for tag in &opts.tags {
debug!("Tagging {} with {tag}", &full_image);
let tagged_image: Reference =
format!("{}/{}:{tag}", image.resolve_registry(), image.repository())
.parse()
.into_diagnostic()?;
debug!("Tagging {} with {tag}", &image);
let tagged_image = Reference::with_tag(
image.registry().into(),
image.repository().into(),
tag.to_string(),
);
let tag_opts = TagOpts::builder()
.src_image(image)
.src_image(image.as_ref())
.dest_image(&tagged_image)
.build();
@ -183,8 +179,10 @@ pub trait BuildDriver: PrivateDriver {
}
image_list
} else {
string_vec![&full_image]
}
_ => {
string_vec![&opts.image]
}
};
Ok(image_list)
@ -293,7 +291,7 @@ pub trait RechunkDriver: RunDriver + BuildDriver + ContainerMountDriver {
Self::build(
&BuildOpts::builder()
.image(raw_image.to_string())
.image(raw_image)
.containerfile(&*opts.containerfile)
.platform(opts.platform)
.privileged(true)

View file

@ -1,4 +1,9 @@
use std::{collections::HashMap, env};
use std::{
borrow::Cow,
collections::HashMap,
env,
path::{Path, PathBuf},
};
use blue_build_utils::{
constants::{GITHUB_ACTIONS, GITLAB_CI, IMAGE_VERSION_LABEL},
@ -6,6 +11,7 @@ use blue_build_utils::{
};
use clap::ValueEnum;
use log::trace;
use oci_distribution::Reference;
use serde::Deserialize;
use serde_json::Value;
@ -313,3 +319,85 @@ impl TryFrom<std::path::PathBuf> for OciDir {
Ok(Self(format!("oci:{}", value.display())))
}
}
/// An image ref that could reference
/// a remote registry or a local tarball.
#[derive(Debug, Clone)]
pub enum ImageRef<'scope> {
Remote(Cow<'scope, Reference>),
LocalTar(Cow<'scope, Path>),
}
impl ImageRef<'_> {
#[must_use]
pub fn remote_ref(&self) -> Option<&Reference> {
match self {
Self::Remote(remote) => Some(remote.as_ref()),
Self::LocalTar(_) => None,
}
}
}
impl<'scope> From<&'scope Self> for ImageRef<'scope> {
fn from(value: &'scope ImageRef) -> Self {
match value {
Self::Remote(remote) => Self::Remote(Cow::Borrowed(remote.as_ref())),
Self::LocalTar(path) => Self::LocalTar(Cow::Borrowed(path.as_ref())),
}
}
}
impl<'scope> From<&'scope Reference> for ImageRef<'scope> {
fn from(value: &'scope Reference) -> Self {
Self::Remote(Cow::Borrowed(value))
}
}
impl From<Reference> for ImageRef<'_> {
fn from(value: Reference) -> Self {
Self::Remote(Cow::Owned(value))
}
}
impl<'scope> From<&'scope Path> for ImageRef<'scope> {
fn from(value: &'scope Path) -> Self {
Self::LocalTar(Cow::Borrowed(value))
}
}
impl<'scope> From<&'scope PathBuf> for ImageRef<'scope> {
fn from(value: &'scope PathBuf) -> Self {
Self::from(value.as_path())
}
}
impl From<PathBuf> for ImageRef<'_> {
fn from(value: PathBuf) -> Self {
Self::LocalTar(Cow::Owned(value))
}
}
impl From<ImageRef<'_>> for String {
fn from(value: ImageRef<'_>) -> Self {
Self::from(&value)
}
}
impl From<&ImageRef<'_>> for String {
fn from(value: &ImageRef<'_>) -> Self {
format!("{value}")
}
}
impl std::fmt::Display for ImageRef<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Remote(remote) => remote.whole(),
Self::LocalTar(path) => format!("oci-archive:{}", path.display()),
}
)
}
}

View file

@ -339,7 +339,7 @@ impl BuildCommand {
BuildTagPushOpts::builder()
.containerfile(containerfile)
.platform(self.platform)
.archive_path(PathBuf::from(format!(
.image(PathBuf::from(format!(
"{}/{}.{ARCHIVE_SUFFIX}",
archive_dir.to_string_lossy().trim_end_matches('/'),
recipe.name.to_lowercase().replace('/', "_"),