From 08028c2e0cca92d9b7f1dbe4181b74211e1b84df Mon Sep 17 00:00:00 2001 From: Elad Kaplan Date: Sun, 13 Oct 2024 13:56:00 +0300 Subject: [PATCH 1/8] support new template and scaffold --- xtask/Cargo.toml | 3 + xtask/src/bin/main.rs | 46 +++++++- xtask/src/fuzzy_steps/generate_project.rs | 121 +++++++++++++++++++++ xtask/src/fuzzy_steps/mod.rs | 2 + xtask/src/fuzzy_steps/scaffold.rs | 126 ++++++++++++++++++++++ xtask/src/lib.rs | 1 + 6 files changed, 297 insertions(+), 2 deletions(-) create mode 100644 xtask/src/fuzzy_steps/generate_project.rs create mode 100644 xtask/src/fuzzy_steps/mod.rs create mode 100644 xtask/src/fuzzy_steps/scaffold.rs diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index b59f1859c..d3bbd0575 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -14,6 +14,8 @@ path = "src/bin/main.rs" required-features = [] [dependencies] +serde = "1.0.210" +serde_yaml = "0.9" clap = { version = "4.4.7", features = ["derive"] } eyre = "0.6" duct = "0.13.6" @@ -24,5 +26,6 @@ lazy_static = "1.4.0" thiserror = "1" tabled = "0.14.0" colored = "2.1.0" +crazy-train = "0.1.0" [dev-dependencies] diff --git a/xtask/src/bin/main.rs b/xtask/src/bin/main.rs index 2925e61b3..3146c68b4 100644 --- a/xtask/src/bin/main.rs +++ b/xtask/src/bin/main.rs @@ -1,10 +1,10 @@ -use std::env; - use cargo_metadata::{semver::Version, MetadataCommand, Package}; use clap::{ ArgAction::{SetFalse, SetTrue}, Parser, Subcommand, }; +use std::env; +use xtask::fuzzy_steps; #[derive(Parser)] #[command(author, version, about, long_about = None)] @@ -29,6 +29,18 @@ enum Commands { #[arg(short, long, action = SetFalse)] exclude_starters: bool, }, + Fuzzy { + #[arg(short, long, value_parser = clap::value_parser!(u64))] + seed: Option, + #[command(subcommand)] + command: FuzzyCommands, + }, +} + +#[derive(Subcommand)] +enum FuzzyCommands { + GenerateTemplate, + Scaffold, } fn main() -> eyre::Result<()> { @@ -69,6 +81,36 @@ fn main() -> eyre::Result<()> { } xtask::CmdExit::ok() } + Commands::Fuzzy { command, seed } => { + let randomizer = seed.map_or_else(crazy_train::Randomizer::default, |seed| { + crazy_train::Randomizer::with_seed(seed) + }); + + let temp_dir = env::temp_dir().join("loco"); + + let runner = match command { + FuzzyCommands::GenerateTemplate => { + fuzzy_steps::generate_project::run(randomizer, temp_dir.as_path()) + } + FuzzyCommands::Scaffold => { + fuzzy_steps::scaffold::run(randomizer, temp_dir.as_path()) + } + }; + + let result = runner.run(); + + if temp_dir.exists() { + std::fs::remove_dir_all(temp_dir).expect("remove dir"); + } + + if let Err(err) = &result { + println!("{err}"); + + xtask::CmdExit::error_with_message("step failed") + } else { + xtask::CmdExit::ok() + } + } }; res.exit(); diff --git a/xtask/src/fuzzy_steps/generate_project.rs b/xtask/src/fuzzy_steps/generate_project.rs new file mode 100644 index 000000000..5620dc00d --- /dev/null +++ b/xtask/src/fuzzy_steps/generate_project.rs @@ -0,0 +1,121 @@ +use crazy_train::{executer, step, Randomizer, StringDef}; +use regex::Regex; +use serde::{Deserialize, Serialize}; +use std::path::{Path, PathBuf}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct GenerateProjectStep { + pub location: PathBuf, + pub project_name: String, + pub run_check: bool, + pub run_test: bool, +} + +impl GenerateProjectStep { + pub fn new( + randomizer: &Randomizer, + root_dir: &Path, + project_name: Option<&str>, + run_check: bool, + run_test: bool, + ) -> Self { + let project_name = project_name.map_or_else( + || { + randomizer + .string(StringDef::from_randomizer(randomizer)) + .to_string() + }, + std::string::ToString::to_string, + ); + + Self { + location: root_dir.join(randomizer.path()), + project_name, + run_check, + run_test, + } + } +} + +impl step::StepTrait for GenerateProjectStep { + fn setup(&self) -> crazy_train::Result<()> { + Ok(std::fs::create_dir_all(&self.location)?) + } + + fn plan(&self, _randomizer: &Randomizer) -> crazy_train::Result { + let command = format!("loco new --name '{}' --template saas --db sqlite --bg async --assets serverside --path {}", self.project_name,self.location.display()); + + Ok(step::Plan { + id: std::any::type_name::().to_string(), + command, + }) + } + + fn is_success( + &self, + execution_result: &executer::Output, + ) -> std::result::Result { + let re_invalid_project_name = Regex::new(r"Error: app name is invalid, illegal characters. keep names simple: myapp or my_app\n\nLocation:\n").unwrap(); + let re_folder_exists = Regex::new(r"šŸ™€ The specified path '.*.' already exist\n").unwrap(); + let re_successfully = Regex::new(r"\nšŸš‚ Loco app generated successfully in:\n.*").unwrap(); + + if StringDef::contains_unicode(&self.project_name) + || self + .project_name + .chars() + .any(|c| StringDef::contains_symbols(&format!("{c}")) && c != '_') + { + if execution_result.status_code != Some(1) { + return Err("expected status code 1"); + } else if !re_invalid_project_name.is_match(&execution_result.stderr) { + return Err("stderr not match to the error pattern"); + } else if !execution_result.stdout.is_empty() { + return Err("stdout should be empty"); + } + Ok(false) + } else if re_folder_exists.is_match(&execution_result.stderr) { + if execution_result.status_code != Some(1) { + return Err("when folder exists expected to get exit code 1"); + } + Ok(false) + } else if execution_result.status_code == Some(0) { + if re_successfully.is_match(&execution_result.stderr) { + Ok(true) + } else { + return Err("command success with unexpected stderr"); + } + } else { + Err("error not handled") + } + } + + fn run_check(&self) -> Option { + if self.run_check { + Some(format!( + "cd {} && cargo check", + self.location.join(&self.project_name).display() + )) + } else { + None + } + } + + fn run_test(&self) -> Option { + if self.run_check { + Some(format!( + "cd {} && cargo test", + self.location.join(&self.project_name).display() + )) + } else { + None + } + } + fn to_yaml(&self) -> serde_yaml::Value { + serde_yaml::to_value(self).expect("to yaml") + } +} + +pub fn run(randomizer: Randomizer, temp_dir: &Path) -> crazy_train::Runner { + let step = GenerateProjectStep::new(&randomizer, temp_dir, None, true, true); + crazy_train::new(vec![Box::new(step)]).randomizer(randomizer) +} diff --git a/xtask/src/fuzzy_steps/mod.rs b/xtask/src/fuzzy_steps/mod.rs new file mode 100644 index 000000000..15811ae58 --- /dev/null +++ b/xtask/src/fuzzy_steps/mod.rs @@ -0,0 +1,2 @@ +pub mod generate_project; +pub mod scaffold; diff --git a/xtask/src/fuzzy_steps/scaffold.rs b/xtask/src/fuzzy_steps/scaffold.rs new file mode 100644 index 000000000..d582d0fce --- /dev/null +++ b/xtask/src/fuzzy_steps/scaffold.rs @@ -0,0 +1,126 @@ +use super::generate_project::GenerateProjectStep; +use crazy_train::{executer, step, Randomizer, Result, StringDef}; +use serde::{Deserialize, Serialize}; +use std::path::{Path, PathBuf}; + +const SCAFFOLD_MAPPING: &str = include_str!("../../../src/gen/mappings.json"); + +#[derive(Serialize, Deserialize, Debug)] +struct FieldType { + name: String, +} + +#[derive(Serialize, Deserialize, Debug)] +struct Mappings { + field_types: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +struct ScaffoldStep { + pub template_location: PathBuf, + pub fields: Vec, + pub rand_table_name: bool, + pub rand_fields_name: bool, +} + +impl ScaffoldStep { + fn new(template_location: &Path, rand_table_name: bool, rand_fields_name: bool) -> Self { + let field_mapping: Mappings = serde_yaml::from_str(SCAFFOLD_MAPPING).expect("mapping"); + + let fields = field_mapping + .field_types + .iter() + .map(|t| t.name.clone()) + .collect::>(); + + Self { + template_location: template_location.to_path_buf(), + fields, + rand_fields_name, + rand_table_name, + } + } +} + +impl step::StepTrait for ScaffoldStep { + fn plan(&self, randomizer: &Randomizer) -> Result { + let table_name = if self.rand_table_name { + randomizer.string(StringDef::from_randomizer(randomizer)) + } else { + randomizer.string(StringDef::default()) + } + .to_string(); + + let shuffled_fields = randomizer.shuffle(&self.fields); + let random_fields = randomizer.pick_random(&shuffled_fields); + + let fields = random_fields + .iter() + .map(|kind| { + format!( + "'{}:{kind}'", + if self.rand_fields_name { + randomizer.string(StringDef::from_randomizer(randomizer)) + } else { + randomizer.string(StringDef::default()) + } + ) + }) + .collect::>(); + + let command = format!( + "cd {} && cargo loco generate scaffold {} {} --api", + self.template_location.display(), + table_name, + fields.join(" ") + ); + Ok(step::Plan { + id: std::any::type_name::().to_string(), + command, + }) + } + + fn is_success( + &self, + execution_result: &executer::Output, + ) -> std::result::Result { + if execution_result.status_code == Some(1) { + Ok(false) + } else { + Ok(true) + } + } + + fn run_check(&self) -> Option { + Some(format!( + "cd {} && cargo check", + self.template_location.display() + )) + } + + fn run_test(&self) -> Option { + Some(format!( + "cd {} && cargo test", + self.template_location.display() + )) + } + + fn to_yaml(&self) -> serde_yaml::Value { + serde_yaml::to_value(self).expect("to yaml") + } +} + +pub fn run(randomizer: Randomizer, temp_dir: &Path) -> crazy_train::Runner { + let template_step = + GenerateProjectStep::new(&randomizer, temp_dir, Some("test_scaffold"), false, false); + let scaffold_step = ScaffoldStep::new( + template_step + .location + .join(&template_step.project_name) + .as_path(), + true, + true, + ); + + crazy_train::new(vec![Box::new(template_step), Box::new(scaffold_step)]).randomizer(randomizer) +} diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 3dc8ab414..84ad09072 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -2,6 +2,7 @@ use std::process::exit; pub mod bump_version; pub mod ci; pub mod errors; +pub mod fuzzy_steps; pub mod out; pub mod prompt; pub mod utils; From e5adbc52951d5afe8a5baacbec966a45a98a0caf Mon Sep 17 00:00:00 2001 From: Elad Kaplan Date: Tue, 15 Oct 2024 09:51:06 +0300 Subject: [PATCH 2/8] add to ci --- .github/workflows/crazt-train.yml | 27 +++++++++++ xtask/src/bin/main.rs | 77 +++++++++++++++++++++++-------- 2 files changed, 84 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/crazt-train.yml diff --git a/.github/workflows/crazt-train.yml b/.github/workflows/crazt-train.yml new file mode 100644 index 000000000..c23d885f0 --- /dev/null +++ b/.github/workflows/crazt-train.yml @@ -0,0 +1,27 @@ +name: Crazy Train - Fuzzy loco cli generator + +on: + pull_request: + +# on: +# schedule: +# - cron: 0 * * * * + +env: + RUST_TOOLCHAIN: stable + TOOLCHAIN_PROFILE: minimal + +jobs: + generate_template: + name: Generate Template + runs-on: ubuntu-latest + + steps: + - name: Checkout the code + uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.RUST_TOOLCHAIN }} + - run: | + cargo xtask fuzzy generate-template --times 100 + \ No newline at end of file diff --git a/xtask/src/bin/main.rs b/xtask/src/bin/main.rs index 3146c68b4..3c2725afd 100644 --- a/xtask/src/bin/main.rs +++ b/xtask/src/bin/main.rs @@ -3,6 +3,7 @@ use clap::{ ArgAction::{SetFalse, SetTrue}, Parser, Subcommand, }; +use colored::Colorize; use std::env; use xtask::fuzzy_steps; @@ -30,6 +31,8 @@ enum Commands { exclude_starters: bool, }, Fuzzy { + #[arg(global = true, short, long, default_value_t = 1)] + times: u64, #[arg(short, long, value_parser = clap::value_parser!(u64))] seed: Option, #[command(subcommand)] @@ -43,6 +46,10 @@ enum FuzzyCommands { Scaffold, } +struct FuzzyResult { + seed: u64, + error: Option, +} fn main() -> eyre::Result<()> { let cli = Cli::parse(); let project_dir = env::current_dir()?; @@ -81,32 +88,62 @@ fn main() -> eyre::Result<()> { } xtask::CmdExit::ok() } - Commands::Fuzzy { command, seed } => { - let randomizer = seed.map_or_else(crazy_train::Randomizer::default, |seed| { - crazy_train::Randomizer::with_seed(seed) - }); + Commands::Fuzzy { + command, + seed, + times, + } => { + let mut results: Vec = (1..=times) + .map(|_| { + let randomizer = seed.map_or_else(crazy_train::Randomizer::default, |seed| { + crazy_train::Randomizer::with_seed(seed) + }); + let seed = randomizer.seed; + let temp_dir = env::temp_dir().join("loco"); - let temp_dir = env::temp_dir().join("loco"); + let runner = match command { + FuzzyCommands::GenerateTemplate => { + fuzzy_steps::generate_project::run(randomizer, temp_dir.as_path()) + } + FuzzyCommands::Scaffold => { + fuzzy_steps::scaffold::run(randomizer, temp_dir.as_path()) + } + }; - let runner = match command { - FuzzyCommands::GenerateTemplate => { - fuzzy_steps::generate_project::run(randomizer, temp_dir.as_path()) - } - FuzzyCommands::Scaffold => { - fuzzy_steps::scaffold::run(randomizer, temp_dir.as_path()) - } - }; + let result: Result<(), crazy_train::Error> = runner.run(); - let result = runner.run(); + if temp_dir.exists() { + std::fs::remove_dir_all(temp_dir).expect("remove dir"); + } + FuzzyResult { + seed, + error: result.err(), + } + }) + .collect(); - if temp_dir.exists() { - std::fs::remove_dir_all(temp_dir).expect("remove dir"); - } + results.sort_by(|a, b| a.error.is_some().cmp(&b.error.is_some())); + let mut has_error = false; + + println!(); + println!("===================================="); + println!(" Results Summary "); + println!("===================================="); - if let Err(err) = &result { - println!("{err}"); + for result in results { + if let Some(err) = result.error { + has_error = true; + println!( + "{}", + format!("seed {}: error\n\n {}\n", result.seed, err).red() + ); + } else { + println!("{}", format!("seed {}: passed", result.seed).green()); + } + } - xtask::CmdExit::error_with_message("step failed") + if has_error { + xtask::CmdExit::error_with_message("failed") } else { xtask::CmdExit::ok() } From acbf18015160d93f1ac1b55a03a5f390e06a28ad Mon Sep 17 00:00:00 2001 From: Elad Kaplan Date: Tue, 15 Oct 2024 10:13:31 +0300 Subject: [PATCH 3/8] change times --- .github/workflows/crazt-train.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/crazt-train.yml b/.github/workflows/crazt-train.yml index c23d885f0..6aaa082df 100644 --- a/.github/workflows/crazt-train.yml +++ b/.github/workflows/crazt-train.yml @@ -23,5 +23,5 @@ jobs: with: toolchain: ${{ env.RUST_TOOLCHAIN }} - run: | - cargo xtask fuzzy generate-template --times 100 + cargo xtask fuzzy generate-template --times 10 \ No newline at end of file From 2cbc84a7d5c0a5b15544f3eb4a0300069fb9cc15 Mon Sep 17 00:00:00 2001 From: Elad Kaplan Date: Tue, 15 Oct 2024 10:21:49 +0300 Subject: [PATCH 4/8] install loco cli --- .github/workflows/crazt-train.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/crazt-train.yml b/.github/workflows/crazt-train.yml index 6aaa082df..1926cfd3f 100644 --- a/.github/workflows/crazt-train.yml +++ b/.github/workflows/crazt-train.yml @@ -23,5 +23,6 @@ jobs: with: toolchain: ${{ env.RUST_TOOLCHAIN }} - run: | + cargo install loco-cli cargo xtask fuzzy generate-template --times 10 \ No newline at end of file From f3b638a33095a074b54984da18c14bd178d33774 Mon Sep 17 00:00:00 2001 From: Elad Kaplan Date: Tue, 15 Oct 2024 10:36:58 +0300 Subject: [PATCH 5/8] random template and asset --- .github/workflows/crazt-train.yml | 7 ++-- xtask/src/fuzzy_steps/generate_project.rs | 42 +++++++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/.github/workflows/crazt-train.yml b/.github/workflows/crazt-train.yml index 1926cfd3f..121d64bae 100644 --- a/.github/workflows/crazt-train.yml +++ b/.github/workflows/crazt-train.yml @@ -22,7 +22,10 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ env.RUST_TOOLCHAIN }} - - run: | + - name: Install loco-cli + run: | cargo install loco-cli - cargo xtask fuzzy generate-template --times 10 + - name: generate loco template + run: | + cargo xtask fuzzy generate-template --times 100 \ No newline at end of file diff --git a/xtask/src/fuzzy_steps/generate_project.rs b/xtask/src/fuzzy_steps/generate_project.rs index 5620dc00d..402ddd08a 100644 --- a/xtask/src/fuzzy_steps/generate_project.rs +++ b/xtask/src/fuzzy_steps/generate_project.rs @@ -9,6 +9,8 @@ pub struct GenerateProjectStep { pub project_name: String, pub run_check: bool, pub run_test: bool, + pub templates: Vec, + pub assets: Vec, } impl GenerateProjectStep { @@ -33,6 +35,12 @@ impl GenerateProjectStep { project_name, run_check, run_test, + templates: vec![ + "saas".to_string(), + "rest-api".to_string(), + "lightweight-service".to_string(), + ], + assets: vec!["serverside".to_string(), "clientside".to_string()], } } } @@ -42,8 +50,38 @@ impl step::StepTrait for GenerateProjectStep { Ok(std::fs::create_dir_all(&self.location)?) } - fn plan(&self, _randomizer: &Randomizer) -> crazy_train::Result { - let command = format!("loco new --name '{}' --template saas --db sqlite --bg async --assets serverside --path {}", self.project_name,self.location.display()); + fn plan(&self, randomizer: &Randomizer) -> crazy_train::Result { + let template = if randomizer.bool() { + randomizer + .string(StringDef::from_randomizer(randomizer)) + .to_string() + } else { + randomizer + .shuffle(&self.templates) + .first() + .expect("template") + .to_string() + }; + + let asset = if randomizer.bool() { + randomizer + .string(StringDef::from_randomizer(randomizer)) + .to_string() + } else { + randomizer + .shuffle(&self.assets) + .first() + .expect("asset") + .to_string() + }; + + let command = format!( + "loco new --name '{}' --template '{}' --db sqlite --bg async --assets '{}' --path {}", + self.project_name, + template, + asset, + self.location.display() + ); Ok(step::Plan { id: std::any::type_name::().to_string(), From b2b1d3aa4ba32926fa4935c934058374f62f0a56 Mon Sep 17 00:00:00 2001 From: Elad Kaplan Date: Tue, 15 Oct 2024 10:44:26 +0300 Subject: [PATCH 6/8] move seed flag to global --- xtask/src/bin/main.rs | 2 +- xtask/src/fuzzy_steps/generate_project.rs | 43 ++--------------------- 2 files changed, 4 insertions(+), 41 deletions(-) diff --git a/xtask/src/bin/main.rs b/xtask/src/bin/main.rs index 3c2725afd..57ecd5c35 100644 --- a/xtask/src/bin/main.rs +++ b/xtask/src/bin/main.rs @@ -33,7 +33,7 @@ enum Commands { Fuzzy { #[arg(global = true, short, long, default_value_t = 1)] times: u64, - #[arg(short, long, value_parser = clap::value_parser!(u64))] + #[arg(global = true, short, long, value_parser = clap::value_parser!(u64))] seed: Option, #[command(subcommand)] command: FuzzyCommands, diff --git a/xtask/src/fuzzy_steps/generate_project.rs b/xtask/src/fuzzy_steps/generate_project.rs index 402ddd08a..571ef63ca 100644 --- a/xtask/src/fuzzy_steps/generate_project.rs +++ b/xtask/src/fuzzy_steps/generate_project.rs @@ -9,8 +9,6 @@ pub struct GenerateProjectStep { pub project_name: String, pub run_check: bool, pub run_test: bool, - pub templates: Vec, - pub assets: Vec, } impl GenerateProjectStep { @@ -35,12 +33,6 @@ impl GenerateProjectStep { project_name, run_check, run_test, - templates: vec![ - "saas".to_string(), - "rest-api".to_string(), - "lightweight-service".to_string(), - ], - assets: vec!["serverside".to_string(), "clientside".to_string()], } } } @@ -50,38 +42,9 @@ impl step::StepTrait for GenerateProjectStep { Ok(std::fs::create_dir_all(&self.location)?) } - fn plan(&self, randomizer: &Randomizer) -> crazy_train::Result { - let template = if randomizer.bool() { - randomizer - .string(StringDef::from_randomizer(randomizer)) - .to_string() - } else { - randomizer - .shuffle(&self.templates) - .first() - .expect("template") - .to_string() - }; - - let asset = if randomizer.bool() { - randomizer - .string(StringDef::from_randomizer(randomizer)) - .to_string() - } else { - randomizer - .shuffle(&self.assets) - .first() - .expect("asset") - .to_string() - }; - - let command = format!( - "loco new --name '{}' --template '{}' --db sqlite --bg async --assets '{}' --path {}", - self.project_name, - template, - asset, - self.location.display() - ); + fn plan(&self, _randomizer: &Randomizer) -> crazy_train::Result { + // TODO:: --template and --assets should be random also + let command = format!("loco new --name '{}' --template saas --db sqlite --bg async --assets serverside --path {}", self.project_name,self.location.display()); Ok(step::Plan { id: std::any::type_name::().to_string(), From 9d7eb8bb26b5b04a96ffe25555da992c9dae04c8 Mon Sep 17 00:00:00 2001 From: Elad Kaplan Date: Tue, 15 Oct 2024 13:54:15 +0300 Subject: [PATCH 7/8] fails on the first error + escape shell command --- xtask/Cargo.toml | 1 + xtask/src/bin/main.rs | 17 ++++------------- xtask/src/fuzzy_steps/generate_project.rs | 4 +++- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index d3bbd0575..59f5e7eca 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -27,5 +27,6 @@ thiserror = "1" tabled = "0.14.0" colored = "2.1.0" crazy-train = "0.1.0" +shell-escape = "0.1.5" [dev-dependencies] diff --git a/xtask/src/bin/main.rs b/xtask/src/bin/main.rs index 57ecd5c35..749b43956 100644 --- a/xtask/src/bin/main.rs +++ b/xtask/src/bin/main.rs @@ -3,7 +3,6 @@ use clap::{ ArgAction::{SetFalse, SetTrue}, Parser, Subcommand, }; -use colored::Colorize; use std::env; use xtask::fuzzy_steps; @@ -123,7 +122,6 @@ fn main() -> eyre::Result<()> { .collect(); results.sort_by(|a, b| a.error.is_some().cmp(&b.error.is_some())); - let mut has_error = false; println!(); println!("===================================="); @@ -132,21 +130,14 @@ fn main() -> eyre::Result<()> { for result in results { if let Some(err) = result.error { - has_error = true; - println!( - "{}", - format!("seed {}: error\n\n {}\n", result.seed, err).red() - ); + println!("seed {}: error\n\n {}\n", result.seed, err); + xtask::CmdExit::error_with_message("failed").exit(); } else { - println!("{}", format!("seed {}: passed", result.seed).green()); + println!("seed {}: passed", result.seed); } } - if has_error { - xtask::CmdExit::error_with_message("failed") - } else { - xtask::CmdExit::ok() - } + xtask::CmdExit::ok() } }; diff --git a/xtask/src/fuzzy_steps/generate_project.rs b/xtask/src/fuzzy_steps/generate_project.rs index 571ef63ca..67f3ad588 100644 --- a/xtask/src/fuzzy_steps/generate_project.rs +++ b/xtask/src/fuzzy_steps/generate_project.rs @@ -44,7 +44,9 @@ impl step::StepTrait for GenerateProjectStep { fn plan(&self, _randomizer: &Randomizer) -> crazy_train::Result { // TODO:: --template and --assets should be random also - let command = format!("loco new --name '{}' --template saas --db sqlite --bg async --assets serverside --path {}", self.project_name,self.location.display()); + let escaped_project_name = + shell_escape::escape(self.project_name.clone().into()).to_string(); + let command = format!("loco new --name {} --template saas --db sqlite --bg async --assets serverside --path {}", escaped_project_name,self.location.display()); Ok(step::Plan { id: std::any::type_name::().to_string(), From c31a5bec8feae2514c2dbe184ee0597ef407286c Mon Sep 17 00:00:00 2001 From: Elad Kaplan Date: Tue, 15 Oct 2024 14:49:00 +0300 Subject: [PATCH 8/8] stop after one --- xtask/src/bin/main.rs | 59 ++++++++--------------- xtask/src/fuzzy_steps/generate_project.rs | 5 +- 2 files changed, 24 insertions(+), 40 deletions(-) diff --git a/xtask/src/bin/main.rs b/xtask/src/bin/main.rs index 749b43956..40191a490 100644 --- a/xtask/src/bin/main.rs +++ b/xtask/src/bin/main.rs @@ -45,10 +45,6 @@ enum FuzzyCommands { Scaffold, } -struct FuzzyResult { - seed: u64, - error: Option, -} fn main() -> eyre::Result<()> { let cli = Cli::parse(); let project_dir = env::current_dir()?; @@ -92,48 +88,33 @@ fn main() -> eyre::Result<()> { seed, times, } => { - let mut results: Vec = (1..=times) - .map(|_| { - let randomizer = seed.map_or_else(crazy_train::Randomizer::default, |seed| { - crazy_train::Randomizer::with_seed(seed) - }); - let seed = randomizer.seed; - let temp_dir = env::temp_dir().join("loco"); - - let runner = match command { - FuzzyCommands::GenerateTemplate => { - fuzzy_steps::generate_project::run(randomizer, temp_dir.as_path()) - } - FuzzyCommands::Scaffold => { - fuzzy_steps::scaffold::run(randomizer, temp_dir.as_path()) - } - }; - - let result: Result<(), crazy_train::Error> = runner.run(); + for _ in 1..=times { + let randomizer = seed.map_or_else(crazy_train::Randomizer::default, |seed| { + crazy_train::Randomizer::with_seed(seed) + }); + let seed = randomizer.seed; + let temp_dir = env::temp_dir().join("loco"); - if temp_dir.exists() { - std::fs::remove_dir_all(temp_dir).expect("remove dir"); + let runner = match command { + FuzzyCommands::GenerateTemplate => { + fuzzy_steps::generate_project::run(randomizer, temp_dir.as_path()) } - FuzzyResult { - seed, - error: result.err(), + FuzzyCommands::Scaffold => { + fuzzy_steps::scaffold::run(randomizer, temp_dir.as_path()) } - }) - .collect(); + }; - results.sort_by(|a, b| a.error.is_some().cmp(&b.error.is_some())); + let result: Result<(), crazy_train::Error> = runner.run(); + + if temp_dir.exists() { + std::fs::remove_dir_all(temp_dir).expect("remove dir"); + } - println!(); - println!("===================================="); - println!(" Results Summary "); - println!("===================================="); + if let Err(err) = result { + println!("seed {seed}"); + println!("{err}"); - for result in results { - if let Some(err) = result.error { - println!("seed {}: error\n\n {}\n", result.seed, err); xtask::CmdExit::error_with_message("failed").exit(); - } else { - println!("seed {}: passed", result.seed); } } diff --git a/xtask/src/fuzzy_steps/generate_project.rs b/xtask/src/fuzzy_steps/generate_project.rs index 67f3ad588..e7f53a9f9 100644 --- a/xtask/src/fuzzy_steps/generate_project.rs +++ b/xtask/src/fuzzy_steps/generate_project.rs @@ -58,7 +58,10 @@ impl step::StepTrait for GenerateProjectStep { &self, execution_result: &executer::Output, ) -> std::result::Result { - let re_invalid_project_name = Regex::new(r"Error: app name is invalid, illegal characters. keep names simple: myapp or my_app\n\nLocation:\n").unwrap(); + let re_invalid_project_name = Regex::new( + r"(the first character must be a|characters must be Unicode XID characters|the name cannot start with a digit)", + ) + .unwrap(); let re_folder_exists = Regex::new(r"šŸ™€ The specified path '.*.' already exist\n").unwrap(); let re_successfully = Regex::new(r"\nšŸš‚ Loco app generated successfully in:\n.*").unwrap();