Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "star-setup"
version = "0.4.7"
version = "0.4.8"
edition = "2021"
repository = "https://github.com/star-setup/core"
description = "Lightweight CLI to clone, configure, and wire single or multi-repo ecosystems"
Expand Down
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ Clones multiple repositories into a single workspace and auto-detects the build
# Clone and build a test repo and a manual repo list
star-setup username/repo --repos user/lib1 user/lib2

# Clone and build a test repo and a saved profile
# Clone and build a saved profile
star-setup --profile myprofile

# Override a profile's test repo
star-setup username/repo --profile myprofile
```

Expand Down Expand Up @@ -280,7 +283,10 @@ star-setup username/repo --config myconfig
### Profile Mode
Profiles represent a saved ecosystem of libraries commonly used together.
```bash
# Add a profile
# Add a profile with a test repo and dependencies
star-setup profile add myprofile --test-repo user/app user/lib1 user/lib2

# Add a dependency-only profile
star-setup profile add myprofile user/lib1 user/lib2

# List profiles
Expand All @@ -290,7 +296,7 @@ star-setup profile list
star-setup profile remove myprofile

# Use a profile
star-setup username/repo --profile myprofile
star-setup --profile myprofile
```

### Development
Expand Down
4 changes: 2 additions & 2 deletions src/cli/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
resolve_with_config, BuildFlags, ConfigCommand, ConnectionFlags, DiagnosticFlags,
MonoRepoFlags, ProfileCommand, ResolvedArgs, WorkspaceCommand,
},
config::SetupConfig,
config::Config,
};
use clap::{Parser, Subcommand};

Expand Down Expand Up @@ -57,7 +57,7 @@ impl Args {
/// Parses CLI arguments and resolves them against the provided `SetupConfig`.
/// # Errors
/// Returns an error if the named config does not exist in the loaded `SetupConfig`.
pub fn parse_with_config(config: &SetupConfig) -> Result<ResolvedArgs, String> {
pub fn parse_with_config(config: &Config) -> Result<ResolvedArgs, String> {
resolve_with_config(Args::parse(), config)
}
}
5 changes: 4 additions & 1 deletion src/cli/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ pub enum ProfileAction {
Add {
/// Name of the profile.
name: String,
/// Repository list (username/repo ...).
/// Test repository (user/repo).
#[arg(long)]
test_repo: Option<String>,
/// Repository list (user/lib1 user/lib2).
repos: Vec<String>,
},
/// Remove a named profile.
Expand Down
4 changes: 2 additions & 2 deletions src/cli/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
Args, BuildFlags, BuildType, ConnectionFlags, DiagnosticFlags, MonoRepoFlags, ResolvedArgs,
ResolvedBuildFlags, ResolvedConnectionFlags, ResolvedMonoFlags,
},
config::{ConfigEntry, SetupConfig},
config::{Config, ConfigEntry},
ctx::RunFlags,
};

Expand Down Expand Up @@ -115,7 +115,7 @@ fn resolve_mono_flags(mono: MonoRepoFlags, default: Option<&ConfigEntry>) -> Res
/// Resolves raw `Args` into `ResolvedArgs` by applying config defaults and CLI overrides.
/// # Errors
/// Returns an error if the named config does not exist in the provided `SetupConfig`.
pub fn resolve_with_config(args: Args, config: &SetupConfig) -> Result<ResolvedArgs, String> {
pub fn resolve_with_config(args: Args, config: &Config) -> Result<ResolvedArgs, String> {
let config_name = args.config_name.as_deref().unwrap_or("default");
let default = config.configs.get(config_name);

Expand Down
26 changes: 14 additions & 12 deletions src/commands/handlers.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
use crate::{
cli::{
ConfigAction, ProfileAction, WorkspaceAction,
WorkspaceAction::{Clean, Status, Update},
},
config::{
add_config, create_default_config, list_configs, remove_config, ConfigEntry, SetupConfig,
ConfigAction, ProfileAction,
WorkspaceAction::{self, Clean, Status, Update},
},
config::{add_config, create_default_config, list_configs, remove_config, Config, ConfigEntry},
ctx::{with_runner, IoCtx, RunFlags},
profile::{add_profile, list_profiles, remove_profile},
profile::{add_profile, list_profiles, remove_profile, Profile},
workspace::resolve_workspace,
};
use std::{error::Error, iter::once, path::PathBuf};
use std::{error::Error, path::PathBuf};

/// Handles configuration-related subcommands.
/// # Errors
/// Returns an error if configuration initializing, addition, or removal fails.
pub fn handle_config_cmd(
action: ConfigAction,
config: &mut SetupConfig,
config: &mut Config,
config_path: PathBuf,
yes: bool,
io: &mut IoCtx,
Expand Down Expand Up @@ -46,17 +44,21 @@ pub fn handle_config_cmd(
/// Returns an error if adding or removing profiles encounters an I/O or validation failure.
pub fn handle_profile_cmd(
action: ProfileAction,
config: &mut SetupConfig,
config: &mut Config,
yes: bool,
io: &mut IoCtx,
flags: RunFlags,
) -> Result<(), Box<dyn Error>> {
match action {
ProfileAction::List => list_profiles(config, io),
ProfileAction::Remove { name } => remove_profile(config, &name, yes, io, flags)?,
ProfileAction::Add { name, repos } => {
let vals = once(name).chain(repos).collect::<Vec<_>>();
add_profile(config, &vals, yes, io, flags)?;
ProfileAction::Add {
name,
test_repo,
repos,
} => {
let profile = Profile::from_args(test_repo, repos)?;
add_profile(config, &name, &profile, yes, io, flags)?;
}
}
Ok(())
Expand Down
4 changes: 3 additions & 1 deletion src/commands/mono/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ pub use display::{print_setup_complete, resolve_setup_paths};
pub mod mode;
pub use mode::mono_repo_mode;
pub mod resolve;
pub use resolve::{resolve_repos_for_mono, resolve_test_repo};
pub use resolve::{
resolve_profile, resolve_repos_for_mono, resolve_test_repo, resolve_test_repo_for_mono,
};
pub mod wraps;
pub use wraps::hoist_wraps;
pub mod setup;
Expand Down
15 changes: 8 additions & 7 deletions src/commands/mono/mode.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use crate::{
cli::{detect_mono_build_system, BuildSystem::Npm, ResolvedArgs},
commands::{
build_project, build_repo_list, extract_repo_input, maybe_open_dev_server,
build_project, build_repo_list, maybe_open_dev_server,
mono::{
display::{resolve_setup_paths, SetupPaths},
generate_mono_config, generate_watch_scripts, open_watch_scripts, print_setup_complete,
resolve::{resolve_profile, resolve_test_repo_for_mono},
},
prepare_build_dir, print_mode_header, resolve_repos_for_mono, resolve_test_repo, ModeHeader,
prepare_build_dir, print_mode_header, resolve_repos_for_mono, ModeHeader,
},
config::SetupConfig,
config::Config,
ctx::RunCtx,
repository::{clone_repos, repo_dir_name},
utils::{detect_or_dry_run, dry_run_or_do},
Expand All @@ -24,14 +25,14 @@ use std::{
/// Returns an error if no repository is specified, directory creation fails, or any build system command fails.
pub fn mono_repo_mode(
args: &ResolvedArgs,
config: &SetupConfig,
config: &Config,
base_dir: &Path,
ctx: &mut RunCtx<'_, '_>,
) -> Result<(), String> {
let total = Instant::now();
let repo_input = extract_repo_input(args)?;
let test_repo = resolve_test_repo(repo_input)?;
let deps = resolve_repos_for_mono(args, config, &mut ctx.io)?;
let profile = resolve_profile(args, config, &mut ctx.io)?;
let test_repo = resolve_test_repo_for_mono(args, profile)?;
let deps = resolve_repos_for_mono(args, profile);
let repos = build_repo_list(&test_repo, &deps);

print_mode_header(
Expand Down
58 changes: 40 additions & 18 deletions src/commands/mono/resolve.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use crate::{cli::ResolvedArgs, config::SetupConfig, ctx::IoCtx, profile::list_profiles};
use crate::{
cli::ResolvedArgs,
config::Config,
ctx::IoCtx,
profile::{list_profiles, Profile},
};

/// Normalizes a repository input to `username/repo` format.
/// # Errors
Expand All @@ -24,26 +29,43 @@ pub fn resolve_test_repo(repo_input: &str) -> Result<String, String> {
}
}

/// Resolves the list of repositories for mono-repo mode from a profile or explicit repo list.
/// Resolves the named profile for mono-repo mode, if one was requested.
/// # Errors
/// Returns an error if the specified profile does not exist, or has no repositories.
pub fn resolve_repos_for_mono(
/// Returns an error if `--profile` names a profile that doesn't exist.
pub fn resolve_profile<'a>(
args: &ResolvedArgs,
config: &SetupConfig,
config: &'a Config,
io: &mut IoCtx<'_>,
) -> Result<Vec<String>, String> {
if let Some(profile_name) = &args.mono.profile {
let profile_repos = config.profiles.get(profile_name).ok_or_else(|| {
) -> Result<Option<&'a Profile>, String> {
match &args.mono.profile {
Some(name) => config.profiles.get(name).map(Some).ok_or_else(|| {
list_profiles(config, io);
format!("Profile '{profile_name}' not found")
})?;
if profile_repos.is_empty() {
return Err(format!("Profile '{profile_name}' has no repositories"));
}
Ok(profile_repos.clone())
} else if let Some(r) = &args.mono.repos {
Ok(r.clone())
} else {
Err("No repos or profile specified for mono-repo mode".to_string())
format!("Profile '{name}' not found")
}),
None => Ok(None),
}
}

/// Resolves the test repo for mono-repo mode: CLI positional wins, else the profile's.
/// # Errors
/// Returns an error if neither a positional repo nor a profile test repo is available.
pub fn resolve_test_repo_for_mono(
args: &ResolvedArgs,
profile: Option<&Profile>,
) -> Result<String, String> {
match args.repo.as_deref() {
Some(r) => resolve_test_repo(r.trim_end_matches('/')),
None => profile
.and_then(|p| p.test_repo.clone())
.ok_or_else(|| "No repository specified".to_string()),
}
}

/// Resolves the dependency repositories for mono-repo mode from a profile or explicit list.
#[must_use]
pub fn resolve_repos_for_mono(args: &ResolvedArgs, profile: Option<&Profile>) -> Vec<String> {
profile
.map(|p| p.deps.clone())
.or_else(|| args.mono.repos.clone())
.unwrap_or_default()
}
14 changes: 7 additions & 7 deletions src/config/crud.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
use crate::{
config::{format_entry, persist_or_dry_run, save_config, ConfigEntry, SetupConfig},
config::{format_entry, persist_or_dry_run, save_config, Config, ConfigEntry},
ctx::{IoCtx, RunFlags},
prompts::confirm_abort,
};
use dunce::canonicalize;
use std::path::PathBuf;

/// Inserts or overwrites a named configuration entry.
pub fn insert_config(config: &mut SetupConfig, name: &str, entry: ConfigEntry) {
pub fn insert_config(config: &mut Config, name: &str, entry: ConfigEntry) {
config.configs.insert(name.to_string(), entry);
}

/// Removes a named configuration entry. Returns `true` if it existed.
pub fn remove_config_entry(config: &mut SetupConfig, name: &str) -> bool {
pub fn remove_config_entry(config: &mut Config, name: &str) -> bool {
config.configs.remove(name).is_some()
}

/// Returns `true` if a configuration with the given name exists.
#[must_use]
pub fn has_config(config: &SetupConfig, name: &str) -> bool {
pub fn has_config(config: &Config, name: &str) -> bool {
config.configs.contains_key(name)
}

Expand Down Expand Up @@ -51,7 +51,7 @@ pub fn create_default_config(
)
.ok();
} else {
let mut config = SetupConfig::new();
let mut config = Config::new();
config.path = Some(path.clone());
config
.configs
Expand Down Expand Up @@ -79,7 +79,7 @@ pub fn create_default_config(
/// # Errors
/// Returns an error if saving the config file fails.
pub fn add_config(
config: &mut SetupConfig,
config: &mut Config,
name: &str,
entry: ConfigEntry,
yes: bool,
Expand Down Expand Up @@ -120,7 +120,7 @@ pub fn add_config(
/// # Errors
/// Returns an error if saving the config file fails.
pub fn remove_config(
config: &mut SetupConfig,
config: &mut Config,
name: &str,
yes: bool,
io: &mut IoCtx<'_>,
Expand Down
4 changes: 2 additions & 2 deletions src/config/display.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
config::{ConfigEntry, SetupConfig},
config::{Config, ConfigEntry},
ctx::IoCtx,
};
use std::fmt::Write as FmtWrite;
Expand Down Expand Up @@ -44,7 +44,7 @@ pub fn format_entry(e: &ConfigEntry) -> String {
}

/// Lists all saved configuration entries.
pub fn list_configs(config: &SetupConfig, io: &mut IoCtx<'_>) {
pub fn list_configs(config: &Config, io: &mut IoCtx<'_>) {
if config.configs.is_empty() {
writeln!(io.output, " No configurations created.").ok();
writeln!(
Expand Down
16 changes: 8 additions & 8 deletions src/config/io.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
config::SetupConfig,
config::Config,
ctx::{IoCtx, RunFlags},
};
use serde_json::{from_str, to_string_pretty};
Expand Down Expand Up @@ -37,7 +37,7 @@ pub fn load_config(
verbose: bool,
timing: bool,
output: &mut impl Write,
) -> SetupConfig {
) -> Config {
if verbose {
writeln!(output, "Loading config").ok();
}
Expand All @@ -53,7 +53,7 @@ pub fn load_config(
}
let result = crate::time!(timing, output, "Read config", {
match read_to_string(path) {
Ok(contents) => match from_str::<SetupConfig>(&contents) {
Ok(contents) => match from_str::<Config>(&contents) {
Ok(mut config) => {
config.path = Some(path.clone());
if verbose {
Expand Down Expand Up @@ -93,14 +93,14 @@ pub fn load_config(
if verbose || timing {
writeln!(output).ok();
}
SetupConfig::new()
Config::new()
}

/// Serializes the configuration and writes it to the path stored in `config.path`.
/// # Errors
/// Returns an error if serialization fails or if the file cannot be written.
pub fn save_config(
config: &mut SetupConfig,
config: &mut Config,
timing: bool,
output: &mut impl Write,
) -> Result<PathBuf, String> {
Expand Down Expand Up @@ -128,12 +128,12 @@ pub fn save_config(
/// # Errors
/// Returns an error if saving the config file fails.
pub fn persist_or_dry_run(
config: &mut SetupConfig,
config: &mut Config,
flags: RunFlags,
io: &mut IoCtx<'_>,
would_msg: &str,
mutate: impl FnOnce(&mut SetupConfig),
on_saved: impl FnOnce(&SetupConfig, &Path, &mut IoCtx<'_>),
mutate: impl FnOnce(&mut Config),
on_saved: impl FnOnce(&Config, &Path, &mut IoCtx<'_>),
) -> Result<(), String> {
if flags.dry_run {
writeln!(io.output, " {would_msg}").ok();
Expand Down
Loading
Loading