chore: Move recipe out to its own module (#18)
This commit is contained in:
parent
67f2d34058
commit
c32a0604ce
5 changed files with 220 additions and 203 deletions
|
|
@ -30,10 +30,11 @@ use tokio::runtime::Runtime;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::template::TemplateCommand,
|
commands::template::TemplateCommand,
|
||||||
|
module_recipe::Recipe,
|
||||||
ops::{self, ARCHIVE_SUFFIX},
|
ops::{self, ARCHIVE_SUFFIX},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{template::Recipe, BlueBuildCommand};
|
use super::BlueBuildCommand;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Args, TypedBuilder)]
|
#[derive(Debug, Clone, Args, TypedBuilder)]
|
||||||
pub struct BuildCommand {
|
pub struct BuildCommand {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ use typed_builder::TypedBuilder;
|
||||||
use users::{Users, UsersCache};
|
use users::{Users, UsersCache};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::{build::BuildCommand, template::Recipe},
|
commands::build::BuildCommand,
|
||||||
|
module_recipe::Recipe,
|
||||||
ops::{self, ARCHIVE_SUFFIX, LOCAL_BUILD},
|
ops::{self, ARCHIVE_SUFFIX, LOCAL_BUILD},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ use serde::{Deserialize, Serialize};
|
||||||
use serde_yaml::Value;
|
use serde_yaml::Value;
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
|
use crate::module_recipe::Recipe;
|
||||||
|
|
||||||
use super::BlueBuildCommand;
|
use super::BlueBuildCommand;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Template, TypedBuilder)]
|
#[derive(Debug, Clone, Template, TypedBuilder)]
|
||||||
|
|
@ -31,132 +33,6 @@ pub struct ContainerFileTemplate<'a> {
|
||||||
#[template(path = "export.sh", escape = "none")]
|
#[template(path = "export.sh", escape = "none")]
|
||||||
pub struct ExportsTemplate;
|
pub struct ExportsTemplate;
|
||||||
|
|
||||||
#[derive(Serialize, Clone, Deserialize, Debug, TypedBuilder)]
|
|
||||||
pub struct Recipe {
|
|
||||||
#[builder(setter(into))]
|
|
||||||
pub name: String,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
pub description: String,
|
|
||||||
|
|
||||||
#[serde(alias = "base-image")]
|
|
||||||
#[builder(setter(into))]
|
|
||||||
pub base_image: String,
|
|
||||||
|
|
||||||
#[serde(alias = "image-version")]
|
|
||||||
#[builder(setter(into))]
|
|
||||||
pub image_version: String,
|
|
||||||
|
|
||||||
#[serde(alias = "blue-build-tag")]
|
|
||||||
#[builder(default, setter(into, strip_option))]
|
|
||||||
pub blue_build_tag: Option<String>,
|
|
||||||
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub modules_ext: ModuleExt,
|
|
||||||
|
|
||||||
#[serde(flatten)]
|
|
||||||
#[builder(setter(into))]
|
|
||||||
pub extra: IndexMap<String, Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Recipe {
|
|
||||||
#[must_use]
|
|
||||||
pub fn generate_tags(&self) -> Vec<String> {
|
|
||||||
trace!("Recipe::generate_tags()");
|
|
||||||
debug!("Generating image tags for {}", &self.name);
|
|
||||||
|
|
||||||
let mut tags: Vec<String> = Vec::new();
|
|
||||||
let image_version = &self.image_version;
|
|
||||||
let timestamp = Local::now().format("%Y%m%d").to_string();
|
|
||||||
|
|
||||||
if let (Ok(commit_branch), Ok(default_branch), Ok(commit_sha), Ok(pipeline_source)) = (
|
|
||||||
env::var("CI_COMMIT_REF_NAME"),
|
|
||||||
env::var("CI_DEFAULT_BRANCH"),
|
|
||||||
env::var("CI_COMMIT_SHORT_SHA"),
|
|
||||||
env::var("CI_PIPELINE_SOURCE"),
|
|
||||||
) {
|
|
||||||
trace!("CI_COMMIT_REF_NAME={commit_branch}, CI_DEFAULT_BRANCH={default_branch},CI_COMMIT_SHORT_SHA={commit_sha}, CI_PIPELINE_SOURCE={pipeline_source}");
|
|
||||||
warn!("Detected running in Gitlab, pulling information from CI variables");
|
|
||||||
|
|
||||||
if let Ok(mr_iid) = env::var("CI_MERGE_REQUEST_IID") {
|
|
||||||
trace!("CI_MERGE_REQUEST_IID={mr_iid}");
|
|
||||||
if pipeline_source == "merge_request_event" {
|
|
||||||
debug!("Running in a MR");
|
|
||||||
tags.push(format!("mr-{mr_iid}-{image_version}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if default_branch == commit_branch {
|
|
||||||
debug!("Running on the default branch");
|
|
||||||
tags.push(image_version.to_string());
|
|
||||||
tags.push(format!("{image_version}-{timestamp}"));
|
|
||||||
tags.push(timestamp);
|
|
||||||
} else {
|
|
||||||
debug!("Running on branch {commit_branch}");
|
|
||||||
tags.push(format!("{commit_branch}-{image_version}"));
|
|
||||||
}
|
|
||||||
|
|
||||||
tags.push(format!("{commit_sha}-{image_version}"));
|
|
||||||
} else if let (
|
|
||||||
Ok(github_event_name),
|
|
||||||
Ok(github_event_number),
|
|
||||||
Ok(github_sha),
|
|
||||||
Ok(github_ref_name),
|
|
||||||
) = (
|
|
||||||
env::var("GITHUB_EVENT_NAME"),
|
|
||||||
env::var("PR_EVENT_NUMBER"),
|
|
||||||
env::var("GITHUB_SHA"),
|
|
||||||
env::var("GITHUB_REF_NAME"),
|
|
||||||
) {
|
|
||||||
trace!("GITHUB_EVENT_NAME={github_event_name},PR_EVENT_NUMBER={github_event_number},GITHUB_SHA={github_sha},GITHUB_REF_NAME={github_ref_name}");
|
|
||||||
warn!("Detected running in Github, pulling information from GITHUB variables");
|
|
||||||
|
|
||||||
let mut short_sha = github_sha;
|
|
||||||
short_sha.truncate(7);
|
|
||||||
|
|
||||||
if github_event_name == "pull_request" {
|
|
||||||
debug!("Running in a PR");
|
|
||||||
tags.push(format!("pr-{github_event_number}-{image_version}"));
|
|
||||||
} else if github_ref_name == "live" {
|
|
||||||
tags.push(image_version.to_owned());
|
|
||||||
tags.push(format!("{image_version}-{timestamp}"));
|
|
||||||
tags.push("latest".to_string());
|
|
||||||
} else {
|
|
||||||
tags.push(format!("br-{github_ref_name}-{image_version}"));
|
|
||||||
}
|
|
||||||
tags.push(format!("{short_sha}-{image_version}"));
|
|
||||||
} else {
|
|
||||||
warn!("Running locally");
|
|
||||||
tags.push(format!("{image_version}-local"));
|
|
||||||
}
|
|
||||||
info!("Finished generating tags!");
|
|
||||||
debug!("Tags: {tags:#?}");
|
|
||||||
tags
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Clone, Deserialize, Debug, Template, TypedBuilder)]
|
|
||||||
#[template(path = "Containerfile.module", escape = "none")]
|
|
||||||
pub struct ModuleExt {
|
|
||||||
#[builder(default, setter(into))]
|
|
||||||
pub modules: Vec<Module>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, TypedBuilder)]
|
|
||||||
pub struct Module {
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
#[builder(default, setter(into, strip_option))]
|
|
||||||
pub module_type: Option<String>,
|
|
||||||
|
|
||||||
#[serde(rename = "from-file")]
|
|
||||||
#[builder(default, setter(into, strip_option))]
|
|
||||||
pub from_file: Option<String>,
|
|
||||||
|
|
||||||
#[serde(flatten)]
|
|
||||||
#[builder(default, setter(into))]
|
|
||||||
pub config: IndexMap<String, Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Args, TypedBuilder)]
|
#[derive(Debug, Clone, Args, TypedBuilder)]
|
||||||
pub struct TemplateCommand {
|
pub struct TemplateCommand {
|
||||||
/// The recipe file to create a template from
|
/// The recipe file to create a template from
|
||||||
|
|
@ -228,81 +104,6 @@ fn print_script(script_contents: &ExportsTemplate) -> String {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_containerfile_list(module: &Module) -> Option<Vec<String>> {
|
|
||||||
if module.module_type.as_ref()? == "containerfile" {
|
|
||||||
Some(
|
|
||||||
module
|
|
||||||
.config
|
|
||||||
.get("containerfiles")?
|
|
||||||
.as_sequence()?
|
|
||||||
.iter()
|
|
||||||
.filter_map(|t| Some(t.as_str()?.to_owned()))
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_containerfile(containerfile: &str) -> String {
|
|
||||||
trace!("print_containerfile({containerfile})");
|
|
||||||
debug!("Loading containerfile contents for {containerfile}");
|
|
||||||
|
|
||||||
let path = format!("config/containerfiles/{containerfile}/Containerfile");
|
|
||||||
|
|
||||||
let file = fs::read_to_string(&path).unwrap_or_else(|e| {
|
|
||||||
error!("Failed to read file {path}: {e}");
|
|
||||||
process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
trace!("Containerfile contents {path}:\n{file}");
|
|
||||||
|
|
||||||
file
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_module_from_file(file_name: &str) -> String {
|
|
||||||
trace!("get_module_from_file({file_name})");
|
|
||||||
|
|
||||||
let io_err_fn = |e| {
|
|
||||||
error!("Failed to read module {file_name}: {e}");
|
|
||||||
process::exit(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
let file_path = PathBuf::from("config").join(file_name);
|
|
||||||
|
|
||||||
let file = fs::read_to_string(file_path).unwrap_or_else(io_err_fn);
|
|
||||||
|
|
||||||
let serde_err_fn = |e| {
|
|
||||||
error!("Failed to deserialize module {file_name}: {e}");
|
|
||||||
process::exit(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
let template_err_fn = |e| {
|
|
||||||
error!("Failed to render module {file_name}: {e}");
|
|
||||||
process::exit(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
serde_yaml::from_str::<ModuleExt>(file.as_str()).map_or_else(
|
|
||||||
|_| {
|
|
||||||
let module = serde_yaml::from_str::<Module>(file.as_str()).unwrap_or_else(serde_err_fn);
|
|
||||||
|
|
||||||
ModuleExt::builder()
|
|
||||||
.modules(vec![module])
|
|
||||||
.build()
|
|
||||||
.render()
|
|
||||||
.unwrap_or_else(template_err_fn)
|
|
||||||
},
|
|
||||||
|module_ext| module_ext.render().unwrap_or_else(template_err_fn),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_module_context(module: &Module) -> String {
|
|
||||||
serde_json::to_string(module).unwrap_or_else(|e| {
|
|
||||||
error!("Failed to parse module: {e}");
|
|
||||||
process::exit(1);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn running_gitlab_actions() -> bool {
|
fn running_gitlab_actions() -> bool {
|
||||||
trace!(" running_gitlab_actions()");
|
trace!(" running_gitlab_actions()");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,5 @@
|
||||||
#![allow(clippy::module_name_repetitions)]
|
#![allow(clippy::module_name_repetitions)]
|
||||||
|
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
|
pub mod module_recipe;
|
||||||
mod ops;
|
mod ops;
|
||||||
|
|
|
||||||
|
|
@ -1 +1,214 @@
|
||||||
|
use std::{env, fs, path::PathBuf, process};
|
||||||
|
|
||||||
|
use askama::Template;
|
||||||
|
use chrono::Local;
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use log::{debug, error, info, trace, warn};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_yaml::Value;
|
||||||
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
|
#[derive(Serialize, Clone, Deserialize, Debug, TypedBuilder)]
|
||||||
|
pub struct Recipe {
|
||||||
|
#[builder(setter(into))]
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
pub description: String,
|
||||||
|
|
||||||
|
#[serde(alias = "base-image")]
|
||||||
|
#[builder(setter(into))]
|
||||||
|
pub base_image: String,
|
||||||
|
|
||||||
|
#[serde(alias = "image-version")]
|
||||||
|
#[builder(setter(into))]
|
||||||
|
pub image_version: String,
|
||||||
|
|
||||||
|
#[serde(alias = "blue-build-tag")]
|
||||||
|
#[builder(default, setter(into, strip_option))]
|
||||||
|
pub blue_build_tag: Option<String>,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub modules_ext: ModuleExt,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
#[builder(setter(into))]
|
||||||
|
pub extra: IndexMap<String, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Recipe {
|
||||||
|
#[must_use]
|
||||||
|
pub fn generate_tags(&self) -> Vec<String> {
|
||||||
|
trace!("Recipe::generate_tags()");
|
||||||
|
debug!("Generating image tags for {}", &self.name);
|
||||||
|
|
||||||
|
let mut tags: Vec<String> = Vec::new();
|
||||||
|
let image_version = &self.image_version;
|
||||||
|
let timestamp = Local::now().format("%Y%m%d").to_string();
|
||||||
|
|
||||||
|
if let (Ok(commit_branch), Ok(default_branch), Ok(commit_sha), Ok(pipeline_source)) = (
|
||||||
|
env::var("CI_COMMIT_REF_NAME"),
|
||||||
|
env::var("CI_DEFAULT_BRANCH"),
|
||||||
|
env::var("CI_COMMIT_SHORT_SHA"),
|
||||||
|
env::var("CI_PIPELINE_SOURCE"),
|
||||||
|
) {
|
||||||
|
trace!("CI_COMMIT_REF_NAME={commit_branch}, CI_DEFAULT_BRANCH={default_branch},CI_COMMIT_SHORT_SHA={commit_sha}, CI_PIPELINE_SOURCE={pipeline_source}");
|
||||||
|
warn!("Detected running in Gitlab, pulling information from CI variables");
|
||||||
|
|
||||||
|
if let Ok(mr_iid) = env::var("CI_MERGE_REQUEST_IID") {
|
||||||
|
trace!("CI_MERGE_REQUEST_IID={mr_iid}");
|
||||||
|
if pipeline_source == "merge_request_event" {
|
||||||
|
debug!("Running in a MR");
|
||||||
|
tags.push(format!("mr-{mr_iid}-{image_version}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if default_branch == commit_branch {
|
||||||
|
debug!("Running on the default branch");
|
||||||
|
tags.push(image_version.to_string());
|
||||||
|
tags.push(format!("{image_version}-{timestamp}"));
|
||||||
|
tags.push(timestamp);
|
||||||
|
} else {
|
||||||
|
debug!("Running on branch {commit_branch}");
|
||||||
|
tags.push(format!("{commit_branch}-{image_version}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
tags.push(format!("{commit_sha}-{image_version}"));
|
||||||
|
} else if let (
|
||||||
|
Ok(github_event_name),
|
||||||
|
Ok(github_event_number),
|
||||||
|
Ok(github_sha),
|
||||||
|
Ok(github_ref_name),
|
||||||
|
) = (
|
||||||
|
env::var("GITHUB_EVENT_NAME"),
|
||||||
|
env::var("PR_EVENT_NUMBER"),
|
||||||
|
env::var("GITHUB_SHA"),
|
||||||
|
env::var("GITHUB_REF_NAME"),
|
||||||
|
) {
|
||||||
|
trace!("GITHUB_EVENT_NAME={github_event_name},PR_EVENT_NUMBER={github_event_number},GITHUB_SHA={github_sha},GITHUB_REF_NAME={github_ref_name}");
|
||||||
|
warn!("Detected running in Github, pulling information from GITHUB variables");
|
||||||
|
|
||||||
|
let mut short_sha = github_sha;
|
||||||
|
short_sha.truncate(7);
|
||||||
|
|
||||||
|
if github_event_name == "pull_request" {
|
||||||
|
debug!("Running in a PR");
|
||||||
|
tags.push(format!("pr-{github_event_number}-{image_version}"));
|
||||||
|
} else if github_ref_name == "live" {
|
||||||
|
tags.push(image_version.to_owned());
|
||||||
|
tags.push(format!("{image_version}-{timestamp}"));
|
||||||
|
tags.push("latest".to_string());
|
||||||
|
} else {
|
||||||
|
tags.push(format!("br-{github_ref_name}-{image_version}"));
|
||||||
|
}
|
||||||
|
tags.push(format!("{short_sha}-{image_version}"));
|
||||||
|
} else {
|
||||||
|
warn!("Running locally");
|
||||||
|
tags.push(format!("{image_version}-local"));
|
||||||
|
}
|
||||||
|
info!("Finished generating tags!");
|
||||||
|
debug!("Tags: {tags:#?}");
|
||||||
|
tags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Clone, Deserialize, Debug, Template, TypedBuilder)]
|
||||||
|
#[template(path = "Containerfile.module", escape = "none")]
|
||||||
|
pub struct ModuleExt {
|
||||||
|
#[builder(default, setter(into))]
|
||||||
|
pub modules: Vec<Module>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, TypedBuilder)]
|
||||||
|
pub struct Module {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
#[builder(default, setter(into, strip_option))]
|
||||||
|
pub module_type: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "from-file")]
|
||||||
|
#[builder(default, setter(into, strip_option))]
|
||||||
|
pub from_file: Option<String>,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
#[builder(default, setter(into))]
|
||||||
|
pub config: IndexMap<String, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================================================== //
|
||||||
|
// ========================= Helpers ====================== //
|
||||||
|
// ======================================================== //
|
||||||
|
|
||||||
|
fn get_containerfile_list(module: &Module) -> Option<Vec<String>> {
|
||||||
|
if module.module_type.as_ref()? == "containerfile" {
|
||||||
|
Some(
|
||||||
|
module
|
||||||
|
.config
|
||||||
|
.get("containerfiles")?
|
||||||
|
.as_sequence()?
|
||||||
|
.iter()
|
||||||
|
.filter_map(|t| Some(t.as_str()?.to_owned()))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_containerfile(containerfile: &str) -> String {
|
||||||
|
trace!("print_containerfile({containerfile})");
|
||||||
|
debug!("Loading containerfile contents for {containerfile}");
|
||||||
|
|
||||||
|
let path = format!("config/containerfiles/{containerfile}/Containerfile");
|
||||||
|
|
||||||
|
let file = fs::read_to_string(&path).unwrap_or_else(|e| {
|
||||||
|
error!("Failed to read file {path}: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
trace!("Containerfile contents {path}:\n{file}");
|
||||||
|
|
||||||
|
file
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_module_from_file(file_name: &str) -> String {
|
||||||
|
trace!("get_module_from_file({file_name})");
|
||||||
|
|
||||||
|
let io_err_fn = |e| {
|
||||||
|
error!("Failed to read module {file_name}: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let file_path = PathBuf::from("config").join(file_name);
|
||||||
|
|
||||||
|
let file = fs::read_to_string(file_path).unwrap_or_else(io_err_fn);
|
||||||
|
|
||||||
|
let serde_err_fn = |e| {
|
||||||
|
error!("Failed to deserialize module {file_name}: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let template_err_fn = |e| {
|
||||||
|
error!("Failed to render module {file_name}: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
serde_yaml::from_str::<ModuleExt>(file.as_str()).map_or_else(
|
||||||
|
|_| {
|
||||||
|
let module = serde_yaml::from_str::<Module>(file.as_str()).unwrap_or_else(serde_err_fn);
|
||||||
|
|
||||||
|
ModuleExt::builder()
|
||||||
|
.modules(vec![module])
|
||||||
|
.build()
|
||||||
|
.render()
|
||||||
|
.unwrap_or_else(template_err_fn)
|
||||||
|
},
|
||||||
|
|module_ext| module_ext.render().unwrap_or_else(template_err_fn),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_module_context(module: &Module) -> String {
|
||||||
|
serde_json::to_string(module).unwrap_or_else(|e| {
|
||||||
|
error!("Failed to parse module: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue