refactor: Create SigningDriver and CiDriver (#197)

This also includes a new `login` command. The signing and CI logic is now using the Driver trait system along with a new experimental sigstore signing driver. New static macros have also been created to make implementation management easier for `Command` usage and `Driver` trait implementation calls.

---------

Co-authored-by: xyny <60004820+xynydev@users.noreply.github.com>
This commit is contained in:
Gerald Pinder 2024-08-12 23:52:07 -04:00 committed by GitHub
parent 3ecb0d3d93
commit 8ce83ba7ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
63 changed files with 6468 additions and 2083 deletions

View file

@ -0,0 +1,83 @@
use std::{borrow::Cow, path::Path};
use typed_builder::TypedBuilder;
use super::CompressionType;
/// Options for building
#[derive(Debug, Clone, TypedBuilder)]
pub struct BuildOpts<'a> {
#[builder(setter(into))]
pub image: Cow<'a, str>,
#[builder(default)]
pub squash: bool,
#[builder(setter(into))]
pub containerfile: Cow<'a, Path>,
}
#[derive(Debug, Clone, TypedBuilder)]
pub struct TagOpts<'a> {
#[builder(setter(into))]
pub src_image: Cow<'a, str>,
#[builder(setter(into))]
pub dest_image: Cow<'a, str>,
}
#[derive(Debug, Clone, TypedBuilder)]
pub struct PushOpts<'a> {
#[builder(setter(into))]
pub image: Cow<'a, str>,
#[builder(default, setter(strip_option))]
pub compression_type: Option<CompressionType>,
}
/// Options for building, tagging, and pusing images.
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone, TypedBuilder)]
pub struct BuildTagPushOpts<'a> {
/// The base image name.
///
/// NOTE: You cannot have this set with `archive_path` set.
#[builder(default, setter(into, strip_option))]
pub image: Option<Cow<'a, str>>,
/// The path to the archive file.
///
/// NOTE: You cannot have this set with image set.
#[builder(default, setter(into, strip_option))]
pub archive_path: Option<Cow<'a, str>>,
/// The path to the Containerfile to build.
#[builder(setter(into))]
pub containerfile: Cow<'a, Path>,
/// The list of tags for the image being built.
#[builder(default, setter(into))]
pub tags: Cow<'a, [String]>,
/// Enable pushing the image.
#[builder(default)]
pub push: bool,
/// Enable retry logic for pushing.
#[builder(default)]
pub retry_push: bool,
/// Number of times to retry pushing.
///
/// Defaults to 1.
#[builder(default = 1)]
pub retry_count: u8,
/// The compression type to use when pushing.
#[builder(default)]
pub compression: CompressionType,
/// Run all steps in a single layer.
#[builder(default)]
pub squash: bool,
}

View file

@ -0,0 +1,12 @@
use std::borrow::Cow;
use typed_builder::TypedBuilder;
#[derive(Debug, Clone, TypedBuilder)]
pub struct GetMetadataOpts<'a> {
#[builder(setter(into))]
pub image: Cow<'a, str>,
#[builder(default, setter(into, strip_option))]
pub tag: Option<Cow<'a, str>>,
}

View file

@ -0,0 +1,82 @@
use std::borrow::Cow;
use typed_builder::TypedBuilder;
#[derive(Debug, Clone, TypedBuilder)]
pub struct RunOpts<'scope> {
#[builder(setter(into))]
pub image: Cow<'scope, str>,
#[builder(default, setter(into))]
pub args: Cow<'scope, [String]>,
#[builder(default, setter(into))]
pub env_vars: Cow<'scope, [RunOptsEnv<'scope>]>,
#[builder(default, setter(into))]
pub volumes: Cow<'scope, [RunOptsVolume<'scope>]>,
#[builder(default, setter(strip_option))]
pub uid: Option<u32>,
#[builder(default, setter(strip_option))]
pub gid: Option<u32>,
#[builder(default, setter(into))]
pub workdir: Cow<'scope, str>,
#[builder(default)]
pub privileged: bool,
#[builder(default)]
pub pull: bool,
#[builder(default)]
pub remove: bool,
}
#[derive(Debug, Clone, TypedBuilder)]
pub struct RunOptsVolume<'scope> {
#[builder(setter(into))]
pub path_or_vol_name: Cow<'scope, str>,
#[builder(setter(into))]
pub container_path: Cow<'scope, str>,
}
#[macro_export]
macro_rules! run_volumes {
($($host:expr => $container:expr),+ $(,)?) => {
{
[
$($crate::drivers::opts::RunOptsVolume::builder()
.path_or_vol_name($host)
.container_path($container)
.build(),)*
]
}
};
}
#[derive(Debug, Clone, TypedBuilder)]
pub struct RunOptsEnv<'scope> {
#[builder(setter(into))]
pub key: Cow<'scope, str>,
#[builder(setter(into))]
pub value: Cow<'scope, str>,
}
#[macro_export]
macro_rules! run_envs {
($($key:expr => $value:expr),+ $(,)?) => {
{
[
$($crate::drivers::opts::RunOptsEnv::builder()
.key($key)
.value($value)
.build(),)*
]
}
};
}

