mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-08 03:10:26 +00:00
parent
a523a883fb
commit
0fbf4dc46f
5 changed files with 339 additions and 34 deletions
File diff suppressed because one or more lines are too long
272
nodes/simplify_gpt/diff.patch
Normal file
272
nodes/simplify_gpt/diff.patch
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
diff --git a/lib/crates/fabro-cli/src/commands/run.rs b/lib/crates/fabro-cli/src/commands/run.rs
|
||||
index dfe34fa9..d41bef0e 100644
|
||||
--- a/lib/crates/fabro-cli/src/commands/run.rs
|
||||
+++ b/lib/crates/fabro-cli/src/commands/run.rs
|
||||
@@ -8,8 +8,7 @@ use anyhow::{bail, Context};
|
||||
use chrono::{Local, Utc};
|
||||
use clap::{Args, ValueEnum};
|
||||
use fabro_agent::{
|
||||
- DockerSandbox, DockerSandboxConfig, LocalSandbox, Sandbox, WorktreeConfig, WorktreeEvent,
|
||||
- WorktreeEventCallback, WorktreeSandbox,
|
||||
+ DockerSandbox, DockerSandboxConfig, LocalSandbox, Sandbox, WorktreeConfig, WorktreeSandbox,
|
||||
};
|
||||
use fabro_config::run::{RunDefaults, WorkflowRunConfig};
|
||||
use fabro_config::{project as project_config, run as run_config, sandbox as sandbox_config};
|
||||
@@ -423,25 +422,6 @@ struct CostAccumulator {
|
||||
has_pricing: bool,
|
||||
}
|
||||
|
||||
-/// Build a callback that forwards [`WorktreeEvent`]s as workflow run events on `emitter`.
|
||||
-fn worktree_event_callback(emitter: Arc<EventEmitter>) -> WorktreeEventCallback {
|
||||
- Arc::new(move |event| match event {
|
||||
- WorktreeEvent::BranchCreated { branch, sha } => {
|
||||
- emitter.emit(&fabro_workflows::event::WorkflowRunEvent::GitBranch { branch, sha });
|
||||
- }
|
||||
- WorktreeEvent::WorktreeAdded { path, branch } => {
|
||||
- emitter
|
||||
- .emit(&fabro_workflows::event::WorkflowRunEvent::GitWorktreeAdd { path, branch });
|
||||
- }
|
||||
- WorktreeEvent::WorktreeRemoved { path } => {
|
||||
- emitter.emit(&fabro_workflows::event::WorkflowRunEvent::GitWorktreeRemove { path });
|
||||
- }
|
||||
- WorktreeEvent::Reset { sha } => {
|
||||
- emitter.emit(&fabro_workflows::event::WorkflowRunEvent::GitReset { sha });
|
||||
- }
|
||||
- })
|
||||
-}
|
||||
-
|
||||
/// Create a [`LocalSandbox`] wired to emit [`WorkflowRunEvent::Sandbox`] events.
|
||||
fn local_sandbox_with_callback(cwd: PathBuf, emitter: Arc<EventEmitter>) -> Arc<dyn Sandbox> {
|
||||
let mut env = LocalSandbox::new(cwd);
|
||||
@@ -1168,7 +1148,7 @@ pub async fn run_command(
|
||||
skip_branch_creation: false,
|
||||
};
|
||||
let mut wt_sandbox = WorktreeSandbox::new(inner, wt_config);
|
||||
- wt_sandbox.set_event_callback(worktree_event_callback(Arc::clone(&emitter)));
|
||||
+ wt_sandbox.set_event_callback(Arc::clone(&emitter).worktree_callback());
|
||||
|
||||
match wt_sandbox.initialize().await {
|
||||
Ok(()) => {
|
||||
@@ -1866,7 +1846,7 @@ async fn run_from_branch(
|
||||
skip_branch_creation: true, // branch already exists on resume
|
||||
};
|
||||
let mut wt_sandbox = WorktreeSandbox::new(inner, wt_config);
|
||||
- wt_sandbox.set_event_callback(worktree_event_callback(Arc::clone(&emitter)));
|
||||
+ wt_sandbox.set_event_callback(Arc::clone(&emitter).worktree_callback());
|
||||
|
||||
wt_sandbox.initialize().await.map_err(|e| {
|
||||
anyhow::anyhow!("failed to attach worktree to {run_branch}: {e}")
|
||||
diff --git a/lib/crates/fabro-sandbox/src/worktree.rs b/lib/crates/fabro-sandbox/src/worktree.rs
|
||||
index 3283ae14..d9806166 100644
|
||||
--- a/lib/crates/fabro-sandbox/src/worktree.rs
|
||||
+++ b/lib/crates/fabro-sandbox/src/worktree.rs
|
||||
@@ -18,7 +18,6 @@ pub enum WorktreeEvent {
|
||||
BranchCreated { branch: String, sha: String },
|
||||
WorktreeAdded { path: String, branch: String },
|
||||
WorktreeRemoved { path: String },
|
||||
- Reset { sha: String },
|
||||
}
|
||||
|
||||
/// Callback type for worktree lifecycle events.
|
||||
@@ -143,30 +142,6 @@ impl Sandbox for WorktreeSandbox {
|
||||
branch: self.config.branch_name.clone(),
|
||||
});
|
||||
|
||||
- if !self.config.skip_branch_creation {
|
||||
- let reset_cmd = format!("{GIT} reset --hard {sha}");
|
||||
- let result = self
|
||||
- .inner
|
||||
- .exec_command(
|
||||
- &reset_cmd,
|
||||
- 30_000,
|
||||
- Some(&self.config.worktree_path),
|
||||
- None,
|
||||
- None,
|
||||
- )
|
||||
- .await?;
|
||||
- if result.exit_code != 0 {
|
||||
- return Err(format!(
|
||||
- "git reset --hard failed (exit {}): {}",
|
||||
- result.exit_code,
|
||||
- result.stderr.trim()
|
||||
- ));
|
||||
- }
|
||||
- self.emit(WorktreeEvent::Reset {
|
||||
- sha: self.config.base_sha.clone(),
|
||||
- });
|
||||
- }
|
||||
-
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -359,8 +334,8 @@ mod tests {
|
||||
wt.initialize().await.unwrap();
|
||||
|
||||
let cmds = mock.captured_commands.lock().unwrap().clone();
|
||||
- // branch --force, worktree remove (best-effort), worktree add, reset --hard
|
||||
- assert_eq!(cmds.len(), 4, "expected 4 git commands, got: {cmds:?}");
|
||||
+ // branch --force, worktree remove (best-effort), worktree add
|
||||
+ assert_eq!(cmds.len(), 3, "expected 3 git commands, got: {cmds:?}");
|
||||
assert!(cmds[0].contains("branch --force"), "cmd[0]: {}", cmds[0]);
|
||||
assert!(
|
||||
cmds[1].contains("worktree remove --force"),
|
||||
@@ -368,11 +343,10 @@ mod tests {
|
||||
cmds[1]
|
||||
);
|
||||
assert!(cmds[2].contains("worktree add"), "cmd[2]: {}", cmds[2]);
|
||||
- assert!(cmds[3].contains("reset --hard"), "cmd[3]: {}", cmds[3]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
- async fn initialize_emits_branch_worktree_reset_events() {
|
||||
+ async fn initialize_emits_branch_and_worktree_events() {
|
||||
let (inner, _mock) = make_mock();
|
||||
let mut wt = WorktreeSandbox::new(inner, make_config("/tmp/wt"));
|
||||
|
||||
@@ -383,7 +357,6 @@ mod tests {
|
||||
WorktreeEvent::BranchCreated { .. } => "BranchCreated",
|
||||
WorktreeEvent::WorktreeAdded { .. } => "WorktreeAdded",
|
||||
WorktreeEvent::WorktreeRemoved { .. } => "WorktreeRemoved",
|
||||
- WorktreeEvent::Reset { .. } => "Reset",
|
||||
};
|
||||
events_clone.lock().unwrap().push(label.to_string());
|
||||
}));
|
||||
@@ -391,7 +364,7 @@ mod tests {
|
||||
wt.initialize().await.unwrap();
|
||||
|
||||
let captured = events.lock().unwrap();
|
||||
- assert_eq!(*captured, vec!["BranchCreated", "WorktreeAdded", "Reset"]);
|
||||
+ assert_eq!(*captured, vec!["BranchCreated", "WorktreeAdded"]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -416,26 +389,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
- #[tokio::test]
|
||||
- async fn initialize_reset_uses_worktree_path_as_working_dir() {
|
||||
- let (inner, mock) = make_mock();
|
||||
- let wt = WorktreeSandbox::new(inner, make_config("/tmp/wt"));
|
||||
-
|
||||
- wt.initialize().await.unwrap();
|
||||
-
|
||||
- let wdirs = mock.captured_working_dirs.lock().unwrap().clone();
|
||||
- // reset command is at index 3, should use worktree path
|
||||
- assert_eq!(
|
||||
- wdirs[3],
|
||||
- Some("/tmp/wt".to_string()),
|
||||
- "reset --hard should run in worktree dir"
|
||||
- );
|
||||
- // branch, remove, add commands use None (inner's default)
|
||||
- assert_eq!(wdirs[0], None, "branch command should use inner default");
|
||||
- assert_eq!(wdirs[1], None, "worktree remove should use inner default");
|
||||
- assert_eq!(wdirs[2], None, "worktree add should use inner default");
|
||||
- }
|
||||
-
|
||||
// -----------------------------------------------------------------------
|
||||
// initialize() — skip_branch_creation = true
|
||||
// -----------------------------------------------------------------------
|
||||
@@ -470,7 +423,6 @@ mod tests {
|
||||
WorktreeEvent::BranchCreated { .. } => "BranchCreated",
|
||||
WorktreeEvent::WorktreeAdded { .. } => "WorktreeAdded",
|
||||
WorktreeEvent::WorktreeRemoved { .. } => "WorktreeRemoved",
|
||||
- WorktreeEvent::Reset { .. } => "Reset",
|
||||
};
|
||||
events_clone.lock().unwrap().push(label.to_string());
|
||||
}));
|
||||
diff --git a/lib/crates/fabro-workflows/src/event.rs b/lib/crates/fabro-workflows/src/event.rs
|
||||
index 227f18fa..03c7d3bd 100644
|
||||
--- a/lib/crates/fabro-workflows/src/event.rs
|
||||
+++ b/lib/crates/fabro-workflows/src/event.rs
|
||||
@@ -1,9 +1,10 @@
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
+use std::sync::Arc;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::outcome::StageUsage;
|
||||
-use fabro_agent::{AgentEvent, SandboxEvent};
|
||||
+use fabro_agent::{AgentEvent, SandboxEvent, WorktreeEvent, WorktreeEventCallback};
|
||||
|
||||
/// Events emitted during workflow run execution for observability.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -1070,6 +1071,22 @@ impl EventEmitter {
|
||||
pub fn touch(&self) {
|
||||
self.last_event_at.store(epoch_millis(), Ordering::Relaxed);
|
||||
}
|
||||
+
|
||||
+ /// Build a [`WorktreeEventCallback`] that forwards worktree lifecycle events as
|
||||
+ /// [`WorkflowRunEvent`]s on this emitter.
|
||||
+ pub fn worktree_callback(self: Arc<Self>) -> WorktreeEventCallback {
|
||||
+ Arc::new(move |event| match event {
|
||||
+ WorktreeEvent::BranchCreated { branch, sha } => {
|
||||
+ self.emit(&WorkflowRunEvent::GitBranch { branch, sha });
|
||||
+ }
|
||||
+ WorktreeEvent::WorktreeAdded { path, branch } => {
|
||||
+ self.emit(&WorkflowRunEvent::GitWorktreeAdd { path, branch });
|
||||
+ }
|
||||
+ WorktreeEvent::WorktreeRemoved { path } => {
|
||||
+ self.emit(&WorkflowRunEvent::GitWorktreeRemove { path });
|
||||
+ }
|
||||
+ })
|
||||
+ }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
diff --git a/lib/crates/fabro-workflows/src/handler/parallel.rs b/lib/crates/fabro-workflows/src/handler/parallel.rs
|
||||
index 5ff62054..63ca994f 100644
|
||||
--- a/lib/crates/fabro-workflows/src/handler/parallel.rs
|
||||
+++ b/lib/crates/fabro-workflows/src/handler/parallel.rs
|
||||
@@ -3,14 +3,14 @@ use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use async_trait::async_trait;
|
||||
-use fabro_agent::{Sandbox, WorktreeConfig, WorktreeEvent, WorktreeEventCallback, WorktreeSandbox};
|
||||
+use fabro_agent::{Sandbox, WorktreeConfig, WorktreeSandbox};
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
use crate::context::keys;
|
||||
use crate::context::Context;
|
||||
use crate::engine::set_hook_node;
|
||||
use crate::error::FabroError;
|
||||
-use crate::event::{EventEmitter, WorkflowRunEvent};
|
||||
+use crate::event::WorkflowRunEvent;
|
||||
use crate::millis_u64;
|
||||
use crate::outcome::{Outcome, StageStatus};
|
||||
use fabro_graphviz::graph::{Graph, Node};
|
||||
@@ -91,24 +91,6 @@ fn parse_error_policy(raw: &str) -> ErrorPolicy {
|
||||
}
|
||||
}
|
||||
|
||||
-/// Bridge a [`WorktreeEvent`] to the corresponding workflow run event on `emitter`.
|
||||
-fn worktree_event_callback(emitter: Arc<EventEmitter>) -> WorktreeEventCallback {
|
||||
- Arc::new(move |event| match event {
|
||||
- WorktreeEvent::BranchCreated { branch, sha } => {
|
||||
- emitter.emit(&WorkflowRunEvent::GitBranch { branch, sha });
|
||||
- }
|
||||
- WorktreeEvent::WorktreeAdded { path, branch } => {
|
||||
- emitter.emit(&WorkflowRunEvent::GitWorktreeAdd { path, branch });
|
||||
- }
|
||||
- WorktreeEvent::WorktreeRemoved { path } => {
|
||||
- emitter.emit(&WorkflowRunEvent::GitWorktreeRemove { path });
|
||||
- }
|
||||
- WorktreeEvent::Reset { sha } => {
|
||||
- emitter.emit(&WorkflowRunEvent::GitReset { sha });
|
||||
- }
|
||||
- })
|
||||
-}
|
||||
-
|
||||
struct BranchResult {
|
||||
id: String,
|
||||
outcome: Outcome,
|
||||
@@ -313,8 +295,7 @@ impl Handler for ParallelHandler {
|
||||
skip_branch_creation: false,
|
||||
};
|
||||
let mut wt_sandbox = WorktreeSandbox::new(Arc::clone(&services.sandbox), wt_config);
|
||||
- wt_sandbox
|
||||
- .set_event_callback(worktree_event_callback(Arc::clone(&services.emitter)));
|
||||
+ wt_sandbox.set_event_callback(Arc::clone(&services.emitter).worktree_callback());
|
||||
wt_sandbox
|
||||
.initialize()
|
||||
.await
|
||||
5
nodes/verify/script_invocation.json
Normal file
5
nodes/verify/script_invocation.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"command": "cargo clippy -q --workspace -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1",
|
||||
"language": "shell",
|
||||
"timeout_ms": null
|
||||
}
|
||||
5
nodes/verify/script_timing.json
Normal file
5
nodes/verify/script_timing.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"duration_ms": 3878,
|
||||
"exit_code": 100,
|
||||
"timed_out": false
|
||||
}
|
||||
6
nodes/verify/status.json
Normal file
6
nodes/verify/status.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Reference in a new issue