diff --git a/lib/crates/fabro-api/src/server.rs b/lib/crates/fabro-api/src/server.rs index 111f0e546..5f1ac01fe 100644 --- a/lib/crates/fabro-api/src/server.rs +++ b/lib/crates/fabro-api/src/server.rs @@ -529,6 +529,7 @@ async fn start_run( }), ..Default::default() }; + let run_labels = config.labels.clone(); let persisted = match operations::create( &req.dot_source, RunCreateOptions { @@ -536,7 +537,7 @@ async fn start_run( run_dir: Some(run_dir.clone()), run_id: Some(run_id.clone()), workflow_slug: None, - labels: std::collections::HashMap::new(), + labels: run_labels, base_branch: None, working_directory: Some( std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from(".")), diff --git a/lib/crates/fabro-cli/src/commands/run/create.rs b/lib/crates/fabro-cli/src/commands/run/create.rs index fa3b4c427..ac57babfe 100644 --- a/lib/crates/fabro-cli/src/commands/run/create.rs +++ b/lib/crates/fabro-cli/src/commands/run/create.rs @@ -76,7 +76,11 @@ pub async fn create_run( run_dir: Some(run_dir.clone()), run_id: Some(run_id.clone()), workflow_slug: source_input.workflow_slug.clone(), - labels: parse_labels(&args.label), + labels: { + let mut labels = source_input.config.labels.clone(); + labels.extend(parse_labels(&args.label)); + labels + }, base_branch, working_directory: Some(working_directory.clone()), host_repo_path: Some(working_directory.to_string_lossy().to_string()), diff --git a/lib/crates/fabro-cli/src/commands/run/execute.rs b/lib/crates/fabro-cli/src/commands/run/execute.rs index a59f41bb5..1cca9ab33 100644 --- a/lib/crates/fabro-cli/src/commands/run/execute.rs +++ b/lib/crates/fabro-cli/src/commands/run/execute.rs @@ -528,7 +528,12 @@ pub(crate) fn load_workflow_source_input( let raw_source = read_workflow_file(&dot_path)?; let cli_goal = resolve_cli_goal(goal, goal_file)?; - let goal_override = cli_goal.or_else(|| config.goal.clone()); + let goal_override = cli_goal.or_else(|| config.goal.clone()).or_else(|| { + config + .goal_file + .as_ref() + .and_then(|path| resolve_cli_goal(None, Some(path)).ok().flatten()) + }); let workflow_toml_path = if resolved_workflow_path .extension() @@ -847,7 +852,11 @@ async fn run_command_impl( run_dir: Some(run_dir.clone()), run_id: Some(run_id.clone()), workflow_slug: source_input.workflow_slug.clone(), - labels: parse_labels(&label_vec), + labels: { + let mut labels = source_input.config.labels.clone(); + labels.extend(parse_labels(&label_vec)); + labels + }, base_branch: detected_base_branch.clone(), working_directory: Some(original_cwd.clone()), host_repo_path: Some(original_cwd.to_string_lossy().to_string()), diff --git a/lib/crates/fabro-config/src/config.rs b/lib/crates/fabro-config/src/config.rs index 63e03e945..3c376bc26 100644 --- a/lib/crates/fabro-config/src/config.rs +++ b/lib/crates/fabro-config/src/config.rs @@ -31,9 +31,15 @@ pub struct FabroConfig { #[serde(default, skip_serializing_if = "Option::is_none")] pub goal: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub goal_file: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] pub graph: Option, + #[serde(default, skip_serializing_if = "HashMap::is_empty")] + pub labels: HashMap, + // --- Run defaults fields (inlined) --- #[serde(default, alias = "directory", skip_serializing_if = "Option::is_none")] pub work_dir: Option, @@ -178,9 +184,17 @@ impl FabroConfig { if overlay.goal.is_some() { self.goal = overlay.goal; } + if overlay.goal_file.is_some() { + self.goal_file = overlay.goal_file; + } if overlay.graph.is_some() { self.graph = overlay.graph; } + if !overlay.labels.is_empty() { + let mut merged = std::mem::take(&mut self.labels); + merged.extend(overlay.labels); + self.labels = merged; + } // --- Run defaults fields --- if overlay.work_dir.is_some() {