View file

@ -0,0 +1,115 @@
use std::{
borrow::Cow,
env, fs,
path::{Path, PathBuf},
};
use miette::{IntoDiagnostic, Result};
use typed_builder::TypedBuilder;
use zeroize::{Zeroize, Zeroizing};
pub enum PrivateKey {
Env(String),
Path(PathBuf),
}
pub trait PrivateKeyContents<T>
where
T: Zeroize,
{
/// Gets's the contents of the `PrivateKey`.
///
/// # Errors
/// Will error if the file or the environment couldn't be read.
fn contents(&self) -> Result<Zeroizing<T>>;
}
impl PrivateKeyContents<Vec<u8>> for PrivateKey {
fn contents(&self) -> Result<Zeroizing<Vec<u8>>> {
let key: Zeroizing<String> = self.contents()?;
Ok(Zeroizing::new(key.as_bytes().to_vec()))
}
}
impl PrivateKeyContents<String> for PrivateKey {
fn contents(&self) -> Result<Zeroizing<String>> {
Ok(Zeroizing::new(match *self {
Self::Env(ref env) => env::var(env).into_diagnostic()?,
Self::Path(ref path) => fs::read_to_string(path).into_diagnostic()?,
}))
}
}
impl std::fmt::Display for PrivateKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(
match *self {
Self::Env(ref env) => format!("env://{env}"),
Self::Path(ref path) => format!("{}", path.display()),
}
.as_str(),
)
}
}
#[derive(Debug, Clone, TypedBuilder)]
pub struct GenerateKeyPairOpts<'scope> {
#[builder(setter(into, strip_option))]
pub dir: Option<Cow<'scope, Path>>,
}
#[derive(Debug, Clone, TypedBuilder)]
pub struct CheckKeyPairOpts<'scope> {
#[builder(setter(into, strip_option))]
pub dir: Option<Cow<'scope, Path>>,
}
#[derive(Debug, Clone, TypedBuilder)]
pub struct SignOpts<'scope> {
#[builder(setter(into))]
pub image: Cow<'scope, str>,
#[builder(default, setter(into, strip_option))]
pub key: Option<Cow<'scope, str>>,
#[builder(default, setter(into, strip_option))]
pub dir: Option<Cow<'scope, Path>>,
}
#[derive(Debug, Clone)]
pub enum VerifyType<'scope> {
File(Cow<'scope, Path>),
Keyless {
issuer: Cow<'scope, str>,
identity: Cow<'scope, str>,
},
}
#[derive(Debug, Clone, TypedBuilder)]
pub struct VerifyOpts<'scope> {
#[builder(setter(into))]
pub image: Cow<'scope, str>,
pub verify_type: VerifyType<'scope>,
}
#[derive(Debug, Clone, TypedBuilder)]
pub struct SignVerifyOpts<'scope> {
#[builder(setter(into))]
pub image: Cow<'scope, str>,
#[builder(default, setter(into, strip_option))]
pub tag: Option<Cow<'scope, str>>,
#[builder(default, setter(into, strip_option))]
pub dir: Option<Cow<'scope, Path>>,
/// Enable retry logic for pushing.
#[builder(default)]
pub retry_push: bool,
/// Number of times to retry pushing.
///
/// Defaults to 1.
#[builder(default = 1)]
pub retry_count: u8,
}