//! Daemon-local config (`bento-daemon.toml`) — paths and listen address that //! belong to the machine bentod runs on, not to the build matrix (which lives //! in the separate topology file, see [`crate::topology`]). use anyhow::{Context, Result}; use serde::Deserialize; use std::path::PathBuf; #[derive(Debug, Clone, Deserialize)] pub struct Config { pub listen: String, pub db_path: PathBuf, pub topology_path: PathBuf, /// Root of the Syncthing private layer (`~/Code/_private`); the `secret()` /// host function reads credential files relative to here. Never logged. pub secrets_root: PathBuf, /// Where collected artifacts land (`///`). pub dist_root: PathBuf, /// Root for per-step run logs /// (`////.log`). #[serde(default = "default_logs_root")] pub logs_root: PathBuf, /// Override the per-step wall-clock budget (in seconds) for EVERY step, /// replacing the per-kind defaults in `engine::step_budget`. Unset (the /// default) uses those. Mainly an escape hatch for a constrained host or a /// test that needs a short deadline. #[serde(default)] pub step_timeout_secs: Option, /// Seconds to wait between `notarize` retry attempts. Unset (the default) /// uses the 15-second production backoff; a test drives the retry loop with /// `Some(0)` so it doesn't actually sleep. Same test-seam shape as /// `step_timeout_secs`. #[serde(default)] pub notarize_backoff_secs: Option, /// Pin every build host to the release tag `v` and verify they all /// report the same commit BEFORE any target builds — so `mbp`/`astra`/`fw13` /// can't each build whatever `main` happened to be at pull time. On by /// default; turn off only to build from an untagged commit (or in tests /// whose repos aren't git checkouts). #[serde(default = "default_true")] pub pin_release_sha: bool, /// The command that installs a service binary and restarts its unit, run on /// the service host with ` ` appended. /// /// Bento never installs or restarts anything itself. It stages bytes and /// calls this, and what it calls is a root script whose arguments are /// re-checked on the far side — so the sudoers grant on a production box is /// ONE auditable script rather than a broad `install` + `systemctl` grant. /// Same shape as Sando's `install-companion.sh`. /// /// Configurable because the sudo path differs per host fleet, and because a /// test needs to point it somewhere that is not root. It is read from the /// same trusted daemon config that already names the secrets root, so this /// adds no trust boundary that was not already there. #[serde(default = "default_deploy_installer")] pub deploy_installer: String, } fn default_true() -> bool { true } fn default_deploy_installer() -> String { "sudo /usr/local/lib/bento/install-service.sh".into() } fn default_logs_root() -> PathBuf { PathBuf::from("/srv/bento/logs") } impl Config { pub fn load() -> Result { let path = std::env::var("BENTO_CONFIG").unwrap_or_else(|_| "bento-daemon.toml".into()); let raw = std::fs::read_to_string(&path) .with_context(|| format!("reading daemon config at {path}"))?; Ok(toml::from_str(&raw)?) } #[cfg(test)] pub fn for_tests(root: &std::path::Path) -> Self { Self { listen: "127.0.0.1:0".into(), db_path: root.join("bento.db"), topology_path: root.join("bento.toml"), secrets_root: root.join("secrets"), dist_root: root.join("dist"), logs_root: root.join("logs"), step_timeout_secs: None, notarize_backoff_secs: None, // Test repos are plain dirs, not git checkouts; the barrier is // exercised by its own tests that build a real tagged repo. pin_release_sha: false, deploy_installer: default_deploy_installer(), } } }