refactor: Move templates to their own crate (#83)
This PR logically separates out parts of the code to their own crates. This will be useful for future Tauri App development.
This commit is contained in:
parent
ce8f889dc2
commit
910e0434b6
34 changed files with 620 additions and 512 deletions
128
utils/src/command_output.rs
Normal file
128
utils/src/command_output.rs
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
use std::{
|
||||
ffi::OsStr,
|
||||
fmt::Debug,
|
||||
io::{Error, ErrorKind, Result},
|
||||
process::{Command, Stdio},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use process_control::{ChildExt, Control};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CommandOutput {
|
||||
pub stdout: String,
|
||||
pub stderr: String,
|
||||
}
|
||||
|
||||
/// # Attempt to resolve `binary_name` from and creates a new `Command` pointing at it
|
||||
/// # This allows executing cmd files on Windows and prevents running executable from cwd on Windows
|
||||
/// # This function also initializes std{err,out,in} to protect against processes changing the console mode
|
||||
/// #
|
||||
/// # Errors
|
||||
///
|
||||
fn create_command<T: AsRef<OsStr>>(binary_name: T) -> Result<Command> {
|
||||
let binary_name = binary_name.as_ref();
|
||||
log::trace!("Creating Command for binary {:?}", binary_name);
|
||||
|
||||
let full_path = match which::which(binary_name) {
|
||||
Ok(full_path) => {
|
||||
log::trace!("Using {:?} as {:?}", full_path, binary_name);
|
||||
full_path
|
||||
}
|
||||
Err(error) => {
|
||||
log::trace!("Unable to find {:?} in PATH, {:?}", binary_name, error);
|
||||
return Err(Error::new(ErrorKind::NotFound, error));
|
||||
}
|
||||
};
|
||||
|
||||
let mut cmd = Command::new(full_path);
|
||||
cmd.stderr(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stdin(Stdio::null());
|
||||
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
/// Execute a command and return the output on stdout and stderr if successful
|
||||
pub fn exec_cmd<T: AsRef<OsStr> + Debug, U: AsRef<OsStr> + Debug>(
|
||||
cmd: T,
|
||||
args: &[U],
|
||||
time_limit: Duration,
|
||||
) -> Option<CommandOutput> {
|
||||
log::trace!("Executing command {:?} with args {:?}", cmd, args);
|
||||
internal_exec_cmd(cmd, args, time_limit)
|
||||
}
|
||||
|
||||
fn internal_exec_cmd<T: AsRef<OsStr> + Debug, U: AsRef<OsStr> + Debug>(
|
||||
cmd: T,
|
||||
args: &[U],
|
||||
time_limit: Duration,
|
||||
) -> Option<CommandOutput> {
|
||||
let mut cmd = create_command(cmd).ok()?;
|
||||
cmd.args(args);
|
||||
exec_timeout(&mut cmd, time_limit)
|
||||
}
|
||||
|
||||
fn exec_timeout(cmd: &mut Command, time_limit: Duration) -> Option<CommandOutput> {
|
||||
let start = Instant::now();
|
||||
let process = match cmd.spawn() {
|
||||
Ok(process) => process,
|
||||
Err(error) => {
|
||||
log::trace!("Unable to run {:?}, {:?}", cmd.get_program(), error);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
match process
|
||||
.controlled_with_output()
|
||||
.time_limit(time_limit)
|
||||
.terminate_for_timeout()
|
||||
.wait()
|
||||
{
|
||||
Ok(Some(output)) => {
|
||||
let stdout_string = match String::from_utf8(output.stdout) {
|
||||
Ok(stdout) => stdout,
|
||||
Err(error) => {
|
||||
log::warn!("Unable to decode stdout: {:?}", error);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let stderr_string = match String::from_utf8(output.stderr) {
|
||||
Ok(stderr) => stderr,
|
||||
Err(error) => {
|
||||
log::warn!("Unable to decode stderr: {:?}", error);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
log::trace!(
|
||||
"stdout: {:?}, stderr: {:?}, exit code: \"{:?}\", took {:?}",
|
||||
stdout_string,
|
||||
stderr_string,
|
||||
output.status.code(),
|
||||
start.elapsed()
|
||||
);
|
||||
|
||||
if !output.status.success() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(CommandOutput {
|
||||
stdout: stdout_string,
|
||||
stderr: stderr_string,
|
||||
})
|
||||
}
|
||||
Ok(None) => {
|
||||
log::warn!("Executing command {:?} timed out.", cmd.get_program());
|
||||
log::warn!("You can set command_timeout in your config to a higher value to allow longer-running commands to keep executing.");
|
||||
None
|
||||
}
|
||||
Err(error) => {
|
||||
log::trace!(
|
||||
"Executing command {:?} failed by: {:?}",
|
||||
cmd.get_program(),
|
||||
error
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
68
utils/src/constants.rs
Normal file
68
utils/src/constants.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// Paths
|
||||
pub const ARCHIVE_SUFFIX: &str = "tar.gz";
|
||||
pub const COSIGN_PATH: &str = "./cosign.pub";
|
||||
pub const GITIGNORE_PATH: &str = ".gitignore";
|
||||
pub const LOCAL_BUILD: &str = "/etc/bluebuild";
|
||||
pub const CONTAINER_FILE: &str = "Containerfile";
|
||||
pub const MODULES_PATH: &str = "./config/modules";
|
||||
pub const RECIPE_PATH: &str = "./config/recipe.yml";
|
||||
pub const RUN_PODMAN_SOCK: &str = "/run/podman/podman.sock";
|
||||
pub const VAR_RUN_PODMAN_PODMAN_SOCK: &str = "/var/run/podman/podman.sock";
|
||||
pub const VAR_RUN_PODMAN_SOCK: &str = "/var/run/podman.sock";
|
||||
|
||||
// Labels
|
||||
pub const BUILD_ID_LABEL: &str = "org.blue-build.build-id";
|
||||
|
||||
// Cosign vars
|
||||
pub const COSIGN_PRIVATE_KEY: &str = "COSIGN_PRIVATE_KEY";
|
||||
pub const GITHUB_TOKEN_ISSUER_URL: &str = "https://token.actions.githubusercontent.com";
|
||||
pub const SIGSTORE_ID_TOKEN: &str = "SIGSTORE_ID_TOKEN";
|
||||
|
||||
// GitHub CI vars
|
||||
pub const GITHUB_ACTIONS: &str = "GITHUB_ACTIONS";
|
||||
pub const GITHUB_ACTOR: &str = "GITHUB_ACTOR";
|
||||
pub const GITHUB_EVENT_NAME: &str = "GITHUB_EVENT_NAME";
|
||||
pub const GITHUB_REF_NAME: &str = "GITHUB_REF_NAME";
|
||||
pub const GITHUB_REPOSITORY_OWNER: &str = "GITHUB_REPOSITORY_OWNER";
|
||||
pub const GITHUB_SHA: &str = "GITHUB_SHA";
|
||||
pub const GITHUB_TOKEN: &str = "GH_TOKEN";
|
||||
pub const GITHUB_WORKFLOW_REF: &str = "GITHUB_WORKFLOW_REF";
|
||||
pub const PR_EVENT_NUMBER: &str = "GH_PR_EVENT_NUMBER";
|
||||
|
||||
// GitLab CI vars
|
||||
pub const CI_COMMIT_REF_NAME: &str = "CI_COMMIT_REF_NAME";
|
||||
pub const CI_COMMIT_SHORT_SHA: &str = "CI_COMMIT_SHORT_SHA";
|
||||
pub const CI_DEFAULT_BRANCH: &str = "CI_DEFAULT_BRANCH";
|
||||
pub const CI_MERGE_REQUEST_IID: &str = "CI_MERGE_REQUEST_IID";
|
||||
pub const CI_PIPELINE_SOURCE: &str = "CI_PIPELINE_SOURCE";
|
||||
pub const CI_PROJECT_NAME: &str = "CI_PROJECT_NAME";
|
||||
pub const CI_PROJECT_NAMESPACE: &str = "CI_PROJECT_NAMESPACE";
|
||||
pub const CI_PROJECT_URL: &str = "CI_PROJECT_URL";
|
||||
pub const CI_SERVER_HOST: &str = "CI_SERVER_HOST";
|
||||
pub const CI_SERVER_PROTOCOL: &str = "CI_SERVER_PROTOCOL";
|
||||
pub const CI_REGISTRY: &str = "CI_REGISTRY";
|
||||
pub const CI_REGISTRY_PASSWORD: &str = "CI_REGISTRY_PASSWORD";
|
||||
pub const CI_REGISTRY_USER: &str = "CI_REGISTRY_USER";
|
||||
|
||||
// Terminal vars
|
||||
pub const TERM_PROGRAM: &str = "TERM_PROGRAM";
|
||||
pub const LC_TERMINAL: &str = "LC_TERMINAL";
|
||||
pub const TERM_PROGRAM_VERSION: &str = "TERM_PROGRAM_VERSION";
|
||||
pub const LC_TERMINAL_VERSION: &str = "LC_TERMINAL_VERSION";
|
||||
pub const XDG_RUNTIME_DIR: &str = "XDG_RUNTIME_DIR";
|
||||
|
||||
// Misc
|
||||
pub const UNKNOWN_SHELL: &str = "<unknown shell>";
|
||||
pub const UNKNOWN_VERSION: &str = "<unknown version>";
|
||||
pub const UNKNOWN_TERMINAL: &str = "<unknown terminal>";
|
||||
pub const GITHUB_CHAR_LIMIT: usize = 8100; // Magic number accepted by Github
|
||||
|
||||
// Messages
|
||||
pub const LABELED_ERROR_MESSAGE: &str =
|
||||
"It looks you have a BlueBuild-generated Containerfile that is not .gitignored. \
|
||||
Do you want to remove it from git and add it to .gitignore? (This will still continue the build)";
|
||||
|
||||
pub const NO_LABEL_ERROR_MESSAGE: &str =
|
||||
"It looks you have a Containerfile that has not been generated by BlueBuild. \
|
||||
Running `build` will override your Containerfile and add an entry to the .gitignore. \
|
||||
Do you want to continue?";
|
||||
79
utils/src/lib.rs
Normal file
79
utils/src/lib.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
pub mod command_output;
|
||||
pub mod constants;
|
||||
|
||||
use std::{io::Write, path::PathBuf, process::Command, thread, time::Duration};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use format_serde_error::SerdeError;
|
||||
use log::{debug, trace};
|
||||
|
||||
pub use command_output::*;
|
||||
|
||||
pub fn check_command_exists(command: &str) -> Result<()> {
|
||||
trace!("check_command_exists({command})");
|
||||
debug!("Checking if {command} exists");
|
||||
|
||||
trace!("which {command}");
|
||||
if Command::new("which")
|
||||
.arg(command)
|
||||
.output()?
|
||||
.status
|
||||
.success()
|
||||
{
|
||||
debug!("Command {command} does exist");
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"Command {command} doesn't exist and is required to build the image"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append_to_file(file_path: &str, content: &str) -> Result<()> {
|
||||
trace!("append_to_file({file_path}, {content})");
|
||||
debug!("Appending {content} to {file_path}");
|
||||
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(file_path)?;
|
||||
|
||||
writeln!(file, "\n{content}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn retry<V, F>(mut attempts: u8, delay: u64, f: F) -> anyhow::Result<V>
|
||||
where
|
||||
F: Fn() -> anyhow::Result<V>,
|
||||
{
|
||||
loop {
|
||||
match f() {
|
||||
Ok(v) => return Ok(v),
|
||||
Err(e) if attempts == 1 => return Err(e),
|
||||
_ => {
|
||||
attempts -= 1;
|
||||
thread::sleep(Duration::from_secs(delay));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn home_dir() -> Option<PathBuf> {
|
||||
directories::BaseDirs::new().map(|base_dirs| base_dirs.home_dir().to_path_buf())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue