diff --git a/lib/crates/fabro-daytona/src/lib.rs b/lib/crates/fabro-daytona/src/lib.rs index 7623d4b1c..83ad28f25 100644 --- a/lib/crates/fabro-daytona/src/lib.rs +++ b/lib/crates/fabro-daytona/src/lib.rs @@ -301,11 +301,11 @@ impl DaytonaSandbox { /// Clone a repository into the given Daytona sandbox. /// /// Resolves GitHub App credentials (if available), clones the repo, - /// emits `GitCloneStarted`/`GitCloneCompleted` events, and sets up - /// push credentials for later use. + /// emits `GitCloneStarted`/`GitCloneCompleted`/`GitCloneFailed` events, + /// and sets up push credentials for later use. /// /// On failure returns `Err` with a user-facing message. The caller is - /// responsible for emitting `GitCloneFailed` / `InitializeFailed`. + /// responsible for emitting `InitializeFailed`. async fn clone_repo( &self, sandbox: &daytona_sdk::Sandbox, @@ -351,7 +351,7 @@ impl DaytonaSandbox { ) .await .map_err(|e| { - if self.github_app.is_none() { + let err = if self.github_app.is_none() { format!( "Git clone failed: {e}. If this is a private repository, \ configure a GitHub App with `fabro install` and install it \ @@ -359,7 +359,12 @@ impl DaytonaSandbox { ) } else { format!("Failed to clone repo into Daytona sandbox: {e}") - } + }; + self.emit(SandboxEvent::GitCloneFailed { + url: url.clone(), + error: err.clone(), + }); + err })?; let clone_duration = u64::try_from(clone_start.elapsed().as_millis()).unwrap_or(u64::MAX); @@ -368,23 +373,22 @@ impl DaytonaSandbox { duration_ms: clone_duration, }); - // Store origin URL and set push credentials for later pushes + // Store origin URL for credential refresh (matches ExeSandbox behaviour) + let _ = self.origin_url.set(url.clone()); + + // Set push credentials for later pushes if let Some(token) = clone_token { - let _ = self.origin_url.set(url.clone()); - let process_svc = sandbox.process().await.ok(); - if let Some(ps) = process_svc { - let origin = self.origin_url.get().expect("just set"); - let auth_url = - origin.replacen("https://", &format!("https://x-access-token:{token}@"), 1); - let cmd = format!( - "git -c maintenance.auto=0 remote set-url origin {}", - shell_quote(&auth_url), - ); - let opts = daytona_sdk::ExecuteCommandOptions { - cwd: Some(WORKING_DIRECTORY.to_string()), - ..Default::default() - }; - let wrapped = wrap_bash_command(&cmd); + let auth_url = url.replacen("https://", &format!("https://x-access-token:{token}@"), 1); + let cmd = format!( + "git -c maintenance.auto=0 remote set-url origin {}", + shell_quote(&auth_url), + ); + let opts = daytona_sdk::ExecuteCommandOptions { + cwd: Some(WORKING_DIRECTORY.to_string()), + ..Default::default() + }; + let wrapped = wrap_bash_command(&cmd); + if let Ok(ps) = sandbox.process().await { if let Ok(r) = ps.execute_command(&wrapped, opts).await { if r.exit_code != 0 { tracing::warn!( @@ -554,10 +558,7 @@ impl Sandbox for DaytonaSandbox { // Clone the repo into the sandbox (if clone_params were provided) if let Some(ref clone_params) = self.clone_params { if let Err(err) = self.clone_repo(&sandbox, clone_params).await { - self.emit(SandboxEvent::GitCloneFailed { - url: clone_params.url.clone(), - error: err.clone(), - }); + // GitCloneFailed already emitted by clone_repo let duration_ms = u64::try_from(init_start.elapsed().as_millis()).unwrap_or(u64::MAX); self.emit(SandboxEvent::InitializeFailed {