Skip to main content

horto_os_ui_shared/steps/
m1_minimal.rs

1//! Minimal pipeline step: cockpit packages + hostname env (`m1`).
2//!
3//! reference: horto-os/scripts/m1_minimal_setup_run.sh
4use crate::context::{HostContext, PlannedAction};
5use crate::embed;
6use crate::error::{HortoError, Result};
7use crate::kits::{apt, envfile, fs};
8use crate::step::Step;
9use std::collections::BTreeMap;
10
11/// Minimal setup: install cockpit (when root) and write `minimal_setup_vars.env`.
12pub struct M1Minimal;
13
14impl Step for M1Minimal {
15    fn id(&self) -> &'static str {
16        "m1"
17    }
18    fn title(&self) -> &'static str {
19        "Minimal: cockpit + hostname env"
20    }
21    fn reference_script(&self) -> &'static str {
22        "m1_minimal_setup_run.sh"
23    }
24    fn step_version(&self) -> u32 {
25        1
26    }
27    fn is_done(&self, ctx: &HostContext) -> bool {
28        let path = ctx.paths.minimal_env_file();
29        path.exists()
30            && envfile::load(&path)
31                .ok()
32                .and_then(|m| envfile::require_keys(&m, &["MY_HOSTNAME"]).ok())
33                .is_some()
34    }
35    fn plan(&self, ctx: &mut HostContext) -> Result<Vec<PlannedAction>> {
36        ctx.plan_action("apt update");
37        ctx.plan_action("apt install -y cockpit cockpit-networkmanager");
38        ctx.plan_action(format!(
39            "write {} with MY_HOSTNAME",
40            ctx.paths.minimal_env_file().display()
41        ));
42        Ok(ctx.planned.clone())
43    }
44    fn apply(&self, ctx: &mut HostContext) -> Result<()> {
45        fs::ensure_dir(ctx, &ctx.paths.active_setup.clone())?;
46        if ctx.is_dry_run() || crate::context::is_root() {
47            apt::apt_update(ctx)?;
48            apt::apt_install(ctx, &["cockpit", "cockpit-networkmanager"])?;
49        } else {
50            ctx.log("Not root; skipping cockpit apt install");
51        }
52
53        let active = ctx.paths.minimal_env_file();
54        if ctx.is_dry_run() {
55            ctx.plan_action(format!("write {}", active.display()));
56            return Ok(());
57        }
58
59        let map = if active.exists() {
60            envfile::load(&active)?
61        } else {
62            let template = embed::get_str("config/minimal_setup_vars.env")
63                .ok_or_else(|| HortoError::EmbedMissing("config/minimal_setup_vars.env".into()))?;
64            envfile::parse(&template)
65        };
66
67        let hostname = ctx.prompt(
68            "Device hostname",
69            map.get("MY_HOSTNAME")
70                .map_or("Horto-OS_xxx", String::as_str),
71        );
72        if hostname.is_empty() {
73            return Err(HortoError::msg("MY_HOSTNAME is empty"));
74        }
75        let mut out = BTreeMap::new();
76        envfile::set_key(&mut out, "MY_HOSTNAME", hostname);
77        envfile::write(&active, &out)?;
78        ctx.log(format!(
79            "Loaded minimal deployment variables from {}",
80            active.display()
81        ));
82        ctx.log("Manual network configuration remains your responsibility in minimal mode.");
83        Ok(())
84    }
85}