diff --git a/src/commands/bug_report.rs b/src/commands/bug_report.rs index 466fb70d..d67c58bc 100644 --- a/src/commands/bug_report.rs +++ b/src/commands/bug_report.rs @@ -1,17 +1,13 @@ -use crate::module_recipe::{Module, ModuleExt, Recipe}; +use crate::module_recipe::Recipe; use crate::shadow; -use anyhow::Result; use askama::Template; use clap::Args; use clap_complete::Shell; -use format_serde_error::SerdeError; use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher}; use log::{debug, error, trace}; use requestty::question::{completions, Completions}; use std::borrow::Cow; -use std::fs; -use std::path::PathBuf; use std::time::Duration; use typed_builder::TypedBuilder; @@ -344,9 +340,7 @@ fn generate_github_issue( environment: &Environment, recipe: &Option, ) -> anyhow::Result { - let recipe = recipe - .as_ref() - .map_or_else(Default::default, |r| print_full_recipe(r)); + let recipe = serde_yaml::to_string(recipe)?; let github_template = GithubIssueTemplate::builder() .bb_version(shadow::PKG_VERSION) @@ -381,68 +375,6 @@ fn make_github_issue_link(body: &str) -> String { .collect() } -fn get_module_from_file(file_name: &str) -> Result { - let file_path = PathBuf::from("config").join(file_name); - let file_path = if file_path.is_absolute() { - file_path - } else { - std::env::current_dir()?.join(file_path) - }; - - let file = fs::read_to_string(file_path.clone())?; - - serde_yaml::from_str::(&file).map_or_else( - |err| -> Result { - error!( - "Failed to parse module from {}: {}", - file_path.display(), - SerdeError::new(file.to_owned(), err).to_string() - ); - - let module = - serde_yaml::from_str::(&file).map_err(|err| SerdeError::new(file, err))?; - Ok(ModuleExt::builder().modules(vec![module]).build()) - }, - Ok, - ) -} - -fn get_modules(modules: &[Module]) -> Vec { - modules - .iter() - .flat_map(|module| { - if let Some(file_name) = &module.from_file { - match get_module_from_file(file_name) { - Err(e) => { - error!("Failed to get module from {file_name}: {e}"); - vec![] - } - Ok(module_ext) => get_modules(&module_ext.modules), - } - } else { - vec![module.clone()] - } - }) - .collect() -} - -fn print_full_recipe(recipe: &Recipe) -> String { - let module_list: Vec = get_modules(&recipe.modules_ext.modules); - - let recipe = Recipe::builder() - .name(recipe.name.as_ref()) - .description(recipe.description.as_ref()) - .base_image(recipe.base_image.as_ref()) - .image_version(recipe.image_version.as_ref()) - .extra(recipe.extra.clone()) - .modules_ext(ModuleExt::builder().modules(module_list).build()) - .build(); - - serde_yaml::to_string(&recipe).unwrap_or_else(|e| { - error!("Failed to serialize recipe: {e}"); - format!("Error rendering recipe!!\n{e}") - }) -} // ============================================================================= // #[cfg(test)] diff --git a/src/commands/build.rs b/src/commands/build.rs index aaf70429..9ea8bb8a 100644 --- a/src/commands/build.rs +++ b/src/commands/build.rs @@ -9,7 +9,6 @@ use std::{ use anyhow::{anyhow, bail, Result}; use clap::Args; -use format_serde_error::SerdeError; use log::{debug, info, trace, warn}; use typed_builder::TypedBuilder; @@ -196,9 +195,7 @@ impl BuildCommand { bail!("Failed to get credentials"); } - let recipe_str = fs::read_to_string(recipe_path)?; - let recipe: Recipe = serde_yaml::from_str(&recipe_str) - .map_err(|err| SerdeError::new(recipe_str.to_owned(), err))?; + let recipe = Recipe::parse(&recipe_path)?; trace!("recipe: {recipe:#?}"); // Get values for image @@ -282,9 +279,7 @@ impl BuildCommand { fn build_image(&self, recipe_path: &Path) -> Result<()> { trace!("BuildCommand::build_image()"); - let recipe_str = fs::read_to_string(recipe_path)?; - let recipe: Recipe = serde_yaml::from_str(&recipe_str) - .map_err(|err| SerdeError::new(recipe_str.to_owned(), err))?; + let recipe = Recipe::parse(&recipe_path)?; let tags = recipe.generate_tags(); diff --git a/src/commands/local.rs b/src/commands/local.rs index cd91d897..767fb7cb 100644 --- a/src/commands/local.rs +++ b/src/commands/local.rs @@ -6,7 +6,6 @@ use std::{ use anyhow::{bail, Result}; use clap::Args; -use format_serde_error::SerdeError; use log::{debug, info, trace}; use typed_builder::TypedBuilder; use users::{Users, UsersCache}; @@ -44,9 +43,7 @@ impl BlueBuildCommand for UpgradeCommand { check_can_run()?; - let recipe_str = fs::read_to_string(&self.common.recipe)?; - let recipe: Recipe = serde_yaml::from_str(&recipe_str) - .map_err(|err| SerdeError::new(recipe_str.to_owned(), err))?; + let recipe = Recipe::parse(&self.common.recipe)?; let mut build = BuildCommand::builder() .recipe(self.common.recipe.clone()) @@ -93,9 +90,7 @@ impl BlueBuildCommand for RebaseCommand { check_can_run()?; - let recipe_str = fs::read_to_string(&self.common.recipe)?; - let recipe: Recipe = serde_yaml::from_str(&recipe_str) - .map_err(|err| SerdeError::new(recipe_str.to_owned(), err))?; + let recipe = Recipe::parse(&self.common.recipe)?; let mut build = BuildCommand::builder() .recipe(self.common.recipe.clone()) diff --git a/src/commands/template.rs b/src/commands/template.rs index 6a282383..7e02fe2b 100644 --- a/src/commands/template.rs +++ b/src/commands/template.rs @@ -7,13 +7,12 @@ use std::{ use anyhow::Result; use askama::Template; use clap::Args; -use format_serde_error::SerdeError; use log::{debug, error, info, trace}; use typed_builder::TypedBuilder; use crate::{ constants::{self}, - module_recipe::{Module, ModuleExt, Recipe}, + module_recipe::{Module, Recipe}, }; use super::BlueBuildCommand; @@ -24,18 +23,10 @@ pub struct ContainerFileTemplate<'a> { recipe: &'a Recipe<'a>, recipe_path: &'a Path, - module_template: ModuleTemplate<'a>, - #[builder(default)] export_script: ExportsTemplate, } -#[derive(Debug, Clone, Template, TypedBuilder)] -#[template(path = "Containerfile.module", escape = "none")] -pub struct ModuleTemplate<'a> { - module_ext: &'a ModuleExt, -} - #[derive(Debug, Clone, Default, Template)] #[template(path = "export.sh", escape = "none")] pub struct ExportsTemplate; @@ -83,11 +74,6 @@ impl TemplateCommand { let template = ContainerFileTemplate::builder() .recipe(&recipe_de) .recipe_path(&recipe_path) - .module_template( - ModuleTemplate::builder() - .module_ext(&recipe_de.modules_ext) - .build(), - ) .build(); let output_str = template.render()?; @@ -168,47 +154,6 @@ fn print_containerfile(containerfile: &str) -> String { file } -#[must_use] -fn template_module_from_file(file_name: &str) -> String { - debug!("get_module_from_file({file_name})"); - - let file_path = PathBuf::from("config").join(file_name); - let file = fs::read_to_string(file_path).unwrap_or_else(|e| { - error!("Failed to read 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::(file.as_str()).map_or_else( - |_| { - let module = serde_yaml::from_str::(file.as_str()).unwrap_or_else(|err| { - error!( - "Failed to deserialize module {file_name}: {}", - SerdeError::new(file_name.to_owned(), err) - ); - process::exit(1); - }); - - ModuleTemplate::builder() - .module_ext(&ModuleExt::builder().modules(vec![module]).build()) - .build() - .render() - .unwrap_or_else(template_err_fn) - }, - |module_ext| { - ModuleTemplate::builder() - .module_ext(&module_ext) - .build() - .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}"); diff --git a/src/module_recipe.rs b/src/module_recipe.rs index 7656ffb1..df6ed5e6 100644 --- a/src/module_recipe.rs +++ b/src/module_recipe.rs @@ -1,10 +1,16 @@ -use std::{borrow::Cow, collections::HashMap, env, fs, path::Path, process::Command}; +use std::{ + borrow::Cow, + collections::HashMap, + env, fs, + path::{Path, PathBuf}, + process::Command, +}; use anyhow::Result; use chrono::Local; use format_serde_error::SerdeError; use indexmap::IndexMap; -use log::{debug, info, trace, warn}; +use log::{debug, error, info, trace, warn}; use serde::{Deserialize, Serialize}; use serde_json::Value as JsonValue; use serde_yaml::Value; @@ -136,7 +142,12 @@ impl<'a> Recipe<'a> { debug!("Recipe contents: {file}"); - Ok(serde_yaml::from_str::(&file).map_err(|err| SerdeError::new(file, err))?) + let mut recipe = + serde_yaml::from_str::(&file).map_err(|err| SerdeError::new(file, err))?; + + recipe.modules_ext.modules = Module::get_modules(&recipe.modules_ext.modules); + + Ok(recipe) } fn get_os_version(&self) -> String { @@ -197,6 +208,34 @@ pub struct ModuleExt { pub modules: Vec, } +impl ModuleExt { + pub fn parse_module_from_file(file_name: &str) -> Result { + let file_path = PathBuf::from("config").join(file_name); + let file_path = if file_path.is_absolute() { + file_path + } else { + std::env::current_dir()?.join(file_path) + }; + + let file = fs::read_to_string(file_path.clone())?; + + serde_yaml::from_str::(&file).map_or_else( + |err| -> Result { + error!( + "Failed to parse module from {}: {}", + file_path.display(), + SerdeError::new(file.to_owned(), err).to_string() + ); + + let module = serde_yaml::from_str::(&file) + .map_err(|err| SerdeError::new(file, err))?; + Ok(ModuleExt::builder().modules(vec![module]).build()) + }, + Ok, + ) + } +} + #[derive(Serialize, Deserialize, Debug, Clone, TypedBuilder)] pub struct Module { #[builder(default, setter(into, strip_option))] @@ -212,6 +251,27 @@ pub struct Module { pub config: IndexMap, } +impl Module { + pub fn get_modules(modules: &[Self]) -> Vec { + modules + .iter() + .flat_map(|module| { + if let Some(file_name) = &module.from_file { + match ModuleExt::parse_module_from_file(file_name) { + Err(e) => { + error!("Failed to get module from {file_name}: {e}"); + vec![] + } + Ok(module_ext) => Self::get_modules(&module_ext.modules), + } + } else { + vec![module.clone()] + } + }) + .collect() + } +} + #[derive(Deserialize, Debug, Clone)] struct ImageInspection { #[serde(alias = "Labels")] diff --git a/templates/Containerfile b/templates/Containerfile index b2388514..8b0ed170 100644 --- a/templates/Containerfile +++ b/templates/Containerfile @@ -49,6 +49,25 @@ ARG CONFIG_DIRECTORY="/tmp/config" ARG IMAGE_NAME="{{ recipe.name }}" ARG BASE_IMAGE="{{ recipe.base_image }}" -{{ module_template }} +{%- for module in recipe.modules_ext.modules %} + {%- if let Some(type) = module.module_type %} + {%- if type == "containerfile" %} + {%- if let Some(containerfiles) = self::get_containerfile_list(module) %} + {%- for c in containerfiles %} +{{ self::print_containerfile(c) }} + {%- endfor %} + {%- endif %} + {%- else if type == "files" %} + {%- if let Some(files) = self::get_files_list(module) %} + {%- for (src, dest) in files %} +COPY {{ src }} {{ dest }} + {%- endfor %} + {%- endif %} + {%- else %} +RUN chmod +x /tmp/modules/{{ type }}/{{ type }}.sh && source /tmp/exports.sh && /tmp/modules/{{ type }}/{{ type }}.sh '{{ self::print_module_context(module) }}' + {%- endif %} + {%- endif %} +{%- endfor %} + RUN rm -rf /tmp/* /var/* && ostree container commit diff --git a/templates/Containerfile.module b/templates/Containerfile.module deleted file mode 100644 index 21d46e4d..00000000 --- a/templates/Containerfile.module +++ /dev/null @@ -1,22 +0,0 @@ -{%- for module in module_ext.modules %} - {%- if let Some(type) = module.module_type %} - {%- if type == "containerfile" %} - {%- if let Some(containerfiles) = self::get_containerfile_list(module) %} - {%- for c in containerfiles %} -{{ self::print_containerfile(c) }} - {%- endfor %} - {%- endif %} - {%- else if type == "files" %} - {%- if let Some(files) = self::get_files_list(module) %} - {%- for (src, dest) in files %} -COPY {{ src }} {{ dest }} - {%- endfor %} - {%- endif %} - {%- else %} -RUN chmod +x /tmp/modules/{{ type }}/{{ type }}.sh && source /tmp/exports.sh && /tmp/modules/{{ type }}/{{ type }}.sh '{{ self::print_module_context(module) }}' - {%- endif %} - {%- else if let Some(from_file) = module.from_file %} -{{ self::template_module_from_file(from_file) }} - {%- endif %} -{%- endfor %} -