fabro(01KN4VB09T999RE1V2DD8Z3YC3): implement (success)

Fabro-Run: 01KN4VB09T999RE1V2DD8Z3YC3
Fabro-Completed: 5
Fabro-Checkpoint: 6fb4d5968b

⚒️ Generated with [Fabro](https://fabro.sh)
This commit is contained in:
Fabro 2026-04-01 15:53:56 +00:00
parent 8ef171aa84
commit 8297346b35
4 changed files with 100 additions and 1 deletions

View file

@ -585,6 +585,9 @@ pub(crate) struct PrCreateArgs {
/// LLM model for generating PR description
#[arg(long)]
pub(crate) model: Option<String>,
/// Create PR even if the run status is not success/partial_success
#[arg(short, long)]
pub(crate) force: bool,
}
#[derive(Args)]

View file

@ -75,6 +75,9 @@ async fn create_from(
match conclusion.status {
StageStatus::Success | StageStatus::PartialSuccess => {}
status if args.force => {
tracing::warn!("Run status is '{status}', proceeding because --force was specified");
}
status => bail!("Run status is '{status}', expected success or partial_success"),
}

View file

@ -1,6 +1,6 @@
use fabro_test::{fabro_snapshot, test_context};
use super::support::{setup_completed_dry_run, setup_created_dry_run};
use super::support::{setup_completed_dry_run, setup_created_dry_run, setup_failed_run};
#[test]
fn help() {
@ -22,6 +22,7 @@ fn help() {
--json Output as JSON [env: FABRO_JSON=]
--model <MODEL> LLM model for generating PR description
--debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
-f, --force Create PR even if the run status is not success/partial_success
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
@ -63,3 +64,35 @@ fn pr_create_completed_dry_run_without_run_branch_errors() {
error: Run has no run_branch — was it run with git push enabled?
");
}
#[test]
fn pr_create_failed_run_rejects_without_force() {
let context = test_context!();
let run = setup_failed_run(&context);
let mut cmd = context.command();
cmd.args(["pr", "create", &run.run_id]);
fabro_snapshot!(context.filters(), cmd, @"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
error: Run status is 'fail', expected success or partial_success
");
}
#[test]
fn pr_create_failed_run_proceeds_with_force() {
let context = test_context!();
let run = setup_failed_run(&context);
let mut cmd = context.command();
cmd.args(["pr", "create", "--force", &run.run_id]);
fabro_snapshot!(context.filters(), cmd, @"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
error: Run has no run_branch — was it run with git push enabled?
");
}

View file

@ -288,6 +288,66 @@ worktree_mode = "never"
WorkspaceRunSetup { run, workspace_dir }
}
pub(crate) fn setup_failed_run(context: &TestContext) -> RunSetup {
let workspace_dir = context.temp_dir.join("failed-run");
std::fs::create_dir_all(&workspace_dir)
.unwrap_or_else(|err| panic!("failed to create {}: {err}", workspace_dir.display()));
write_text_file(
&workspace_dir.join("fail.fabro"),
r#"digraph Fail {
graph [goal="Always fail", default_max_retries=0]
start [shape=Mdiamond]
exit [shape=Msquare]
boom [shape=parallelogram, script="exit 1", goal_gate=true]
start -> boom -> exit
}
"#,
);
write_text_file(
&workspace_dir.join("run.toml"),
r#"version = 1
graph = "fail.fabro"
goal = "Always fail"
[sandbox]
provider = "local"
[sandbox.local]
worktree_mode = "never"
"#,
);
let mut cmd = context.command();
cmd.current_dir(&workspace_dir);
cmd.timeout(COMMAND_TIMEOUT);
cmd.env("OPENAI_API_KEY", "test");
cmd.args([
"run",
"--auto-approve",
"--no-retro",
"--sandbox",
"local",
"--provider",
"openai",
"run.toml",
]);
// The workflow is expected to fail (script exits 1), but the CLI may still
// exit 0. We only care that conclusion.json records a non-success status.
let _output = cmd.output().expect("command should execute");
let run = only_run(context);
let conclusion = read_json(&run.run_dir.join("conclusion.json"));
let status = conclusion["status"]
.as_str()
.expect("conclusion.json should have a status field");
assert_eq!(
status, "fail",
"setup_failed_run should produce a failed conclusion"
);
run
}
fn run_local_workflow(context: &TestContext, workspace_dir: &Path, workflow: &str) -> RunSetup {
let mut cmd = context.command();
cmd.current_dir(workspace_dir);