Skip to content

Commit

Permalink
Use serde to print the entire file in the template
Browse files Browse the repository at this point in the history
  • Loading branch information
gmpinder committed Jan 30, 2024
1 parent 0583dcd commit f6d513d
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 92 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
.vscode/

# Local testing for bb recipe files
/config/
/config/
49 changes: 47 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ typed-builder = "0.18.1"
urlencoding = "2.1.3"
users = "0.11.0"
which = "6"
format_serde_error = "0.3.0"

[features]
default = []
Expand Down
82 changes: 69 additions & 13 deletions src/commands/bug_report.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use crate::module_recipe::Recipe;
use crate::module_recipe::{Module, ModuleExt, Recipe};
use crate::shadow;

use askama::Template;
use clap::Args;
use clap_complete::Shell;
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
use log::{debug, error, trace};
use requestty::question::{completions, Completions};
use std::borrow::Cow;
use std::path::PathBuf;
use std::time::Duration;
use std::{fs, process};
use typed_builder::TypedBuilder;

use super::utils::exec_cmd;
Expand All @@ -34,16 +37,13 @@ pub struct BugReportCommand {

impl BlueBuildCommand for BugReportCommand {
fn try_run(&mut self) -> anyhow::Result<()> {
log::debug!(
debug!(
"Generating bug report for hash: {}\n",
shadow::BB_COMMIT_HASH
);
log::debug!("Shadow Versioning:\n{}", shadow::VERSION.trim());
debug!("Shadow Versioning:\n{}", shadow::VERSION.trim());

BugReportCommand::builder()
.recipe_path(self.recipe_path.clone())
.build()
.create_bugreport()
self.create_bugreport()
}
}

Expand Down Expand Up @@ -90,7 +90,7 @@ impl BugReportCommand {
.color(Colors::BrightWhiteFg)
);

let warning_message = "Please copy the above report and open an issue manually.";
const WARNING_MESSAGE: &str = "Please copy the above report and open an issue manually.";
let question = requestty::Question::confirm("anonymous")
.message(
"Forward the pre-filled report above to GitHub in your browser?"
Expand All @@ -111,11 +111,11 @@ impl BugReportCommand {
return Err(e.into());
}
} else {
println!("{warning_message}");
println!("{WARNING_MESSAGE}");
}
}
Err(_) => {
println!("Will not open an issue in your browser! {warning_message}");
println!("Will not open an issue in your browser! {WARNING_MESSAGE}");
}
}

Expand All @@ -133,7 +133,7 @@ impl BugReportCommand {
} else if let Ok(recipe) = get_config_file("recipe", "Enter path to recipe file") {
recipe
} else {
log::trace!("Failed to get recipe");
trace!("Failed to get recipe");
String::new()
};

Expand Down Expand Up @@ -162,7 +162,7 @@ fn get_config_file(title: &str, message: &str) -> anyhow::Result<String> {
Ok(requestty::Answer::String(path)) => Ok(path),
Ok(_) => unreachable!(),
Err(e) => {
log::trace!("Failed to get file: {}", e);
trace!("Failed to get file: {}", e);
Err(e.into())
}
}
Expand Down Expand Up @@ -345,7 +345,7 @@ fn generate_github_issue(
) -> anyhow::Result<String> {
let recipe = recipe
.as_ref()
.map_or_else(String::new, |recipe| recipe.render().unwrap_or_default());
.map_or_else(|| "No recipe provided".into(), |r| print_full_recipe(r));

let github_template = GithubIssueTemplate::builder()
.bb_version(shadow::PKG_VERSION)
Expand Down Expand Up @@ -380,6 +380,62 @@ fn make_github_issue_link(body: &str) -> String {
.collect()
}

fn get_module_from_file(file_name: &str) -> ModuleExt {
let file_path = PathBuf::from("config").join(file_name);
let file_path = if file_path.is_absolute() {
file_path
} else {
std::env::current_dir()
.unwrap_or_else(|e| {
error!("Failed to get current directory: {e}");
process::exit(1);
})
.join(file_path)
};

let file = fs::read_to_string(file_path.clone()).unwrap_or_else(|e| {
error!("Failed to read module {}: {e}", file_path.display());
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(|e| {
error!("Failed to deserialize module {file_name}: {e}");
process::exit(1);
});

ModuleExt::builder().modules(vec![module]).build()
},
|module_ext| module_ext,
)
}

fn print_full_recipe(recipe: &Recipe) -> String {
let mut module_list: Vec<Module> = vec![];

recipe.modules_ext.modules.iter().for_each(|module| {
if let Some(file_name) = &module.from_file {
module_list.extend(get_module_from_file(file_name).modules);
} else {
module_list.push(module.clone());
}
});

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}");
"Error rendering recipe!!".into()
})
}
// ============================================================================= //

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion src/commands/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use super::BlueBuildCommand;
#[derive(Debug, Clone, Template, TypedBuilder)]
#[template(path = "Containerfile")]
pub struct ContainerFileTemplate<'a> {
recipe: &'a Recipe,
recipe: &'a Recipe<'a>,
recipe_path: &'a Path,

