mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-08 03:10:26 +00:00
Add --force option for fabro pr create (#155)
Co-authored-by: Fabro <noreply@fabro.sh> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3a9f6def9b
commit
38a6db8ea6
4 changed files with 100 additions and 1 deletions
|
|
@ -672,6 +672,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)]
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ pub(super) async fn create_command(
|
|||
|
||||
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"),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use fabro_test::{fabro_snapshot, test_context};
|
||||
|
||||
use super::support::{setup_completed_fast_dry_run, setup_created_fast_dry_run};
|
||||
use super::support::{setup_completed_fast_dry_run, setup_created_fast_dry_run, setup_failed_run};
|
||||
|
||||
#[test]
|
||||
fn help() {
|
||||
|
|
@ -23,6 +23,7 @@ fn help() {
|
|||
--server <SERVER> Fabro server target: http(s) URL or absolute Unix socket path [env: FABRO_SERVER=]
|
||||
--debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
|
||||
--model <MODEL> LLM model for generating PR description
|
||||
-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=]
|
||||
|
|
@ -79,3 +80,35 @@ fn pr_create_uses_store_run_record_without_run_json() {
|
|||
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?
|
||||
");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -378,6 +378,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
|
||||
|
||||
[workflow]
|
||||
graph = "fail.fabro"
|
||||
|
||||
[run]
|
||||
goal = "Always fail"
|
||||
|
||||
[run.sandbox]
|
||||
provider = "local"
|
||||
|
||||
[run.sandbox.local]
|
||||
worktree_mode = "never"
|
||||
"#,
|
||||
);
|
||||
|
||||
// The workflow is expected to fail (script exits 1), but the CLI may still
|
||||
// exit 0. The `pr create` tests verify that the conclusion has a fail status.
|
||||
let run_id = unique_run_id();
|
||||
let mut cmd = context.run_cmd();
|
||||
cmd.current_dir(&workspace_dir);
|
||||
cmd.timeout(COMMAND_TIMEOUT);
|
||||
cmd.env("OPENAI_API_KEY", "test");
|
||||
cmd.args([
|
||||
"--run-id",
|
||||
run_id.as_str(),
|
||||
"--auto-approve",
|
||||
"--no-retro",
|
||||
"--sandbox",
|
||||
"local",
|
||||
"--provider",
|
||||
"openai",
|
||||
"run.toml",
|
||||
]);
|
||||
let _output = cmd.output().expect("command should execute");
|
||||
|
||||
RunSetup {
|
||||
run_dir: context.find_run_dir(&run_id),
|
||||
run_id,
|
||||
}
|
||||
}
|
||||
|
||||
fn run_local_workflow(context: &TestContext, workspace_dir: &Path, workflow: &str) -> RunSetup {
|
||||
let run_id = unique_run_id();
|
||||
let mut cmd = context.run_cmd();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue