Centralize global CLI override application

This commit is contained in:
Bryan Helmkamp 2026-03-29 21:08:16 -04:00
parent 79a366022f
commit d7e32f1380
No known key found for this signature in database
7 changed files with 65 additions and 26 deletions

View file

@ -16,14 +16,30 @@ pub(crate) fn load_cli_settings() -> anyhow::Result<FabroSettings> {
ConfigLayer::cli()?.resolve()
}
pub(crate) fn cli_layer_with_globals(globals: &GlobalArgs) -> anyhow::Result<ConfigLayer> {
let layer = ConfigLayer::cli()?;
Ok(apply_global_overrides(layer, globals))
}
pub(crate) fn load_cli_settings_with_globals(
globals: &GlobalArgs,
) -> anyhow::Result<FabroSettings> {
let mut layer = ConfigLayer::cli()?;
cli_layer_with_globals(globals)?.resolve()
}
pub(crate) fn apply_global_overrides(mut layer: ConfigLayer, globals: &GlobalArgs) -> ConfigLayer {
if let Some(dir) = &globals.storage_dir {
layer.storage_dir = Some(dir.clone());
layer.mode = Some(ExecutionMode::Standalone);
}
layer.resolve()
#[cfg(feature = "server")]
if let Some(url) = &globals.server_url {
layer.server.get_or_insert_with(Default::default).base_url = Some(url.clone());
layer.mode = Some(ExecutionMode::Server);
}
layer
}
#[cfg(feature = "server")]

View file

@ -2,6 +2,7 @@ use std::io::Write;
use std::path::Path;
use crate::args::{ConfigCommand, ConfigNamespace, ConfigShowArgs, GlobalArgs};
use crate::cli_config;
use fabro_config::{ConfigLayer, FabroSettings};
pub(crate) fn dispatch(ns: ConfigNamespace, globals: &GlobalArgs) -> anyhow::Result<()> {
@ -16,10 +17,7 @@ fn merged_config(workflow: Option<&Path>, globals: &GlobalArgs) -> anyhow::Resul
Some(path) => ConfigLayer::for_workflow(path, &cwd)?,
None => ConfigLayer::project(&cwd)?,
};
let mut cli = ConfigLayer::cli()?;
if let Some(dir) = &globals.storage_dir {
cli.storage_dir = Some(dir.clone());
}
let cli = cli_config::cli_layer_with_globals(globals)?;
base.combine(cli).resolve()
}

View file