module_template: ModuleTemplate<'a>,
Expand Down
65 changes: 10 additions & 55 deletions src/module_recipe.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,31 @@
use std::{
env, fs,
path::{Path, PathBuf},
process, vec,
};
use std::{borrow::Cow, env, fs, path::Path, process};

use askama::Template;
use chrono::Local;
use indexmap::IndexMap;
use log::{debug, error, trace, warn};
use serde::{Deserialize, Serialize};
use serde_yaml::Value;
use typed_builder::TypedBuilder;

#[derive(Default, Serialize, Clone, Deserialize, Debug, TypedBuilder, Template)]
#[template(path = "recipe.j2", escape = "none", whitespace = "suppress")]
pub struct Recipe {
#[derive(Default, Serialize, Clone, Deserialize, Debug, TypedBuilder)]
pub struct Recipe<'a> {
#[builder(setter(into))]
pub name: String,
pub name: Cow<'a, str>,

#[builder(setter(into))]
pub description: String,
pub description: Cow<'a, str>,

#[serde(alias = "base-image")]
#[builder(setter(into))]
pub base_image: String,
pub base_image: Cow<'a, str>,

#[serde(alias = "image-version")]
#[builder(setter(into))]
pub image_version: String,
pub image_version: Cow<'a, str>,

#[serde(alias = "blue-build-tag")]
#[builder(default, setter(into, strip_option))]
pub blue_build_tag: Option<String>,
pub blue_build_tag: Option<Cow<'a, str>>,

#[serde(flatten)]
pub modules_ext: ModuleExt,
Expand All @@ -41,7 +35,7 @@ pub struct Recipe {
pub extra: IndexMap<String, Value>,
}

impl Recipe {
impl<'a> Recipe<'a> {
#[must_use]
pub fn generate_tags(&self) -> Vec<String> {
trace!("Recipe::generate_tags()");
Expand Down Expand Up @@ -100,7 +94,7 @@ impl Recipe {
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(image_version.to_string());
tags.push(format!("{image_version}-{timestamp}"));
tags.push("latest".to_string());
} else {
Expand All @@ -118,8 +112,6 @@ impl Recipe {

/// # Parse a recipe file
/// #
/// # Panics
/// #
/// # Errors
pub fn parse<P: AsRef<Path>>(path: &P) -> anyhow::Result<Self> {
let file_path = if Path::new(path.as_ref()).is_absolute() {
Expand Down Expand Up @@ -166,40 +158,3 @@ pub struct Module {
#[builder(default, setter(into))]
pub config: IndexMap<String, Value>,
}

fn get_module_from_file(file_name: &str) -> ModuleExt {
let file_path = PathBuf::from("config").join(file_name);
let file_path = if file_path.is_absolute() {
file_path
} else {
std::env::current_dir().unwrap().join(file_path)
};

let file = fs::read_to_string(file_path.clone()).unwrap_or_else(|e| {
error!("Failed to read module {}: {e}", file_path.display());
String::default()
});

serde_yaml::from_str::<ModuleExt>(file.as_str()).map_or_else(
|_| {
let module = serde_yaml::from_str::<Module>(file.as_str()).unwrap_or_else(|e| {
error!("Failed to deserialize module {file_name}: {e}");
process::exit(1);
});

ModuleExt::builder().modules(vec![module]).build()
},
|module_ext| module_ext,
)
}

// Any filter defined in the module `filters` is accessible in your template.
mod filters {
/// Strip leading and trailing whitespace
pub fn trim_end<T: std::fmt::Display>(s: T) -> ::askama::Result<String> {
let s = s.to_string();
println!("Trimming: {s}");
println!("Trimmed: {}", s.trim_end());
Ok(s.trim_end().to_owned())
}
}
Loading

0 comments on commit f6d513d

Please sign in to comment.