diff --git a/process/drivers.rs b/process/drivers.rs index 80dcd11..e1a57eb 100644 --- a/process/drivers.rs +++ b/process/drivers.rs @@ -295,7 +295,7 @@ impl BuildDriver for Driver { impl_build_driver!(login()) } - fn build_tag_push(opts: &BuildTagPushOpts) -> Result<()> { + fn build_tag_push(opts: &BuildTagPushOpts) -> Result> { impl_build_driver!(build_tag_push(opts)) } } diff --git a/process/drivers/docker_driver.rs b/process/drivers/docker_driver.rs index a05922e..f6433ad 100644 --- a/process/drivers/docker_driver.rs +++ b/process/drivers/docker_driver.rs @@ -213,7 +213,7 @@ impl BuildDriver for DockerDriver { Ok(()) } - fn build_tag_push(opts: &BuildTagPushOpts) -> Result<()> { + fn build_tag_push(opts: &BuildTagPushOpts) -> Result> { trace!("DockerDriver::build_tag_push({opts:#?})"); if opts.squash { @@ -243,63 +243,62 @@ impl BuildDriver for DockerDriver { ], ); - let mut final_image = String::new(); - - match (opts.image.as_deref(), opts.archive_path.as_deref()) { + let final_images = match (opts.image.as_deref(), opts.archive_path.as_deref()) { (Some(image), None) => { - if opts.tags.is_empty() { - final_image.push_str(image); + let images = if opts.tags.is_empty() { cmd!(command, "-t", image); + string_vec![image] } else { - final_image.push_str( - format!("{image}:{}", opts.tags.first().map_or("", String::as_str)) - .as_str(), - ); - opts.tags.iter().for_each(|tag| { cmd!(command, "-t", format!("{image}:{tag}")); }); - } + opts.tags + .iter() + .map(|tag| format!("{image}:{tag}")) + .collect() + }; + let first_image = images.first().unwrap(); if opts.push { cmd!( command, "--output", format!( - "type=image,name={image},push=true,compression={},oci-mediatypes=true", + "type=image,name={first_image},push=true,compression={},oci-mediatypes=true", opts.compression ) ); } else { cmd!(command, "--load"); } + images } (None, Some(archive_path)) => { - final_image.push_str(archive_path); - cmd!(command, "--output", format!("type=oci,dest={archive_path}")); + string_vec![archive_path] } (Some(_), Some(_)) => bail!("Cannot use both image and archive path"), (None, None) => bail!("Need either the image or archive path set"), - } + }; + let display_image = final_images.first().unwrap(); // There will always be at least one image cmd!(command, "."); trace!("{command:?}"); if command - .status_image_ref_progress(&final_image, "Building Image") + .status_image_ref_progress(display_image, "Building Image") .into_diagnostic()? .success() { if opts.push { - info!("Successfully built and pushed image {}", final_image); + info!("Successfully built and pushed image {}", display_image); } else { - info!("Successfully built image {}", final_image); + info!("Successfully built image {}", display_image); } } else { - bail!("Failed to build image {}", final_image); + bail!("Failed to build image {}", display_image); } - Ok(()) + Ok(final_images) } } diff --git a/process/drivers/github_driver.rs b/process/drivers/github_driver.rs index 75d010d..a86b601 100644 --- a/process/drivers/github_driver.rs +++ b/process/drivers/github_driver.rs @@ -65,23 +65,23 @@ impl CiDriver for GithubDriver { .iter() .flat_map(|alt| { string_vec![ + &**alt, + format!("{alt}-{os_version}"), format!("{timestamp}-{alt}-{os_version}"), format!("{short_sha}-{alt}-{os_version}"), - format!("{alt}-{os_version}"), - &**alt, ] }) .collect(), (false, None, Ok(event_name), Ok(event_num)) if event_name == PR_EVENT => { vec![ - format!("{short_sha}-{os_version}"), format!("pr-{event_num}-{os_version}"), + format!("{short_sha}-{os_version}"), ] } (false, None, _, _) => { vec![ - format!("{short_sha}-{os_version}"), format!("br-{ref_name}-{os_version}"), + format!("{short_sha}-{os_version}"), ] } (false, Some(alt_tags), Ok(event_name), Ok(event_num)) if event_name == PR_EVENT => { @@ -89,8 +89,8 @@ impl CiDriver for GithubDriver { .iter() .flat_map(|alt| { vec![ - format!("{short_sha}-{alt}-{os_version}"), format!("pr-{event_num}-{alt}-{os_version}"), + format!("{short_sha}-{alt}-{os_version}"), ] }) .collect() @@ -99,8 +99,8 @@ impl CiDriver for GithubDriver { .iter() .flat_map(|alt| { vec![ - format!("{short_sha}-{alt}-{os_version}"), format!("br-{ref_name}-{alt}-{os_version}"), + format!("{short_sha}-{alt}-{os_version}"), ] }) .collect(), diff --git a/process/drivers/gitlab_driver.rs b/process/drivers/gitlab_driver.rs index bc881ac..deaefdd 100644 --- a/process/drivers/gitlab_driver.rs +++ b/process/drivers/gitlab_driver.rs @@ -71,23 +71,23 @@ impl CiDriver for GitlabDriver { .iter() .flat_map(|alt| { string_vec![ + &**alt, + format!("{alt}-{os_version}"), format!("{timestamp}-{alt}-{os_version}"), format!("{short_sha}-{alt}-{os_version}"), - format!("{alt}-{os_version}"), - &**alt, ] }) .collect(), (false, None, Ok(mr_iid), Ok(pipeline_source)) if pipeline_source == MR_EVENT => { vec![ - format!("{short_sha}-{os_version}"), format!("mr-{mr_iid}-{os_version}"), + format!("{short_sha}-{os_version}"), ] } (false, None, _, _) => { vec![ - format!("{short_sha}-{os_version}"), format!("br-{ref_name}-{os_version}"), + format!("{short_sha}-{os_version}"), ] } (false, Some(alt_tags), Ok(mr_iid), Ok(pipeline_source)) @@ -97,8 +97,8 @@ impl CiDriver for GitlabDriver { .iter() .flat_map(|alt| { vec![ - format!("{short_sha}-{alt}-{os_version}"), format!("mr-{mr_iid}-{alt}-{os_version}"), + format!("{short_sha}-{alt}-{os_version}"), ] }) .collect() @@ -107,8 +107,8 @@ impl CiDriver for GitlabDriver { .iter() .flat_map(|alt| { vec![ - format!("{short_sha}-{alt}-{os_version}"), format!("br-{ref_name}-{alt}-{os_version}"), + format!("{short_sha}-{alt}-{os_version}"), ] }) .collect(), diff --git a/process/drivers/local_driver.rs b/process/drivers/local_driver.rs index 5cda4ce..acd2b21 100644 --- a/process/drivers/local_driver.rs +++ b/process/drivers/local_driver.rs @@ -1,4 +1,4 @@ -use blue_build_utils::string_vec; +use blue_build_utils::{cmd, string_vec}; use log::trace; use miette::bail; @@ -25,12 +25,39 @@ impl CiDriver for LocalDriver { fn generate_tags(opts: &GenerateTagsOpts) -> miette::Result> { trace!("LocalDriver::generate_tags({opts:?})"); let os_version = Driver::get_os_version(opts.oci_ref)?; + let timestamp = blue_build_utils::get_tag_timestamp(); + let short_sha = commit_sha(); + Ok(opts.alt_tags.as_ref().map_or_else( - || string_vec![format!("local-{os_version}")], + || { + let mut tags = string_vec![ + "latest", + ×tamp, + format!("{os_version}"), + format!("{timestamp}-{os_version}"), + ]; + + if let Some(short_sha) = &short_sha { + tags.push(format!("{short_sha}-{os_version}")); + } + + tags + }, |alt_tags| { alt_tags .iter() - .flat_map(|alt| string_vec![format!("local-{alt}-{os_version}")]) + .flat_map(|alt| { + let mut tags = string_vec![ + &**alt, + format!("{alt}-{os_version}"), + format!("{timestamp}-{alt}-{os_version}"), + ]; + if let Some(short_sha) = &short_sha { + tags.push(format!("{short_sha}-{alt}-{os_version}")); + } + + tags + }) .collect() }, )) @@ -46,3 +73,15 @@ impl CiDriver for LocalDriver { Ok(String::from("localhost")) } } + +fn commit_sha() -> Option { + let output = cmd!("git", "rev-parse", "--short", "HEAD").output().ok()?; + + if output.status.success() { + String::from_utf8(output.stdout) + .ok() + .map(|s| s.trim().to_string()) + } else { + None + } +} diff --git a/process/drivers/traits.rs b/process/drivers/traits.rs index c1c229c..1bd0984 100644 --- a/process/drivers/traits.rs +++ b/process/drivers/traits.rs @@ -4,7 +4,7 @@ use std::{ process::{ExitStatus, Output}, }; -use blue_build_utils::{constants::COSIGN_PUB_PATH, retry}; +use blue_build_utils::{constants::COSIGN_PUB_PATH, retry, string_vec}; use log::{debug, info, trace}; use miette::{bail, miette, Context, IntoDiagnostic, Result}; use oci_distribution::Reference; @@ -72,7 +72,7 @@ pub trait BuildDriver { /// /// # Errors /// Will error if building, tagging, or pusing fails. - fn build_tag_push(opts: &BuildTagPushOpts) -> Result<()> { + fn build_tag_push(opts: &BuildTagPushOpts) -> Result> { trace!("BuildDriver::build_tag_push({opts:#?})"); let full_image = match (opts.archive_path.as_ref(), opts.image.as_ref()) { @@ -96,22 +96,26 @@ pub trait BuildDriver { info!("Building image {full_image}"); Self::build(&build_opts)?; - if !opts.tags.is_empty() && opts.archive_path.is_none() { + let image_list: Vec = if !opts.tags.is_empty() && opts.archive_path.is_none() { let image = opts .image .as_ref() .ok_or_else(|| miette!("Image is required in order to tag"))?; debug!("Tagging all images"); + let mut image_list = Vec::with_capacity(opts.tags.len()); + for tag in opts.tags.as_ref() { debug!("Tagging {} with {tag}", &full_image); + let tagged_image = format!("{image}:{tag}"); let tag_opts = TagOpts::builder() .src_image(&full_image) - .dest_image(format!("{image}:{tag}")) + .dest_image(&tagged_image) .build(); Self::tag(&tag_opts)?; + image_list.push(tagged_image); if opts.push { let retry_count = if opts.retry_push { opts.retry_count } else { 0 }; @@ -132,9 +136,13 @@ pub trait BuildDriver { })?; } } - } - Ok(()) + image_list + } else { + string_vec![&full_image] + }; + + Ok(image_list) } } diff --git a/process/logging.rs b/process/logging.rs index f515732..03fb54f 100644 --- a/process/logging.rs +++ b/process/logging.rs @@ -395,7 +395,8 @@ where } } -fn gen_random_ansi_color() -> u8 { +#[must_use] +pub fn gen_random_ansi_color() -> u8 { // ANSI extended color range // https://www.ditig.com/publications/256-colors-cheat-sheet const LOW_END: u8 = 21; // Blue1 #0000ff rgb(0,0,255) hsl(240,100%,50%) @@ -404,7 +405,7 @@ fn gen_random_ansi_color() -> u8 { rand::thread_rng().gen_range(LOW_END..=HIGH_END) } -fn color_str(text: T, ansi_color: u8) -> String +pub fn color_str(text: T, ansi_color: u8) -> String where T: AsRef, { diff --git a/src/commands/build.rs b/src/commands/build.rs index 947f629..3ad437c 100644 --- a/src/commands/build.rs +++ b/src/commands/build.rs @@ -3,12 +3,15 @@ use std::{ path::{Path, PathBuf}, }; -use blue_build_process_management::drivers::{ - opts::{ - BuildTagPushOpts, CheckKeyPairOpts, CompressionType, GenerateImageNameOpts, - GenerateTagsOpts, SignVerifyOpts, +use blue_build_process_management::{ + drivers::{ + opts::{ + BuildTagPushOpts, CheckKeyPairOpts, CompressionType, GenerateImageNameOpts, + GenerateTagsOpts, SignVerifyOpts, + }, + BuildDriver, CiDriver, Driver, DriverArgs, SigningDriver, }, - BuildDriver, CiDriver, Driver, DriverArgs, SigningDriver, + logging::{color_str, gen_random_ansi_color}, }; use blue_build_recipe::Recipe; use blue_build_utils::{ @@ -206,18 +209,31 @@ impl BuildCommand { trace!("BuildCommand::build_image()"); - recipe_paths + let images = recipe_paths .par_iter() - .try_for_each(|recipe_path| -> Result<()> { + .try_fold(Vec::new, |mut images, recipe_path| -> Result> { let containerfile = if recipe_paths.len() > 1 { blue_build_utils::generate_containerfile_path(recipe_path)? } else { PathBuf::from(CONTAINER_FILE) }; - self.build(recipe_path, &containerfile) + images.extend(self.build(recipe_path, &containerfile)?); + Ok(images) + }) + .try_reduce(Vec::new, |mut init, image_names| { + let color = gen_random_ansi_color(); + init.extend(image_names.iter().map(|image| color_str(image, color))); + Ok(init) })?; - info!("Build complete!"); + info!( + "Finished building:\n{}", + images + .iter() + .map(|image| format!("\t- {image}")) + .collect::>() + .join("\n") + ); Ok(()) } @@ -225,13 +241,21 @@ impl BuildCommand { fn start(&self, recipe_path: &Path) -> Result<()> { trace!("BuildCommand::start()"); - self.build(recipe_path, Path::new(CONTAINER_FILE))?; + let images = self.build(recipe_path, Path::new(CONTAINER_FILE))?; + let color = gen_random_ansi_color(); - info!("Build complete!"); + info!( + "Finished building:\n{}", + images + .iter() + .map(|image| format!("\t- {}", color_str(image, color))) + .collect::>() + .join("\n") + ); Ok(()) } - fn build(&self, recipe_path: &Path, containerfile: &Path) -> Result<()> { + fn build(&self, recipe_path: &Path, containerfile: &Path) -> Result> { let recipe = Recipe::parse(recipe_path)?; let tags = Driver::generate_tags( &GenerateTagsOpts::builder() @@ -264,7 +288,7 @@ impl BuildCommand { .build() }; - Driver::build_tag_push(&opts)?; + let images = Driver::build_tag_push(&opts)?; if self.push && !self.no_sign { let opts = SignVerifyOpts::builder() @@ -279,7 +303,7 @@ impl BuildCommand { Driver::sign_and_verify(&opts)?; } - Ok(()) + Ok(images) } fn image_name(&self, recipe: &Recipe) -> Result {