diff --git a/Cargo.lock b/Cargo.lock index 45a6fc7..9d0ea0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,7 +61,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -71,7 +71,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -92,6 +92,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "block-buffer" version = "0.10.4" @@ -143,7 +149,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -187,6 +193,16 @@ dependencies = [ "clap_derive", ] +[[package]] +name = "clap-verbosity-flag" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c90e95e5bd4e8ac34fa6f37c774b0c6f8ed06ea90c79931fd448fcf941a9767" +dependencies = [ + "clap", + "log", +] + [[package]] name = "clap_builder" version = "4.4.4" @@ -264,6 +280,19 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "env_logger" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + [[package]] name = "envmnt" version = "0.8.4" @@ -280,6 +309,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "fnv" version = "1.0.7" @@ -341,7 +380,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "ignore", "walkdir", ] @@ -364,6 +403,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + [[package]] name = "humansize" version = "2.1.3" @@ -373,6 +418,12 @@ dependencies = [ "libm", ] +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "iana-time-zone" version = "0.1.57" @@ -433,6 +484,17 @@ dependencies = [ "hashbrown 0.14.0", ] +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "itoa" version = "1.0.9" @@ -466,6 +528,12 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + [[package]] name = "log" version = "0.4.20" @@ -680,6 +748,19 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "rustix" +version = "0.38.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "rusty-hook" version = "0.11.2" @@ -816,6 +897,15 @@ dependencies = [ "unic-segment", ] +[[package]] +name = "termcolor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.48" @@ -869,6 +959,9 @@ dependencies = [ "cfg-if", "chrono", "clap", + "clap-verbosity-flag", + "env_logger", + "log", "rusty-hook", "serde", "serde_json", @@ -1069,7 +1162,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1078,7 +1171,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -1087,13 +1189,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -1102,38 +1219,80 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/Cargo.toml b/Cargo.toml index a3d126d..2ee2409 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,9 @@ anyhow = "1.0.75" cfg-if = "1.0.0" chrono = "0.4.31" clap = { version = "4.4.4", features = ["derive"] } +clap-verbosity-flag = "2.1.1" +env_logger = "0.10.1" +log = "0.4.20" serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.107" serde_yaml = "0.9.25" diff --git a/src/bin/ublue.rs b/src/bin/ublue.rs index 48bebb7..9997027 100644 --- a/src/bin/ublue.rs +++ b/src/bin/ublue.rs @@ -1,7 +1,10 @@ use std::path::PathBuf; +use std::process; use anyhow::Result; use clap::{Parser, Subcommand}; +use clap_verbosity_flag::{InfoLevel, Verbosity}; +use log::{error, info, trace}; use ublue_rs::{self}; #[derive(Parser, Debug)] @@ -9,6 +12,9 @@ use ublue_rs::{self}; struct UblueArgs { #[command(subcommand)] command: CommandArgs, + + #[clap(flatten)] + verbosity: Verbosity, } #[derive(Debug, Subcommand)] @@ -79,13 +85,26 @@ enum CommandArgs { fn main() -> Result<()> { let args = UblueArgs::parse(); + env_logger::builder() + .filter_level(args.verbosity.log_level_filter()) + .init(); + + trace!("{args:#?}"); + match args.command { CommandArgs::Template { recipe, containerfile, output, } => { - ublue_rs::template_file(&recipe, containerfile.as_ref(), output.as_ref())?; + info!("Templating for recipe at {}", recipe.display()); + + if let Err(e) = + ublue_rs::template_file(&recipe, containerfile.as_ref(), output.as_ref()) + { + error!("Failed to template file: {e}"); + process::exit(1); + } } #[cfg(feature = "init")] CommandArgs::Init { dir } => { @@ -106,19 +125,29 @@ fn main() -> Result<()> { username, password, } => { - ublue_rs::template_file( + info!("Templating for recipe at {}", recipe.display()); + + if let Err(e) = ublue_rs::template_file( &recipe, containerfile.as_ref(), Some(&PathBuf::from("Containerfile")), - )?; - ublue_rs::build::build_image( + ) { + error!("Failed to template file: {e}"); + process::exit(1); + } + + info!("Building image for recipe at {}", recipe.display()); + if let Err(e) = ublue_rs::build::build_image( &recipe, registry.as_ref(), registry_path.as_ref(), username.as_ref(), password.as_ref(), push, - )?; + ) { + error!("Failed to build image: {e}"); + process::exit(1); + } } } Ok(()) diff --git a/src/build.rs b/src/build.rs index 4935d1f..de596a8 100644 --- a/src/build.rs +++ b/src/build.rs @@ -2,11 +2,15 @@ use std::{env, fs, path::Path, process::Command}; use anyhow::{anyhow, bail, Result}; use chrono::Local; +use log::{debug, info, trace, warn}; use crate::module_recipe::Recipe; fn check_command_exists(command: &str) -> Result<()> { - eprintln!("Checking if {command} exists..."); + debug!("Checking if {command} exists"); + trace!("ublue_rs::build::check_command_exists({command})"); + + trace!("command -v {command}"); match Command::new("command") .arg("-v") .arg(command) @@ -14,7 +18,7 @@ fn check_command_exists(command: &str) -> Result<()> { .success() { true => { - eprintln!("Command {command} does exist"); + debug!("Command {command} does exist"); Ok(()) } false => Err(anyhow!( @@ -24,48 +28,53 @@ fn check_command_exists(command: &str) -> Result<()> { } fn generate_tags(recipe: &Recipe) -> Vec { - eprintln!("Generating image tags for {}", &recipe.name); + debug!("Generating image tags for {}", &recipe.name); + trace!("ublue_rs::build::generate_tags({recipe:?})"); let mut tags: Vec = Vec::new(); let image_version = recipe.image_version; let timestamp = Local::now().format("%Y%m%d").to_string(); if env::var("CI").is_ok() { - eprintln!("Detected running in Gitlab, pulling information from CI variables..."); + warn!("Detected running in Gitlab, pulling information from CI variables"); if let (Ok(mr_iid), Ok(pipeline_source)) = ( env::var("CI_MERGE_REQUEST_IID"), env::var("CI_PIPELINE_SOURCE"), ) { + trace!("CI_MERGE_REQUEST_IID={mr_iid}, CI_PIPELINE_SOURCE={pipeline_source}"); if pipeline_source == "merge_request_event" { - eprintln!("Running in a MR..."); + debug!("Running in a MR"); tags.push(format!("{mr_iid}-{image_version}")); } } if let Ok(commit_sha) = env::var("CI_COMMIT_SHORT_SHA") { + trace!("CI_COMMIT_SHORT_SHA={commit_sha}"); tags.push(format!("{commit_sha}-{image_version}")); } - if let (Ok(commit_branch), Ok(default_branch)) = - (env::var("CI_COMMIT_BRANCH"), env::var("CI_DEFAULT_BRANCH")) - { + if let (Ok(commit_branch), Ok(default_branch)) = ( + env::var("CI_COMMIT_REF_NAME"), + env::var("CI_DEFAULT_BRANCH"), + ) { + trace!("CI_COMMIT_REF_NAME={commit_branch}, CI_DEFAULT_BRANCH={default_branch}"); if default_branch != commit_branch { - eprintln!("Running on branch {commit_branch}..."); + debug!("Running on branch {commit_branch}"); tags.push(format!("br-{commit_branch}-{image_version}")); } else { - eprintln!("Running on the default branch..."); + debug!("Running on the default branch"); tags.push(image_version.to_string()); tags.push(format!("{image_version}-{timestamp}")); tags.push(timestamp.to_string()); } } } else { - eprintln!("Running locally..."); + warn!("Running locally"); tags.push(format!("{image_version}-local")); } - eprintln!("Finished generating tags!"); - eprintln!("Tags: {tags:?}"); + info!("Finished generating tags!"); + trace!("Tags: {tags:#?}"); tags } @@ -74,7 +83,9 @@ fn login( username: Option<&String>, password: Option<&String>, ) -> Result<()> { - eprintln!("Attempting to login to the registry"); + info!("Attempting to login to the registry"); + trace!("ublue_rs::build::login({registry:?}, {username:?}, [MASKED])"); + let registry = match registry { Some(registry) => registry.to_owned(), None => env::var("CI_REGISTRY")?, @@ -90,6 +101,7 @@ fn login( None => env::var("CI_REGISTRY_PASSWORD")?, }; + trace!("buildah login -u {username} -p [MASKED] {registry}"); match Command::new("buildah") .arg("login") .arg("-u") @@ -100,10 +112,11 @@ fn login( .status()? .success() { - true => eprintln!("Buildah login success at {registry} for user {username}!"), + true => info!("Buildah login success at {registry} for user {username}!"), false => return Err(anyhow!("Failed to login for buildah!")), } + trace!("cosign login -u {username} -p [MASKED] {registry}"); match Command::new("cosign") .arg("login") .arg("-u") @@ -114,7 +127,7 @@ fn login( .status()? .success() { - true => eprintln!("Cosign login success at {registry} for user {username}!"), + true => info!("Cosign login success at {registry} for user {username}!"), false => return Err(anyhow!("Failed to login for cosign!")), } @@ -127,22 +140,27 @@ fn generate_full_image_name( registry_path: Option<&String>, push: bool, ) -> Result { - eprintln!("Generating full image name"); + info!("Generating full image name"); + trace!( + "ublue_rs::build::generate_full_image_name({recipe:#?}, {registry:?}, {registry_path:?})" + ); + let image_name = recipe.name.as_str(); let image_name = if env::var("CI").is_ok() { - eprintln!("Detected running in Gitlab CI..."); + warn!("Detected running in Gitlab CI"); if let (Ok(registry), Ok(project_namespace), Ok(project_name)) = ( env::var("CI_REGISTRY"), env::var("CI_PROJECT_NAMESPACE"), env::var("CI_PROJECT_NAME"), ) { + trace!("CI_REGISTRY={registry}, CI_PROJECT_NAMESPACE={project_namespace}, CI_PROJECT_NAME={project_name}"); format!("{registry}/{project_namespace}/{project_name}/{image_name}") } else { bail!("Unable to generate image name for Gitlab CI env!") } } else { - eprintln!("Detected running locally..."); + warn!("Detected running locally"); if let (Some(registry), Some(registry_path)) = (registry, registry_path) { format!( "{}/{}/{image_name}", @@ -157,44 +175,52 @@ fn generate_full_image_name( } }; - eprintln!("Using image name {image_name}"); + info!("Using image name {image_name}"); Ok(image_name) } fn build(image_name: &str, tags: &[String], push: bool) -> Result<()> { + trace!("ublue_rs::build::build({image_name}, {tags:#?}, {push})"); + let mut tags_iter = tags.iter(); let first_tag = tags_iter .next() .ok_or(anyhow!("We got here with no tags!?"))?; - let mut build = Command::new("buildah") + let full_image = format!("{image_name}:{first_tag}"); + + trace!("buildah build -t {full_image}"); + let status = Command::new("buildah") .arg("build") .arg("-t") - .arg(format!("{image_name}:{first_tag}")) - .spawn()?; - - let status = build.wait()?; + .arg(&full_image) + .status()?; if status.success() { - eprintln!("Successfully built {image_name}"); + info!("Successfully built {image_name}"); } else { bail!("Failed to build {image_name}"); } if tags.len() > 1 { - eprintln!("Tagging all images..."); - for tag in tags_iter { - eprintln!("Tagging {image_name} with {tag}"); - let mut child = Command::new("buildah") - .arg("tag") - .arg(format!("{image_name}:{first_tag}")) - .arg(format!("{image_name}:{tag}")) - .spawn()?; + debug!("Tagging all images"); - if child.wait()?.success() { - eprintln!("Successfully tagged {image_name}:{tag}!"); + for tag in tags_iter { + debug!("Tagging {image_name} with {tag}"); + + let tag_image = format!("{image_name}:{tag}"); + + trace!("buildah tag {full_image} {tag_image}"); + let status = Command::new("buildah") + .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}"); } @@ -202,16 +228,20 @@ fn build(image_name: &str, tags: &[String], push: bool) -> Result<()> { } if push { - eprintln!("Pushing all images..."); + debug!("Pushing all images"); for tag in tags.iter() { - eprintln!("Pushing image {image_name}:{tag}..."); - let mut child = Command::new("buildah") - .arg("push") - .arg(format!("{image_name}:{tag}")) - .spawn()?; + debug!("Pushing image {image_name}:{tag}"); - if child.wait()?.success() { - eprintln!("Successfully pushed {image_name}:{tag}!") + let tag_image = format!("{image_name}:{tag}"); + + trace!("buildah push {tag_image}"); + let status = Command::new("buildah") + .arg("push") + .arg(&tag_image) + .status()?; + + if status.success() { + info!("Successfully pushed {image_name}:{tag}!") } else { bail!("Failed to push image {image_name}:{tag}"); } @@ -224,7 +254,11 @@ fn build(image_name: &str, tags: &[String], push: bool) -> Result<()> { } fn sign_images(image_name: &str, tag: &str) -> Result<()> { + trace!("ublue_rs::build::sign_images({image_name}, {tag})"); + if env::var("SIGSTORE_ID_TOKEN").is_ok() && env::var("CI").is_ok() { + debug!("SIGSTORE_ID_TOKEN detected, signing image"); + if let ( Ok(project_url), Ok(default_branch), @@ -238,48 +272,62 @@ fn sign_images(image_name: &str, tag: &str) -> Result<()> { env::var("CI_SERVER_PROTOCOL"), env::var("CI_SERVER_HOST"), ) { + trace!("CI_PROJECT_URL={project_url}, CI_DEFAULT_BRANCH={default_branch}, CI_COMMIT_REF_NAME={commit_branch}, CI_SERVER_PROTOCOL={server_protocol}, CI_SERVER_HOST={server_host}"); + if default_branch == commit_branch { - eprintln!("Retrieving image digest..."); + debug!("On default branch, retrieving image digest"); + + let image_name_tag = format!("{image_name}:{tag}"); + let image_url = format!("docker://{image_name_tag}"); + + trace!("skopeo inspect --format='{{.Digest}}' {image_url}"); let image_digest = String::from_utf8( Command::new("skopeo") .arg("inspect") .arg("--format='{{.Digest}}'") - .arg(format!("docker://{image_name}:{tag}")) + .arg(&image_url) .output()? .stdout, )?; - eprintln!("Signing image: {image_name}@{image_digest}"); + let image_digest = format!("{image_name}@{image_digest}"); - let mut child = Command::new("cosign") + info!("Signing image: {image_digest}"); + + trace!("cosign sign {image_digest}"); + let status = Command::new("cosign") .arg("sign") - .arg(format!("{image_name}@{image_digest}")) - .spawn()?; + .arg(&image_digest) + .status()?; - if child.wait()?.success() { - eprintln!("Successfully signed image!"); + if status.success() { + info!("Successfully signed image!"); } else { - bail!("Failed to sign image: {image_name}@{image_digest}"); + bail!("Failed to sign image: {image_digest}"); } - let mut child = Command::new("cosign") + let cert_ident = + format!("{project_url}//.gitlab-ci.yml@refs/heads/{default_branch}"); + + let cert_oidc = format!("{server_protocol}://{server_host}"); + + trace!("cosign verify --certificate-identity {cert_ident}"); + let status = Command::new("cosign") .arg("verify") .arg("--certificate-identity") - .arg(format!( - "{project_url}//.gitlab-ci.yml@refs/heads/{default_branch}" - )) + .arg(&cert_ident) .arg("--certificate-oidc-issuer") - .arg(format!("{server_protocol}://{server_host}")) - .arg(format!("{image_name}:{tag}")) - .spawn()?; + .arg(&cert_oidc) + .arg(&image_name_tag) + .status()?; - if !child.wait()?.success() { - eprintln!("Failed to verify image!"); + if !status.success() { + bail!("Failed to verify image!"); } } } } else { - eprintln!("No SIGSTORE_ID_TOKEN detected, not signing image"); + debug!("No SIGSTORE_ID_TOKEN detected, not signing image"); } Ok(()) @@ -293,6 +341,7 @@ pub fn build_image( password: Option<&String>, push: bool, ) -> Result<()> { + trace!("ublue_rs::build_image({recipe:?}, {registry:?}, {registry_path:?}, {username:?}, [MASKED], {push})"); check_command_exists("buildah")?; if push { check_command_exists("cosign")?; @@ -310,7 +359,7 @@ pub fn build_image( } build(&image_name, &tags, push)?; - eprintln!("Build complete!"); + info!("Build complete!"); Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 7c3fdbe..47355c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,40 +18,58 @@ pub mod module_recipe; use std::{ collections::HashMap, - fs::{self, read_to_string}, + fs, path::{Path, PathBuf}, }; use anyhow::Result; +use log::{debug, info, trace}; use module_recipe::Recipe; use tera::{Context, Tera}; pub const DEFAULT_CONTAINERFILE: &str = include_str!("../templates/Containerfile.tera"); fn setup_tera(recipe: &Path, containerfile: Option<&PathBuf>) -> Result<(Tera, Context)> { - let recipe_de = serde_yaml::from_str::(fs::read_to_string(recipe)?.as_str())?; + trace!("ublue_rs::setup_tera({recipe:?}, {containerfile:?})"); + debug!("Deserializing recipe"); + let recipe_de = serde_yaml::from_str::(fs::read_to_string(recipe)?.as_str())?; + trace!("recipe_de: {recipe_de:#?}"); + + debug!("Building context"); let mut context = Context::from_serialize(recipe_de)?; + + trace!("add to context 'recipe': {recipe:?}"); context.insert("recipe", &recipe); let mut tera = Tera::default(); match containerfile { Some(containerfile) => { - tera.add_raw_template("Containerfile", &read_to_string(containerfile)?)? + debug!("Using {} as the template", containerfile.display()); + tera.add_raw_template("Containerfile", &fs::read_to_string(containerfile)?)? } None => tera.add_raw_template("Containerfile", DEFAULT_CONTAINERFILE)?, } + debug!("Registering function `print_containerfile`"); tera.register_function( "print_containerfile", |args: &HashMap| -> tera::Result { + trace!("tera fn print_containerfile({args:#?})"); match args.get("containerfile") { Some(v) => match v.as_str() { - Some(containerfile) => Ok(read_to_string(format!( - "config/containerfiles/{containerfile}/Containerfile" - ))? - .into()), + Some(containerfile) => { + debug!("Loading containerfile contents for {containerfile}"); + + let path = format!("config/containerfiles/{containerfile}/Containerfile"); + let path = Path::new(path.as_str()); + + let file = fs::read_to_string(path)?; + + trace!("Containerfile contents {}:\n{file}", path.display()); + Ok(file.into()) + } None => Err("Arg containerfile wasn't a string".into()), }, None => Err("Needs the argument 'containerfile'".into()), @@ -59,33 +77,41 @@ fn setup_tera(recipe: &Path, containerfile: Option<&PathBuf>) -> Result<(Tera, C }, ); + debug!("Registering function `print_module_context`"); tera.register_function( "print_module_context", |args: &HashMap| -> tera::Result { + trace!("tera fn print_module_context({args:#?})"); match args.get("module") { - Some(v) => Ok(match serde_json::to_string(v) { - Ok(s) => s, - Err(_) => "Unable to serialize".into(), - } - .into()), + Some(v) => match serde_json::to_string(v) { + Ok(s) => Ok(s.into()), + Err(e) => Err(format!("Unable to serialize: {e}").into()), + }, None => Err("Needs the argument 'module'".into()), } }, ); + debug!("Registering function `get_module_from_file`"); tera.register_function( "get_module_from_file", |args: &HashMap| -> tera::Result { + trace!("tera fn get_module_from_file({args:#?})"); match args.get("file") { Some(v) => { let file = match v.as_str() { Some(s) => s, None => return Err("Property 'from-file' must be a string".into()), }; + + trace!("from-file: {file}"); match serde_yaml::from_str::( - read_to_string(format!("config/{file}"))?.as_str(), + fs::read_to_string(format!("config/{file}"))?.as_str(), ) { - Ok(context) => Ok(context), + Ok(context) => { + trace!("context: {context}"); + Ok(context) + } Err(_) => Err(format!("Unable to deserialize file {file}").into()), } } @@ -102,13 +128,27 @@ pub fn template_file( containerfile: Option<&PathBuf>, output: Option<&PathBuf>, ) -> Result<()> { + trace!("ublue_rs::template_file({recipe:?}, {containerfile:?}, {output:?})"); + + debug!("Setting up tera"); let (tera, context) = setup_tera(recipe, containerfile)?; + + trace!("tera: {tera:#?}"); + trace!("context: {context:#?}"); + + debug!("Rendering Containerfile"); let output_str = tera.render("Containerfile", &context)?; if let Some(output) = output { + debug!("Templating to file {}", output.display()); + trace!("Containerfile:\n{output_str}"); + std::fs::write(output, output_str)?; } else { + debug!("Templating to stdout"); println!("{output_str}"); } + + info!("Finished templating Containerfile"); Ok(()) }