169 lines
4.5 KiB
Rust
169 lines
4.5 KiB
Rust
pub mod command_output;
|
|
pub mod constants;
|
|
pub mod credentials;
|
|
mod macros;
|
|
pub mod semver;
|
|
pub mod syntax_highlighting;
|
|
#[cfg(feature = "test")]
|
|
pub mod test_utils;
|
|
pub mod traits;
|
|
|
|
use std::{
|
|
ops::{AsyncFnMut, Not},
|
|
os::unix::ffi::OsStrExt,
|
|
path::{Path, PathBuf},
|
|
thread,
|
|
time::Duration,
|
|
};
|
|
|
|
use base64::prelude::*;
|
|
use blake2::{
|
|
Blake2bVar,
|
|
digest::{Update, VariableOutput},
|
|
};
|
|
use cached::proc_macro::once;
|
|
use chrono::Local;
|
|
use comlexr::cmd;
|
|
use format_serde_error::SerdeError;
|
|
use log::{trace, warn};
|
|
use miette::{Context, IntoDiagnostic, Result, miette};
|
|
|
|
use crate::constants::CONTAINER_FILE;
|
|
|
|
pub use command_output::*;
|
|
|
|
/// Checks for the existance of a given command.
|
|
///
|
|
/// # Errors
|
|
/// Will error if the command doesn't exist.
|
|
pub fn check_command_exists(command: &str) -> Result<()> {
|
|
trace!("check_command_exists({command})");
|
|
|
|
trace!("which {command}");
|
|
if cmd!("which", command)
|
|
.output()
|
|
.into_diagnostic()?
|
|
.status
|
|
.success()
|
|
{
|
|
trace!("Command {command} does exist");
|
|
Ok(())
|
|
} else {
|
|
Err(miette!(
|
|
"Command {command} doesn't exist and is required to build the image"
|
|
))
|
|
}
|
|
}
|
|
|
|
/// Creates a serde error for displaying the file
|
|
/// and where the error occurred.
|
|
pub fn serde_yaml_err(contents: &str) -> impl Fn(serde_yaml::Error) -> SerdeError + '_ {
|
|
|err: serde_yaml::Error| {
|
|
let location = err.location();
|
|
let location = location.as_ref();
|
|
SerdeError::new(
|
|
contents.to_string(),
|
|
(
|
|
err.into(),
|
|
location.map_or(0, serde_yaml::Location::line).into(),
|
|
location.map_or(0, serde_yaml::Location::column).into(),
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
/// Performs a retry on a given closure with a given nubmer of attempts and delay.
|
|
///
|
|
/// # Errors
|
|
/// Will error when retries have been expended.
|
|
pub fn retry<V, F>(mut retries: u8, delay_secs: u64, mut f: F) -> miette::Result<V>
|
|
where
|
|
F: FnMut() -> miette::Result<V> + Send,
|
|
{
|
|
loop {
|
|
match f() {
|
|
Ok(v) => return Ok(v),
|
|
Err(e) if retries == 0 => return Err(e),
|
|
Err(e) => {
|
|
retries -= 1;
|
|
warn!("Failed operation, will retry {retries} more time(s). Error:\n{e:?}");
|
|
thread::sleep(Duration::from_secs(delay_secs));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Performs a retry on a given closure with a given nubmer of attempts and delay.
|
|
///
|
|
/// # Errors
|
|
/// Will error when retries have been expended.
|
|
pub async fn retry_async<V, F>(mut retries: u8, delay_secs: u64, mut f: F) -> miette::Result<V>
|
|
where
|
|
F: AsyncFnMut() -> miette::Result<V>,
|
|
{
|
|
loop {
|
|
match f().await {
|
|
Ok(v) => return Ok(v),
|
|
Err(e) if retries == 0 => return Err(e),
|
|
Err(e) => {
|
|
retries -= 1;
|
|
warn!("Failed operation, will retry {retries} more time(s). Error:\n{e:?}");
|
|
thread::sleep(Duration::from_secs(delay_secs));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn home_dir() -> Option<PathBuf> {
|
|
directories::BaseDirs::new().map(|base_dirs| base_dirs.home_dir().to_path_buf())
|
|
}
|
|
|
|
/// Generates a 1-1 related Containerfile to a recipe.
|
|
/// The file is in the format of `Containerfile.{path_hash}`.
|
|
///
|
|
/// # Errors
|
|
/// Will error if unable to create a hash of the
|
|
pub fn generate_containerfile_path<T: AsRef<Path>>(path: T) -> Result<PathBuf> {
|
|
const HASH_SIZE: usize = 8;
|
|
let mut buf = [0u8; HASH_SIZE];
|
|
|
|
let mut hasher = Blake2bVar::new(HASH_SIZE).into_diagnostic()?;
|
|
hasher.update(path.as_ref().as_os_str().as_bytes());
|
|
hasher.finalize_variable(&mut buf).into_diagnostic()?;
|
|
|
|
Ok(PathBuf::from(format!(
|
|
"{CONTAINER_FILE}.{}",
|
|
BASE64_URL_SAFE_NO_PAD.encode(buf)
|
|
)))
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn get_tag_timestamp() -> String {
|
|
Local::now().format("%Y%m%d").to_string()
|
|
}
|
|
|
|
/// Get's the env var wrapping it with a miette error
|
|
///
|
|
/// # Errors
|
|
/// Will error if the env var doesn't exist.
|
|
pub fn get_env_var(key: &str) -> Result<String> {
|
|
std::env::var(key)
|
|
.into_diagnostic()
|
|
.with_context(|| format!("Failed to get {key}'"))
|
|
}
|
|
|
|
/// Checks if an environment variable is set and isn't empty.
|
|
#[must_use]
|
|
pub fn has_env_var(key: &str) -> bool {
|
|
get_env_var(key).is_ok_and(|v| v.is_empty().not())
|
|
}
|
|
|
|
/// Checks if the process is running as root.
|
|
///
|
|
/// This call is cached to reduce syscalls.
|
|
#[once]
|
|
#[must_use]
|
|
pub fn running_as_root() -> bool {
|
|
nix::unistd::Uid::effective().is_root()
|
|
}
|