1use anyhow::{Context, Result};
11use robonix_cli::output;
12use std::path::PathBuf;
13
14use super::teardown;
15
16pub async fn execute(file: PathBuf) -> Result<()> {
17 let manifest_path = file.canonicalize().with_context(|| {
18 format!(
19 "manifest not found: {} (rbnx shutdown defaults to ./robonix_manifest.yaml; \
20 pass -f to point elsewhere)",
21 file.display()
22 )
23 })?;
24 let manifest_dir = manifest_path
25 .parent()
26 .context("manifest has no parent directory")?
27 .to_path_buf();
28
29 let state_path = teardown::state_path(&manifest_dir);
30 if !state_path.exists() {
31 anyhow::bail!(
32 "no boot state at {} — has `rbnx boot` been run from this manifest? \
33 (state is written when boot starts spawning components and removed on shutdown.)",
34 state_path.display()
35 );
36 }
37
38 let state = teardown::read_state(&state_path)?;
39 output::action(
40 "Shutting down",
41 &format!(
42 "{} ({} component(s))",
43 state.manifest_path,
44 state.components.len()
45 ),
46 );
47
48 teardown::teardown(&state.components).await;
49
50 if state.boot_pid != 0 && state.boot_pid != std::process::id() {
55 let pid = nix::unistd::Pid::from_raw(state.boot_pid as i32);
56 let _ = nix::sys::signal::kill(pid, nix::sys::signal::Signal::SIGTERM);
57 }
58
59 let _ = std::fs::remove_file(&state_path);
60 output::success(&format!("torn down — removed {}", state_path.display()));
61 Ok(())
62}