From d2100ed92fcce034b5131cc52a693b5a0d5a6c76 Mon Sep 17 00:00:00 2001 From: Masonlet Date: Fri, 26 Jun 2026 19:41:25 -0400 Subject: [PATCH 1/6] refactor: consolidate test fixtures into tests/common/args.rs --- src/cli/build/types.rs | 15 ++++++ src/cli/flags.rs | 6 +++ src/config/crud.rs | 19 ++++++-- tests/cli.rs | 2 + tests/cli/resolve.rs | 51 +-------------------- tests/commands/build.rs | 68 ++++------------------------ tests/commands/mono/mode.rs | 52 +-------------------- tests/commands/mono/resolve.rs | 52 +-------------------- tests/commands/single.rs | 53 +--------------------- tests/common/args.rs | 83 ++++++++++++++++++++++++++++++++++ tests/common/mod.rs | 6 +++ tests/interactive.rs | 59 ++---------------------- 12 files changed, 145 insertions(+), 321 deletions(-) create mode 100644 tests/common/args.rs diff --git a/src/cli/build/types.rs b/src/cli/build/types.rs index 102ea7b..27fd7f3 100644 --- a/src/cli/build/types.rs +++ b/src/cli/build/types.rs @@ -44,6 +44,21 @@ impl BuildType { } } +impl std::str::FromStr for BuildSystem { + type Err = String; + + /// Parses a build system string. + /// # Errors + /// Returns an error if the string does not match `cmake` or `meson`. + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "cmake" => Ok(Self::Cmake), + "meson" => Ok(Self::Meson), + _ => Err(format!("Unknown build system '{s}'. Valid: cmake, meson")), + } + } +} + impl std::str::FromStr for BuildType { type Err = String; diff --git a/src/cli/flags.rs b/src/cli/flags.rs index 48a2b04..39633f6 100644 --- a/src/cli/flags.rs +++ b/src/cli/flags.rs @@ -1,3 +1,4 @@ +use crate::cli::BuildSystem; use clap::Args as ClapArgs; #[allow(clippy::struct_excessive_bools)] @@ -29,6 +30,10 @@ pub struct BuildFlags { #[arg(short = 'd', long)] pub build_dir: Option, + /// Build system to use, skipping auto-detection + #[arg(long, value_name = "BUILD_SYSTEM")] + pub build_system: Option, + /// Skip building, only configure #[arg(short = 'n', long, conflicts_with = "build")] pub no_build: bool, @@ -115,6 +120,7 @@ pub struct DiagnosticFlags { /// Show timing information for each phase #[arg(long)] pub timing: bool, + /// If set, print commands instead of executing them without making any changes. #[arg(long)] pub dry_run: bool, diff --git a/src/config/crud.rs b/src/config/crud.rs index c74e913..621befb 100644 --- a/src/config/crud.rs +++ b/src/config/crud.rs @@ -96,11 +96,20 @@ pub fn add_config( } if io.dry_run { - writeln!(io.output, "Would save configuration '{name}' to config file").ok(); + writeln!( + io.output, + "Would save configuration '{name}' to config file" + ) + .ok(); } else { insert_config(config, name, entry); let path = save_config(config)?; - writeln!(io.output, "Configuration '{name}' added successfully to {}", path.display()).ok(); + writeln!( + io.output, + "Configuration '{name}' added successfully to {}", + path.display() + ) + .ok(); let e: &ConfigEntry = &config.configs[name]; writeln!(io.output, "Configuration details:").ok(); write!(io.output, "{}", format_entry(e)).ok(); @@ -132,7 +141,11 @@ pub fn remove_config( } if io.dry_run { - writeln!(io.output, "Would remove configuration '{name}' from config file").ok(); + writeln!( + io.output, + "Would remove configuration '{name}' from config file" + ) + .ok(); } else { remove_config_entry(config, name); let path = save_config(config)?; diff --git a/tests/cli.rs b/tests/cli.rs index ff28415..e7b2e01 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -1,4 +1,6 @@ #[path = "cli/build.rs"] mod build; +#[path = "common/mod.rs"] +mod common; #[path = "cli/resolve.rs"] mod resolve; diff --git a/tests/cli/resolve.rs b/tests/cli/resolve.rs index d88c4b5..4e56b54 100644 --- a/tests/cli/resolve.rs +++ b/tests/cli/resolve.rs @@ -1,8 +1,6 @@ +use crate::common::default_args; use star_setup::{ - cli::{ - resolve_bool, resolve_with_config, Args, BuildFlags, BuildType, ConfigFlags, ConnectionFlags, - DiagnosticFlags, MonoRepoFlags, ProfileFlags, - }, + cli::{resolve_bool, resolve_with_config, BuildType}, config::{ConfigEntry, SetupConfig}, }; @@ -71,51 +69,6 @@ fn test_resolve_bool() { } } -fn default_args() -> Args { - Args { - repo: None, - yes: false, - diagnostic: DiagnosticFlags { - timing: false, - dry_run: false, - }, - connection: ConnectionFlags { - ssh: false, - https: false, - verbose: false, - no_verbose: false, - }, - build: BuildFlags { - build_type: None, - build_dir: None, - no_build: false, - build: false, - clean: false, - no_clean: false, - cmake_flags: vec![], - meson_flags: vec![], - }, - mono: MonoRepoFlags { - mono_repo: false, - mono_dir: None, - repos: None, - profile: None, - }, - config: ConfigFlags { - init_config: false, - config_name: None, - config_add: None, - config_remove: None, - list_configs: false, - }, - profile: ProfileFlags { - profile_add: None, - profile_remove: None, - list_profiles: false, - }, - } -} - // resolve_with_config tests #[test] fn test_resolve_with_config_defaults_when_no_config() { diff --git a/tests/commands/build.rs b/tests/commands/build.rs index e5bce0a..9c7b591 100644 --- a/tests/commands/build.rs +++ b/tests/commands/build.rs @@ -1,65 +1,15 @@ -use super::common::{empty_input, make_io, sink, MockRunner}; +use super::common::{default_resolved_with_no_build, empty_input, make_io, sink, MockRunner}; use star_setup::{ - cli::{ - resolve_with_config, Args, BuildFlags, BuildSystem, ConfigFlags, ConnectionFlags, - DiagnosticFlags, MonoRepoFlags, ProfileFlags, - }, + cli::BuildSystem, commands::{build_project, cmake_build, meson_build}, - config::SetupConfig, ctx::RunCtx, }; use tempfile::TempDir; -fn default_resolved(no_build: bool) -> star_setup::cli::ResolvedArgs { - let args = Args { - repo: Some("user/repo".to_string()), - yes: false, - diagnostic: DiagnosticFlags { - timing: false, - dry_run: false, - }, - connection: ConnectionFlags { - ssh: false, - https: false, - verbose: false, - no_verbose: false, - }, - build: BuildFlags { - build_type: None, - build_dir: None, - no_build, - build: false, - clean: false, - no_clean: false, - cmake_flags: vec![], - meson_flags: vec![], - }, - mono: MonoRepoFlags { - mono_repo: false, - mono_dir: None, - repos: None, - profile: None, - }, - config: ConfigFlags { - init_config: false, - config_name: None, - config_add: None, - config_remove: None, - list_configs: false, - }, - profile: ProfileFlags { - profile_add: None, - profile_remove: None, - list_profiles: false, - }, - }; - resolve_with_config(args, &SetupConfig::new()).unwrap() -} - #[test] fn test_cmake_build_configure_only() { let tmp = TempDir::new().unwrap(); - let args = default_resolved(true); + let args = default_resolved_with_no_build(true); let mut input = empty_input(); let mut output = sink(); let mut runner = MockRunner::new(); @@ -77,7 +27,7 @@ fn test_cmake_build_configure_only() { #[test] fn test_cmake_build_with_build_step() { let tmp = TempDir::new().unwrap(); - let args = default_resolved(false); + let args = default_resolved_with_no_build(false); let mut input = empty_input(); let mut output = sink(); let mut runner = MockRunner::new(); @@ -95,7 +45,7 @@ fn test_cmake_build_with_build_step() { #[test] fn test_cmake_build_mono_flag() { let tmp = TempDir::new().unwrap(); - let args = default_resolved(true); + let args = default_resolved_with_no_build(true); let mut input = empty_input(); let mut output = sink(); let mut runner = MockRunner::new(); @@ -112,7 +62,7 @@ fn test_cmake_build_mono_flag() { #[test] fn test_meson_build_configure_only() { let tmp = TempDir::new().unwrap(); - let args = default_resolved(true); + let args = default_resolved_with_no_build(true); let mut input = empty_input(); let mut output = sink(); let mut runner = MockRunner::new(); @@ -131,7 +81,7 @@ fn test_meson_build_configure_only() { #[test] fn test_meson_build_with_build_step() { let tmp = TempDir::new().unwrap(); - let args = default_resolved(false); + let args = default_resolved_with_no_build(false); let mut input = empty_input(); let mut output = sink(); let mut runner = MockRunner::new(); @@ -149,7 +99,7 @@ fn test_meson_build_with_build_step() { #[test] fn test_build_project_dispatches_cmake() { let tmp = TempDir::new().unwrap(); - let args = default_resolved(true); + let args = default_resolved_with_no_build(true); let mut input = empty_input(); let mut output = sink(); let mut runner = MockRunner::new(); @@ -174,7 +124,7 @@ fn test_build_project_dispatches_cmake() { #[test] fn test_build_project_dispatches_meson() { let tmp = TempDir::new().unwrap(); - let args = default_resolved(true); + let args = default_resolved_with_no_build(true); let mut input = empty_input(); let mut output = sink(); let mut runner = MockRunner::new(); diff --git a/tests/commands/mono/mode.rs b/tests/commands/mono/mode.rs index 577dd2c..04a4f11 100644 --- a/tests/commands/mono/mode.rs +++ b/tests/commands/mono/mode.rs @@ -1,61 +1,11 @@ -use super::super::common::{empty_input, make_io, sink, MockRunner}; +use crate::common::{default_resolved_mono, empty_input, make_io, sink, MockRunner}; use star_setup::{ - cli::{ - resolve_with_config, Args, BuildFlags, ConfigFlags, ConnectionFlags, DiagnosticFlags, - MonoRepoFlags, ProfileFlags, - }, commands::mono_repo_mode, config::SetupConfig, ctx::{DryRunRunner, RunCtx}, }; use tempfile::TempDir; -fn default_resolved_mono(repos: Vec) -> star_setup::cli::ResolvedArgs { - let args = Args { - repo: Some("user/test-repo".to_string()), - yes: true, - diagnostic: DiagnosticFlags { - timing: false, - dry_run: false, - }, - connection: ConnectionFlags { - ssh: false, - https: false, - verbose: false, - no_verbose: false, - }, - build: BuildFlags { - build_type: None, - build_dir: None, - no_build: true, - build: false, - clean: false, - no_clean: false, - cmake_flags: vec![], - meson_flags: vec![], - }, - mono: MonoRepoFlags { - mono_repo: true, - mono_dir: None, - repos: Some(repos), - profile: None, - }, - config: ConfigFlags { - init_config: false, - config_name: None, - config_add: None, - config_remove: None, - list_configs: false, - }, - profile: ProfileFlags { - profile_add: None, - profile_remove: None, - list_profiles: false, - }, - }; - resolve_with_config(args, &SetupConfig::new()).unwrap() -} - fn make_cmake_repo(repos_path: &std::path::Path, name: &str) { let dir = repos_path.join(name); std::fs::create_dir_all(&dir).unwrap(); diff --git a/tests/commands/mono/resolve.rs b/tests/commands/mono/resolve.rs index c95edf9..2500d3c 100644 --- a/tests/commands/mono/resolve.rs +++ b/tests/commands/mono/resolve.rs @@ -1,9 +1,5 @@ -use crate::common::{empty_input, make_io, sink, MockRunner}; +use crate::common::{default_resolved, empty_input, make_io, sink, MockRunner}; use star_setup::{ - cli::{ - resolve_with_config, Args, BuildFlags, ConfigFlags, ConnectionFlags, DiagnosticFlags, - MonoRepoFlags, ProfileFlags, - }, commands::{mono::generate_mono_config, resolve_repos_for_mono, resolve_test_repo}, config::SetupConfig, ctx::RunCtx, @@ -51,52 +47,6 @@ fn test_resolve_test_repo_errors() { } } -fn default_resolved() -> star_setup::cli::ResolvedArgs { - let args = Args { - repo: Some("user/repo".to_string()), - yes: false, - diagnostic: DiagnosticFlags { - timing: false, - dry_run: false, - }, - connection: ConnectionFlags { - ssh: false, - https: false, - verbose: false, - no_verbose: false, - }, - build: BuildFlags { - build_type: None, - build_dir: None, - no_build: false, - build: false, - clean: false, - no_clean: false, - cmake_flags: vec![], - meson_flags: vec![], - }, - mono: MonoRepoFlags { - mono_repo: false, - mono_dir: None, - repos: None, - profile: None, - }, - config: ConfigFlags { - init_config: false, - config_name: None, - config_add: None, - config_remove: None, - list_configs: false, - }, - profile: ProfileFlags { - profile_add: None, - profile_remove: None, - list_profiles: false, - }, - }; - resolve_with_config(args, &SetupConfig::new()).unwrap() -} - #[test] fn test_resolve_repos_for_mono_empty_profile_errors() { let mut config = SetupConfig::new(); diff --git a/tests/commands/single.rs b/tests/commands/single.rs index fe00d71..0582f6b 100644 --- a/tests/commands/single.rs +++ b/tests/commands/single.rs @@ -1,61 +1,10 @@ -use super::common::{make_io, sink, MockRunner}; +use super::common::{default_resolved, make_io, sink, MockRunner}; use star_setup::{ - cli::{ - resolve_with_config, Args, BuildFlags, ConfigFlags, ConnectionFlags, DiagnosticFlags, - MonoRepoFlags, ProfileFlags, - }, commands::single_repo_mode, - config::SetupConfig, ctx::{DryRunRunner, RunCtx}, }; use tempfile::TempDir; -fn default_resolved() -> star_setup::cli::ResolvedArgs { - let args = Args { - repo: Some("user/repo".to_string()), - yes: false, - diagnostic: DiagnosticFlags { - timing: false, - dry_run: false, - }, - connection: ConnectionFlags { - ssh: false, - https: false, - verbose: false, - no_verbose: false, - }, - build: BuildFlags { - build_type: None, - build_dir: None, - no_build: true, - build: false, - clean: false, - no_clean: false, - cmake_flags: vec![], - meson_flags: vec![], - }, - mono: MonoRepoFlags { - mono_repo: false, - mono_dir: None, - repos: None, - profile: None, - }, - config: ConfigFlags { - init_config: false, - config_name: None, - config_add: None, - config_remove: None, - list_configs: false, - }, - profile: ProfileFlags { - profile_add: None, - profile_remove: None, - list_profiles: false, - }, - }; - resolve_with_config(args, &SetupConfig::new()).unwrap() -} - fn make_repo_fixture(base: &std::path::Path) { let repo_dir = base.join("user-repo"); std::fs::create_dir_all(&repo_dir).unwrap(); diff --git a/tests/common/args.rs b/tests/common/args.rs new file mode 100644 index 0000000..de566fb --- /dev/null +++ b/tests/common/args.rs @@ -0,0 +1,83 @@ +#![allow(dead_code)] + +use star_setup::{ + cli::{ + resolve_with_config, Args, BuildFlags, ConfigFlags, ConnectionFlags, DiagnosticFlags, + MonoRepoFlags, ProfileFlags, + }, + config::SetupConfig, +}; + +pub fn default_args() -> Args { + Args { + repo: None, + yes: false, + diagnostic: DiagnosticFlags { + timing: false, + dry_run: false, + }, + connection: ConnectionFlags { + ssh: false, + https: false, + verbose: false, + no_verbose: false, + }, + build: BuildFlags { + build_type: None, + build_dir: None, + build_system: None, + no_build: false, + build: false, + clean: false, + no_clean: false, + cmake_flags: vec![], + meson_flags: vec![], + }, + mono: MonoRepoFlags { + mono_repo: false, + mono_dir: None, + repos: None, + profile: None, + }, + config: ConfigFlags { + init_config: false, + config_name: None, + config_add: None, + config_remove: None, + list_configs: false, + }, + profile: ProfileFlags { + profile_add: None, + profile_remove: None, + list_profiles: false, + }, + } +} + +pub fn default_resolved() -> star_setup::cli::ResolvedArgs { + let mut args = default_args(); + args.repo = Some("user/repo".to_string()); + args.build.no_build = true; + resolve_with_config(args, &SetupConfig::new()).unwrap() +} + +pub fn default_resolved_with_no_build(no_build: bool) -> star_setup::cli::ResolvedArgs { + let mut args = default_args(); + args.repo = Some("user/repo".to_string()); + args.build.no_build = no_build; + resolve_with_config(args, &SetupConfig::new()).unwrap() +} + +pub fn default_resolved_interactive() -> star_setup::cli::ResolvedArgs { + resolve_with_config(default_args(), &SetupConfig::new()).unwrap() +} + +pub fn default_resolved_mono(repos: Vec) -> star_setup::cli::ResolvedArgs { + let mut args = default_args(); + args.repo = Some("user/test-repo".to_string()); + args.yes = true; + args.build.no_build = true; + args.mono.mono_repo = true; + args.mono.repos = Some(repos); + resolve_with_config(args, &SetupConfig::new()).unwrap() +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 307ecdf..7930bb7 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -4,3 +4,9 @@ pub use io::{empty_input, make_io, sink}; pub mod runner; #[allow(unused_imports)] pub use runner::MockRunner; +pub mod args; +#[allow(unused_imports)] +pub use args::{ + default_args, default_resolved, default_resolved_interactive, default_resolved_mono, + default_resolved_with_no_build, +}; diff --git a/tests/interactive.rs b/tests/interactive.rs index 9472145..5f55bf7 100644 --- a/tests/interactive.rs +++ b/tests/interactive.rs @@ -1,59 +1,6 @@ -use star_setup::{ - cli::{ - resolve_with_config, Args, BuildFlags, ConfigFlags, ConnectionFlags, DiagnosticFlags, - MonoRepoFlags, ProfileFlags, - }, - config::SetupConfig, - interactive::interactive_mode, -}; +use star_setup::interactive::interactive_mode; mod common; -use common::make_io; - -fn default_resolved() -> star_setup::cli::ResolvedArgs { - let args = Args { - repo: None, - yes: false, - diagnostic: DiagnosticFlags { - timing: false, - dry_run: false, - }, - connection: ConnectionFlags { - ssh: false, - https: false, - verbose: false, - no_verbose: false, - }, - build: BuildFlags { - build_type: None, - build_dir: None, - no_build: false, - build: false, - clean: false, - no_clean: false, - cmake_flags: vec![], - meson_flags: vec![], - }, - mono: MonoRepoFlags { - mono_repo: false, - mono_dir: None, - repos: None, - profile: None, - }, - config: ConfigFlags { - init_config: false, - config_name: None, - config_add: None, - config_remove: None, - list_configs: false, - }, - profile: ProfileFlags { - profile_add: None, - profile_remove: None, - list_profiles: false, - }, - }; - resolve_with_config(args, &SetupConfig::new()).unwrap() -} +use common::{default_resolved, default_resolved_interactive, make_io}; fn input_with_suffix(prefix: &[u8]) -> Vec { let mut v = prefix.to_vec(); @@ -80,7 +27,7 @@ fn test_interactive_mode_ssh_enabled() { let mut input = input.as_ref(); let mut output = Vec::new(); let mut io = make_io(&mut input, &mut output); - let mut args = default_resolved(); + let mut args = default_resolved_interactive(); interactive_mode(&mut args, &mut io).unwrap(); assert!(args.connection.ssh); } From 6a11ac4c1e3de466d2d700240cbd99d66a363e3b Mon Sep 17 00:00:00 2001 From: Masonlet Date: Fri, 26 Jun 2026 19:45:46 -0400 Subject: [PATCH 2/6] feat: add --build-system flag to skip auto-detection --- src/cli/resolve.rs | 1 + src/cli/resolved.rs | 3 ++- src/commands/mono/mode.rs | 26 +++++++++++++------------- src/commands/single.rs | 11 +++++++++-- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/cli/resolve.rs b/src/cli/resolve.rs index adcee24..a4076b8 100644 --- a/src/cli/resolve.rs +++ b/src/cli/resolve.rs @@ -95,6 +95,7 @@ pub fn resolve_with_config(mut args: Args, config: &SetupConfig) -> Result, pub no_build: bool, pub clean: bool, pub cmake_flags: Vec, diff --git a/src/commands/mono/mode.rs b/src/commands/mono/mode.rs index 7d2bef2..5cc3e64 100644 --- a/src/commands/mono/mode.rs +++ b/src/commands/mono/mode.rs @@ -59,22 +59,22 @@ pub fn mono_repo_mode( let build_path = mono_repo_path.join(&args.build.build_dir); - let canonical_map = if ctx.io.dry_run { - prepare_build_dir(build_path.as_path(), args.build.clean, ctx)?; - None + let build_system = if let Some(bs) = args.build.build_system { + Some(bs) + } else if !ctx.io.dry_run { + Some(detect_mono_build_system(&repo_dirs, ctx)?) } else { - let build_system = detect_mono_build_system(&repo_dirs, ctx)?; - let map = generate_mono_config( - build_system, - &mono_repo_path, - &repos_path, - &repo_dirs, - &repos, - ctx, - )?; + None + }; + + let canonical_map = if let Some(bs) = build_system { + let map = generate_mono_config(bs, &mono_repo_path, &repos_path, &repo_dirs, &repos, ctx)?; prepare_build_dir(build_path.as_path(), args.build.clean, ctx)?; - configure_and_build(args, &mono_repo_path, &build_path, build_system, true, ctx)?; + configure_and_build(args, &mono_repo_path, &build_path, bs, true, ctx)?; map + } else { + prepare_build_dir(build_path.as_path(), args.build.clean, ctx)?; + None }; let paths = if ctx.io.dry_run { diff --git a/src/commands/single.rs b/src/commands/single.rs index 1cfb605..ed91519 100644 --- a/src/commands/single.rs +++ b/src/commands/single.rs @@ -59,8 +59,15 @@ pub fn single_repo_mode( let build_path = repo_path.join(&args.build.build_dir); prepare_build_dir(&build_path, args.build.clean, ctx)?; - if !ctx.io.dry_run { - let build_system = detect_build_system(&repo_path, ctx)?; + let build_system = if let Some(bs) = args.build.build_system { + Some(bs) + } else if !ctx.io.dry_run { + Some(detect_build_system(&repo_path, ctx)?) + } else { + None + }; + + if let Some(build_system) = build_system { configure_and_build(args, &repo_path, &build_path, build_system, false, ctx)?; } From 8ca590ceb9d8e8638388bcd7b8c87dba5ac01530 Mon Sep 17 00:00:00 2001 From: Masonlet Date: Fri, 26 Jun 2026 19:48:29 -0400 Subject: [PATCH 3/6] test: add BuildSystem FromStr and build-system flag tests --- tests/cli/build/types.rs | 11 +++++++++++ tests/commands/single.rs | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/tests/cli/build/types.rs b/tests/cli/build/types.rs index 992e9d2..bd366c6 100644 --- a/tests/cli/build/types.rs +++ b/tests/cli/build/types.rs @@ -52,3 +52,14 @@ fn test_from_str_error() { use std::str::FromStr; assert!(BuildType::from_str("unknown").is_err()); } + +#[test] +fn test_build_system_from_str() { + use star_setup::cli::BuildSystem; + use std::str::FromStr; + assert_eq!(BuildSystem::from_str("cmake").unwrap(), BuildSystem::Cmake); + assert_eq!(BuildSystem::from_str("CMAKE").unwrap(), BuildSystem::Cmake); + assert_eq!(BuildSystem::from_str("meson").unwrap(), BuildSystem::Meson); + assert_eq!(BuildSystem::from_str("MESON").unwrap(), BuildSystem::Meson); + assert!(BuildSystem::from_str("ninja").is_err()); +} diff --git a/tests/commands/single.rs b/tests/commands/single.rs index 0582f6b..68167e9 100644 --- a/tests/commands/single.rs +++ b/tests/commands/single.rs @@ -154,3 +154,23 @@ fn test_single_repo_mode_dry_run_clean_prints_would_remove() { assert!(out.contains("Would remove directory:")); assert!(std::fs::read_dir(tmp.path()).unwrap().next().is_none()); } + +#[test] +fn test_single_repo_mode_with_build_system_flag() { + let tmp = TempDir::new().unwrap(); + let mut args = default_resolved(); + args.build.build_system = Some(star_setup::cli::BuildSystem::Cmake); + make_repo_fixture(tmp.path()); + + let mut input = b"n\n".as_ref(); + let mut output = sink(); + let mut runner = MockRunner::new(); + let mut ctx = RunCtx { + io: make_io(&mut input, &mut output), + runner: &mut runner, + }; + + single_repo_mode(&args, tmp.path(), &mut ctx).unwrap(); + + assert!(runner.calls.iter().any(|(cmd, _)| cmd[0] == "cmake")); +} From 11c2c887d6c3aa38bb9346e6fa05eadfbb62376b Mon Sep 17 00:00:00 2001 From: Masonlet Date: Fri, 26 Jun 2026 19:50:34 -0400 Subject: [PATCH 4/6] docs: add build-system flag --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8e2bebe..8e59f37 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ cargo install --git https://github.com/star-setup/core |------|-------------| | `--build-type ` | Build type: `Debug` (default) or `Release` | | `--build-dir ` | Build output directory (default: `build`) | +| `--build-system ` | Skip auto-detection and use `cmake` or `meson` | | `--no-build` | Configure only, skip build step | | `--clean` | Remove build directory before configuring | | `--cmake-arg ` | Pass additional argument to CMake | From 9e9997df0a74f7d8883f064d095d66f4fcf95be6 Mon Sep 17 00:00:00 2001 From: Masonlet Date: Fri, 26 Jun 2026 19:50:55 -0400 Subject: [PATCH 5/6] chore: bump v0.3.2 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ab5f76a..b67bc2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "star-setup" -version = "0.3.1" +version = "0.3.2" edition = "2021" repository = "https://github.com/star-setup/core" description = "Lightweight CLI to clone, configure, and wire single or multi-repo ecosystems" From d61c1bf5f7ced39946137f23a8e09a524b5da62f Mon Sep 17 00:00:00 2001 From: Masonlet Date: Fri, 26 Jun 2026 19:54:15 -0400 Subject: [PATCH 6/6] test: add mono mode build-system flag test --- tests/commands/mono/mode.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/commands/mono/mode.rs b/tests/commands/mono/mode.rs index 04a4f11..db00f31 100644 --- a/tests/commands/mono/mode.rs +++ b/tests/commands/mono/mode.rs @@ -61,3 +61,27 @@ fn test_mono_repo_mode_dry_run_makes_no_fs_changes() { assert!(std::fs::read_dir(tmp.path()).unwrap().next().is_none()); } + +#[test] +fn test_mono_repo_mode_with_build_system_flag() { + let tmp = TempDir::new().unwrap(); + let mut args = default_resolved_mono(vec!["user/lib1".to_string()]); + args.build.build_system = Some(star_setup::cli::BuildSystem::Cmake); + + let repos_path = tmp.path().join(&args.mono.mono_dir).join("repos"); + std::fs::create_dir_all(&repos_path).unwrap(); + make_cmake_repo(&repos_path, "user-lib1"); + make_cmake_repo(&repos_path, "user-test-repo"); + + let mut input = empty_input(); + let mut output = sink(); + let mut runner = MockRunner::new(); + let mut ctx = RunCtx { + io: make_io(&mut input, &mut output), + runner: &mut runner, + }; + + mono_repo_mode(&args, &SetupConfig::new(), tmp.path(), &mut ctx).unwrap(); + + assert!(runner.calls.iter().any(|(cmd, _)| cmd[0] == "cmake")); +}