refactor: Make use of Reference to ensure typing
This commit is contained in:
parent
f85a761a29
commit
4a7293889d
15 changed files with 230 additions and 205 deletions
|
|
@ -200,35 +200,30 @@ impl Driver {
|
|||
let _ = oci_ref; // silence lint
|
||||
|
||||
if true {
|
||||
return Ok(40);
|
||||
return Ok(41);
|
||||
}
|
||||
}
|
||||
|
||||
info!("Retrieving OS version from {oci_ref}");
|
||||
|
||||
let inspect_opts = GetMetadataOpts::builder()
|
||||
.image(format!(
|
||||
"{}/{}",
|
||||
oci_ref.resolve_registry(),
|
||||
oci_ref.repository()
|
||||
))
|
||||
.tag(oci_ref.tag().unwrap_or("latest"))
|
||||
.platform(platform)
|
||||
.build();
|
||||
|
||||
let os_version = Self::get_metadata(&inspect_opts)
|
||||
.and_then(|inspection| {
|
||||
inspection.get_version().ok_or_else(|| {
|
||||
miette!(
|
||||
"Failed to parse version from metadata for {}",
|
||||
oci_ref.to_string().bold()
|
||||
)
|
||||
})
|
||||
let os_version = Self::get_metadata(
|
||||
&GetMetadataOpts::builder()
|
||||
.image(oci_ref)
|
||||
.platform(platform)
|
||||
.build(),
|
||||
)
|
||||
.and_then(|inspection| {
|
||||
inspection.get_version().ok_or_else(|| {
|
||||
miette!(
|
||||
"Failed to parse version from metadata for {}",
|
||||
oci_ref.to_string().bold()
|
||||
)
|
||||
})
|
||||
.or_else(|err| {
|
||||
warn!("Unable to get version via image inspection due to error:\n{err:?}");
|
||||
get_version_run_image(oci_ref)
|
||||
})?;
|
||||
})
|
||||
.or_else(|err| {
|
||||
warn!("Unable to get version via image inspection due to error:\n{err:?}");
|
||||
get_version_run_image(oci_ref)
|
||||
})?;
|
||||
trace!("os_version: {os_version}");
|
||||
Ok(os_version)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::{io::Write, process::Stdio};
|
||||
|
||||
use blue_build_utils::{cmd, credentials::Credentials};
|
||||
use colored::Colorize;
|
||||
use log::{debug, error, info, trace};
|
||||
use miette::{bail, miette, IntoDiagnostic, Result};
|
||||
use semver::Version;
|
||||
|
|
@ -83,13 +84,20 @@ impl BuildDriver for BuildahDriver {
|
|||
fn tag(opts: &TagOpts) -> Result<()> {
|
||||
trace!("BuildahDriver::tag({opts:#?})");
|
||||
|
||||
let mut command = cmd!("buildah", "tag", &*opts.src_image, &*opts.dest_image,);
|
||||
let dest_image_str = opts.dest_image.to_string();
|
||||
|
||||
let mut command = cmd!(
|
||||
"buildah",
|
||||
"tag",
|
||||
opts.src_image.to_string(),
|
||||
&dest_image_str,
|
||||
);
|
||||
|
||||
trace!("{command:?}");
|
||||
if command.status().into_diagnostic()?.success() {
|
||||
info!("Successfully tagged {}!", opts.dest_image);
|
||||
info!("Successfully tagged {}!", dest_image_str.bold().green());
|
||||
} else {
|
||||
bail!("Failed to tag image {}", opts.dest_image);
|
||||
bail!("Failed to tag image {}", dest_image_str.bold().red());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -97,6 +105,8 @@ impl BuildDriver for BuildahDriver {
|
|||
fn push(opts: &PushOpts) -> Result<()> {
|
||||
trace!("BuildahDriver::push({opts:#?})");
|
||||
|
||||
let image_str = opts.image.to_string();
|
||||
|
||||
let command = cmd!(
|
||||
"buildah",
|
||||
"push",
|
||||
|
|
@ -104,18 +114,18 @@ impl BuildDriver for BuildahDriver {
|
|||
"--compression-format={}",
|
||||
opts.compression_type.unwrap_or_default()
|
||||
),
|
||||
&*opts.image,
|
||||
&image_str,
|
||||
);
|
||||
|
||||
trace!("{command:?}");
|
||||
let status = command
|
||||
.build_status(&opts.image, "Pushing Image")
|
||||
.build_status(&image_str, "Pushing Image")
|
||||
.into_diagnostic()?;
|
||||
|
||||
if status.success() {
|
||||
info!("Successfully pushed {}!", opts.image);
|
||||
info!("Successfully pushed {}!", image_str.bold().green());
|
||||
} else {
|
||||
bail!("Failed to push image {}", opts.image);
|
||||
bail!("Failed to push image {}", image_str.bold().red());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use blue_build_utils::{
|
|||
constants::{COSIGN_PASSWORD, COSIGN_PUB_PATH, COSIGN_YES},
|
||||
credentials::Credentials,
|
||||
};
|
||||
use colored::Colorize;
|
||||
use log::{debug, trace};
|
||||
use miette::{bail, miette, Context, IntoDiagnostic, Result};
|
||||
|
||||
|
|
@ -121,27 +122,32 @@ impl SigningDriver for CosignDriver {
|
|||
}
|
||||
|
||||
fn sign(opts: &SignOpts) -> Result<()> {
|
||||
let image_digest: &str = opts.image.as_ref();
|
||||
if opts.image.digest().is_none() {
|
||||
bail!(
|
||||
"Image ref {} is not a digest ref",
|
||||
opts.image.to_string().bold().red(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut command = cmd!(
|
||||
"cosign",
|
||||
"sign",
|
||||
if let Some(ref key) = opts.key => format!("--key={key}"),
|
||||
"--recursive",
|
||||
image_digest,
|
||||
opts.image.to_string(),
|
||||
COSIGN_PASSWORD => "",
|
||||
COSIGN_YES => "true",
|
||||
);
|
||||
|
||||
trace!("{command:?}");
|
||||
if !command.status().into_diagnostic()?.success() {
|
||||
bail!("Failed to sign {image_digest}");
|
||||
bail!("Failed to sign {}", opts.image.to_string().bold().red());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify(opts: &VerifyOpts) -> Result<()> {
|
||||
let image_name_tag: &str = opts.image.as_ref();
|
||||
let mut command = cmd!(
|
||||
"cosign",
|
||||
"verify",
|
||||
|
|
@ -157,12 +163,12 @@ impl SigningDriver for CosignDriver {
|
|||
),
|
||||
};
|
||||
},
|
||||
image_name_tag
|
||||
opts.image.to_string(),
|
||||
);
|
||||
|
||||
trace!("{command:?}");
|
||||
if !command.status().into_diagnostic()?.success() {
|
||||
bail!("Failed to verify {image_name_tag}");
|
||||
bail!("Failed to verify {}", opts.image.to_string().bold().red());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use blue_build_utils::{
|
|||
string_vec,
|
||||
};
|
||||
use cached::proc_macro::cached;
|
||||
use colored::Colorize;
|
||||
use log::{debug, info, trace, warn};
|
||||
use miette::{bail, miette, IntoDiagnostic, Result};
|
||||
use once_cell::sync::Lazy;
|
||||
|
|
@ -154,15 +155,17 @@ impl BuildDriver for DockerDriver {
|
|||
fn tag(opts: &TagOpts) -> Result<()> {
|
||||
trace!("DockerDriver::tag({opts:#?})");
|
||||
|
||||
let dest_image_str = opts.dest_image.to_string();
|
||||
|
||||
trace!("docker tag {} {}", opts.src_image, opts.dest_image);
|
||||
let status = cmd!("docker", "tag", &*opts.src_image, &*opts.dest_image,)
|
||||
let status = cmd!("docker", "tag", opts.src_image.to_string(), &dest_image_str)
|
||||
.status()
|
||||
.into_diagnostic()?;
|
||||
|
||||
if status.success() {
|
||||
info!("Successfully tagged {}!", opts.dest_image);
|
||||
info!("Successfully tagged {}!", dest_image_str.bold().green());
|
||||
} else {
|
||||
bail!("Failed to tag image {}", opts.dest_image);
|
||||
bail!("Failed to tag image {}", dest_image_str.bold().red());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -170,15 +173,17 @@ impl BuildDriver for DockerDriver {
|
|||
fn push(opts: &PushOpts) -> Result<()> {
|
||||
trace!("DockerDriver::push({opts:#?})");
|
||||
|
||||
let image_str = opts.image.to_string();
|
||||
|
||||
trace!("docker push {}", opts.image);
|
||||
let status = cmd!("docker", "push", &*opts.image)
|
||||
let status = cmd!("docker", "push", &image_str)
|
||||
.status()
|
||||
.into_diagnostic()?;
|
||||
|
||||
if status.success() {
|
||||
info!("Successfully pushed {}!", opts.image);
|
||||
info!("Successfully pushed {}!", image_str.bold().green());
|
||||
} else {
|
||||
bail!("Failed to push image {}", opts.image);
|
||||
bail!("Failed to push image {}", image_str.bold().red());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -315,18 +320,25 @@ impl BuildDriver for DockerDriver {
|
|||
],
|
||||
);
|
||||
|
||||
let final_images = match (opts.image.as_deref(), opts.archive_path.as_deref()) {
|
||||
let final_images = match (opts.image, opts.archive_path.as_deref()) {
|
||||
(Some(image), None) => {
|
||||
let images = if opts.tags.is_empty() {
|
||||
cmd!(command, "-t", image);
|
||||
let image = image.to_string();
|
||||
cmd!(command, "-t", &image);
|
||||
string_vec![image]
|
||||
} else {
|
||||
opts.tags.iter().for_each(|tag| {
|
||||
cmd!(command, "-t", format!("{image}:{tag}"));
|
||||
cmd!(
|
||||
command,
|
||||
"-t",
|
||||
format!("{}/{}:{tag}", image.resolve_registry(), image.repository())
|
||||
);
|
||||
});
|
||||
opts.tags
|
||||
.iter()
|
||||
.map(|tag| format!("{image}:{tag}"))
|
||||
.map(|tag| {
|
||||
format!("{}/{}:{tag}", image.resolve_registry(), image.repository())
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
let first_image = images.first().unwrap();
|
||||
|
|
@ -348,8 +360,12 @@ impl BuildDriver for DockerDriver {
|
|||
images
|
||||
}
|
||||
(None, Some(archive_path)) => {
|
||||
cmd!(command, "--output", format!("type=oci,dest={archive_path}"));
|
||||
string_vec![archive_path]
|
||||
cmd!(
|
||||
command,
|
||||
"--output",
|
||||
format!("type=oci,dest={}", archive_path.display())
|
||||
);
|
||||
string_vec![archive_path.display().to_string()]
|
||||
}
|
||||
(Some(_), Some(_)) => bail!("Cannot use both image and archive path"),
|
||||
(None, None) => bail!("Need either the image or archive path set"),
|
||||
|
|
@ -385,16 +401,12 @@ impl InspectDriver for DockerDriver {
|
|||
#[cached(
|
||||
result = true,
|
||||
key = "String",
|
||||
convert = r#"{ format!("{}-{:?}-{}", &*opts.image, opts.tag.as_ref(), opts.platform)}"#,
|
||||
convert = r#"{ format!("{}-{}", opts.image, opts.platform)}"#,
|
||||
sync_writes = true
|
||||
)]
|
||||
fn get_metadata_cache(opts: &GetMetadataOpts) -> Result<ImageMetadata> {
|
||||
trace!("DockerDriver::get_metadata({opts:#?})");
|
||||
|
||||
let url = opts.tag.as_ref().map_or_else(
|
||||
|| format!("{}", opts.image),
|
||||
|tag| format!("{}:{tag}", opts.image),
|
||||
);
|
||||
let image_str = opts.image.to_string();
|
||||
|
||||
let mut command = cmd!(
|
||||
"docker",
|
||||
|
|
@ -409,16 +421,16 @@ fn get_metadata_cache(opts: &GetMetadataOpts) -> Result<ImageMetadata> {
|
|||
"inspect",
|
||||
"--format",
|
||||
"{{json .}}",
|
||||
&url
|
||||
&image_str,
|
||||
);
|
||||
trace!("{command:?}");
|
||||
|
||||
let output = command.output().into_diagnostic()?;
|
||||
|
||||
if output.status.success() {
|
||||
info!("Successfully inspected image {url}!");
|
||||
info!("Successfully inspected image {}!", image_str.bold().green());
|
||||
} else {
|
||||
bail!("Failed to inspect image {url}")
|
||||
bail!("Failed to inspect image {}", image_str.bold().red())
|
||||
}
|
||||
|
||||
serde_json::from_slice::<metadata::Metadata>(&output.stdout)
|
||||
|
|
|
|||
|
|
@ -230,11 +230,11 @@ mod test {
|
|||
setup_default_branch,
|
||||
None,
|
||||
string_vec![
|
||||
format!("{}-40", &*TIMESTAMP),
|
||||
format!("{}-41", &*TIMESTAMP),
|
||||
"latest",
|
||||
&*TIMESTAMP,
|
||||
format!("{COMMIT_SHA}-40"),
|
||||
"40",
|
||||
format!("{COMMIT_SHA}-41"),
|
||||
"41",
|
||||
],
|
||||
)]
|
||||
#[case::default_branch_alt_tags(
|
||||
|
|
@ -242,43 +242,43 @@ mod test {
|
|||
Some(bon::vec![TEST_TAG_1, TEST_TAG_2]),
|
||||
string_vec![
|
||||
TEST_TAG_1,
|
||||
format!("{TEST_TAG_1}-40"),
|
||||
format!("{}-{TEST_TAG_1}-40", &*TIMESTAMP),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_1}-40"),
|
||||
format!("{TEST_TAG_1}-41"),
|
||||
format!("{}-{TEST_TAG_1}-41", &*TIMESTAMP),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_1}-41"),
|
||||
TEST_TAG_2,
|
||||
format!("{TEST_TAG_2}-40"),
|
||||
format!("{}-{TEST_TAG_2}-40", &*TIMESTAMP),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_2}-40"),
|
||||
format!("{TEST_TAG_2}-41"),
|
||||
format!("{}-{TEST_TAG_2}-41", &*TIMESTAMP),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_2}-41"),
|
||||
],
|
||||
)]
|
||||
#[case::pr_branch(
|
||||
setup_pr_branch,
|
||||
None,
|
||||
string_vec!["pr-12-40", format!("{COMMIT_SHA}-40")],
|
||||
string_vec!["pr-12-41", format!("{COMMIT_SHA}-41")],
|
||||
)]
|
||||
#[case::pr_branch_alt_tags(
|
||||
setup_pr_branch,
|
||||
Some(bon::vec![TEST_TAG_1, TEST_TAG_2]),
|
||||
string_vec![
|
||||
format!("pr-12-{TEST_TAG_1}-40"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_1}-40"),
|
||||
format!("pr-12-{TEST_TAG_2}-40"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_2}-40"),
|
||||
format!("pr-12-{TEST_TAG_1}-41"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_1}-41"),
|
||||
format!("pr-12-{TEST_TAG_2}-41"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_2}-41"),
|
||||
],
|
||||
)]
|
||||
#[case::branch(
|
||||
setup_branch,
|
||||
None,
|
||||
string_vec![format!("{COMMIT_SHA}-40"), "br-test-40"],
|
||||
string_vec![format!("{COMMIT_SHA}-41"), "br-test-41"],
|
||||
)]
|
||||
#[case::branch_alt_tags(
|
||||
setup_branch,
|
||||
Some(bon::vec![TEST_TAG_1, TEST_TAG_2]),
|
||||
string_vec![
|
||||
format!("br-{BR_REF_NAME}-{TEST_TAG_1}-40"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_1}-40"),
|
||||
format!("br-{BR_REF_NAME}-{TEST_TAG_2}-40"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_2}-40"),
|
||||
format!("br-{BR_REF_NAME}-{TEST_TAG_1}-41"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_1}-41"),
|
||||
format!("br-{BR_REF_NAME}-{TEST_TAG_2}-41"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_2}-41"),
|
||||
],
|
||||
)]
|
||||
fn generate_tags(
|
||||
|
|
|
|||
|
|
@ -238,11 +238,11 @@ mod test {
|
|||
setup_default_branch,
|
||||
None,
|
||||
string_vec![
|
||||
format!("{}-40", &*TIMESTAMP),
|
||||
format!("{}-41", &*TIMESTAMP),
|
||||
"latest",
|
||||
&*TIMESTAMP,
|
||||
format!("{COMMIT_SHA}-40"),
|
||||
"40",
|
||||
format!("{COMMIT_SHA}-41"),
|
||||
"41",
|
||||
],
|
||||
)]
|
||||
#[case::default_branch_alt_tags(
|
||||
|
|
@ -250,43 +250,43 @@ mod test {
|
|||
Some(bon::vec![TEST_TAG_1, TEST_TAG_2]),
|
||||
string_vec![
|
||||
TEST_TAG_1,
|
||||
format!("{TEST_TAG_1}-40"),
|
||||
format!("{}-{TEST_TAG_1}-40", &*TIMESTAMP),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_1}-40"),
|
||||
format!("{TEST_TAG_1}-41"),
|
||||
format!("{}-{TEST_TAG_1}-41", &*TIMESTAMP),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_1}-41"),
|
||||
TEST_TAG_2,
|
||||
format!("{TEST_TAG_2}-40"),
|
||||
format!("{}-{TEST_TAG_2}-40", &*TIMESTAMP),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_2}-40"),
|
||||
format!("{TEST_TAG_2}-41"),
|
||||
format!("{}-{TEST_TAG_2}-41", &*TIMESTAMP),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_2}-41"),
|
||||
],
|
||||
)]
|
||||
#[case::pr_branch(
|
||||
setup_mr_branch,
|
||||
None,
|
||||
string_vec!["mr-12-40", format!("{COMMIT_SHA}-40")],
|
||||
string_vec!["mr-12-41", format!("{COMMIT_SHA}-41")],
|
||||
)]
|
||||
#[case::pr_branch_alt_tags(
|
||||
setup_mr_branch,
|
||||
Some(bon::vec![TEST_TAG_1, TEST_TAG_2]),
|
||||
string_vec![
|
||||
format!("mr-12-{TEST_TAG_1}-40"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_1}-40"),
|
||||
format!("mr-12-{TEST_TAG_2}-40"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_2}-40"),
|
||||
format!("mr-12-{TEST_TAG_1}-41"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_1}-41"),
|
||||
format!("mr-12-{TEST_TAG_2}-41"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_2}-41"),
|
||||
],
|
||||
)]
|
||||
#[case::branch(
|
||||
setup_branch,
|
||||
None,
|
||||
string_vec![format!("{COMMIT_SHA}-40"), "br-test-40"],
|
||||
string_vec![format!("{COMMIT_SHA}-41"), "br-test-41"],
|
||||
)]
|
||||
#[case::branch_alt_tags(
|
||||
setup_branch,
|
||||
Some(bon::vec![TEST_TAG_1, TEST_TAG_2]),
|
||||
string_vec![
|
||||
format!("br-{BR_REF_NAME}-{TEST_TAG_1}-40"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_1}-40"),
|
||||
format!("br-{BR_REF_NAME}-{TEST_TAG_2}-40"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_2}-40"),
|
||||
format!("br-{BR_REF_NAME}-{TEST_TAG_1}-41"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_1}-41"),
|
||||
format!("br-{BR_REF_NAME}-{TEST_TAG_2}-41"),
|
||||
format!("{COMMIT_SHA}-{TEST_TAG_2}-41"),
|
||||
],
|
||||
)]
|
||||
fn generate_tags(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::{borrow::Cow, path::Path};
|
||||
|
||||
use bon::Builder;
|
||||
use oci_distribution::Reference;
|
||||
|
||||
use crate::drivers::types::Platform;
|
||||
|
||||
|
|
@ -26,16 +27,14 @@ pub struct BuildOpts<'scope> {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Builder)]
|
||||
#[builder(on(Cow<'_, str>, into))]
|
||||
pub struct TagOpts<'scope> {
|
||||
pub src_image: Cow<'scope, str>,
|
||||
pub dest_image: Cow<'scope, str>,
|
||||
pub src_image: &'scope Reference,
|
||||
pub dest_image: &'scope Reference,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Builder)]
|
||||
pub struct PushOpts<'scope> {
|
||||
#[builder(into)]
|
||||
pub image: Cow<'scope, str>,
|
||||
pub image: &'scope Reference,
|
||||
pub compression_type: Option<CompressionType>,
|
||||
}
|
||||
|
||||
|
|
@ -55,14 +54,13 @@ pub struct BuildTagPushOpts<'scope> {
|
|||
/// NOTE: This SHOULD NOT contain the tag of the image.
|
||||
///
|
||||
/// NOTE: You cannot have this set with `archive_path` set.
|
||||
#[builder(into)]
|
||||
pub image: Option<Cow<'scope, str>>,
|
||||
pub image: Option<&'scope Reference>,
|
||||
|
||||
/// The path to the archive file.
|
||||
///
|
||||
/// NOTE: You cannot have this set with image set.
|
||||
#[builder(into)]
|
||||
pub archive_path: Option<Cow<'scope, str>>,
|
||||
pub archive_path: Option<Cow<'scope, Path>>,
|
||||
|
||||
/// The path to the Containerfile to build.
|
||||
#[builder(into)]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use bon::Builder;
|
||||
use oci_distribution::Reference;
|
||||
|
||||
use crate::drivers::types::Platform;
|
||||
|
||||
|
|
@ -8,10 +7,7 @@ use crate::drivers::types::Platform;
|
|||
#[builder(derive(Clone))]
|
||||
pub struct GetMetadataOpts<'scope> {
|
||||
#[builder(into)]
|
||||
pub image: Cow<'scope, str>,
|
||||
|
||||
#[builder(into)]
|
||||
pub tag: Option<Cow<'scope, str>>,
|
||||
pub image: &'scope Reference,
|
||||
|
||||
#[builder(default)]
|
||||
pub platform: Platform,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use std::{
|
|||
|
||||
use bon::Builder;
|
||||
use miette::{IntoDiagnostic, Result};
|
||||
use oci_distribution::Reference;
|
||||
use zeroize::{Zeroize, Zeroizing};
|
||||
|
||||
use crate::drivers::types::Platform;
|
||||
|
|
@ -69,7 +70,7 @@ pub struct CheckKeyPairOpts<'scope> {
|
|||
#[derive(Debug, Clone, Builder)]
|
||||
pub struct SignOpts<'scope> {
|
||||
#[builder(into)]
|
||||
pub image: Cow<'scope, str>,
|
||||
pub image: &'scope Reference,
|
||||
|
||||
#[builder(into)]
|
||||
pub key: Option<Cow<'scope, str>>,
|
||||
|
|
@ -90,17 +91,14 @@ pub enum VerifyType<'scope> {
|
|||
#[derive(Debug, Clone, Builder)]
|
||||
pub struct VerifyOpts<'scope> {
|
||||
#[builder(into)]
|
||||
pub image: Cow<'scope, str>,
|
||||
pub image: &'scope Reference,
|
||||
pub verify_type: VerifyType<'scope>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Builder)]
|
||||
pub struct SignVerifyOpts<'scope> {
|
||||
#[builder(into)]
|
||||
pub image: Cow<'scope, str>,
|
||||
|
||||
#[builder(into)]
|
||||
pub tag: Option<Cow<'scope, str>>,
|
||||
pub image: &'scope Reference,
|
||||
|
||||
#[builder(into)]
|
||||
pub dir: Option<Cow<'scope, Path>>,
|
||||
|
|
|
|||
|
|
@ -167,15 +167,17 @@ impl BuildDriver for PodmanDriver {
|
|||
fn tag(opts: &TagOpts) -> Result<()> {
|
||||
trace!("PodmanDriver::tag({opts:#?})");
|
||||
|
||||
let mut command = cmd!("podman", "tag", &*opts.src_image, &*opts.dest_image,);
|
||||
let dest_image_str = opts.dest_image.to_string();
|
||||
|
||||
let mut command = cmd!("podman", "tag", opts.src_image.to_string(), &dest_image_str);
|
||||
|
||||
trace!("{command:?}");
|
||||
let status = command.status().into_diagnostic()?;
|
||||
|
||||
if status.success() {
|
||||
info!("Successfully tagged {}!", opts.dest_image);
|
||||
info!("Successfully tagged {}!", dest_image_str.bold().green());
|
||||
} else {
|
||||
bail!("Failed to tag image {}", opts.dest_image);
|
||||
bail!("Failed to tag image {}", dest_image_str.bold().red());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -183,6 +185,8 @@ impl BuildDriver for PodmanDriver {
|
|||
fn push(opts: &PushOpts) -> Result<()> {
|
||||
trace!("PodmanDriver::push({opts:#?})");
|
||||
|
||||
let image_str = opts.image.to_string();
|
||||
|
||||
let command = cmd!(
|
||||
"podman",
|
||||
"push",
|
||||
|
|
@ -190,18 +194,18 @@ impl BuildDriver for PodmanDriver {
|
|||
"--compression-format={}",
|
||||
opts.compression_type.unwrap_or_default()
|
||||
),
|
||||
&*opts.image,
|
||||
&image_str,
|
||||
);
|
||||
|
||||
trace!("{command:?}");
|
||||
let status = command
|
||||
.build_status(&opts.image, "Pushing Image")
|
||||
.build_status(&image_str, "Pushing Image")
|
||||
.into_diagnostic()?;
|
||||
|
||||
if status.success() {
|
||||
info!("Successfully pushed {}!", opts.image);
|
||||
info!("Successfully pushed {}!", image_str.bold().green());
|
||||
} else {
|
||||
bail!("Failed to push image {}", opts.image)
|
||||
bail!("Failed to push image {}", image_str.bold().red());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -283,23 +287,20 @@ impl InspectDriver for PodmanDriver {
|
|||
#[cached(
|
||||
result = true,
|
||||
key = "String",
|
||||
convert = r#"{ format!("{}-{:?}-{}", &*opts.image, opts.tag.as_ref(), opts.platform)}"#,
|
||||
convert = r#"{ format!("{}-{}", opts.image, opts.platform)}"#,
|
||||
sync_writes = true
|
||||
)]
|
||||
fn get_metadata_cache(opts: &GetMetadataOpts) -> Result<ImageMetadata> {
|
||||
trace!("PodmanDriver::get_metadata({opts:#?})");
|
||||
|
||||
let url = opts.tag.as_deref().map_or_else(
|
||||
|| format!("{}", opts.image),
|
||||
|tag| format!("{}:{tag}", opts.image),
|
||||
);
|
||||
let image_str = opts.image.to_string();
|
||||
|
||||
let progress = Logger::multi_progress().add(
|
||||
ProgressBar::new_spinner()
|
||||
.with_style(ProgressStyle::default_spinner())
|
||||
.with_message(format!(
|
||||
"Inspecting metadata for {}, pulling image...",
|
||||
url.bold()
|
||||
image_str.bold()
|
||||
)),
|
||||
);
|
||||
progress.enable_steady_tick(Duration::from_millis(100));
|
||||
|
|
@ -311,17 +312,17 @@ fn get_metadata_cache(opts: &GetMetadataOpts) -> Result<ImageMetadata> {
|
|||
"--platform",
|
||||
opts.platform.to_string(),
|
||||
],
|
||||
&url,
|
||||
&image_str,
|
||||
);
|
||||
trace!("{command:?}");
|
||||
|
||||
let output = command.output().into_diagnostic()?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!("Failed to pull {} for inspection!", url.bold());
|
||||
bail!("Failed to pull {} for inspection!", image_str.bold().red());
|
||||
}
|
||||
|
||||
let mut command = cmd!("podman", "image", "inspect", "--format=json", &url);
|
||||
let mut command = cmd!("podman", "image", "inspect", "--format=json", &image_str);
|
||||
trace!("{command:?}");
|
||||
|
||||
let output = command.output().into_diagnostic()?;
|
||||
|
|
@ -330,9 +331,9 @@ fn get_metadata_cache(opts: &GetMetadataOpts) -> Result<ImageMetadata> {
|
|||
Logger::multi_progress().remove(&progress);
|
||||
|
||||
if output.status.success() {
|
||||
debug!("Successfully inspected image {url}!");
|
||||
debug!("Successfully inspected image {}!", image_str.bold().green());
|
||||
} else {
|
||||
bail!("Failed to inspect image {url}");
|
||||
bail!("Failed to inspect image {}", image_str.bold().red());
|
||||
}
|
||||
serde_json::from_slice::<Vec<PodmanImageMetadata>>(&output.stdout)
|
||||
.into_diagnostic()
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use blue_build_utils::{
|
|||
credentials::Credentials,
|
||||
retry,
|
||||
};
|
||||
use colored::Colorize;
|
||||
use log::{debug, trace};
|
||||
use miette::{bail, miette, Context, IntoDiagnostic};
|
||||
use sigstore::{
|
||||
|
|
@ -107,12 +108,16 @@ impl SigningDriver for SigstoreDriver {
|
|||
fn sign(opts: &SignOpts) -> miette::Result<()> {
|
||||
trace!("SigstoreDriver::sign({opts:?})");
|
||||
|
||||
if opts.image.digest().is_none() {
|
||||
bail!(
|
||||
"Image ref {} is not a digest ref",
|
||||
opts.image.to_string().bold().red(),
|
||||
);
|
||||
}
|
||||
|
||||
let path = opts.dir.as_ref().map_or_else(|| Path::new("."), |dir| dir);
|
||||
let mut client = ClientBuilder::default().build().into_diagnostic()?;
|
||||
|
||||
let image_digest: &str = opts.image.as_ref();
|
||||
let image_digest: OciReference = image_digest.parse().into_diagnostic()?;
|
||||
trace!("{image_digest:?}");
|
||||
let image_digest: OciReference = opts.image.to_string().parse().into_diagnostic()?;
|
||||
|
||||
let signing_scheme = SigningScheme::default();
|
||||
let key: Zeroizing<Vec<u8>> = get_private_key(path)?.contents()?;
|
||||
|
|
@ -174,8 +179,7 @@ impl SigningDriver for SigstoreDriver {
|
|||
fn verify(opts: &VerifyOpts) -> miette::Result<()> {
|
||||
let mut client = ClientBuilder::default().build().into_diagnostic()?;
|
||||
|
||||
let image_digest: &str = opts.image.as_ref();
|
||||
let image_digest: OciReference = image_digest.parse().into_diagnostic()?;
|
||||
let image_digest: OciReference = opts.image.to_string().parse().into_diagnostic()?;
|
||||
trace!("{image_digest:?}");
|
||||
|
||||
let signing_scheme = SigningScheme::default();
|
||||
|
|
|
|||
|
|
@ -23,21 +23,18 @@ impl InspectDriver for SkopeoDriver {
|
|||
#[cached(
|
||||
result = true,
|
||||
key = "String",
|
||||
convert = r#"{ format!("{}-{:?}-{}", &*opts.image, opts.tag.as_ref(), opts.platform)}"#,
|
||||
convert = r#"{ format!("{}-{}", opts.image, opts.platform)}"#,
|
||||
sync_writes = true
|
||||
)]
|
||||
fn get_metadata_cache(opts: &GetMetadataOpts) -> Result<ImageMetadata> {
|
||||
trace!("SkopeoDriver::get_metadata({opts:#?})");
|
||||
|
||||
let url = opts.tag.as_ref().map_or_else(
|
||||
|| format!("docker://{}", opts.image),
|
||||
|tag| format!("docker://{}:{tag}", opts.image),
|
||||
);
|
||||
let image_str = opts.image.to_string();
|
||||
|
||||
let progress = Logger::multi_progress().add(
|
||||
ProgressBar::new_spinner()
|
||||
.with_style(ProgressStyle::default_spinner())
|
||||
.with_message(format!("Inspecting metadata for {}", url.bold())),
|
||||
.with_message(format!("Inspecting metadata for {}", image_str.bold())),
|
||||
);
|
||||
progress.enable_steady_tick(Duration::from_millis(100));
|
||||
|
||||
|
|
@ -48,7 +45,7 @@ fn get_metadata_cache(opts: &GetMetadataOpts) -> Result<ImageMetadata> {
|
|||
opts.platform.arch(),
|
||||
],
|
||||
"inspect",
|
||||
&url,
|
||||
format!("docker://{image_str}"),
|
||||
stderr = Stdio::inherit(),
|
||||
);
|
||||
trace!("{command:?}");
|
||||
|
|
@ -59,9 +56,9 @@ fn get_metadata_cache(opts: &GetMetadataOpts) -> Result<ImageMetadata> {
|
|||
Logger::multi_progress().remove(&progress);
|
||||
|
||||
if output.status.success() {
|
||||
debug!("Successfully inspected image {url}!");
|
||||
debug!("Successfully inspected image {}!", image_str.bold().green());
|
||||
} else {
|
||||
bail!("Failed to inspect image {url}")
|
||||
bail!("Failed to inspect image {}", image_str.bold().red());
|
||||
}
|
||||
serde_json::from_slice(&output.stdout).into_diagnostic()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use std::{
|
|||
|
||||
use blue_build_utils::{constants::COSIGN_PUB_PATH, retry, string_vec};
|
||||
use log::{debug, info, trace};
|
||||
use miette::{bail, miette, Context, IntoDiagnostic, Result};
|
||||
use miette::{bail, Context, IntoDiagnostic, Result};
|
||||
use oci_distribution::Reference;
|
||||
use semver::{Version, VersionReq};
|
||||
|
||||
|
|
@ -127,12 +127,9 @@ pub trait BuildDriver: PrivateDriver {
|
|||
|
||||
let full_image = match (opts.archive_path.as_ref(), opts.image.as_ref()) {
|
||||
(Some(archive_path), None) => {
|
||||
format!("oci-archive:{archive_path}")
|
||||
format!("oci-archive:{}", archive_path.display())
|
||||
}
|
||||
(None, Some(image)) => opts
|
||||
.tags
|
||||
.first()
|
||||
.map_or_else(|| image.to_string(), |tag| format!("{image}:{tag}")),
|
||||
(None, Some(image)) => image.to_string(),
|
||||
(Some(_), Some(_)) => bail!("Cannot use both image and archive path"),
|
||||
(None, None) => bail!("Need either the image or archive path set"),
|
||||
};
|
||||
|
|
@ -148,25 +145,25 @@ pub trait BuildDriver: PrivateDriver {
|
|||
Self::build(&build_opts)?;
|
||||
|
||||
let image_list: Vec<String> = if !opts.tags.is_empty() && opts.archive_path.is_none() {
|
||||
let image = opts
|
||||
.image
|
||||
.as_ref()
|
||||
.ok_or_else(|| miette!("Image is required in order to tag"))?;
|
||||
let image = opts.image.unwrap();
|
||||
debug!("Tagging all images");
|
||||
|
||||
let mut image_list = Vec::with_capacity(opts.tags.len());
|
||||
|
||||
for tag in &opts.tags {
|
||||
debug!("Tagging {} with {tag}", &full_image);
|
||||
let tagged_image = format!("{image}:{tag}");
|
||||
let tagged_image: Reference =
|
||||
format!("{}/{}:{tag}", image.resolve_registry(), image.repository())
|
||||
.parse()
|
||||
.into_diagnostic()?;
|
||||
|
||||
let tag_opts = TagOpts::builder()
|
||||
.src_image(&full_image)
|
||||
.src_image(image)
|
||||
.dest_image(&tagged_image)
|
||||
.build();
|
||||
|
||||
Self::tag(&tag_opts)?;
|
||||
image_list.push(tagged_image);
|
||||
image_list.push(tagged_image.to_string());
|
||||
|
||||
if opts.push {
|
||||
let retry_count = if opts.retry_push { opts.retry_count } else { 0 };
|
||||
|
|
@ -174,12 +171,10 @@ pub trait BuildDriver: PrivateDriver {
|
|||
debug!("Pushing all images");
|
||||
// Push images with retries (1s delay between retries)
|
||||
blue_build_utils::retry(retry_count, 5, || {
|
||||
let tag_image = format!("{image}:{tag}");
|
||||
|
||||
debug!("Pushing image {tag_image}");
|
||||
debug!("Pushing image {tagged_image}");
|
||||
|
||||
let push_opts = PushOpts::builder()
|
||||
.image(&tag_image)
|
||||
.image(&tagged_image)
|
||||
.compression_type(opts.compression)
|
||||
.build();
|
||||
|
||||
|
|
@ -516,23 +511,20 @@ pub trait SigningDriver: PrivateDriver {
|
|||
.as_ref()
|
||||
.map_or_else(|| PathBuf::from("."), |d| d.to_path_buf());
|
||||
|
||||
let image_name: &str = opts.image.as_ref();
|
||||
let inspect_opts = GetMetadataOpts::builder()
|
||||
.image(image_name)
|
||||
.platform(opts.platform);
|
||||
|
||||
let inspect_opts = if let Some(ref tag) = opts.tag {
|
||||
inspect_opts.tag(&**tag).build()
|
||||
} else {
|
||||
inspect_opts.build()
|
||||
};
|
||||
|
||||
let image_digest = Driver::get_metadata(&inspect_opts)?.digest;
|
||||
let image_name_tag = opts
|
||||
.tag
|
||||
.as_ref()
|
||||
.map_or_else(|| image_name.to_owned(), |t| format!("{image_name}:{t}"));
|
||||
let image_digest = format!("{image_name}@{image_digest}");
|
||||
let image_digest = Driver::get_metadata(
|
||||
&GetMetadataOpts::builder()
|
||||
.image(opts.image)
|
||||
.platform(opts.platform)
|
||||
.build(),
|
||||
)?
|
||||
.digest;
|
||||
let image_digest: Reference = format!(
|
||||
"{}/{}@{image_digest}",
|
||||
opts.image.resolve_registry(),
|
||||
opts.image.repository(),
|
||||
)
|
||||
.parse()
|
||||
.into_diagnostic()?;
|
||||
|
||||
let (sign_opts, verify_opts) = match (Driver::get_ci_driver(), get_private_key(&path)) {
|
||||
// Cosign public/private key pair
|
||||
|
|
@ -543,7 +535,7 @@ pub trait SigningDriver: PrivateDriver {
|
|||
.key(priv_key.to_string())
|
||||
.build(),
|
||||
VerifyOpts::builder()
|
||||
.image(&image_name_tag)
|
||||
.image(opts.image)
|
||||
.verify_type(VerifyType::File(path.join(COSIGN_PUB_PATH).into()))
|
||||
.build(),
|
||||
),
|
||||
|
|
@ -551,7 +543,7 @@ pub trait SigningDriver: PrivateDriver {
|
|||
(CiDriverType::Github | CiDriverType::Gitlab, _) => (
|
||||
SignOpts::builder().dir(&path).image(&image_digest).build(),
|
||||
VerifyOpts::builder()
|
||||
.image(&image_name_tag)
|
||||
.image(opts.image)
|
||||
.verify_type(VerifyType::Keyless {
|
||||
issuer: Driver::oidc_provider()?.into(),
|
||||
identity: Driver::keyless_cert_identity()?.into(),
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ use bon::Builder;
|
|||
use clap::Args;
|
||||
use log::{info, trace, warn};
|
||||
use miette::{bail, IntoDiagnostic, Result};
|
||||
use oci_distribution::Reference;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use crate::commands::generate::GenerateCommand;
|
||||
|
|
@ -299,12 +300,15 @@ impl BuildCommand {
|
|||
.build(),
|
||||
)?;
|
||||
let image_name = self.image_name(&recipe)?;
|
||||
let image: Reference = format!("{image_name}:{}", tags.first().map_or("latest", |tag| tag))
|
||||
.parse()
|
||||
.into_diagnostic()?;
|
||||
|
||||
let build_fn = || -> Result<Vec<String>> {
|
||||
Driver::build_tag_push(&self.archive.as_ref().map_or_else(
|
||||
|| {
|
||||
BuildTagPushOpts::builder()
|
||||
.image(&image_name)
|
||||
.image(&image)
|
||||
.containerfile(containerfile)
|
||||
.platform(self.platform)
|
||||
.tags(tags.collect_cow_vec())
|
||||
|
|
@ -319,11 +323,11 @@ impl BuildCommand {
|
|||
BuildTagPushOpts::builder()
|
||||
.containerfile(containerfile)
|
||||
.platform(self.platform)
|
||||
.archive_path(format!(
|
||||
.archive_path(PathBuf::from(format!(
|
||||
"{}/{}.{ARCHIVE_SUFFIX}",
|
||||
archive_dir.to_string_lossy().trim_end_matches('/'),
|
||||
recipe.name.to_lowercase().replace('/', "_"),
|
||||
))
|
||||
)))
|
||||
.squash(self.squash)
|
||||
.build()
|
||||
},
|
||||
|
|
@ -337,6 +341,10 @@ impl BuildCommand {
|
|||
InspectDriver, RechunkDriver,
|
||||
};
|
||||
|
||||
let base_image: Reference = format!("{}:{}", &recipe.base_image, &recipe.image_version)
|
||||
.parse()
|
||||
.into_diagnostic()?;
|
||||
|
||||
Driver::rechunk(
|
||||
&RechunkOpts::builder()
|
||||
.image(&image_name)
|
||||
|
|
@ -357,8 +365,7 @@ impl BuildCommand {
|
|||
.base_digest(
|
||||
Driver::get_metadata(
|
||||
&GetMetadataOpts::builder()
|
||||
.image(&*recipe.base_image)
|
||||
.tag(&*recipe.image_version)
|
||||
.image(&base_image)
|
||||
.platform(self.platform)
|
||||
.build(),
|
||||
)?
|
||||
|
|
@ -382,10 +389,9 @@ impl BuildCommand {
|
|||
if self.push && !self.no_sign {
|
||||
Driver::sign_and_verify(
|
||||
&SignVerifyOpts::builder()
|
||||
.image(&image_name)
|
||||
.image(&image)
|
||||
.retry_push(self.retry_push)
|
||||
.retry_count(self.retry_count)
|
||||
.maybe_tag(tags.first())
|
||||
.platform(self.platform)
|
||||
.build(),
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use cached::proc_macro::cached;
|
|||
use clap::{crate_version, Args};
|
||||
use log::{debug, info, trace, warn};
|
||||
use miette::{IntoDiagnostic, Result};
|
||||
use oci_distribution::Reference;
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
use crate::commands::validate::ValidateCommand;
|
||||
|
|
@ -132,6 +133,10 @@ impl GenerateCommand {
|
|||
|
||||
info!("Templating for recipe at {}", recipe_path.display());
|
||||
|
||||
let base_image: Reference = format!("{}:{}", &recipe.base_image, &recipe.image_version)
|
||||
.parse()
|
||||
.into_diagnostic()?;
|
||||
|
||||
let template = ContainerFileTemplate::builder()
|
||||
.os_version(
|
||||
Driver::get_os_version()
|
||||
|
|
@ -144,12 +149,11 @@ impl GenerateCommand {
|
|||
.recipe_path(recipe_path.as_path())
|
||||
.registry(registry)
|
||||
.repo(Driver::get_repo_url()?)
|
||||
.build_scripts_image(determine_scripts_tag(self.platform)?)
|
||||
.build_scripts_image(determine_scripts_tag(self.platform)?.to_string())
|
||||
.base_digest(
|
||||
Driver::get_metadata(
|
||||
&GetMetadataOpts::builder()
|
||||
.image(&*recipe.base_image)
|
||||
.tag(&*recipe.image_version)
|
||||
.image(&base_image)
|
||||
.platform(self.platform)
|
||||
.build(),
|
||||
)?
|
||||
|
|
@ -178,24 +182,30 @@ impl GenerateCommand {
|
|||
convert = r#"{ platform }"#,
|
||||
sync_writes = true
|
||||
)]
|
||||
fn determine_scripts_tag(platform: Platform) -> Result<String> {
|
||||
let version = format!("v{}", crate_version!());
|
||||
let opts = GetMetadataOpts::builder()
|
||||
.image(BUILD_SCRIPTS_IMAGE_REF)
|
||||
.platform(platform);
|
||||
fn determine_scripts_tag(platform: Platform) -> Result<Reference> {
|
||||
let image: Reference = format!("{BUILD_SCRIPTS_IMAGE_REF}:{}", shadow::COMMIT_HASH)
|
||||
.parse()
|
||||
.into_diagnostic()?;
|
||||
let opts = GetMetadataOpts::builder().platform(platform);
|
||||
|
||||
Driver::get_metadata(&opts.clone().tag(shadow::COMMIT_HASH).build())
|
||||
Driver::get_metadata(&opts.clone().image(&image).build())
|
||||
.inspect_err(|e| trace!("{e:?}"))
|
||||
.map(|_| format!("{BUILD_SCRIPTS_IMAGE_REF}:{}", shadow::COMMIT_HASH))
|
||||
.map(|_| image)
|
||||
.or_else(|_| {
|
||||
Driver::get_metadata(&opts.clone().tag(shadow::BRANCH).build())
|
||||
let image: Reference = format!("{BUILD_SCRIPTS_IMAGE_REF}:{}", shadow::BRANCH)
|
||||
.parse()
|
||||
.into_diagnostic()?;
|
||||
Driver::get_metadata(&opts.clone().image(&image).build())
|
||||
.inspect_err(|e| trace!("{e:?}"))
|
||||
.map(|_| format!("{BUILD_SCRIPTS_IMAGE_REF}:{}", shadow::BRANCH))
|
||||
.map(|_| image)
|
||||
})
|
||||
.or_else(|_| {
|
||||
Driver::get_metadata(&opts.tag(&version).build())
|
||||
let image: Reference = format!("{BUILD_SCRIPTS_IMAGE_REF}:v{}", crate_version!())
|
||||
.parse()
|
||||
.into_diagnostic()?;
|
||||
Driver::get_metadata(&opts.image(&image).build())
|
||||
.inspect_err(|e| trace!("{e:?}"))
|
||||
.map(|_| format!("{BUILD_SCRIPTS_IMAGE_REF}:{version}"))
|
||||
.map(|_| image)
|
||||
})
|
||||
.inspect(|image| debug!("Using build scripts image: {image}"))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue