mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-05 08:10:39 +00:00
Add --config CLI arg to override server config path
load_server_config() now accepts an optional explicit path. When provided, it reads from that path (erroring if missing) instead of the default ~/.arc/server.toml. The --config flag is wired through ServeArgs and the hot-reload polling loop. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
15da1c04b9
commit
ba90d1e90e
4 changed files with 43 additions and 9 deletions
|
|
@ -1,3 +1,4 @@
|
|||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::Duration;
|
||||
|
||||
|
|
@ -50,6 +51,10 @@ pub struct ServeArgs {
|
|||
/// Maximum number of concurrent run executions
|
||||
#[arg(long)]
|
||||
pub max_concurrent_runs: Option<usize>,
|
||||
|
||||
/// Path to server config file (default: ~/.arc/server.toml)
|
||||
#[arg(long)]
|
||||
pub config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
/// Start the HTTP API server.
|
||||
|
|
@ -82,7 +87,8 @@ pub async fn serve_command(args: ServeArgs, styles: &'static Styles) -> anyhow::
|
|||
};
|
||||
|
||||
// Initialize data directory and SQLite database
|
||||
let server_config = crate::server_config::load_server_config()?;
|
||||
let config_path = args.config;
|
||||
let server_config = crate::server_config::load_server_config(config_path.as_deref())?;
|
||||
let data_dir = crate::server_config::resolve_data_dir(&server_config);
|
||||
|
||||
// Shared config for live reloading
|
||||
|
|
@ -160,12 +166,13 @@ pub async fn serve_command(args: ServeArgs, styles: &'static Styles) -> anyhow::
|
|||
// Spawn config polling task (skip in demo mode)
|
||||
if !args.demo {
|
||||
let config_for_poll = Arc::clone(&shared_config);
|
||||
let config_path_for_poll = config_path.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(5));
|
||||
interval.tick().await; // skip first immediate tick
|
||||
loop {
|
||||
interval.tick().await;
|
||||
match crate::server_config::load_server_config() {
|
||||
match crate::server_config::load_server_config(config_path_for_poll.as_deref()) {
|
||||
Ok(new_config) => {
|
||||
let changed = {
|
||||
let cfg = config_for_poll.read().expect("config lock poisoned");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use arc_workflows::cli::run_config::RunDefaults;
|
||||
use serde::Deserialize;
|
||||
use tracing::debug;
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
|
|
@ -106,13 +107,22 @@ pub struct ServerConfig {
|
|||
pub run_defaults: RunDefaults,
|
||||
}
|
||||
|
||||
/// Load server config from `~/.arc/server.toml`, returning defaults if the file doesn't exist.
|
||||
pub fn load_server_config() -> anyhow::Result<ServerConfig> {
|
||||
/// Load server config from an explicit path or `~/.arc/server.toml`, returning defaults if the
|
||||
/// default file doesn't exist. An explicit path that doesn't exist is an error.
|
||||
pub fn load_server_config(path: Option<&Path>) -> anyhow::Result<ServerConfig> {
|
||||
if let Some(explicit) = path {
|
||||
debug!(path = %explicit.display(), "Loading server config from explicit path");
|
||||
let contents = std::fs::read_to_string(explicit)?;
|
||||
return Ok(toml::from_str(&contents)?);
|
||||
}
|
||||
|
||||
let Some(home) = dirs::home_dir() else {
|
||||
debug!("No home directory found, using default server config");
|
||||
return Ok(ServerConfig::default());
|
||||
};
|
||||
let path = home.join(".arc").join("server.toml");
|
||||
match std::fs::read_to_string(&path) {
|
||||
let default_path = home.join(".arc").join("server.toml");
|
||||
debug!(path = %default_path.display(), "Loading server config");
|
||||
match std::fs::read_to_string(&default_path) {
|
||||
Ok(contents) => Ok(toml::from_str(&contents)?),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(ServerConfig::default()),
|
||||
Err(e) => Err(e.into()),
|
||||
|
|
@ -354,4 +364,21 @@ authentication_strategies = ["jwt"]
|
|||
);
|
||||
assert!(config.api.tls.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_server_config_from_explicit_path() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let path = dir.path().join("custom.toml");
|
||||
std::fs::write(&path, r#"max_concurrent_runs = 42"#).unwrap();
|
||||
let config = load_server_config(Some(&path)).unwrap();
|
||||
assert_eq!(config.max_concurrent_runs, Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_server_config_explicit_path_missing_is_error() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let path = dir.path().join("nonexistent.toml");
|
||||
let result = load_server_config(Some(&path));
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -890,7 +890,7 @@ pub async fn run_doctor(verbose: bool, live: bool) -> i32 {
|
|||
|
||||
let brave_key_set = std::env::var("BRAVE_SEARCH_API_KEY").is_ok();
|
||||
|
||||
let server_config = arc_api::server_config::load_server_config().unwrap_or_default();
|
||||
let server_config = arc_api::server_config::load_server_config(None).unwrap_or_default();
|
||||
|
||||
let api_status = ApiStatus {
|
||||
base_url: server_config.api.base_url.clone(),
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ async fn main() -> Result<()> {
|
|||
RunCommand::Start(args) => {
|
||||
let styles: &'static arc_util::terminal::Styles =
|
||||
Box::leak(Box::new(arc_util::terminal::Styles::detect_stderr()));
|
||||
let server_config = arc_api::server_config::load_server_config()?;
|
||||
let server_config = arc_api::server_config::load_server_config(None)?;
|
||||
arc_workflows::cli::run::run_command(args, server_config.run_defaults, styles)
|
||||
.await?;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue