mod ci;
mod cli;
mod docs;
mod execution;
mod model_injection;
pub mod models;
mod output;
mod utils;
use crate::cli::{cli_builder, construct_cli};
use crate::execution::{execute_stages, execute_tasks};
use crate::model_injection::{
inject_pipeline_metadata, inject_task_metadata, inject_template_values,
};
use crate::models::{AllResults, Validate};
use crate::models::{PassFail, TaskResult};
use std::collections::HashMap;
use std::error::Error;
type RoxResult<T> = Result<T, Box<dyn Error>>;
fn get_filepath_arg_value() -> String {
let cli = cli_builder(false);
let cli_matches = cli.clone().arg_required_else_help(false).get_matches();
cli_matches.get_one::<String>("roxfile").unwrap().to_owned()
}
pub async fn rox() -> RoxResult<()> {
let start = std::time::Instant::now();
let execution_start = chrono::Utc::now().to_rfc3339();
let file_path = get_filepath_arg_value();
let roxfile = utils::parse_file_contents(utils::load_file(&file_path));
roxfile.validate()?;
utils::print_horizontal_rule();
let tasks = inject_task_metadata(roxfile.tasks, &file_path);
let pipelines = inject_pipeline_metadata(roxfile.pipelines);
let docs = roxfile.docs;
let ci = roxfile.ci;
let cli = construct_cli(&tasks, &pipelines, &docs, &ci);
let cli_matches = cli.get_matches();
let template_map: HashMap<String, models::Template> = std::collections::HashMap::from_iter(
roxfile
.templates
.into_iter()
.flatten()
.map(|template| (template.name.to_owned(), template)),
);
let task_map: HashMap<String, models::Task> = std::collections::HashMap::from_iter(
tasks
.into_iter()
.map(|task| match task.uses.to_owned() {
Some(task_use) => {
inject_template_values(task, template_map.get(&task_use).unwrap())
}
None => task,
})
.map(|task| (task.name.to_owned(), task)),
);
let (_, args) = cli_matches.subcommand().unwrap();
let subcommand_name = args.subcommand_name().unwrap_or("default");
let results: Vec<Vec<TaskResult>> = match cli_matches.subcommand_name().unwrap() {
"docs" => {
let docs_map: HashMap<String, models::Docs> = std::collections::HashMap::from_iter(
docs.into_iter()
.flatten()
.map(|doc| (doc.name.to_owned(), doc)),
);
docs::display_docs(docs_map.get(subcommand_name).unwrap());
std::process::exit(0);
}
"logs" => {
let number = args.get_one::<i8>("number").unwrap();
output::display_logs(number);
std::process::exit(0);
}
"ci" => {
assert!(ci.is_some());
ci::display_ci_status(ci.unwrap()).await;
std::process::exit(0);
}
"pl" => {
let pipeline_map: HashMap<String, models::Pipeline> =
std::collections::HashMap::from_iter(
pipelines
.into_iter()
.flatten()
.map(|pipeline| (pipeline.name.to_owned(), pipeline)),
);
let parallel = args.get_flag("parallel");
let execution_results = execute_stages(
&pipeline_map.get(subcommand_name).unwrap().stages,
&task_map,
parallel,
);
execution_results
}
"task" => {
let execution_results = vec![execute_tasks(
vec![subcommand_name.to_string()],
0,
&task_map,
false,
)];
execution_results
}
_ => unreachable!("Invalid subcommand"),
};
let results = AllResults {
job_name: subcommand_name.to_string(),
execution_time: execution_start,
results: results.into_iter().flatten().collect(),
};
let log_path = output::write_logs(&results);
println!("> Log file written to: {}", log_path);
output::display_execution_results(&results);
println!(
"> Total elapsed time: {}s | {}ms",
start.elapsed().as_secs(),
start.elapsed().as_millis(),
);
nonzero_exit_if_failure(&results);
Ok(())
}
pub fn nonzero_exit_if_failure(results: &AllResults) {
for result in results.results.iter() {
if result.result == PassFail::Fail {
std::process::exit(2)
}
}
}