diff --git a/crates/maestro-installer/src/install.rs b/crates/maestro-installer/src/install.rs index 16908a7..7bc3b1f 100644 --- a/crates/maestro-installer/src/install.rs +++ b/crates/maestro-installer/src/install.rs @@ -41,7 +41,18 @@ impl SystemctlAction { /// Build the args for `cargo install ` (excluding the `cargo` and /// `install` words themselves). `locked` adds `--locked` so the install uses /// the committed `Cargo.lock`. -pub fn cargo_install_args(source: &CargoSource, crate_name: &str, locked: bool) -> Vec { +/// Build the `cargo install` argument vector. +/// +/// `features`, when `Some`, restricts the build to an explicit feature set via +/// `--no-default-features --features `. The installer uses this to skip +/// the `registry` feature of the umbrella `maestro` crate: a host runs the CLI +/// + daemon (+ TUI); the registry is a separate, centrally-deployed service. +pub fn cargo_install_args( + source: &CargoSource, + crate_name: &str, + locked: bool, + features: Option<&str>, +) -> Vec { let mut args: Vec = Vec::new(); match source { CargoSource::Git { url, branch } => { @@ -60,6 +71,11 @@ pub fn cargo_install_args(source: &CargoSource, crate_name: &str, locked: bool) args.push(crate_name.to_string()); } } + if let Some(list) = features { + args.push("--no-default-features".into()); + args.push("--features".into()); + args.push(list.to_string()); + } if locked { args.push("--locked".into()); } @@ -101,20 +117,41 @@ mod tests { url: "https://git.naps.pt/naps62/maestro".into(), branch: None, }; - let args = cargo_install_args(&src, "maestro", false); + let args = cargo_install_args(&src, "maestro", false, None); assert_eq!( args, vec!["--git", "https://git.naps.pt/naps62/maestro", "maestro",] ); } + #[test] + fn git_args_with_features_omit_defaults() { + let src = CargoSource::Git { + url: "https://git.naps.pt/naps62/maestro".into(), + branch: None, + }; + let args = cargo_install_args(&src, "maestro", true, Some("cli,daemon,tui")); + assert_eq!( + args, + vec![ + "--git", + "https://git.naps.pt/naps62/maestro", + "maestro", + "--no-default-features", + "--features", + "cli,daemon,tui", + "--locked", + ] + ); + } + #[test] fn git_args_with_branch_and_locked() { let src = CargoSource::Git { url: "https://git.example/x".into(), branch: Some("main".into()), }; - let args = cargo_install_args(&src, "maestro", true); + let args = cargo_install_args(&src, "maestro", true, None); assert_eq!( args, vec![ @@ -131,7 +168,7 @@ mod tests { #[test] fn path_args_use_p_flag() { let src = CargoSource::Path(PathBuf::from("/repos/maestro")); - let args = cargo_install_args(&src, "maestro", true); + let args = cargo_install_args(&src, "maestro", true, None); assert_eq!( args, vec!["--path", "/repos/maestro", "-p", "maestro", "--locked",] diff --git a/crates/maestro-installer/src/main.rs b/crates/maestro-installer/src/main.rs index beff358..d147e3b 100644 --- a/crates/maestro-installer/src/main.rs +++ b/crates/maestro-installer/src/main.rs @@ -22,8 +22,14 @@ use maestro_installer::{ }; const DEFAULT_GIT_URL: &str = "https://git.naps.pt/naps62/maestro"; -/// Unified `maestro` crate — single `cargo install` covers CLI + daemon + registry. +/// Unified `maestro` crate. The installer builds it with the host feature set +/// (CLI + daemon + TUI); the `registry` feature is left out (see +/// `MAESTRO_HOST_FEATURES`). const MAESTRO_CRATE: &str = "maestro"; +/// Features installed for a host: CLI + per-host daemon + TUI. The `registry` +/// feature is deliberately omitted — the registry is a separate, centrally- +/// deployed service, not something a host install should compile or run. +const MAESTRO_HOST_FEATURES: &str = "cli,daemon,tui"; const WEB_CRATE: &str = "maestro-web"; const SYSTEMD_PROBE: &str = "/run/systemd/system"; @@ -101,9 +107,9 @@ fn run(args: Args) -> Result<(), Box> { return Ok(()); } - // 1. Install the unified binary (NAP-245). + // 1. Install the unified binary (NAP-245), minus the registry feature. let locked = !args.no_locked; - install_crate(&source, MAESTRO_CRATE, locked)?; + install_crate(&source, MAESTRO_CRATE, locked, Some(MAESTRO_HOST_FEATURES))?; // 2. Offer to run `maestro daemon configure` now (must happen before systemd // start, since the daemon refuses to run without registry_url / token). @@ -164,7 +170,9 @@ fn print_banner(source: &CargoSource) { eprintln!("maestro installer"); eprintln!(); eprintln!("This wizard will:"); - eprintln!(" 1. cargo install `{MAESTRO_CRATE}` (unified CLI + daemon + registry)"); + eprintln!( + " 1. cargo install `{MAESTRO_CRATE}` (CLI + daemon + TUI; registry runs separately)" + ); eprintln!(" 2. (optional) run `maestro daemon configure` to bootstrap the config file"); eprintln!(" 3. (optional) install a systemd user unit and enable it"); eprintln!(" 4. (optional) install `{WEB_CRATE}` + its systemd unit (browser UI)"); @@ -185,9 +193,10 @@ fn install_crate( source: &CargoSource, crate_name: &str, locked: bool, + features: Option<&str>, ) -> Result<(), Box> { let cargo = cargo_bin(); - let args = cargo_install_args(source, crate_name, locked); + let args = cargo_install_args(source, crate_name, locked, features); eprintln!("\n==> cargo install {}", args.join(" ")); let status = Command::new(&cargo).arg("install").args(&args).status()?; if !status.success() { @@ -449,7 +458,7 @@ fn handle_web( // doesn't need to be undone. let source = build_source(args); let locked = !args.no_locked; - if let Err(e) = install_crate(&source, WEB_CRATE, locked) { + if let Err(e) = install_crate(&source, WEB_CRATE, locked, None) { eprintln!("maestro-web install failed: {e}"); eprintln!("(skipping systemd unit; re-run after fixing the build.)"); return Ok(false);