From 331b91df35365e6bee98f8165a97d4ce36afa998 Mon Sep 17 00:00:00 2001 From: Gerald Pinder Date: Sat, 27 Jan 2024 14:03:16 -0500 Subject: [PATCH] chore: Enable Clippy Pedantic lint (#19) --- src/commands.rs | 5 + src/commands/build.rs | 380 +++++++++++++++------------ src/commands/build/build_strategy.rs | 4 +- src/lib.rs | 8 +- 4 files changed, 221 insertions(+), 176 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index 43aaa49..19bcb82 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -8,6 +8,11 @@ pub mod template; use log::error; pub trait BlueBuildCommand { + /// Runs the command and returns a result + /// of the execution + /// + /// # Errors + /// Can return an `anyhow` Error fn try_run(&mut self) -> anyhow::Result<()>; /// Runs the command and exits if there is an error. diff --git a/src/commands/build.rs b/src/commands/build.rs index cda7cc5..7017d45 100644 --- a/src/commands/build.rs +++ b/src/commands/build.rs @@ -36,6 +36,13 @@ use crate::{ use super::BlueBuildCommand; +#[derive(Debug, Default, Clone, TypedBuilder)] +pub struct Credentials { + pub registry: String, + pub username: String, + pub password: String, +} + #[derive(Debug, Clone, Args, TypedBuilder)] pub struct BuildCommand { /// The recipe file to build an image @@ -180,44 +187,11 @@ impl BuildCommand { trace!("BuildCommand::build_image({client:#?})"); - let (registry, username, password) = if self.push { - let registry = match ( - self.registry.as_ref(), - env::var("CI_REGISTRY").ok(), - env::var("GITHUB_ACTIONS").ok(), - ) { - (Some(registry), _, _) => registry.to_owned(), - (None, Some(ci_registry), None) => ci_registry, - (None, None, Some(_)) => "ghcr.io".to_string(), - _ => bail!("Need '--registry' set in order to login"), - }; + let credentials = self.get_login_creds(); - let username = match ( - self.username.as_ref(), - env::var("CI_REGISTRY_USER").ok(), - env::var("GITHUB_ACTOR").ok(), - ) { - (Some(username), _, _) => username.to_owned(), - (None, Some(ci_registry_user), None) => ci_registry_user, - (None, None, Some(github_actor)) => github_actor, - _ => bail!("Need '--username' set in order to login"), - }; - - let password = match ( - self.password.as_ref(), - env::var("CI_REGISTRY_PASSWORD").ok(), - env::var("REGISTRY_TOKEN").ok(), - ) { - (Some(password), _, _) => password.to_owned(), - (None, Some(ci_registry_password), None) => ci_registry_password, - (None, None, Some(registry_token)) => registry_token, - _ => bail!("Need '--password' set in order to login"), - }; - - (registry, username, password) - } else { - Default::default() - }; + if self.push && credentials.is_none() { + bail!("Failed to get credentials"); + } let recipe: Recipe = serde_yaml::from_str(fs::read_to_string(&self.recipe)?.as_str())?; trace!("recipe: {recipe:#?}"); @@ -253,7 +227,7 @@ impl BuildCommand { .stream .trim() .lines() - .map(|line| line.trim()) + .map(str::trim) .filter(|line| !line.is_empty()) .for_each(|line| info!("{line}")), Err(e) => bail!("{e}"), @@ -266,6 +240,18 @@ impl BuildCommand { if self.push { debug!("Pushing is enabled"); + let credentials = + credentials.ok_or(anyhow!("Should have checked for creds earlier"))?; + + push_images_podman_api(&tags, &image_name, &first_image_name, &client, &credentials) + .await?; + + let (registry, username, password) = ( + credentials.registry, + credentials.username, + credentials.password, + ); + info!("Logging into registry using cosign"); trace!("cosign login -u {username} -p [MASKED] {registry}"); if !Command::new("cosign") @@ -283,39 +269,6 @@ impl BuildCommand { } info!("Cosign login success at {registry}"); - let first_image = client.images().get(&first_image_name); - - for tag in &tags { - let full_image_name = format!("{image_name}:{tag}"); - - first_image - .tag(&ImageTagOpts::builder().repo(&image_name).tag(tag).build()) - .await?; - debug!("Tagged image {full_image_name}"); - - let new_image = client.images().get(&full_image_name); - - info!("Pushing {full_image_name}"); - match new_image - .push( - &ImagePushOpts::builder() - .tls_verify(true) - .auth( - RegistryAuth::builder() - .username(&username) - .password(&password) - .server_address(®istry) - .build(), - ) - .build(), - ) - .await - { - Ok(_) => info!("Pushed {full_image_name} successfully!"), - Err(e) => bail!("Failed to push image: {e}"), - } - } - sign_images(&image_name, tags.first().map(String::as_str))?; } Ok(()) @@ -343,49 +296,26 @@ impl BuildCommand { trace!("BuildCommand::login()"); info!("Attempting to login to the registry"); - let registry = match ( - self.registry.as_ref(), - env::var("CI_REGISTRY").ok(), - env::var("GITHUB_ACTIONS").ok(), - ) { - (Some(registry), _, _) => registry.to_owned(), - (None, Some(ci_registry), None) => ci_registry, - (None, None, Some(_)) => "ghcr.io".to_string(), - _ => bail!("Need '--registry' set in order to login"), - }; + let credentials = self + .get_login_creds() + .ok_or(anyhow!("Unable to get credentials"))?; - let username = match ( - self.username.as_ref(), - env::var("CI_REGISTRY_USER").ok(), - env::var("GITHUB_ACTOR").ok(), - ) { - (Some(username), _, _) => username.to_owned(), - (None, Some(ci_registry_user), None) => ci_registry_user, - (None, None, Some(github_actor)) => github_actor, - _ => bail!("Need '--username' set in order to login"), - }; - - let password = match ( - self.password.as_ref(), - env::var("CI_REGISTRY_PASSWORD").ok(), - env::var("REGISTRY_TOKEN").ok(), - ) { - (Some(password), None, None) => password.to_owned(), - (None, Some(ci_registry_password), None) => ci_registry_password, - (None, None, Some(github_token)) => github_token, - _ => bail!("Need '--password' set in order to login"), - }; + let (registry, username, password) = ( + credentials.registry, + credentials.username, + credentials.password, + ); info!("Logging into the registry, {registry}"); if !match ( ops::check_command_exists("buildah"), ops::check_command_exists("podman"), ) { - (Ok(_), _) => { + (Ok(()), _) => { trace!("buildah login -u {username} -p [MASKED] {registry}"); Command::new("buildah") } - (Err(_), Ok(_)) => { + (Err(_), Ok(())) => { trace!("podman login -u {username} -p [MASKED] {registry}"); Command::new("podman") } @@ -508,7 +438,7 @@ impl BuildCommand { ops::check_command_exists("buildah"), ops::check_command_exists("podman"), ) { - (Ok(_), _) => { + (Ok(()), _) => { trace!("buildah build -t {full_image}"); Command::new("buildah") .arg("build") @@ -516,7 +446,7 @@ impl BuildCommand { .arg(&full_image) .status()? } - (Err(_), Ok(_)) => { + (Err(_), Ok(())) => { trace!("podman build . -t {full_image}"); Command::new("podman") .arg("build") @@ -535,81 +465,59 @@ impl BuildCommand { } if tags.len() > 1 && self.archive.is_none() { - debug!("Tagging all images"); - - for tag in tags { - debug!("Tagging {image_name} with {tag}"); - - let tag_image = format!("{image_name}:{tag}"); - - let status = match ( - ops::check_command_exists("buildah"), - ops::check_command_exists("podman"), - ) { - (Ok(_), _) => { - trace!("buildah tag {full_image} {tag_image}"); - Command::new("buildah") - } - (Err(_), Ok(_)) => { - trace!("podman tag {full_image} {tag_image}"); - Command::new("podman") - } - (Err(e1), Err(e2)) => { - bail!("Need either 'buildah' or 'podman' to build: {e1}, {e2}") - } - } - .arg("tag") - .arg(&full_image) - .arg(&tag_image) - .status()?; - - if status.success() { - info!("Successfully tagged {image_name}:{tag}!"); - } else { - bail!("Failed to tag image {image_name}:{tag}"); - } - } + tag_images(tags, image_name, &full_image)?; } if self.push { - debug!("Pushing all images"); - for tag in tags { - debug!("Pushing image {image_name}:{tag}"); - - let tag_image = format!("{image_name}:{tag}"); - - let status = match ( - ops::check_command_exists("buildah"), - ops::check_command_exists("podman"), - ) { - (Ok(_), _) => { - trace!("buildah push {tag_image}"); - Command::new("buildah") - } - (Err(_), Ok(_)) => { - trace!("podman push {tag_image}"); - Command::new("podman") - } - (Err(e1), Err(e2)) => { - bail!("Need either 'buildah' or 'podman' to build: {e1}, {e2}") - } - } - .arg("push") - .arg(&tag_image) - .status()?; - - if status.success() { - info!("Successfully pushed {image_name}:{tag}!"); - } else { - bail!("Failed to push image {image_name}:{tag}"); - } - } - + push_images(tags, image_name)?; sign_images(image_name, tags.first().map(String::as_str))?; } Ok(()) } + + fn get_login_creds(&self) -> Option { + let registry = match ( + self.registry.as_ref(), + env::var("CI_REGISTRY").ok(), + env::var("GITHUB_ACTIONS").ok(), + ) { + (Some(registry), _, _) => registry.to_owned(), + (None, Some(ci_registry), None) => ci_registry, + (None, None, Some(_)) => "ghcr.io".to_string(), + _ => return None, + }; + + let username = match ( + self.username.as_ref(), + env::var("CI_REGISTRY_USER").ok(), + env::var("GITHUB_ACTOR").ok(), + ) { + (Some(username), _, _) => username.to_owned(), + (None, Some(ci_registry_user), None) => ci_registry_user, + (None, None, Some(github_actor)) => github_actor, + _ => return None, + }; + + let password = match ( + self.password.as_ref(), + env::var("CI_REGISTRY_PASSWORD").ok(), + env::var("REGISTRY_TOKEN").ok(), + ) { + (Some(password), _, _) => password.to_owned(), + (None, Some(ci_registry_password), None) => ci_registry_password, + (None, None, Some(registry_token)) => registry_token, + _ => return None, + }; + + Some( + Credentials::builder() + .registry(registry) + .username(username) + .password(password) + .build(), + ) + } } // ======================================================== // @@ -799,3 +707,129 @@ fn check_cosign_files() -> Result<()> { } } } + +fn tag_images(tags: &[String], image_name: &str, full_image: &str) -> Result<()> { + debug!("Tagging all images"); + + for tag in tags { + debug!("Tagging {image_name} with {tag}"); + + let tag_image = format!("{image_name}:{tag}"); + + let status = match ( + ops::check_command_exists("buildah"), + ops::check_command_exists("podman"), + ) { + (Ok(()), _) => { + trace!("buildah tag {full_image} {tag_image}"); + Command::new("buildah") + } + (Err(_), Ok(())) => { + trace!("podman tag {full_image} {tag_image}"); + Command::new("podman") + } + (Err(e1), Err(e2)) => { + bail!("Need either 'buildah' or 'podman' to build: {e1}, {e2}") + } + } + .arg("tag") + .arg(full_image) + .arg(&tag_image) + .status()?; + + if status.success() { + info!("Successfully tagged {image_name}:{tag}!"); + } else { + bail!("Failed to tag image {image_name}:{tag}"); + } + } + + Ok(()) +} + +fn push_images(tags: &[String], image_name: &str) -> Result<()> { + debug!("Pushing all images"); + for tag in tags { + debug!("Pushing image {image_name}:{tag}"); + + let tag_image = format!("{image_name}:{tag}"); + + let status = match ( + ops::check_command_exists("buildah"), + ops::check_command_exists("podman"), + ) { + (Ok(()), _) => { + trace!("buildah push {tag_image}"); + Command::new("buildah") + } + (Err(_), Ok(())) => { + trace!("podman push {tag_image}"); + Command::new("podman") + } + (Err(e1), Err(e2)) => { + bail!("Need either 'buildah' or 'podman' to build: {e1}, {e2}") + } + } + .arg("push") + .arg(&tag_image) + .status()?; + + if status.success() { + info!("Successfully pushed {image_name}:{tag}!"); + } else { + bail!("Failed to push image {image_name}:{tag}"); + } + } + + Ok(()) +} + +#[cfg(feature = "podman-api")] +async fn push_images_podman_api( + tags: &[String], + image_name: &str, + first_image_name: &str, + client: &Podman, + credentials: &Credentials, +) -> Result<()> { + use podman_api::opts::ImageTagOpts; + + let first_image = client.images().get(first_image_name); + let (registry, username, password) = ( + &credentials.registry, + &credentials.username, + &credentials.password, + ); + + for tag in tags { + let full_image_name = format!("{image_name}:{tag}"); + + first_image + .tag(&ImageTagOpts::builder().repo(image_name).tag(tag).build()) + .await?; + debug!("Tagged image {full_image_name}"); + + let new_image = client.images().get(&full_image_name); + + info!("Pushing {full_image_name}"); + match new_image + .push( + &ImagePushOpts::builder() + .tls_verify(true) + .auth( + RegistryAuth::builder() + .username(username) + .password(password) + .server_address(registry) + .build(), + ) + .build(), + ) + .await + { + Ok(_) => info!("Pushed {full_image_name} successfully!"), + Err(e) => bail!("Failed to push image: {e}"), + } + } + Ok(()) +} diff --git a/src/commands/build/build_strategy.rs b/src/commands/build/build_strategy.rs index 3b69607..8769ff7 100644 --- a/src/commands/build/build_strategy.rs +++ b/src/commands/build/build_strategy.rs @@ -48,8 +48,8 @@ impl BuildStrategy { (_, _, _, var_run_podman_sock, _, _) if var_run_podman_sock.exists() => { Self::Socket(var_run_podman_sock) } - (_, _, _, _, Ok(_), _) => Self::Buildah, - (_, _, _, _, _, Ok(_)) => Self::Podman, + (_, _, _, _, Ok(()), _) => Self::Buildah, + (_, _, _, _, _, Ok(())) => Self::Podman, _ => bail!("Could not determine strategy"), }, ) diff --git a/src/lib.rs b/src/lib.rs index 82642f8..85292f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,11 @@ //! The root library for blue-build. -#![warn(clippy::correctness, clippy::suspicious, clippy::perf, clippy::style)] +#![warn( + clippy::correctness, + clippy::suspicious, + clippy::perf, + clippy::style, + clippy::pedantic +)] #![doc( html_logo_url = "https://gitlab.com/wunker-bunker/blue-build/-/raw/main/logos/BlueBuild-logo.png" )]