diff --git a/crates/maestro-installer/src/lib.rs b/crates/maestro-installer/src/lib.rs index 5ee2128..9412eb8 100644 --- a/crates/maestro-installer/src/lib.rs +++ b/crates/maestro-installer/src/lib.rs @@ -14,7 +14,4 @@ pub mod install; pub mod systemd; pub use install::{cargo_install_args, systemctl_user_args, CargoSource, SystemctlAction}; -pub use systemd::{ - is_systemd_host, render_user_unit, render_web_user_unit, user_unit_path, web_user_unit_path, - DEFAULT_WEB_BIND_ADDR, UNIT_FILE_NAME, WEB_UNIT_FILE_NAME, -}; +pub use systemd::{is_systemd_host, render_user_unit, user_unit_path, UNIT_FILE_NAME}; diff --git a/crates/maestro-installer/src/main.rs b/crates/maestro-installer/src/main.rs index f7fbcd2..2cf12f1 100644 --- a/crates/maestro-installer/src/main.rs +++ b/crates/maestro-installer/src/main.rs @@ -16,15 +16,13 @@ use std::process::{Command, ExitCode}; use clap::Parser; use maestro_installer::install::cargo_bin; use maestro_installer::{ - cargo_install_args, is_systemd_host, render_user_unit, render_web_user_unit, - systemctl_user_args, user_unit_path, web_user_unit_path, CargoSource, SystemctlAction, - DEFAULT_WEB_BIND_ADDR, UNIT_FILE_NAME, WEB_UNIT_FILE_NAME, + cargo_install_args, is_systemd_host, render_user_unit, systemctl_user_args, user_unit_path, + CargoSource, SystemctlAction, UNIT_FILE_NAME, }; const DEFAULT_GIT_URL: &str = "https://git.naps.pt/naps62/maestro"; const DAEMON_CRATE: &str = "maestro-daemon"; const CLI_CRATE: &str = "maestro-cli"; -const REGISTRY_CRATE: &str = "maestro-registry"; const SYSTEMD_PROBE: &str = "/run/systemd/system"; #[derive(Debug, Parser)] @@ -63,24 +61,10 @@ struct Args { #[arg(long)] no_configure: bool, - /// Skip the optional local web-UI systemd unit (no - /// `maestro-registry` install, no `maestro-web.service`). - #[arg(long)] - no_web: bool, - /// Override the ExecStart path written to the systemd unit. /// Default: `~/.cargo/bin/maestrod`. #[arg(long, value_name = "PATH")] exec_start: Option, - - /// Override the ExecStart path for `maestro-web.service`. - /// Default: `~/.cargo/bin/maestro-registry`. - #[arg(long, value_name = "PATH")] - web_exec_start: Option, - - /// Loopback `host:port` the local web UI binds to. - #[arg(long, value_name = "ADDR", default_value = DEFAULT_WEB_BIND_ADDR)] - web_bind: String, } fn main() -> ExitCode { @@ -133,7 +117,6 @@ fn run(args: Args) -> Result<(), Box> { // 3. systemd integration. let systemd_detected = is_systemd_host(Path::new(SYSTEMD_PROBE)); - let systemd_active = systemd_detected && !args.no_systemd; if args.no_systemd { eprintln!("\nSkipping systemd step (--no-systemd)."); } else if !systemd_detected { @@ -142,17 +125,7 @@ fn run(args: Args) -> Result<(), Box> { handle_systemd(&args, interactive, configured)?; } - // 4. Optional local web UI (maestro-registry on loopback). - let web_installed = if systemd_active && !args.no_web { - handle_web_unit(&source, &args, interactive)? - } else { - if args.no_web { - eprintln!("\nSkipping local web UI step (--no-web)."); - } - false - }; - - print_summary(configured, systemd_active, web_installed, &args.web_bind); + print_summary(configured, systemd_detected && !args.no_systemd); Ok(()) } @@ -173,8 +146,7 @@ fn print_banner(source: &CargoSource) { eprintln!("This wizard will:"); eprintln!(" 1. cargo install both `{DAEMON_CRATE}` and `{CLI_CRATE}`"); eprintln!(" 2. (optional) run `maestrod configure` to bootstrap the config file"); - eprintln!(" 3. (optional) install the maestrod systemd user unit"); - eprintln!(" 4. (optional) install a local web UI systemd unit (`maestro-web.service`)"); + eprintln!(" 3. (optional) install a systemd user unit and enable it"); eprintln!(); match source { CargoSource::Git { url, branch } => { @@ -332,105 +304,6 @@ fn default_exec_start() -> Result> { .ok_or_else(|| "could not resolve $HOME (needed for ExecStart)".into()) } -fn default_web_exec_start() -> Result> { - maestro_installer::systemd::default_maestro_registry_path() - .ok_or_else(|| "could not resolve $HOME (needed for web ExecStart)".into()) -} - -/// Drop the web-UI unit (and install `maestro-registry` if the user opts in). -/// Returns `true` if the unit ended up on disk after this call. -fn handle_web_unit( - source: &CargoSource, - args: &Args, - interactive: bool, -) -> Result> { - let unit_path = web_user_unit_path().ok_or("could not resolve ~/.config/systemd/user/")?; - let already_installed = unit_path.exists(); - - eprintln!(); - if already_installed { - eprintln!( - "local web UI unit already present at {} — refreshing.", - unit_path.display() - ); - } else { - eprintln!("Local web UI: an optional `maestro-web.service` runs `maestro-registry`"); - eprintln!( - "on {bind} so you can open it in a browser. Useful for single-machine setups;", - bind = args.web_bind - ); - eprintln!("skip if you already have a remote registry deployed."); - } - - let want_unit = if already_installed || !interactive { - true - } else { - confirm( - &format!( - "Install local web UI unit at {} (also runs cargo install maestro-registry)?", - unit_path.display() - ), - false, - )? - }; - if !want_unit { - return Ok(false); - } - - // Install (or update) the registry binary first — the unit's ExecStart - // points at it. - let locked = !args.no_locked; - install_crate(source, REGISTRY_CRATE, locked)?; - - let exec_start = match &args.web_exec_start { - Some(p) => p.clone(), - None => default_web_exec_start()?, - }; - if let Some(parent) = unit_path.parent() { - std::fs::create_dir_all(parent)?; - } - let body = render_web_user_unit(&exec_start, &args.web_bind); - std::fs::write(&unit_path, body)?; - eprintln!("wrote {}", unit_path.display()); - - run_systemctl(SystemctlAction::DaemonReload, WEB_UNIT_FILE_NAME)?; - - if !already_installed { - let want_enable = if !interactive { - true - } else { - confirm("Enable maestro-web.service (start on login)?", true)? - }; - if want_enable { - run_systemctl(SystemctlAction::Enable, WEB_UNIT_FILE_NAME)?; - } - } - - let (action, want) = if already_installed { - let want = if !interactive { - true - } else { - confirm( - "Restart maestro-web.service to pick up the new binary?", - true, - )? - }; - (SystemctlAction::Restart, want) - } else { - let want = if !interactive { - true - } else { - confirm("Start maestro-web.service now?", true)? - }; - (SystemctlAction::Start, want) - }; - if want { - run_systemctl(action, WEB_UNIT_FILE_NAME)?; - } - - Ok(true) -} - fn run_systemctl(action: SystemctlAction, unit: &str) -> Result<(), Box> { let args = systemctl_user_args(action, unit); eprintln!("\n==> systemctl {}", args.join(" ")); @@ -455,7 +328,7 @@ fn confirm(prompt: &str, default_yes: bool) -> io::Result { }) } -fn print_summary(configured: bool, systemd_enabled: bool, web_installed: bool, web_bind: &str) { +fn print_summary(configured: bool, systemd_enabled: bool) { eprintln!(); eprintln!("Done."); eprintln!(); @@ -470,8 +343,4 @@ fn print_summary(configured: bool, systemd_enabled: bool, web_installed: bool, w eprintln!(" • Start the daemon: `maestrod`"); } eprintln!(" • Set the CLI's registry: `maestro set-registry `"); - if web_installed { - eprintln!(" • Open the local web UI: http://{web_bind}"); - eprintln!(" • Web status: `systemctl --user status maestro-web`"); - } } diff --git a/crates/maestro-installer/src/systemd.rs b/crates/maestro-installer/src/systemd.rs index 8b82b6d..a0b8592 100644 --- a/crates/maestro-installer/src/systemd.rs +++ b/crates/maestro-installer/src/systemd.rs @@ -11,16 +11,6 @@ pub const SYSTEMD_PROBE_PATH: &str = "/run/systemd/system"; /// Filename written under `~/.config/systemd/user/`. pub const UNIT_FILE_NAME: &str = "maestrod.service"; -/// Optional second unit: a local `maestro-registry` instance that serves the -/// web UI on loopback. Independent of the daemon process-wise, but useless on -/// its own — `Wants=maestrod.service` + `After=` express that softly. -pub const WEB_UNIT_FILE_NAME: &str = "maestro-web.service"; - -/// Default loopback bind for the local web UI. Picked outside the registry -/// binary's own default (8080) to make it obvious which is which on hosts -/// where both might coexist with a separately-deployed registry. -pub const DEFAULT_WEB_BIND_ADDR: &str = "127.0.0.1:8888"; - /// `true` when the given path exists. Production callers should pass /// [`SYSTEMD_PROBE_PATH`]; tests inject a temp path so they're hermetic. pub fn is_systemd_host(probe: &Path) -> bool { @@ -68,53 +58,6 @@ pub fn default_maestrod_path() -> Option { ) } -/// `~/.config/systemd/user/maestro-web.service`. `None` if `$HOME` / config -/// dir is unresolvable. -pub fn web_user_unit_path() -> Option { - let base = dirs::config_dir()?; - Some(base.join("systemd").join("user").join(WEB_UNIT_FILE_NAME)) -} - -/// Render the web-UI unit body. `exec_start` is the absolute path to -/// `maestro-registry`; `bind_addr` is the `host:port` the registry listens -/// on (set via the `MAESTRO_REGISTRY_BIND_ADDR` env var). -/// -/// `Wants=` rather than `Requires=`: the registry binary doesn't actually -/// need maestrod to start — they're decoupled at the process level. But the -/// web UI has nothing to show without a daemon connected, so we order this -/// after maestrod and politely express the dependency. -pub fn render_web_user_unit(exec_start: &Path, bind_addr: &str) -> String { - format!( - "[Unit]\n\ - Description=Maestro local web UI (maestro-registry on {bind})\n\ - Documentation=https://git.naps.pt/naps62/maestro\n\ - After=network-online.target maestrod.service\n\ - Wants=maestrod.service\n\ - \n\ - [Service]\n\ - Type=simple\n\ - Environment=MAESTRO_REGISTRY_BIND_ADDR={bind}\n\ - ExecStart={exec}\n\ - Restart=on-failure\n\ - RestartSec=5\n\ - \n\ - [Install]\n\ - WantedBy=default.target\n", - exec = exec_start.display(), - bind = bind_addr, - ) -} - -/// Default `ExecStart` for the web unit: `~/.cargo/bin/maestro-registry`. -pub fn default_maestro_registry_path() -> Option { - Some( - dirs::home_dir()? - .join(".cargo") - .join("bin") - .join("maestro-registry"), - ) -} - #[cfg(test)] mod tests { use super::*; @@ -156,25 +99,4 @@ mod tests { let p = user_unit_path().expect("config dir resolvable in test env"); assert!(p.ends_with("systemd/user/maestrod.service"), "got {p:?}"); } - - #[test] - fn web_unit_renders_with_bind_and_exec() { - let body = render_web_user_unit( - Path::new("/home/alice/.cargo/bin/maestro-registry"), - "127.0.0.1:8888", - ); - assert!(body.contains("ExecStart=/home/alice/.cargo/bin/maestro-registry")); - assert!(body.contains("Environment=MAESTRO_REGISTRY_BIND_ADDR=127.0.0.1:8888")); - assert!(body.contains("After=network-online.target maestrod.service")); - assert!(body.contains("Wants=maestrod.service")); - assert!(body.contains("WantedBy=default.target")); - assert!(body.contains("Restart=on-failure")); - assert!(body.ends_with('\n')); - } - - #[test] - fn web_unit_path_uses_correct_filename() { - let p = web_user_unit_path().expect("config dir resolvable in test env"); - assert!(p.ends_with("systemd/user/maestro-web.service"), "got {p:?}"); - } } diff --git a/docs/INSTALL.md b/docs/INSTALL.md index ca059aa..267dcaf 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -43,13 +43,8 @@ The wizard: 3. On systemd hosts, optionally writes a user unit at `~/.config/systemd/user/maestrod.service`, reloads systemd, and offers to enable + start it. -4. On systemd hosts, also offers a **local web UI** unit - (`~/.config/systemd/user/maestro-web.service`). When accepted, this also - `cargo install`s `maestro-registry` and runs it bound to `127.0.0.1:8888` - (override with `--web-bind`). Skip with `--no-web` (or just answer "n"); a - remote registry deployment is the alternative. -Binaries land in `~/.cargo/bin` — make sure that's on your `$PATH`. +Both binaries land in `~/.cargo/bin` — make sure that's on your `$PATH`. ## Flags @@ -62,9 +57,6 @@ Binaries land in `~/.cargo/bin` — make sure that's on your `$PATH`. | `--exec-start ` | Override the systemd unit's `ExecStart` (default `~/.cargo/bin/maestrod`). | | `--no-systemd` | Skip the systemd step even if detected. | | `--no-configure` | Skip the `maestrod configure` step. | -| `--no-web` | Skip the optional local web-UI unit + `maestro-registry` install. | -| `--web-bind ` | Loopback bind for the web UI (default `127.0.0.1:8888`). | -| `--web-exec-start ` | Override the web unit's `ExecStart` (default `~/.cargo/bin/maestro-registry`). | | `-y`, `--yes` | Non-interactive: install binaries + drop systemd unit + reload + enable, but **don't** auto-run `configure` or auto-start the unit (those need real config). | ## What the systemd unit looks like @@ -95,31 +87,6 @@ systemctl --user status maestrod journalctl --user -u maestrod -f ``` -The optional `maestro-web.service` looks like: - -```ini -[Unit] -Description=Maestro local web UI (maestro-registry on 127.0.0.1:8888) -Documentation=https://git.naps.pt/naps62/maestro -After=network-online.target maestrod.service -Wants=maestrod.service - -[Service] -Type=simple -Environment=MAESTRO_REGISTRY_BIND_ADDR=127.0.0.1:8888 -ExecStart=%h/.cargo/bin/maestro-registry -Restart=on-failure -RestartSec=5 - -[Install] -WantedBy=default.target -``` - -`Wants=maestrod.service` (not `Requires=`) — the registry process is -technically independent of the daemon, but the web UI has nothing to show -without one connected, so we order this after `maestrod` and politely -encourage it. - ## Running the wizard without the bootstrap script If you'd rather skip the script: