use anyhow::Result; use sando_daemon::{config, db, events, git, metrics, routes, state, sync, topology}; use std::net::SocketAddr; use std::path::Path; use std::sync::Arc; #[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() // Modules live under the library crate `sando_daemon` (since // the step-5 lib/bin split). `sandod` is kept for any // top-level events that originate in main.rs itself. .unwrap_or_else(|_| "sando_daemon=info,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 executors = Arc::new(state::build_executors(&topo)); let app_state = state::AppState { pool, topo, cfg, prom, active_build: Arc::new(tokio::sync::Mutex::new(None)), events: events::channel(), executors, }; 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(()) }