@ -17,15 +17,12 @@ use fabro_workflows::git::{GitSyncStatus, sync_status};
use fabro_workflows::operations::{ValidateInput, WorkflowInput, validate};
use crate::args::{GlobalArgs, PreflightArgs};
use crate::cli_config::load_cli_settings_with_globals;
use crate::cli_config::{cli_layer_with_globals, load_cli_settings_with_globals};
use crate::shared::github::build_github_app_credentials;
pub(crate) async fn execute(mut args: PreflightArgs, globals: &GlobalArgs) -> anyhow::Result<()> {
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
let mut cli = ConfigLayer::cli()?;
if let Some(dir) = &globals.storage_dir {
cli.storage_dir = Some(dir.clone());
}
let cli = cli_layer_with_globals(globals)?;
let cli_settings: FabroSettings = load_cli_settings_with_globals(globals)?;
args.verbose = args.verbose || cli_settings.verbose_enabled();

View file

@ -1,20 +1,18 @@
use anyhow::Result;
use fabro_config::ConfigLayer;
use fabro_util::terminal::Styles;
use crate::args::{GlobalArgs, RunArgs};
use crate::cli_config;
use crate::cli_config::{self, cli_layer_with_globals};
pub(crate) async fn execute(mut args: RunArgs, globals: &GlobalArgs) -> Result<()> {
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
let cli_settings = cli_config::load_cli_settings_with_globals(globals)?;
let cli = ConfigLayer::cli()?;
let cli = cli_layer_with_globals(globals)?;
args.verbose = args.verbose || cli_settings.verbose_enabled();
let quiet = args.detach;
let prevent_idle_sleep = cli_settings.prevent_idle_sleep_enabled();
let (run_id, run_dir) =
super::create::create_run(&args, cli, styles, quiet, globals.storage_dir.clone())?;
let (run_id, run_dir) = super::create::create_run(&args, cli, styles, quiet)?;
#[cfg(feature = "sleep_inhibitor")]
let _sleep_guard = crate::sleep_inhibitor::guard(prevent_idle_sleep);

View file

@ -16,16 +16,12 @@ pub(crate) fn create_run(
cli_defaults: ConfigLayer,
styles: &Styles,
quiet: bool,
storage_dir_override: Option<PathBuf>,
) -> anyhow::Result<(String, PathBuf)> {
let workflow_path = args
.workflow
.as_ref()
.ok_or_else(|| anyhow::anyhow!("--workflow is required"))?;
let mut cli_args_config = ConfigLayer::try_from(args)?;
if let Some(dir) = storage_dir_override {
cli_args_config.storage_dir = Some(dir);
}
let cli_args_config = ConfigLayer::try_from(args)?;
let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
let settings: FabroSettings = cli_args_config
.combine(ConfigLayer::for_workflow(workflow_path, &cwd)?)

View file

@ -1,11 +1,10 @@
use anyhow::Result;
use fabro_config::ConfigLayer;
use fabro_config::FabroSettingsExt;
use fabro_util::terminal::Styles;
use fabro_workflows::run_lookup::{resolve_run_combined, runs_base};
use crate::args::{GlobalArgs, RunCommands};
use crate::cli_config::load_cli_settings_with_globals;
use crate::cli_config::{cli_layer_with_globals, load_cli_settings_with_globals};
use crate::store;
pub(crate) mod attach;
@ -36,9 +35,8 @@ pub(crate) async fn dispatch(cmd: RunCommands, globals: &GlobalArgs) -> Result<(
RunCommands::Run(args) => command::execute(args, globals).await,
RunCommands::Create(args) => {
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
let cli = ConfigLayer::cli()?;
let (run_id, _run_dir) =
create::create_run(&args, cli, styles, true, globals.storage_dir.clone())?;
let cli = cli_layer_with_globals(globals)?;
let (run_id, _run_dir) = create::create_run(&args, cli, styles, true)?;
println!("{run_id}");
Ok(())
}

View file

@ -6,6 +6,8 @@ use std::time::Duration;
use assert_cmd::Command;
use chrono::TimeZone;
use fabro_config::FabroSettings;
#[cfg(feature = "server")]
use fabro_config::cli::ExecutionMode;
use fabro_config::mcp::McpTransport;
use fabro_git_storage::branchstore::BranchStore;
use fabro_git_storage::gitobj::Store as GitStore;
@ -2017,3 +2019,37 @@ fn config_show_missing_run_config_errors() {
.failure()
.stderr(predicate::str::contains("Workflow not found"));
}
#[test]
#[cfg(feature = "server")]
fn config_show_server_url_overrides_cli_defaults() {
let (home, project) = setup_config_show_fixture();
let cli_toml = home.path().join(".fabro").join("cli.toml");
std::fs::write(
&cli_toml,
format!(
"{}\nmode = \"standalone\"\n[server]\nbase_url = \"https://config.example.com\"\n",
std::fs::read_to_string(&cli_toml).unwrap()
),
)
.unwrap();
let output = arc()
.env("HOME", home.path())
.current_dir(project.path())
.args(["--server-url", "https://cli.example.com", "config", "show"])
.assert()
.success()
.get_output()
.stdout
.clone();
let cfg = parse_config_show(&output);
assert_eq!(cfg.mode, Some(ExecutionMode::Server));
assert_eq!(
cfg.server
.as_ref()
.and_then(|server| server.base_url.as_deref()),
Some("https://cli.example.com")
);
}