From f2a92b243ee4f59a598de0eba1d1ddb8c52a2cc2 Mon Sep 17 00:00:00 2001 From: Scott Werner Date: Fri, 11 Sep 2026 09:42:10 -0600 Subject: [PATCH] Acquire the remote workflow before observing a local Git target For --workflow-git selections, target resolution ran before the remote workflow ref was verified to exist. On Docker and Daytona environments a path target that is a GitHub checkout is observed via observe_git_run_target, which may silently push the attached branch, so a typo in --workflow-ref produced a remote side effect with no run created. Resolve the remote workflow after parent and environment validation but before target observation, restoring the pre-existing workflow-then-target order, and cover it with a caller checkout whose unpushed branch must stay unpublished. Co-Authored-By: Claude Fable 5.1 --- lib/apps/fabro-cli/src/commands/run/create.rs | 12 +++++---- lib/apps/fabro-cli/tests/it/cmd/create.rs | 27 ++++++++++++++++--- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/lib/apps/fabro-cli/src/commands/run/create.rs b/lib/apps/fabro-cli/src/commands/run/create.rs index e3e365e6f..c7145ef73 100644 --- a/lib/apps/fabro-cli/src/commands/run/create.rs +++ b/lib/apps/fabro-cli/src/commands/run/create.rs @@ -44,7 +44,7 @@ pub(crate) async fn create_run( ) }; // Preserve local lookup diagnostics before contacting the server. Remote - // acquisition waits until parent, environment, and target are validated. + // acquisition waits until the parent and environment are validated. let local_package = match &workflow_selection { WorkflowSelection::Local(_) => Some(resolve_workflow().await?), WorkflowSelection::Git { .. } => None, @@ -84,6 +84,12 @@ pub(crate) async fn create_run( }, resolve_run_environment(client.as_ref(), args.environment.as_deref()), )?; + // Observing a local Git target may push its branch. Acquire the remote + // workflow first so a bad --workflow-ref never causes that side effect. + let package = match local_package { + Some(package) => package, + None => resolve_workflow().await?, + }; let (target, dirty_worktree) = resolution::target( &target_selection, environment.settings.provider, @@ -97,10 +103,6 @@ pub(crate) async fn create_run( styles.yellow.apply_to("Warning:"), ); } - let package = match local_package { - Some(package) => package, - None => resolve_workflow().await?, - }; let workflow_version_id = package.closure().root_id(); client .register_workflow_versions( diff --git a/lib/apps/fabro-cli/tests/it/cmd/create.rs b/lib/apps/fabro-cli/tests/it/cmd/create.rs index b94df6061..a232774e3 100644 --- a/lib/apps/fabro-cli/tests/it/cmd/create.rs +++ b/lib/apps/fabro-cli/tests/it/cmd/create.rs @@ -1889,7 +1889,17 @@ fn remote_workflow_explicit_acquisition_failure_has_no_fallback_or_server_mutati let source = root.path().join("source"); write_workflow(&source, ".fabro/workflows/other", "Other"); init_remote_fixture(&source, "trunk"); - write_workflow(root.path(), ".fabro/workflows/review", "Caller"); + // The caller is a GitHub-origin checkout with an unpushed branch, which + // Docker target observation would publish if it ran first. + let caller = root.path().join("caller"); + let origin = root.path().join("origin.git"); + git2::Repository::init_bare(&origin).unwrap(); + write_workflow(&caller, ".fabro/workflows/review", "Caller"); + init_remote_fixture(&caller, "main"); + git2::Repository::open(&caller) + .unwrap() + .remote("origin", "https://github.com/acme/app") + .unwrap(); write_workflow( &context.home_dir.join(".fabro/workflows"), "review", @@ -1899,8 +1909,9 @@ fn remote_workflow_explicit_acquisition_failure_has_no_fallback_or_server_mutati std::fs::write( &config, format!( - "[url \"file://{}\"]\n insteadOf = https://github.com/acme/workflows\n", - source.display() + "[url \"file://{}\"]\n insteadOf = https://github.com/acme/workflows\n[url \"file://{}\"]\n insteadOf = https://github.com/acme/app\n", + source.display(), + origin.display() ), ) .unwrap(); @@ -1911,7 +1922,7 @@ fn remote_workflow_explicit_acquisition_failure_has_no_fallback_or_server_mutati ] { let output = context .create_cmd() - .current_dir(root.path()) + .current_dir(&caller) .env("GIT_CONFIG_GLOBAL", &config) .env("GIT_CONFIG_NOSYSTEM", "1") .env("GIT_CONFIG_COUNT", "0") @@ -1932,4 +1943,12 @@ fn remote_workflow_explicit_acquisition_failure_has_no_fallback_or_server_mutati environment.assert_calls(3); version.assert_calls(0); create.assert_calls(0); + // Acquisition failed before target observation, so nothing was pushed. + assert!( + git2::Repository::open_bare(&origin) + .unwrap() + .find_reference("refs/heads/main") + .is_err(), + "a failed remote workflow acquisition must not publish the target branch" + ); }