feat: run clippy + BlueBuildTrait (#4)
* feat: run clippy + BlueBuildTrait * chore: add default run impl; more clippy * chore: remove vscode folder; not needed * cleanups * Move to commands.rs * Move functions; remove run function implementation from each command * Remove run impl from init commands * Use error log --------- Co-authored-by: Gerald Pinder <gmpinder@gmail.com>
This commit is contained in:
parent
9454baa750
commit
dbbd087b5b
11 changed files with 259 additions and 264 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
/target
|
||||
.sccache/
|
||||
.vscode/
|
||||
14
Cargo.toml
14
Cargo.toml
|
|
@ -8,23 +8,23 @@ license = "Apache-2.0"
|
|||
categories = ["command-line-utilities"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
anyhow = "1"
|
||||
askama = { version = "0.12.1", features = ["serde-json"] }
|
||||
cfg-if = "1.0.0"
|
||||
chrono = "0.4.31"
|
||||
clap = { version = "4.4.4", features = ["derive"] }
|
||||
chrono = "0.4"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
clap-verbosity-flag = "2.1.1"
|
||||
derive_builder = "0.12.0"
|
||||
env_logger = "0.10.1"
|
||||
futures-util = { version = "0.3.30", optional = true }
|
||||
indexmap = { version = "2.1.0", features = ["serde"] }
|
||||
log = "0.4.20"
|
||||
log = "0.4"
|
||||
podman-api = { version = "0.10.0", optional = true }
|
||||
serde = { version = "1.0.188", features = ["derive"] }
|
||||
serde_json = "1.0.107"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_yaml = "0.9.25"
|
||||
sigstore = { version = "0.8.0", optional = true }
|
||||
tokio = { version = "1.35.1", features = ["full"], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
typed-builder = "0.18.0"
|
||||
users = "0.11.0"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
use blue_build::{self, build, local, template};
|
||||
#![warn(clippy::pedantic, clippy::nursery)]
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use clap_verbosity_flag::{InfoLevel, Verbosity};
|
||||
use env_logger::WriteStyle;
|
||||
use log::trace;
|
||||
|
||||
use blue_build::{
|
||||
self,
|
||||
commands::{build, local, template, BlueBuildCommand},
|
||||
};
|
||||
|
||||
#[cfg(feature = "init")]
|
||||
use blue_build::init;
|
||||
use blue_build::commands::init;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "BlueBuild", author, version, about, long_about = None)]
|
||||
|
|
@ -68,15 +74,15 @@ fn main() {
|
|||
trace!("{args:#?}");
|
||||
|
||||
match args.command {
|
||||
#[cfg(feature = "init")]
|
||||
CommandArgs::Init(mut command) => command.run(),
|
||||
|
||||
#[cfg(feature = "init")]
|
||||
CommandArgs::New(mut command) => command.run(),
|
||||
|
||||
CommandArgs::Template(mut command) => command.run(),
|
||||
CommandArgs::Build(mut command) => command.run(),
|
||||
CommandArgs::Template(command) => command.run(),
|
||||
CommandArgs::Upgrade(command) => command.run(),
|
||||
CommandArgs::Rebase(command) => command.run(),
|
||||
|
||||
#[cfg(feature = "init")]
|
||||
CommandArgs::Init(command) => command.run(),
|
||||
|
||||
#[cfg(feature = "init")]
|
||||
CommandArgs::New(command) => command.run(),
|
||||
CommandArgs::Rebase(mut command) => command.run(),
|
||||
CommandArgs::Upgrade(mut command) => command.run(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
20
src/commands.rs
Normal file
20
src/commands.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#[cfg(feature = "init")]
|
||||
pub mod init;
|
||||
|
||||
pub mod build;
|
||||
pub mod local;
|
||||
pub mod template;
|
||||
|
||||
use log::error;
|
||||
|
||||
pub trait BlueBuildCommand {
|
||||
fn try_run(&mut self) -> anyhow::Result<()>;
|
||||
|
||||
/// Runs the command and exits if there is an error.
|
||||
fn run(&mut self) {
|
||||
if let Err(e) = self.try_run() {
|
||||
error!("{e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,10 +29,12 @@ use futures_util::StreamExt;
|
|||
use tokio::runtime::Runtime;
|
||||
|
||||
use crate::{
|
||||
commands::template::TemplateCommand,
|
||||
ops::{self, ARCHIVE_SUFFIX},
|
||||
template::{Recipe, TemplateCommand},
|
||||
};
|
||||
|
||||
use super::{template::Recipe, BlueBuildCommand};
|
||||
|
||||
#[derive(Debug, Clone, Args, TypedBuilder)]
|
||||
pub struct BuildCommand {
|
||||
/// The recipe file to build an image
|
||||
|
|
@ -127,9 +129,9 @@ pub struct BuildCommand {
|
|||
private_key: Option<String>,
|
||||
}
|
||||
|
||||
impl BuildCommand {
|
||||
impl BlueBuildCommand for BuildCommand {
|
||||
/// Runs the command and returns a result.
|
||||
pub fn try_run(&mut self) -> Result<()> {
|
||||
fn try_run(&mut self) -> Result<()> {
|
||||
trace!("BuildCommand::try_run()");
|
||||
|
||||
if self.push && self.archive.is_some() {
|
||||
|
|
@ -168,18 +170,9 @@ impl BuildCommand {
|
|||
#[cfg(not(feature = "podman-api"))]
|
||||
self.build_image()
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs the command and exits if there is an error.
|
||||
pub fn run(&mut self) {
|
||||
trace!("BuildCommand::run()");
|
||||
|
||||
if let Err(e) = self.try_run() {
|
||||
error!("Failed to build image: {e}");
|
||||
process::exit(1);
|
||||
}
|
||||
info!("Finished building!");
|
||||
}
|
||||
|
||||
impl BuildCommand {
|
||||
#[cfg(feature = "podman-api")]
|
||||
async fn build_image_podman_api(&self, client: Podman) -> Result<()> {
|
||||
use podman_api::opts::ImageTagOpts;
|
||||
|
|
@ -327,7 +320,7 @@ impl BuildCommand {
|
|||
}
|
||||
}
|
||||
|
||||
self.sign_images(&image_name, tags.first().map(|x| x.as_str()))?;
|
||||
sign_images(&image_name, tags.first().map(String::as_str))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -434,6 +427,9 @@ impl BuildCommand {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `Err` if the image name cannot be generated.
|
||||
pub fn generate_full_image_name(&self, recipe: &Recipe) -> Result<String> {
|
||||
trace!("BuildCommand::generate_full_image_name({recipe:#?})");
|
||||
info!("Generating full image name");
|
||||
|
|
@ -483,7 +479,7 @@ impl BuildCommand {
|
|||
if self.push {
|
||||
bail!("Need '--registry' and '--registry-path' in order to push image");
|
||||
}
|
||||
recipe.name.to_owned()
|
||||
recipe.name.clone()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -493,6 +489,9 @@ impl BuildCommand {
|
|||
Ok(image_name)
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `Err` if the build fails.
|
||||
fn run_build(&self, image_name: &str, tags: &[String]) -> Result<()> {
|
||||
trace!("BuildCommand::run_build({image_name}, {tags:#?})");
|
||||
|
||||
|
|
@ -579,7 +578,7 @@ impl BuildCommand {
|
|||
|
||||
if self.push {
|
||||
debug!("Pushing all images");
|
||||
for tag in tags.iter() {
|
||||
for tag in tags {
|
||||
debug!("Pushing image {image_name}:{tag}");
|
||||
|
||||
let tag_image = format!("{image_name}:{tag}");
|
||||
|
|
@ -605,138 +604,143 @@ impl BuildCommand {
|
|||
.status()?;
|
||||
|
||||
if status.success() {
|
||||
info!("Successfully pushed {image_name}:{tag}!")
|
||||
info!("Successfully pushed {image_name}:{tag}!");
|
||||
} else {
|
||||
bail!("Failed to push image {image_name}:{tag}");
|
||||
}
|
||||
}
|
||||
|
||||
self.sign_images(image_name, tags.first().map(|x| x.as_str()))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sign_images(&self, image_name: &str, tag: Option<&str>) -> Result<()> {
|
||||
trace!("BuildCommand::sign_images({image_name}, {tag:?})");
|
||||
|
||||
env::set_var("COSIGN_PASSWORD", "");
|
||||
env::set_var("COSIGN_YES", "true");
|
||||
|
||||
let image_digest = get_image_digest(image_name, tag)?;
|
||||
let image_name_tag = tag
|
||||
.map(|t| format!("{image_name}:{t}"))
|
||||
.unwrap_or(image_name.to_owned());
|
||||
|
||||
match (
|
||||
env::var("CI_DEFAULT_BRANCH"),
|
||||
env::var("CI_COMMIT_REF_NAME"),
|
||||
env::var("CI_PROJECT_URL"),
|
||||
env::var("CI_SERVER_PROTOCOL"),
|
||||
env::var("CI_SERVER_HOST"),
|
||||
env::var("SIGSTORE_ID_TOKEN"),
|
||||
env::var("GITHUB_EVENT_NAME"),
|
||||
env::var("GITHUB_REF_NAME"),
|
||||
env::var("COSIGN_PRIVATE_KEY"),
|
||||
) {
|
||||
(
|
||||
Ok(ci_default_branch),
|
||||
Ok(ci_commit_ref),
|
||||
Ok(ci_project_url),
|
||||
Ok(ci_server_protocol),
|
||||
Ok(ci_server_host),
|
||||
Ok(_),
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
) if ci_default_branch == ci_commit_ref => {
|
||||
trace!("CI_PROJECT_URL={ci_project_url}, CI_DEFAULT_BRANCH={ci_default_branch}, CI_COMMIT_REF_NAME={ci_commit_ref}, CI_SERVER_PROTOCOL={ci_server_protocol}, CI_SERVER_HOST={ci_server_host}");
|
||||
|
||||
debug!("On default branch");
|
||||
|
||||
info!("Signing image: {image_digest}");
|
||||
|
||||
trace!("cosign sign {image_digest}");
|
||||
|
||||
if Command::new("cosign")
|
||||
.arg("sign")
|
||||
.arg(&image_digest)
|
||||
.status()?
|
||||
.success()
|
||||
{
|
||||
info!("Successfully signed image!");
|
||||
} else {
|
||||
bail!("Failed to sign image: {image_digest}");
|
||||
}
|
||||
|
||||
let cert_ident =
|
||||
format!("{ci_project_url}//.gitlab-ci.yml@refs/heads/{ci_default_branch}");
|
||||
|
||||
let cert_oidc = format!("{ci_server_protocol}://{ci_server_host}");
|
||||
|
||||
trace!("cosign verify --certificate-identity {cert_ident} --certificate-oidc-issuer {cert_oidc} {image_name_tag}");
|
||||
|
||||
if !Command::new("cosign")
|
||||
.arg("verify")
|
||||
.arg("--certificate-identity")
|
||||
.arg(&cert_ident)
|
||||
.arg("--certificate-oidc-issuer")
|
||||
.arg(&cert_oidc)
|
||||
.arg(&image_name_tag)
|
||||
.status()?
|
||||
.success()
|
||||
{
|
||||
bail!("Failed to verify image!");
|
||||
}
|
||||
}
|
||||
(_, _, _, _, _, _, Ok(github_event_name), Ok(github_ref_name), Ok(_))
|
||||
if github_event_name != "pull_request" && github_ref_name == "live" =>
|
||||
{
|
||||
trace!("GITHUB_EVENT_NAME={github_event_name}, GITHUB_REF_NAME={github_ref_name}");
|
||||
|
||||
debug!("On live branch");
|
||||
|
||||
info!("Signing image: {image_digest}");
|
||||
|
||||
trace!("cosign sign --key=env://COSIGN_PRIVATE_KEY {image_digest}");
|
||||
|
||||
if Command::new("cosign")
|
||||
.arg("sign")
|
||||
.arg("--key=env://COSIGN_PRIVATE_KEY")
|
||||
.arg(&image_digest)
|
||||
.status()?
|
||||
.success()
|
||||
{
|
||||
info!("Successfully signed image!");
|
||||
} else {
|
||||
bail!("Failed to sign image: {image_digest}");
|
||||
}
|
||||
|
||||
trace!("cosign verify --key ./cosign.pub {image_name_tag}");
|
||||
|
||||
if !Command::new("cosign")
|
||||
.arg("verify")
|
||||
.arg("--key=./cosign.pub")
|
||||
.arg(&image_name_tag)
|
||||
.status()?
|
||||
.success()
|
||||
{
|
||||
bail!("Failed to verify image!");
|
||||
}
|
||||
}
|
||||
_ => debug!("Not running in CI with cosign variables, not signing"),
|
||||
sign_images(image_name, tags.first().map(String::as_str))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================== //
|
||||
// ========================= Helpers ====================== //
|
||||
// ======================================================== //
|
||||
|
||||
fn sign_images(image_name: &str, tag: Option<&str>) -> Result<()> {
|
||||
trace!("BuildCommand::sign_images({image_name}, {tag:?})");
|
||||
|
||||
env::set_var("COSIGN_PASSWORD", "");
|
||||
env::set_var("COSIGN_YES", "true");
|
||||
|
||||
let image_digest = get_image_digest(image_name, tag)?;
|
||||
let image_name_tag = tag
|
||||
.map(|t| format!("{image_name}:{t}"))
|
||||
.unwrap_or(image_name.to_owned());
|
||||
|
||||
match (
|
||||
env::var("CI_DEFAULT_BRANCH"),
|
||||
env::var("CI_COMMIT_REF_NAME"),
|
||||
env::var("CI_PROJECT_URL"),
|
||||
env::var("CI_SERVER_PROTOCOL"),
|
||||
env::var("CI_SERVER_HOST"),
|
||||
env::var("SIGSTORE_ID_TOKEN"),
|
||||
env::var("GITHUB_EVENT_NAME"),
|
||||
env::var("GITHUB_REF_NAME"),
|
||||
env::var("COSIGN_PRIVATE_KEY"),
|
||||
) {
|
||||
(
|
||||
Ok(ci_default_branch),
|
||||
Ok(ci_commit_ref),
|
||||
Ok(ci_project_url),
|
||||
Ok(ci_server_protocol),
|
||||
Ok(ci_server_host),
|
||||
Ok(_),
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
) if ci_default_branch == ci_commit_ref => {
|
||||
trace!("CI_PROJECT_URL={ci_project_url}, CI_DEFAULT_BRANCH={ci_default_branch}, CI_COMMIT_REF_NAME={ci_commit_ref}, CI_SERVER_PROTOCOL={ci_server_protocol}, CI_SERVER_HOST={ci_server_host}");
|
||||
|
||||
debug!("On default branch");
|
||||
|
||||
info!("Signing image: {image_digest}");
|
||||
|
||||
trace!("cosign sign {image_digest}");
|
||||
|
||||
if Command::new("cosign")
|
||||
.arg("sign")
|
||||
.arg(&image_digest)
|
||||
.status()?
|
||||
.success()
|
||||
{
|
||||
info!("Successfully signed image!");
|
||||
} else {
|
||||
bail!("Failed to sign image: {image_digest}");
|
||||
}
|
||||
|
||||
let cert_ident =
|
||||
format!("{ci_project_url}//.gitlab-ci.yml@refs/heads/{ci_default_branch}");
|
||||
|
||||
let cert_oidc = format!("{ci_server_protocol}://{ci_server_host}");
|
||||
|
||||
trace!("cosign verify --certificate-identity {cert_ident} --certificate-oidc-issuer {cert_oidc} {image_name_tag}");
|
||||
|
||||
if !Command::new("cosign")
|
||||
.arg("verify")
|
||||
.arg("--certificate-identity")
|
||||
.arg(&cert_ident)
|
||||
.arg("--certificate-oidc-issuer")
|
||||
.arg(&cert_oidc)
|
||||
.arg(&image_name_tag)
|
||||
.status()?
|
||||
.success()
|
||||
{
|
||||
bail!("Failed to verify image!");
|
||||
}
|
||||
}
|
||||
(_, _, _, _, _, _, Ok(github_event_name), Ok(github_ref_name), Ok(_))
|
||||
if github_event_name != "pull_request" && github_ref_name == "live" =>
|
||||
{
|
||||
trace!("GITHUB_EVENT_NAME={github_event_name}, GITHUB_REF_NAME={github_ref_name}");
|
||||
|
||||
debug!("On live branch");
|
||||
|
||||
info!("Signing image: {image_digest}");
|
||||
|
||||
trace!("cosign sign --key=env://COSIGN_PRIVATE_KEY {image_digest}");
|
||||
|
||||
if Command::new("cosign")
|
||||
.arg("sign")
|
||||
.arg("--key=env://COSIGN_PRIVATE_KEY")
|
||||
.arg(&image_digest)
|
||||
.status()?
|
||||
.success()
|
||||
{
|
||||
info!("Successfully signed image!");
|
||||
} else {
|
||||
bail!("Failed to sign image: {image_digest}");
|
||||
}
|
||||
|
||||
trace!("cosign verify --key ./cosign.pub {image_name_tag}");
|
||||
|
||||
if !Command::new("cosign")
|
||||
.arg("verify")
|
||||
.arg("--key=./cosign.pub")
|
||||
.arg(&image_name_tag)
|
||||
.status()?
|
||||
.success()
|
||||
{
|
||||
bail!("Failed to verify image!");
|
||||
}
|
||||
}
|
||||
_ => debug!("Not running in CI with cosign variables, not signing"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_image_digest(image_name: &str, tag: Option<&str>) -> Result<String> {
|
||||
trace!("get_image_digest({image_name}, {tag:?})");
|
||||
|
||||
let image_url = match tag {
|
||||
Some(tag) => format!("docker://{image_name}:{tag}"),
|
||||
None => format!("docker://{image_name}"),
|
||||
let image_url = if let Some(tag) = tag {
|
||||
format!("docker://{image_name}:{tag}")
|
||||
} else {
|
||||
format!("docker://{image_name}")
|
||||
};
|
||||
|
||||
trace!("skopeo inspect --format='{{.Digest}}' {image_url}");
|
||||
|
|
@ -8,9 +8,11 @@ use clap::Args;
|
|||
use log::error;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
const GITLAB_CI_FILE: &'static str = include_str!("../templates/init/gitlab-ci.yml.tera");
|
||||
const RECIPE_FILE: &'static str = include_str!("../templates/init/recipe.yml.tera");
|
||||
const LICENSE_FILE: &'static str = include_str!("../LICENSE");
|
||||
use super::BlueBuildCommand;
|
||||
|
||||
const GITLAB_CI_FILE: &'static str = include_str!("../../templates/init/gitlab-ci.yml.tera");
|
||||
const RECIPE_FILE: &'static str = include_str!("../../templates/init/recipe.yml.tera");
|
||||
const LICENSE_FILE: &'static str = include_str!("../../LICENSE");
|
||||
|
||||
#[derive(Debug, Clone, Default, Args, TypedBuilder)]
|
||||
pub struct NewInitCommon {
|
||||
|
|
@ -30,8 +32,8 @@ pub struct InitCommand {
|
|||
common: NewInitCommon,
|
||||
}
|
||||
|
||||
impl InitCommand {
|
||||
pub fn try_run(&self) -> Result<()> {
|
||||
impl BlueBuildCommand for InitCommand {
|
||||
fn try_run(&mut self) -> Result<()> {
|
||||
let base_dir = match self.dir.as_ref() {
|
||||
Some(dir) => dir,
|
||||
None => std::path::Path::new("./"),
|
||||
|
|
@ -40,14 +42,9 @@ impl InitCommand {
|
|||
self.initialize_directory(base_dir);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&self) {
|
||||
if let Err(e) = self.try_run() {
|
||||
error!("Failed to init ublue project: {e}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
impl InitCommand {
|
||||
fn initialize_directory(&self, base_dir: &Path) {
|
||||
let recipe_path = base_dir.join("recipe.yml");
|
||||
|
||||
|
|
@ -74,19 +71,12 @@ pub struct NewCommand {
|
|||
common: NewInitCommon,
|
||||
}
|
||||
|
||||
impl NewCommand {
|
||||
pub fn try_run(&self) -> Result<()> {
|
||||
impl BlueBuildCommand for NewCommand {
|
||||
fn try_run(&mut self) -> Result<()> {
|
||||
InitCommand::builder()
|
||||
.dir(self.dir.clone())
|
||||
.common(self.common.clone())
|
||||
.build()
|
||||
.try_run()
|
||||
}
|
||||
|
||||
pub fn run(&self) {
|
||||
if let Err(e) = self.try_run() {
|
||||
error!("Failed to create new project: {e}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,11 +11,12 @@ use typed_builder::TypedBuilder;
|
|||
use users::{Users, UsersCache};
|
||||
|
||||
use crate::{
|
||||
build::BuildCommand,
|
||||
commands::{build::BuildCommand, template::Recipe},
|
||||
ops::{self, ARCHIVE_SUFFIX, LOCAL_BUILD},
|
||||
template::Recipe,
|
||||
};
|
||||
|
||||
use super::BlueBuildCommand;
|
||||
|
||||
#[derive(Default, Clone, Debug, TypedBuilder, Args)]
|
||||
pub struct LocalCommonArgs {
|
||||
/// The recipe file to build an image.
|
||||
|
|
@ -35,8 +36,8 @@ pub struct UpgradeCommand {
|
|||
common: LocalCommonArgs,
|
||||
}
|
||||
|
||||
impl UpgradeCommand {
|
||||
pub fn try_run(&self) -> Result<()> {
|
||||
impl BlueBuildCommand for UpgradeCommand {
|
||||
fn try_run(&mut self) -> Result<()> {
|
||||
trace!("UpgradeCommand::try_run()");
|
||||
|
||||
check_can_run()?;
|
||||
|
|
@ -78,15 +79,6 @@ impl UpgradeCommand {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run(&self) {
|
||||
trace!("UpgradeCommand::run()");
|
||||
|
||||
if let Err(e) = self.try_run() {
|
||||
error!("Failed to upgrade image: {e}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug, TypedBuilder, Args)]
|
||||
|
|
@ -95,8 +87,8 @@ pub struct RebaseCommand {
|
|||
common: LocalCommonArgs,
|
||||
}
|
||||
|
||||
impl RebaseCommand {
|
||||
pub fn try_run(&self) -> Result<()> {
|
||||
impl BlueBuildCommand for RebaseCommand {
|
||||
fn try_run(&mut self) -> Result<()> {
|
||||
trace!("RebaseCommand::try_run()");
|
||||
|
||||
check_can_run()?;
|
||||
|
|
@ -142,24 +134,18 @@ impl RebaseCommand {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run(&self) {
|
||||
trace!("RebaseCommand::run()");
|
||||
|
||||
if let Err(e) = self.try_run() {
|
||||
error!("Failed to rebase onto new image: {e}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================== //
|
||||
// ========================= Helpers ====================== //
|
||||
// ======================================================== //
|
||||
|
||||
fn check_can_run() -> Result<()> {
|
||||
trace!("check_can_run()");
|
||||
|
||||
ops::check_command_exists("rpm-ostree")?;
|
||||
|
||||
let cache = UsersCache::new();
|
||||
|
||||
if cache.get_current_uid() != 0 {
|
||||
bail!("You need to be root to rebase a local image! Try using 'sudo'.");
|
||||
}
|
||||
|
|
@ -180,13 +166,7 @@ fn clean_local_build_dir(image_name: &str, rebase: bool) -> Result<()> {
|
|||
);
|
||||
}
|
||||
|
||||
if !local_build_path.exists() {
|
||||
debug!(
|
||||
"Creating build output dir at {}",
|
||||
local_build_path.display()
|
||||
);
|
||||
fs::create_dir_all(local_build_path)?;
|
||||
} else {
|
||||
if local_build_path.exists() {
|
||||
debug!("Cleaning out build dir {LOCAL_BUILD}");
|
||||
|
||||
let entries = fs::read_dir(LOCAL_BUILD)?;
|
||||
|
|
@ -205,6 +185,12 @@ fn clean_local_build_dir(image_name: &str, rebase: bool) -> Result<()> {
|
|||
fs::remove_file(path)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug!(
|
||||
"Creating build output dir at {}",
|
||||
local_build_path.display()
|
||||
);
|
||||
fs::create_dir_all(local_build_path)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -15,6 +15,8 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_yaml::Value;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
use super::BlueBuildCommand;
|
||||
|
||||
#[derive(Debug, Clone, Template, TypedBuilder)]
|
||||
#[template(path = "Containerfile")]
|
||||
pub struct ContainerFileTemplate<'a> {
|
||||
|
|
@ -58,6 +60,7 @@ pub struct Recipe {
|
|||
}
|
||||
|
||||
impl Recipe {
|
||||
#[must_use]
|
||||
pub fn generate_tags(&self) -> Vec<String> {
|
||||
trace!("Recipe::generate_tags()");
|
||||
debug!("Generating image tags for {}", &self.name);
|
||||
|
|
@ -83,14 +86,14 @@ impl Recipe {
|
|||
}
|
||||
}
|
||||
|
||||
if default_branch != commit_branch {
|
||||
debug!("Running on branch {commit_branch}");
|
||||
tags.push(format!("{commit_branch}-{image_version}"));
|
||||
} else {
|
||||
if default_branch == commit_branch {
|
||||
debug!("Running on the default branch");
|
||||
tags.push(image_version.to_string());
|
||||
tags.push(format!("{image_version}-{timestamp}"));
|
||||
tags.push(timestamp.to_string());
|
||||
tags.push(timestamp);
|
||||
} else {
|
||||
debug!("Running on branch {commit_branch}");
|
||||
tags.push(format!("{commit_branch}-{image_version}"));
|
||||
}
|
||||
|
||||
tags.push(format!("{commit_sha}-{image_version}"));
|
||||
|
|
@ -108,7 +111,7 @@ impl Recipe {
|
|||
trace!("GITHUB_EVENT_NAME={github_event_name},PR_EVENT_NUMBER={github_event_number},GITHUB_SHA={github_sha},GITHUB_REF_NAME={github_ref_name}");
|
||||
warn!("Detected running in Github, pulling information from GITHUB variables");
|
||||
|
||||
let mut short_sha = github_sha.clone();
|
||||
let mut short_sha = github_sha;
|
||||
short_sha.truncate(7);
|
||||
|
||||
if github_event_name == "pull_request" {
|
||||
|
|
@ -167,20 +170,15 @@ pub struct TemplateCommand {
|
|||
output: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl TemplateCommand {
|
||||
pub fn try_run(&self) -> Result<()> {
|
||||
impl BlueBuildCommand for TemplateCommand {
|
||||
fn try_run(&mut self) -> Result<()> {
|
||||
info!("Templating for recipe at {}", self.recipe.display());
|
||||
|
||||
self.template_file()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&self) {
|
||||
if let Err(e) = self.try_run() {
|
||||
error!("Failed to template file: {e}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
impl TemplateCommand {
|
||||
fn template_file(&self) -> Result<()> {
|
||||
trace!("TemplateCommand::template_file()");
|
||||
|
||||
|
|
@ -194,18 +192,14 @@ impl TemplateCommand {
|
|||
.build();
|
||||
|
||||
let output_str = template.render()?;
|
||||
if let Some(output) = self.output.as_ref() {
|
||||
debug!("Templating to file {}", output.display());
|
||||
trace!("Containerfile:\n{output_str}");
|
||||
|
||||
match self.output.as_ref() {
|
||||
Some(output) => {
|
||||
debug!("Templating to file {}", output.display());
|
||||
trace!("Containerfile:\n{output_str}");
|
||||
|
||||
std::fs::write(output, output_str)?;
|
||||
}
|
||||
None => {
|
||||
debug!("Templating to stdout");
|
||||
println!("{output_str}");
|
||||
}
|
||||
std::fs::write(output, output_str)?;
|
||||
} else {
|
||||
debug!("Templating to stdout");
|
||||
println!("{output_str}");
|
||||
}
|
||||
|
||||
info!("Finished templating Containerfile");
|
||||
|
|
@ -213,6 +207,10 @@ impl TemplateCommand {
|
|||
}
|
||||
}
|
||||
|
||||
// ======================================================== //
|
||||
// ========================= Helpers ====================== //
|
||||
// ======================================================== //
|
||||
|
||||
fn print_script(script_contents: &ExportsTemplate) -> String {
|
||||
trace!("print_script({script_contents})");
|
||||
|
||||
|
|
@ -284,17 +282,18 @@ fn get_module_from_file(file_name: &str) -> String {
|
|||
process::exit(1);
|
||||
};
|
||||
|
||||
if let Ok(module_ext) = serde_yaml::from_str::<ModuleExt>(file.as_str()) {
|
||||
module_ext.render().unwrap_or_else(template_err_fn)
|
||||
} else {
|
||||
let module = serde_yaml::from_str::<Module>(file.as_str()).unwrap_or_else(serde_err_fn);
|
||||
serde_yaml::from_str::<ModuleExt>(file.as_str()).map_or_else(
|
||||
|_| {
|
||||
let module = serde_yaml::from_str::<Module>(file.as_str()).unwrap_or_else(serde_err_fn);
|
||||
|
||||
ModuleExt::builder()
|
||||
.modules(vec![module])
|
||||
.build()
|
||||
.render()
|
||||
.unwrap_or_else(template_err_fn)
|
||||
}
|
||||
ModuleExt::builder()
|
||||
.modules(vec![module])
|
||||
.build()
|
||||
.render()
|
||||
.unwrap_or_else(template_err_fn)
|
||||
},
|
||||
|module_ext| module_ext.render().unwrap_or_else(template_err_fn),
|
||||
)
|
||||
}
|
||||
|
||||
fn print_module_context(module: &Module) -> String {
|
||||
10
src/lib.rs
10
src/lib.rs
|
|
@ -1,16 +1,12 @@
|
|||
//! The root library for blue-build.
|
||||
|
||||
#![warn(clippy::correctness, clippy::suspicious, clippy::perf, clippy::style)]
|
||||
#![doc(
|
||||
html_logo_url = "https://gitlab.com/wunker-bunker/blue-build/-/raw/main/logos/BlueBuild-logo.png"
|
||||
)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![forbid(unsafe_code)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
#[cfg(feature = "init")]
|
||||
pub mod init;
|
||||
|
||||
pub mod build;
|
||||
pub mod local;
|
||||
pub mod commands;
|
||||
mod ops;
|
||||
pub mod template;
|
||||
|
|
|
|||
23
src/ops.rs
23
src/ops.rs
|
|
@ -1,12 +1,6 @@
|
|||
use std::{
|
||||
env,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use clap::ValueEnum;
|
||||
use anyhow::{anyhow, Result};
|
||||
use log::{debug, trace};
|
||||
use std::process::Command;
|
||||
|
||||
pub const LOCAL_BUILD: &str = "/etc/blue-build";
|
||||
pub const ARCHIVE_SUFFIX: &str = ".tar.gz";
|
||||
|
|
@ -16,18 +10,17 @@ pub fn check_command_exists(command: &str) -> Result<()> {
|
|||
debug!("Checking if {command} exists");
|
||||
|
||||
trace!("which {command}");
|
||||
match Command::new("which")
|
||||
if Command::new("which")
|
||||
.arg(command)
|
||||
.output()?
|
||||
.status
|
||||
.success()
|
||||
{
|
||||
true => {
|
||||
debug!("Command {command} does exist");
|
||||
Ok(())
|
||||
}
|
||||
false => Err(anyhow!(
|
||||
debug!("Command {command} does exist");
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"Command {command} doesn't exist and is required to build the image"
|
||||
)),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue