feat(iso): Create generate-iso command (#192)

## Tasks

- [x] Add ctrl-c handler to kill spawned children
- [x] add more args to support all variables
- [x] Add integration test
This commit is contained in:
Gerald Pinder 2024-09-04 18:17:08 -04:00 committed by GitHub
parent 4634f40840
commit e6cce3d542
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 737 additions and 201 deletions

View file

@ -4,7 +4,7 @@ use std::{
};
use blue_build_process_management::drivers::{
opts::{BuildTagPushOpts, CheckKeyPairOpts, CompressionType},
opts::{BuildTagPushOpts, CheckKeyPairOpts, CompressionType, GenerateTagsOpts, SignVerifyOpts},
BuildDriver, CiDriver, Driver, DriverArgs, SigningDriver,
};
use blue_build_recipe::Recipe;
@ -14,11 +14,13 @@ use blue_build_utils::{
GITIGNORE_PATH, LABELED_ERROR_MESSAGE, NO_LABEL_ERROR_MESSAGE, RECIPE_FILE, RECIPE_PATH,
},
credentials::{Credentials, CredentialsArgs},
string,
};
use clap::Args;
use colored::Colorize;
use log::{debug, info, trace, warn};
use miette::{bail, Context, IntoDiagnostic, Result};
use oci_distribution::Reference;
use typed_builder::TypedBuilder;
use crate::commands::generate::GenerateCommand;
@ -197,7 +199,6 @@ impl BlueBuildCommand for BuildCommand {
impl BuildCommand {
#[cfg(feature = "multi-recipe")]
fn start(&self, recipe_paths: &[PathBuf]) -> Result<()> {
use blue_build_process_management::drivers::opts::SignVerifyOpts;
use rayon::prelude::*;
trace!("BuildCommand::build_image()");
@ -205,54 +206,12 @@ impl BuildCommand {
recipe_paths
.par_iter()
.try_for_each(|recipe_path| -> Result<()> {
let recipe = Recipe::parse(recipe_path)?;
let containerfile = if recipe_paths.len() > 1 {
blue_build_utils::generate_containerfile_path(recipe_path)?
} else {
PathBuf::from(CONTAINER_FILE)
};
let tags = Driver::generate_tags(&recipe)?;
let image_name = self.generate_full_image_name(&recipe)?;
let opts = if let Some(archive_dir) = self.archive.as_ref() {
BuildTagPushOpts::builder()
.containerfile(&containerfile)
.archive_path(format!(
"{}/{}.{ARCHIVE_SUFFIX}",
archive_dir.to_string_lossy().trim_end_matches('/'),
recipe.name.to_lowercase().replace('/', "_"),
))
.squash(self.squash)
.build()
} else {
BuildTagPushOpts::builder()
.image(&image_name)
.containerfile(&containerfile)
.tags(&tags)
.push(self.push)
.retry_push(self.retry_push)
.retry_count(self.retry_count)
.compression(self.compression_format)
.squash(self.squash)
.build()
};
Driver::build_tag_push(&opts)?;
if self.push && !self.no_sign {
let opts = SignVerifyOpts::builder()
.image(&image_name)
.retry_push(self.retry_push)
.retry_count(self.retry_count);
let opts = if let Some(tag) = tags.first() {
opts.tag(tag).build()
} else {
opts.build()
};
Driver::sign_and_verify(&opts)?;
}
Ok(())
self.build(recipe_path, &containerfile)
})?;
info!("Build complete!");
@ -261,18 +220,27 @@ impl BuildCommand {
#[cfg(not(feature = "multi-recipe"))]
fn start(&self, recipe_path: &Path) -> Result<()> {
use blue_build_process_management::drivers::opts::SignVerifyOpts;
trace!("BuildCommand::start()");
self.build(recipe_path, Path::new(CONTAINER_FILE))?;
info!("Build complete!");
Ok(())
}
fn build(&self, recipe_path: &Path, containerfile: &Path) -> Result<()> {
let recipe = Recipe::parse(recipe_path)?;
let containerfile = PathBuf::from(CONTAINER_FILE);
let tags = Driver::generate_tags(&recipe)?;
let image_name = self.generate_full_image_name(&recipe)?;
let tags = Driver::generate_tags(
&GenerateTagsOpts::builder()
.oci_ref(&recipe.base_image_ref()?)
.alt_tags(recipe.alt_tags())
.build(),
)?;
let image_name = self.image_name(&recipe)?;
let opts = if let Some(archive_dir) = self.archive.as_ref() {
BuildTagPushOpts::builder()
.containerfile(&containerfile)
.containerfile(containerfile)
.archive_path(format!(
"{}/{}.{ARCHIVE_SUFFIX}",
archive_dir.to_string_lossy().trim_end_matches('/'),
@ -283,7 +251,7 @@ impl BuildCommand {
} else {
BuildTagPushOpts::builder()
.image(&image_name)
.containerfile(&containerfile)
.containerfile(containerfile)
.tags(&tags)
.push(self.push)
.retry_push(self.retry_push)
@ -308,14 +276,29 @@ impl BuildCommand {
Driver::sign_and_verify(&opts)?;
}
info!("Build complete!");
Ok(())
}
fn image_name(&self, recipe: &Recipe) -> Result<String> {
let image_name = self.generate_full_image_name(recipe)?;
let image_name = if image_name.registry().is_empty() {
string!(image_name.repository())
} else {
format!(
"{}/{}",
image_name.resolve_registry(),
image_name.repository()
)
};
Ok(image_name)
}
/// # Errors
///
/// Will return `Err` if the image name cannot be generated.
fn generate_full_image_name(&self, recipe: &Recipe) -> Result<String> {
fn generate_full_image_name(&self, recipe: &Recipe) -> Result<Reference> {
trace!("BuildCommand::generate_full_image_name({recipe:#?})");
info!("Generating full image name");
@ -324,14 +307,18 @@ impl BuildCommand {
self.registry_namespace.as_ref().map(|s| s.to_lowercase()),
) {
trace!("registry={registry}, registry_path={registry_path}");
format!(
let image = format!(
"{}/{}/{}",
registry.trim().trim_matches('/'),
registry_path.trim().trim_matches('/'),
recipe.name.trim(),
)
);
image
.parse()
.into_diagnostic()
.with_context(|| format!("Unable to parse {image}"))?
} else {
Driver::generate_image_name(recipe)?
Driver::generate_image_name(&recipe.name)?
};
debug!("Using image name '{image_name}'");