diff --git a/lib/crates/fabro-cli/src/command_context.rs b/lib/crates/fabro-cli/src/command_context.rs index e844d2cbb..107f7dbcf 100644 --- a/lib/crates/fabro-cli/src/command_context.rs +++ b/lib/crates/fabro-cli/src/command_context.rs @@ -187,9 +187,9 @@ mod tests { use std::path::PathBuf; use fabro_config::parse_settings_layer; - use fabro_config::user::apply_storage_dir_override; use fabro_types::settings::InterpString; use fabro_types::settings::cli::{CliLayer, CliOutputLayer, OutputFormat, OutputVerbosity}; + use fabro_types::settings::server::{ServerLayer, ServerStorageLayer}; use fabro_util::printer::Printer; use tokio::sync::OnceCell; @@ -223,6 +223,18 @@ mod tests { } } + fn with_storage_dir_override( + mut layer: fabro_types::settings::SettingsLayer, + path: &std::path::Path, + ) -> fabro_types::settings::SettingsLayer { + let server = layer.server.get_or_insert_with(ServerLayer::default); + let storage = server + .storage + .get_or_insert_with(ServerStorageLayer::default); + storage.root = Some(InterpString::parse(&path.display().to_string())); + layer + } + #[test] fn context_exposes_resolved_output_and_explicit_json_state() { let ctx = synthetic_context(true, Printer::Default); @@ -248,9 +260,9 @@ root = "/srv/fabro/default" "#, ) .expect("settings fixture should parse"); - let override_disk_settings = apply_storage_dir_override( + let override_disk_settings = with_storage_dir_override( base_disk_settings.clone(), - Some(std::path::Path::new("/srv/fabro/override")), + std::path::Path::new("/srv/fabro/override"), ); let (base_settings, base_user_settings) = diff --git a/lib/crates/fabro-cli/src/commands/install.rs b/lib/crates/fabro-cli/src/commands/install.rs index 05ec460ad..83d5bbcd6 100644 --- a/lib/crates/fabro-cli/src/commands/install.rs +++ b/lib/crates/fabro-cli/src/commands/install.rs @@ -1464,16 +1464,13 @@ async fn run_install_github_inner( let existing_config_contents = std::fs::read_to_string(&config_path).context("failed to read existing settings.toml")?; - let parsed_settings = user_config::apply_storage_dir_override( - fabro_config::parse_settings_layer(&existing_config_contents) - .context("failed to parse existing settings.toml")?, - args.storage_dir.as_deref(), - ); - let storage_dir = local_server::storage_dir(&parsed_settings).unwrap_or_else(|_| { - args.storage_dir - .clone_path() - .unwrap_or_else(default_storage_dir) - }); + let parsed_settings = fabro_config::parse_settings_layer(&existing_config_contents) + .context("failed to parse existing settings.toml")?; + let storage_dir = args + .storage_dir + .clone_path() + .or_else(|| local_server::storage_dir(&parsed_settings).ok()) + .unwrap_or_else(default_storage_dir); let server_was_running = ServerDaemon::load_running(&Storage::new(&storage_dir).runtime_directory())?.is_some(); let mut doc: toml::Value = toml::from_str(&existing_config_contents) @@ -1777,11 +1774,8 @@ async fn run_install_inner(args: &InstallArgs, ctx: &CommandContext) -> Result<( toml::to_string_pretty(&doc)? }; - let install_settings = user_config::apply_storage_dir_override( - fabro_config::parse_settings_layer(&settings_toml) - .context("failed to parse generated settings.toml")?, - args.storage_dir.as_deref(), - ); + let install_settings = fabro_config::parse_settings_layer(&settings_toml) + .context("failed to parse generated settings.toml")?; fabro_config::ServerSettingsBuilder::from_layer(&install_settings)?; // Secrets and auth material diff --git a/lib/crates/fabro-cli/src/manifest_builder.rs b/lib/crates/fabro-cli/src/manifest_builder.rs index 9ffeb71de..616c4516d 100644 --- a/lib/crates/fabro-cli/src/manifest_builder.rs +++ b/lib/crates/fabro-cli/src/manifest_builder.rs @@ -9,8 +9,9 @@ use std::path::{Component, Path, PathBuf}; use anyhow::{Context, Result, anyhow}; use fabro_api::types; use fabro_config::load::load_settings_for_workflow; +use fabro_config::parse_settings_layer; use fabro_config::project::{self, discover_project_config, resolve_workflow_path}; -use fabro_config::run::{parse_run_config, resolve_run_goal}; +use fabro_config::run::resolve_run_goal; use fabro_graphviz::graph::AttrValue; use fabro_graphviz::parser; use fabro_sandbox::daytona::detect_repo_info; @@ -325,7 +326,8 @@ fn collect_workflow_config_files( config: &types::ManifestWorkflowConfig, files: &mut HashMap, ) -> Result<()> { - let config_layer = parse_run_config(&config.source)?; + let config_layer = parse_settings_layer(&config.source) + .map_err(|err| anyhow!("Failed to parse run config TOML: {err}"))?; let dockerfile = config_layer .run .as_ref() diff --git a/lib/crates/fabro-cli/src/user_config.rs b/lib/crates/fabro-cli/src/user_config.rs index 9907aa709..ad15109a3 100644 --- a/lib/crates/fabro-cli/src/user_config.rs +++ b/lib/crates/fabro-cli/src/user_config.rs @@ -4,7 +4,8 @@ use std::str::FromStr; use anyhow::Result; pub(crate) use fabro_client::ServerTarget; use fabro_config::UserSettingsBuilder; -pub(crate) use fabro_config::user::*; +pub(crate) use fabro_config::user::{active_settings_path, default_storage_dir}; +use fabro_config::user::{default_socket_path, load_settings_config}; use fabro_types::settings::cli::CliTargetSettings; use fabro_types::settings::{CliNamespace, SettingsLayer}; use fabro_util::version::FABRO_VERSION; @@ -31,6 +32,24 @@ pub(crate) fn load_settings_with_config_and_storage_dir( Ok(apply_storage_dir_override(layer, storage_dir)) } +fn apply_storage_dir_override( + mut layer: SettingsLayer, + storage_dir: Option<&Path>, +) -> SettingsLayer { + use fabro_types::settings::InterpString; + use fabro_types::settings::server::{ServerLayer, ServerStorageLayer}; + + if let Some(dir) = storage_dir { + let server = layer.server.get_or_insert_with(ServerLayer::default); + let storage = server + .storage + .get_or_insert_with(ServerStorageLayer::default); + storage.root = Some(InterpString::parse(&dir.display().to_string())); + } + + layer +} + /// Pull the resolved CLI target configuration out of `[cli.target]`. /// Returns either an http(s) URL or a unix socket path. fn cli_target_from_settings(settings: &CliNamespace) -> Option { diff --git a/lib/crates/fabro-config/src/run.rs b/lib/crates/fabro-config/src/run.rs index 2cc603978..a99decc6f 100644 --- a/lib/crates/fabro-config/src/run.rs +++ b/lib/crates/fabro-config/src/run.rs @@ -1,9 +1,8 @@ //! Workflow / run config loading helpers. //! -//! Thin wrappers around `parse_settings_layer` / `load_settings_path` plus -//! path resolution for the `[workflow] graph` override. Runtime types -//! that used to be re-exported from here live under -//! `fabro_types::settings::run` now. +//! Helpers for loading workflow-local settings and resolving runtime goal / +//! graph paths. Runtime types that used to be re-exported from here live +//! under `fabro_types::settings::run` now. #![expect( clippy::disallowed_methods, @@ -15,15 +14,8 @@ use std::path::{Path, PathBuf}; use fabro_types::settings::SettingsLayer; use fabro_types::settings::run::{ResolvedGoalSource, ResolvedRunGoal, RunGoalLayer}; +use crate::Result; use crate::load::{load_settings_path, resolve_goal_file_path}; -use crate::parse::parse_settings_layer; -use crate::{Error, Result}; - -/// Load and parse a run config from a TOML file. -pub fn parse_run_config(contents: &str) -> Result { - parse_settings_layer(contents) - .map_err(|err| Error::parse("Failed to parse run config TOML", err)) -} /// Load and parse a run config from a TOML file. /// diff --git a/lib/crates/fabro-config/src/user.rs b/lib/crates/fabro-config/src/user.rs index 298cf2f48..951ae75aa 100644 --- a/lib/crates/fabro-config/src/user.rs +++ b/lib/crates/fabro-config/src/user.rs @@ -63,24 +63,6 @@ fn load_v2_layer_from_path(path: &Path) -> Result { load_settings_path(path) } -/// Override the resolved storage root in a settings layer with a runtime path. -pub fn apply_storage_dir_override( - mut layer: SettingsLayer, - storage_dir: Option<&Path>, -) -> SettingsLayer { - use fabro_types::settings::interp::InterpString; - use fabro_types::settings::server::{ServerLayer, ServerStorageLayer}; - if let Some(dir) = storage_dir { - let server = layer.server.get_or_insert_with(ServerLayer::default); - let storage = server - .storage - .get_or_insert_with(ServerStorageLayer::default); - storage.root = Some(InterpString::parse(&dir.display().to_string())); - } - - layer -} - #[cfg(test)] mod tests { use super::{ diff --git a/lib/crates/fabro-server/src/run_manifest.rs b/lib/crates/fabro-server/src/run_manifest.rs index dc63b7c66..936e0ece5 100644 --- a/lib/crates/fabro-server/src/run_manifest.rs +++ b/lib/crates/fabro-server/src/run_manifest.rs @@ -5,7 +5,6 @@ use std::sync::Arc; use anyhow::{Result, anyhow, bail}; use fabro_api::types; use fabro_config::project::resolve_working_directory; -use fabro_config::run::parse_run_config; use fabro_config::{WorkflowSettingsBuilder, parse_settings_layer}; use fabro_graphviz::graph::{Graph, is_llm_handler_type}; use fabro_graphviz::render::apply_direction; @@ -199,7 +198,8 @@ fn root_workflow_config_layer( return Ok(SettingsLayer::default()); }; - let mut layer = parse_run_config(&config.source)?; + let mut layer = parse_settings_layer(&config.source) + .map_err(|err| anyhow!("Failed to parse run config TOML: {err}"))?; resolve_manifest_dockerfile(&mut layer, Path::new(&config.path), &workflow.files)?; Ok(layer) } diff --git a/lib/crates/fabro-server/src/serve.rs b/lib/crates/fabro-server/src/serve.rs index f4c6fae27..463b091f1 100644 --- a/lib/crates/fabro-server/src/serve.rs +++ b/lib/crates/fabro-server/src/serve.rs @@ -6,12 +6,12 @@ use std::time::Duration; use anyhow::Context; use clap::Args; use fabro_config::bind::{self, Bind, BindRequest}; -use fabro_config::user::{apply_storage_dir_override, load_settings_config}; +use fabro_config::user::load_settings_config; use fabro_config::{ServerSettingsBuilder, Storage}; use fabro_install::{OBJECT_STORE_ACCESS_KEY_ID_ENV, OBJECT_STORE_SECRET_ACCESS_KEY_ENV}; use fabro_sandbox::SandboxProvider; use fabro_types::settings::server::{ - GithubIntegrationStrategy, ServerLayer, ServerListenLayer, WebhookStrategy, + GithubIntegrationStrategy, ServerLayer, ServerListenLayer, ServerStorageLayer, WebhookStrategy, }; use fabro_types::settings::{ Combine, GithubIntegrationSettings, InterpString, ObjectStoreSettings, ServerListenSettings, @@ -191,7 +191,16 @@ fn apply_runtime_settings( args: &ServeArgs, data_dir: &Path, ) -> SettingsLayer { - apply_storage_dir_override(apply_serve_overrides(base, args), Some(data_dir)) + apply_storage_dir_override(apply_serve_overrides(base, args), data_dir) +} + +fn apply_storage_dir_override(mut settings: SettingsLayer, data_dir: &Path) -> SettingsLayer { + let server = settings.server.get_or_insert_with(ServerLayer::default); + let storage = server + .storage + .get_or_insert_with(ServerStorageLayer::default); + storage.root = Some(InterpString::parse(&data_dir.display().to_string())); + settings } async fn resolve_github_webhook_ip_allowlist(