use anyhow::Result; use std::net::SocketAddr; use std::path::Path; use std::sync::Arc; mod backup; mod build; mod config; mod db; mod deploy; mod error; mod gates; mod git; mod metrics; mod routes; mod state; mod sync; mod topology; #[tokio::main] async fn main() -> Result<()> { tracing_subscriber::fmt() // stdout is block-buffered under systemd (no TTY) so events never // reach journald until the buffer fills or the process exits. stderr // is line-buffered, which is what we want for a long-running service. .with_writer(std::io::stderr) .with_env_filter( tracing_subscriber::EnvFilter::try_from_default_env() // bin target name is `sandod`, NOT the package name `sando-daemon` — // `module_path!()` uses the binary's crate name, so events come from `sandod::*`. .unwrap_or_else(|_| "sandod=info,tower_http=info".into()), ) .init(); let cfg = Arc::new(config::Config::load()?); let topo = Arc::new(topology::Topology::load(&cfg.topology_path)?); tokio::fs::create_dir_all(&cfg.workdir).await?; tokio::fs::create_dir_all(&cfg.release_root).await?; git::ensure_bare_repo(Path::new(&topo.repo.bare_path)).await?; let pool = db::connect(&cfg.db_path).await?; db::migrate(&pool).await?; sync::sync(&pool, &*topo).await?; tracing::info!(tiers = topo.tiers.len(), bare = %topo.repo.bare_path, "topology synced"); let prom = metrics::init(); let addr: SocketAddr = cfg.listen.parse()?; let app_state = state::AppState { pool, topo, cfg, prom }; let app = routes::router(app_state); tracing::info!(%addr, "sando daemon listening"); let listener = tokio::net::TcpListener::bind(addr).await?; axum::serve(listener, app).await?; Ok(()) }