mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-11 03:40:05 +00:00
Add workflow name resolution for arc run via project config
`arc run my-workflow` now resolves to `{arc_root}/workflows/my-workflow/workflow.toml`
when an arc.toml project config exists. Args with file extensions are passed through
unchanged, and missing workflows fall back to the literal arg.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9e37e3781d
commit
9f86d55c74
2 changed files with 111 additions and 0 deletions
|
|
@ -68,6 +68,50 @@ pub fn discover_project_config(start: &Path) -> anyhow::Result<Option<(PathBuf,
|
|||
Ok(None)
|
||||
}
|
||||
|
||||
/// Resolve a workflow argument to a path.
|
||||
///
|
||||
/// - If the arg has a file extension (`.toml`, `.dot`, etc.), return it as-is.
|
||||
/// - If no extension, attempt project-based resolution: find `arc.toml`, resolve
|
||||
/// `{arc_root}/workflows/{name}/workflow.toml`. Falls back to literal arg if
|
||||
/// no project config or no matching workflow file.
|
||||
pub fn resolve_workflow_arg(arg: &Path) -> PathBuf {
|
||||
let start = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||
resolve_workflow_arg_from(arg, &start)
|
||||
}
|
||||
|
||||
fn resolve_workflow_arg_from(arg: &Path, start_dir: &Path) -> PathBuf {
|
||||
if arg.extension().is_some() {
|
||||
tracing::debug!(arg = %arg.display(), "Workflow arg has extension, returning as-is");
|
||||
return arg.to_path_buf();
|
||||
}
|
||||
|
||||
let name = arg.to_string_lossy();
|
||||
match discover_project_config(start_dir) {
|
||||
Ok(Some((config_path, config))) => {
|
||||
let arc_root = resolve_arc_root(&config_path, &config);
|
||||
let candidate = arc_root
|
||||
.join("workflows")
|
||||
.join(&*name)
|
||||
.join("workflow.toml");
|
||||
if candidate.is_file() {
|
||||
tracing::debug!(arg = %arg.display(), resolved = %candidate.display(), "Resolved workflow name via project config");
|
||||
candidate
|
||||
} else {
|
||||
tracing::debug!(arg = %arg.display(), candidate = %candidate.display(), "Workflow file not found, falling back to literal");
|
||||
arg.to_path_buf()
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
tracing::debug!(arg = %arg.display(), "No project config found, returning literal");
|
||||
arg.to_path_buf()
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::debug!(arg = %arg.display(), error = %err, "Error discovering project config, returning literal");
|
||||
arg.to_path_buf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve the arc root directory from a config file path and its config.
|
||||
/// The returned path is the directory containing `arc.toml` joined with the `root` value.
|
||||
pub fn resolve_arc_root(config_path: &Path, config: &ProjectConfig) -> PathBuf {
|
||||
|
|
@ -174,4 +218,70 @@ mod tests {
|
|||
};
|
||||
assert_eq!(resolve_arc_root(config_path, &config), Path::new("/repo/."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_workflow_arg_toml_extension_returned_as_is() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let result = resolve_workflow_arg_from(Path::new("my-workflow.toml"), tmp.path());
|
||||
assert_eq!(result, Path::new("my-workflow.toml"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_workflow_arg_dot_extension_returned_as_is() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let result = resolve_workflow_arg_from(Path::new("my-workflow.dot"), tmp.path());
|
||||
assert_eq!(result, Path::new("my-workflow.dot"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_workflow_arg_no_extension_no_config_returns_literal() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let result = resolve_workflow_arg_from(Path::new("my-workflow"), tmp.path());
|
||||
assert_eq!(result, Path::new("my-workflow"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_workflow_arg_no_extension_with_config_and_workflow_file() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
fs::write(tmp.path().join("arc.toml"), "version = 1\n").unwrap();
|
||||
let wf_dir = tmp.path().join("workflows").join("my-workflow");
|
||||
fs::create_dir_all(&wf_dir).unwrap();
|
||||
fs::write(
|
||||
wf_dir.join("workflow.toml"),
|
||||
"version = 1\ngraph = \"workflow.dot\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = resolve_workflow_arg_from(Path::new("my-workflow"), tmp.path());
|
||||
assert_eq!(result, wf_dir.join("workflow.toml"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_workflow_arg_no_extension_config_but_no_workflow_dir() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
fs::write(tmp.path().join("arc.toml"), "version = 1\n").unwrap();
|
||||
|
||||
let result = resolve_workflow_arg_from(Path::new("my-workflow"), tmp.path());
|
||||
assert_eq!(result, Path::new("my-workflow"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_workflow_arg_custom_root_respected() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
fs::write(
|
||||
tmp.path().join("arc.toml"),
|
||||
"version = 1\n[arc]\nroot = \"arc/\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
let wf_dir = tmp.path().join("arc").join("workflows").join("factory");
|
||||
fs::create_dir_all(&wf_dir).unwrap();
|
||||
fs::write(
|
||||
wf_dir.join("workflow.toml"),
|
||||
"version = 1\ngraph = \"workflow.dot\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = resolve_workflow_arg_from(Path::new("factory"), tmp.path());
|
||||
assert_eq!(result, wf_dir.join("workflow.toml"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@ pub async fn run_command(
|
|||
.workflow
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("--workflow is required unless --run-branch is provided"))?;
|
||||
let workflow_path = &super::project_config::resolve_workflow_arg(workflow_path);
|
||||
|
||||
// 0. Load run config if TOML, resolve DOT path, apply defaults
|
||||
let (dot_path, run_cfg) = if workflow_path.extension().is_some_and(|ext| ext == "toml") {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue