mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-10 03:30:59 +00:00
parent
22d405fe94
commit
334ee79570
5 changed files with 430 additions and 10 deletions
File diff suppressed because one or more lines are too long
390
nodes/simplify_gpt/diff.patch
Normal file
390
nodes/simplify_gpt/diff.patch
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
diff --git a/lib/crates/fabro-workflows/src/cli/run.rs b/lib/crates/fabro-workflows/src/cli/run.rs
|
||||
index 806d1ae..ad8ff7a 100644
|
||||
--- a/lib/crates/fabro-workflows/src/cli/run.rs
|
||||
+++ b/lib/crates/fabro-workflows/src/cli/run.rs
|
||||
@@ -866,10 +866,10 @@ pub async fn run_command(
|
||||
let sb = Arc::clone(sb);
|
||||
rt.spawn(async move {
|
||||
match sb.ssh_access_command().await {
|
||||
- Ok(Some(_ssh_command)) => {
|
||||
+ Ok(Some(ssh_command)) => {
|
||||
// Note: we can't emit from here since emitter is shared;
|
||||
// SSH access info is logged via tracing.
|
||||
- tracing::info!(ssh_command = _ssh_command, "SSH access ready");
|
||||
+ tracing::info!(ssh_command, "SSH access ready");
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(e) => {
|
||||
@@ -1128,7 +1128,7 @@ pub async fn run_command(
|
||||
.map(|c| c.checkpoint.exclude_globs.clone())
|
||||
.unwrap_or_default();
|
||||
let pr_cfg = run_cfg.as_ref().and_then(|c| c.pull_request.as_ref());
|
||||
- let config = RunConfig {
|
||||
+ let mut config = RunConfig {
|
||||
run_dir: run_dir.clone(),
|
||||
cancel_token: None,
|
||||
dry_run: dry_run_mode,
|
||||
@@ -1194,11 +1194,11 @@ pub async fn run_command(
|
||||
let engine_result = if let Some(ref checkpoint_path) = args.resume {
|
||||
let checkpoint = Checkpoint::load(checkpoint_path)?;
|
||||
engine
|
||||
- .run_with_lifecycle(&graph, config, lifecycle, Some(&checkpoint))
|
||||
+ .run_with_lifecycle(&graph, &mut config, lifecycle, Some(&checkpoint))
|
||||
.await
|
||||
} else {
|
||||
engine
|
||||
- .run_with_lifecycle(&graph, config, lifecycle, None)
|
||||
+ .run_with_lifecycle(&graph, &mut config, lifecycle, None)
|
||||
.await
|
||||
};
|
||||
let run_duration_ms = run_start.elapsed().as_millis() as u64;
|
||||
@@ -1206,24 +1206,15 @@ pub async fn run_command(
|
||||
// Restore cwd (worktree is kept for `fabro cp` access; pruned separately)
|
||||
let _ = std::env::set_current_dir(&original_cwd);
|
||||
|
||||
- // Build FinalizeConfig from the (potentially mutated) config returned by the engine.
|
||||
- // For remote sandboxes, run_with_lifecycle fills run_branch, base_sha, base_branch, etc.
|
||||
- let finalize_config = match &engine_result {
|
||||
- Ok((_, ref config)) => FinalizeConfig::from_run_config(config),
|
||||
- Err(_) => {
|
||||
- FinalizeConfig::from_run_config_owned(run_id.clone(), None, Some(original_cwd.clone()))
|
||||
- }
|
||||
- };
|
||||
-
|
||||
{
|
||||
let (status, failure_reason) = match &engine_result {
|
||||
- Ok((ref o, _)) => (o.status.clone(), o.failure_reason().map(String::from)),
|
||||
+ Ok(ref o) => (o.status.clone(), o.failure_reason().map(String::from)),
|
||||
Err(e) => (crate::outcome::StageStatus::Fail, Some(e.to_string())),
|
||||
};
|
||||
|
||||
// Map engine result to RunStatus + StatusReason
|
||||
let (run_status, status_reason) = match &engine_result {
|
||||
- Ok((ref o, _)) => match o.status {
|
||||
+ Ok(ref o) => match o.status {
|
||||
StageStatus::Success | StageStatus::Skipped => (
|
||||
crate::run_status::RunStatus::Succeeded,
|
||||
Some(crate::run_status::StatusReason::Completed),
|
||||
@@ -1301,14 +1292,14 @@ pub async fn run_command(
|
||||
// Auto-derive retro (always, cheap) and optionally run retro agent
|
||||
if !args.no_retro && super::project_config::is_retro_enabled() {
|
||||
let (failed, failure_reason) = match &engine_result {
|
||||
- Ok((ref o, _)) => (
|
||||
+ Ok(ref o) => (
|
||||
o.status == StageStatus::Fail,
|
||||
o.failure_reason().map(String::from),
|
||||
),
|
||||
Err(e) => (true, Some(e.to_string())),
|
||||
};
|
||||
generate_retro(
|
||||
- &finalize_config.run_id,
|
||||
+ &config.run_id,
|
||||
&graph.name,
|
||||
graph.goal(),
|
||||
&run_dir,
|
||||
@@ -1330,18 +1321,18 @@ pub async fn run_command(
|
||||
progress_ui.lock().expect("progress lock poisoned").finish();
|
||||
|
||||
// Write finalize commit with retro.json + final node files (captures last diff.patch)
|
||||
- write_finalize_commit_from(&finalize_config, &run_dir).await;
|
||||
+ write_finalize_commit(&config, &run_dir).await;
|
||||
|
||||
// Auto-create PR on successful completion (skip in dry-run mode)
|
||||
let mut pushed_branch: Option<String> = None;
|
||||
let mut pr_url: Option<String> = None;
|
||||
- if !finalize_config.pull_request_enabled {
|
||||
+ if !config.pull_request_enabled {
|
||||
debug!("Skipping PR creation: pull_request not enabled in config");
|
||||
} else if dry_run_mode {
|
||||
debug!("Skipping PR creation: dry-run mode");
|
||||
} else if let Err(ref e) = engine_result {
|
||||
debug!(error = %e, "Skipping PR creation: engine returned an error");
|
||||
- } else if let Ok((ref outcome, _)) = engine_result {
|
||||
+ } else if let Ok(ref outcome) = engine_result {
|
||||
if !matches!(
|
||||
outcome.status,
|
||||
StageStatus::Success | StageStatus::PartialSuccess
|
||||
@@ -1357,14 +1348,14 @@ pub async fn run_command(
|
||||
Some(ref creds),
|
||||
Some(ref origin),
|
||||
) = (
|
||||
- &finalize_config.base_branch,
|
||||
- &finalize_config.run_branch,
|
||||
+ &config.base_branch,
|
||||
+ &config.run_branch,
|
||||
&github_app,
|
||||
&origin_url,
|
||||
) {
|
||||
// Run branch was pushed during checkpoint commits;
|
||||
// just record it for the PR creation.
|
||||
- if finalize_config.git_checkpoint_enabled {
|
||||
+ if config.git_checkpoint_enabled {
|
||||
pushed_branch = Some(run_branch.clone());
|
||||
}
|
||||
|
||||
@@ -1376,7 +1367,7 @@ pub async fn run_command(
|
||||
graph.goal(),
|
||||
&diff,
|
||||
&model,
|
||||
- finalize_config.pull_request_draft,
|
||||
+ config.pull_request_draft,
|
||||
&run_dir,
|
||||
)
|
||||
.await
|
||||
@@ -1385,7 +1376,7 @@ pub async fn run_command(
|
||||
emitter.emit(&crate::event::WorkflowRunEvent::PullRequestCreated {
|
||||
pr_url: record.html_url.clone(),
|
||||
pr_number: record.number,
|
||||
- draft: finalize_config.pull_request_draft,
|
||||
+ draft: config.pull_request_draft,
|
||||
});
|
||||
pr_url = Some(record.html_url.clone());
|
||||
if let Err(e) = record.save(&run_dir.join("pull_request.json")) {
|
||||
@@ -1407,7 +1398,7 @@ pub async fn run_command(
|
||||
}
|
||||
}
|
||||
|
||||
- let (outcome, _) = engine_result?;
|
||||
+ let outcome = engine_result?;
|
||||
|
||||
// 8. Print result
|
||||
eprintln!("\n{}", styles.bold.apply_to("=== Run Result ==="),);
|
||||
@@ -1759,7 +1750,7 @@ async fn run_from_branch(
|
||||
}
|
||||
|
||||
let meta_branch = Some(crate::git::MetadataStore::branch_name(&run_id));
|
||||
- let config = RunConfig {
|
||||
+ let mut config = RunConfig {
|
||||
run_dir: run_dir.clone(),
|
||||
cancel_token: None,
|
||||
dry_run: dry_run_mode,
|
||||
@@ -1792,25 +1783,17 @@ async fn run_from_branch(
|
||||
|
||||
let run_start = Instant::now();
|
||||
let engine_result = engine
|
||||
- .run_with_lifecycle(&graph, config, lifecycle, Some(&checkpoint))
|
||||
+ .run_with_lifecycle(&graph, &mut config, lifecycle, Some(&checkpoint))
|
||||
.await;
|
||||
let run_duration_ms = run_start.elapsed().as_millis() as u64;
|
||||
|
||||
// Restore cwd (worktree is kept for `fabro cp` access; pruned separately)
|
||||
let _ = std::env::set_current_dir(&original_cwd);
|
||||
|
||||
- // Build FinalizeConfig from the (potentially mutated) config returned by the engine.
|
||||
- let finalize_config = match &engine_result {
|
||||
- Ok((_, ref config)) => FinalizeConfig::from_run_config(config),
|
||||
- Err(_) => {
|
||||
- FinalizeConfig::from_run_config_owned(run_id.clone(), None, Some(original_cwd.clone()))
|
||||
- }
|
||||
- };
|
||||
-
|
||||
// Auto-derive retro
|
||||
if !args.no_retro && super::project_config::is_retro_enabled() {
|
||||
let (failed, failure_reason) = match &engine_result {
|
||||
- Ok((ref o, _)) => (
|
||||
+ Ok(ref o) => (
|
||||
o.status == StageStatus::Fail,
|
||||
o.failure_reason().map(String::from),
|
||||
),
|
||||
@@ -1824,7 +1807,7 @@ async fn run_from_branch(
|
||||
};
|
||||
|
||||
generate_retro(
|
||||
- &finalize_config.run_id,
|
||||
+ &config.run_id,
|
||||
&graph.name,
|
||||
graph.goal(),
|
||||
&run_dir,
|
||||
@@ -1843,14 +1826,14 @@ async fn run_from_branch(
|
||||
}
|
||||
|
||||
// Write finalize commit with retro.json + final node files (captures last diff.patch)
|
||||
- write_finalize_commit_from(&finalize_config, &run_dir).await;
|
||||
+ write_finalize_commit(&config, &run_dir).await;
|
||||
|
||||
// Cleanup sandbox via engine (fires SandboxCleanup hook)
|
||||
let _ = engine
|
||||
- .cleanup_sandbox(&finalize_config.run_id, &graph.name, false)
|
||||
+ .cleanup_sandbox(&config.run_id, &graph.name, false)
|
||||
.await;
|
||||
|
||||
- let (outcome, _) = engine_result?;
|
||||
+ let outcome = engine_result?;
|
||||
|
||||
eprintln!("\n{}", styles.bold.apply_to("=== Run Result ==="),);
|
||||
eprintln!("{}", styles.dim.apply_to(format!("Run: {run_id}")));
|
||||
@@ -2271,63 +2254,11 @@ async fn run_preflight(
|
||||
}
|
||||
}
|
||||
|
||||
-/// Subset of `RunConfig` fields needed after `run_with_lifecycle` consumes the config.
|
||||
-struct FinalizeConfig {
|
||||
- run_id: String,
|
||||
- meta_branch: Option<String>,
|
||||
- host_repo_path: Option<PathBuf>,
|
||||
- git_author: crate::git::GitAuthor,
|
||||
- github_app: Option<fabro_github::GitHubAppCredentials>,
|
||||
- base_branch: Option<String>,
|
||||
- run_branch: Option<String>,
|
||||
- git_checkpoint_enabled: bool,
|
||||
- pull_request_enabled: bool,
|
||||
- pull_request_draft: bool,
|
||||
-}
|
||||
-
|
||||
-impl FinalizeConfig {
|
||||
- /// Build from the (potentially mutated) `RunConfig` returned by the engine.
|
||||
- fn from_run_config(config: &RunConfig) -> Self {
|
||||
- Self {
|
||||
- run_id: config.run_id.clone(),
|
||||
- meta_branch: config.meta_branch.clone(),
|
||||
- host_repo_path: config.host_repo_path.clone(),
|
||||
- git_author: config.git_author.clone(),
|
||||
- github_app: config.github_app.clone(),
|
||||
- base_branch: config.base_branch.clone(),
|
||||
- run_branch: config.run_branch.clone(),
|
||||
- git_checkpoint_enabled: config.git_checkpoint_enabled,
|
||||
- pull_request_enabled: config.pull_request_enabled,
|
||||
- pull_request_draft: config.pull_request_draft,
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- /// Minimal fallback for error paths where the engine didn't return a config.
|
||||
- fn from_run_config_owned(
|
||||
- run_id: String,
|
||||
- meta_branch: Option<String>,
|
||||
- host_repo_path: Option<PathBuf>,
|
||||
- ) -> Self {
|
||||
- Self {
|
||||
- run_id,
|
||||
- meta_branch,
|
||||
- host_repo_path,
|
||||
- git_author: crate::git::GitAuthor::default(),
|
||||
- github_app: None,
|
||||
- base_branch: None,
|
||||
- run_branch: None,
|
||||
- git_checkpoint_enabled: false,
|
||||
- pull_request_enabled: false,
|
||||
- pull_request_draft: false,
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
/// Write a finalize commit to the shadow branch with retro.json and final node files.
|
||||
///
|
||||
/// This captures the last diff.patch (written after the final checkpoint) and retro.json.
|
||||
/// Best-effort: errors are logged as warnings.
|
||||
-async fn write_finalize_commit_from(config: &FinalizeConfig, run_dir: &std::path::Path) {
|
||||
+async fn write_finalize_commit(config: &RunConfig, run_dir: &std::path::Path) {
|
||||
let (Some(ref meta_branch), Some(ref repo_path)) =
|
||||
(&config.meta_branch, &config.host_repo_path)
|
||||
else {
|
||||
diff --git a/lib/crates/fabro-workflows/src/engine.rs b/lib/crates/fabro-workflows/src/engine.rs
|
||||
index f01f447..963844b 100644
|
||||
--- a/lib/crates/fabro-workflows/src/engine.rs
|
||||
+++ b/lib/crates/fabro-workflows/src/engine.rs
|
||||
@@ -1266,16 +1266,15 @@ impl WorkflowRunEngine {
|
||||
/// The sandbox is left alive after return so the caller can run retro, PR creation, etc.
|
||||
/// Call `cleanup_sandbox()` when done.
|
||||
///
|
||||
- /// Returns both the outcome and the (potentially mutated) config — remote git setup
|
||||
- /// fills `run_branch`, `base_sha`, `base_branch`, `meta_branch`, and
|
||||
- /// `git_checkpoint_enabled`, which callers need for PR creation and finalize commits.
|
||||
+ /// The config is taken by mutable reference so the caller retains ownership
|
||||
+ /// and can read any fields mutated by remote git setup after the call.
|
||||
pub async fn run_with_lifecycle(
|
||||
&self,
|
||||
graph: &Graph,
|
||||
- mut config: RunConfig,
|
||||
+ config: &mut RunConfig,
|
||||
lifecycle: LifecycleConfig,
|
||||
checkpoint: Option<&Checkpoint>,
|
||||
- ) -> Result<(Outcome, RunConfig)> {
|
||||
+ ) -> Result<Outcome> {
|
||||
// 1. Initialize sandbox
|
||||
self.services
|
||||
.sandbox
|
||||
@@ -1389,23 +1388,11 @@ impl WorkflowRunEngine {
|
||||
}
|
||||
|
||||
// 7. Execute the workflow graph
|
||||
- let outcome = if let Some(cp) = checkpoint {
|
||||
- let loop_state = LoopState {
|
||||
- node_visits: HashMap::new(),
|
||||
- loop_failure_signatures: cp.loop_failure_signatures.clone(),
|
||||
- restart_failure_signatures: cp.restart_failure_signatures.clone(),
|
||||
- };
|
||||
- let (outcome, _context) = self
|
||||
- .run_internal(graph, &config, Some(cp), None, None, loop_state)
|
||||
- .await?;
|
||||
- outcome
|
||||
+ if let Some(cp) = checkpoint {
|
||||
+ self.run_from_checkpoint(graph, config, cp).await
|
||||
} else {
|
||||
- let (outcome, _context) = self
|
||||
- .run_internal(graph, &config, None, None, None, LoopState::default())
|
||||
- .await?;
|
||||
- outcome
|
||||
- };
|
||||
- Ok((outcome, config))
|
||||
+ self.run(graph, config).await
|
||||
+ }
|
||||
}
|
||||
|
||||
/// Fire the `SandboxCleanup` hook and optionally clean up the sandbox.
|
||||
@@ -5715,13 +5702,9 @@ mod tests {
|
||||
});
|
||||
|
||||
let engine = WorkflowRunEngine::new(make_registry(), Arc::new(emitter), local_env());
|
||||
- let (outcome, _) = engine
|
||||
- .run_with_lifecycle(
|
||||
- &g,
|
||||
- test_run_config(dir.path(), "lifecycle-test"),
|
||||
- test_lifecycle(Vec::new()),
|
||||
- None,
|
||||
- )
|
||||
+ let mut config = test_run_config(dir.path(), "lifecycle-test");
|
||||
+ let outcome = engine
|
||||
+ .run_with_lifecycle(&g, &mut config, test_lifecycle(Vec::new()), None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
@@ -5750,10 +5733,11 @@ mod tests {
|
||||
});
|
||||
|
||||
let engine = WorkflowRunEngine::new(make_registry(), Arc::new(emitter), local_env());
|
||||
- let (outcome, _) = engine
|
||||
+ let mut config = test_run_config(dir.path(), "setup-test");
|
||||
+ let outcome = engine
|
||||
.run_with_lifecycle(
|
||||
&g,
|
||||
- test_run_config(dir.path(), "setup-test"),
|
||||
+ &mut config,
|
||||
test_lifecycle(vec!["echo hello".to_string()]),
|
||||
None,
|
||||
)
|
||||
@@ -5779,10 +5763,11 @@ mod tests {
|
||||
|
||||
let engine =
|
||||
WorkflowRunEngine::new(make_registry(), Arc::new(EventEmitter::new()), local_env());
|
||||
+ let mut config = test_run_config(dir.path(), "setup-fail-test");
|
||||
let result = engine
|
||||
.run_with_lifecycle(
|
||||
&g,
|
||||
- test_run_config(dir.path(), "setup-fail-test"),
|
||||
+ &mut config,
|
||||
test_lifecycle(vec!["exit 1".to_string()]),
|
||||
None,
|
||||
)
|
||||
@@ -5825,10 +5810,11 @@ mod tests {
|
||||
});
|
||||
|
||||
let engine = WorkflowRunEngine::new(make_registry(), Arc::new(emitter), local_env());
|
||||
+ let mut config = test_run_config(dir.path(), "order-test");
|
||||
engine
|
||||
.run_with_lifecycle(
|
||||
&g,
|
||||
- test_run_config(dir.path(), "order-test"),
|
||||
+ &mut config,
|
||||
test_lifecycle(vec!["echo ok".to_string()]),
|
||||
None,
|
||||
)
|
||||
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": 19251,
|
||||
"exit_code": 0,
|
||||
"timed_out": false
|
||||
}
|
||||
6
nodes/verify/status.json
Normal file
6
nodes/verify/status.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"status": "success",
|
||||
"notes": "Script completed: cargo clippy -q --workspace -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1",
|
||||
"failure_reason": null,
|
||||
"timestamp": "2026-03-16T05:01:57.085173+00:00"
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue