use std::process::Command;
use blue_build_utils::logging::CommandLogging;
use log::{error, info, trace};
use miette::{bail, IntoDiagnostic, Result};
use semver::Version;
use serde::Deserialize;
use crate::credentials::{self, Credentials};
use super::{
opts::{BuildOpts, PushOpts, TagOpts},
BuildDriver, DriverVersion,
};
#[derive(Debug, Deserialize)]
struct BuildahVersionJson {
pub version: Version,
}
#[derive(Debug)]
pub struct BuildahDriver;
impl DriverVersion for BuildahDriver {
const VERSION_REQ: &'static str = ">=1.24";
fn version() -> Result<Version> {
trace!("BuildahDriver::version()");
trace!("buildah version --json");
let output = Command::new("buildah")
.arg("version")
.arg("--json")
.output()
.into_diagnostic()?;
let version_json: BuildahVersionJson = serde_json::from_slice(&output.stdout)
.inspect_err(|e| error!("{e}: {}", String::from_utf8_lossy(&output.stdout)))
.into_diagnostic()?;
trace!("{version_json:#?}");
Ok(version_json.version)
}
}
impl BuildDriver for BuildahDriver {
fn build(&self, opts: &BuildOpts) -> Result<()> {
trace!("BuildahDriver::build({opts:#?})");
trace!(
"buildah build --pull=true --layers={} -f {} -t {}",
!opts.squash,
opts.containerfile.display(),
opts.image,
);
let mut command = Command::new("buildah");
command
.arg("build")
.arg("--pull=true")
.arg(format!("--layers={}", !opts.squash))
.arg("-f")
.arg(opts.containerfile.as_ref())
.arg("-t")
.arg(opts.image.as_ref());
let status = command
.status_image_ref_progress(&opts.image, "Building Image")
.into_diagnostic()?;
if status.success() {
info!("Successfully built {}", opts.image);
} else {
bail!("Failed to build {}", opts.image);
}
Ok(())
}
fn tag(&self, opts: &TagOpts) -> Result<()> {
trace!("BuildahDriver::tag({opts:#?})");
trace!("buildah tag {} {}", opts.src_image, opts.dest_image);
let status = Command::new("buildah")
.arg("tag")
.arg(opts.src_image.as_ref())
.arg(opts.dest_image.as_ref())
.status()
.into_diagnostic()?;
if status.success() {
info!("Successfully tagged {}!", opts.dest_image);
} else {
bail!("Failed to tag image {}", opts.dest_image);
}
Ok(())
}
fn push(&self, opts: &PushOpts) -> Result<()> {
trace!("BuildahDriver::push({opts:#?})");
trace!("buildah push {}", opts.image);
let mut command = Command::new("buildah");
command
.arg("push")
.arg(format!(
"--compression-format={}",
opts.compression_type.unwrap_or_default()
))
.arg(opts.image.as_ref());
let status = command
.status_image_ref_progress(&opts.image, "Pushing Image")
.into_diagnostic()?;
if status.success() {
info!("Successfully pushed {}!", opts.image);
} else {
bail!("Failed to push image {}", opts.image);
}
Ok(())
}
fn login(&self) -> Result<()> {
trace!("BuildahDriver::login()");
if let Some(Credentials {
registry,
username,
password,
}) = credentials::get()
{
trace!("buildah login -u {username} -p [MASKED] {registry}");
let output = Command::new("buildah")
.arg("login")
.arg("-u")
.arg(username)
.arg("-p")
.arg(password)
.arg(registry)
.output()
.into_diagnostic()?;
if !output.status.success() {
let err_out = String::from_utf8_lossy(&output.stderr);
bail!("Failed to login for buildah: {err_out}");
}
}
Ok(())
}
}