use std::path::Path;
use clap::Parser;
use crate::engine::flow::Op;
use crate::engine::git::{get_default_branch, sync_main};
use crate::engine::{sync_skills, SkillSyncOptions};
use crate::lf::{Cli, Commands, OpsCommand, ReleaseCommand};
use crate::ops::error::{OpsError, OpsResult};
use crate::ops::progress::Progress;
use crate::ops::{
abandon_branch, commit_workflow, create_or_update_pr, land, next_branch, rebase_with_recovery,
release_bump, release_check, release_notes, release_run, release_status, release_tag, submit,
AbandonOptions, CommitOptions, LandOptions, NextOptions, PrOptions, RebaseOptions,
};
pub fn execute_flow_ops(repo: &Path, item: &Op, progress: &impl Progress) -> OpsResult<()> {
let mut argv = vec!["lf".to_string(), "op".to_string(), item.command.clone()];
argv.extend(item.args.iter().cloned());
let cli = Cli::try_parse_from(argv)
.map_err(|err| OpsError::Message(format!("invalid op item: {err}")))?;
let op = match cli.command {
Some(Commands::Op { op }) => op,
_ => {
return Err(OpsError::Message(
"op item must parse as `lf op <command>`".to_string(),
));
}
};
execute_parsed_ops(repo, &op, progress)
}
fn execute_parsed_ops(repo: &Path, op: &OpsCommand, progress: &impl Progress) -> OpsResult<()> {
match op {
OpsCommand::Land {
strict,
local,
create_pr,
worktree,
message,
title,
body,
} => {
land(
repo,
&LandOptions {
strict: *strict,
local: *local,
create_pr: *create_pr,
worktree: worktree.clone(),
commit_message: message.clone(),
pr_title: title.clone(),
pr_body: body.clone(),
agent: None,
},
progress,
)?;
Ok(())
}
OpsCommand::Submit {
strict,
create_pr,
worktree,
message,
title,
body,
} => {
submit(
repo,
&LandOptions {
strict: *strict,
local: false,
create_pr: *create_pr,
worktree: worktree.clone(),
commit_message: message.clone(),
pr_title: title.clone(),
pr_body: body.clone(),
agent: None,
},
progress,
)?;
Ok(())
}
OpsCommand::Rebase { plan, onto } => {
if *plan {
return Ok(());
}
let base = get_default_branch(repo)?;
let onto_ref = onto
.as_ref()
.map(ToString::to_string)
.unwrap_or_else(|| format!("origin/{base}"));
rebase_with_recovery(
repo,
&RebaseOptions {
onto: onto_ref,
push: true,
},
progress,
)?;
Ok(())
}
OpsCommand::Pr {
model: _,
title,
body,
} => {
create_or_update_pr(
repo,
&PrOptions {
title: title.clone(),
body: body.clone(),
agent: None,
},
progress,
)?;
Ok(())
}
OpsCommand::Sync => {
let base = get_default_branch(repo)?;
if !sync_main(repo, &base)? {
return Err(OpsError::Message(
"working tree dirty; sync aborted".to_string(),
));
}
Ok(())
}
OpsCommand::SyncSkills {
global,
yes: _,
no_prune,
} => {
sync_skills(
repo,
&SkillSyncOptions {
include_global: *global,
prune: !*no_prune,
global_home: None,
},
)?;
Ok(())
}
OpsCommand::Advance { wave } => {
let wave = crate::ops::util::resolve_wave_name(repo, wave.as_deref())
.ok_or_else(|| OpsError::Message("cannot determine wave name".to_string()))?;
let branch = crate::ops::advance_branch(repo, &wave)?;
progress.status(&format!("Advanced to branch: {branch}"));
Ok(())
}
OpsCommand::Next {
create_pr,
no_rebase,
} => {
next_branch(
repo,
&NextOptions {
create_pr: *create_pr,
rebase: !*no_rebase,
wave_name: None,
agent: None,
},
progress,
)?;
Ok(())
}
OpsCommand::Commit {
message,
push,
no_add,
} => {
commit_workflow(
repo,
&CommitOptions {
add: !*no_add,
push: *push,
create_draft_pr: true,
message: message.clone(),
..CommitOptions::for_task("commit")
},
progress,
)?;
Ok(())
}
OpsCommand::Abandon { force, branch } => {
abandon_branch(
repo,
&AbandonOptions {
branch: branch.clone(),
force: *force,
},
progress,
)?;
Ok(())
}
OpsCommand::Release { cmd } => match cmd {
ReleaseCommand::Run { version, target } => {
release_run(
repo,
version.as_deref().unwrap_or("patch"),
target.as_deref(),
progress,
)?;
Ok(())
}
ReleaseCommand::Check { target } => {
release_check(repo, target.as_deref())?;
Ok(())
}
ReleaseCommand::Notes {
version,
prev_tag,
target,
} => {
release_notes(
repo,
version,
prev_tag.as_deref(),
target.as_deref(),
progress,
)?;
Ok(())
}
ReleaseCommand::Bump { version, target } => {
release_bump(repo, version, target.as_deref(), progress)
}
ReleaseCommand::Tag { version, target } => {
release_tag(repo, version, target.as_deref())?;
Ok(())
}
ReleaseCommand::Status { target } => {
release_status(repo, target.as_deref())?;
Ok(())
}
},
OpsCommand::Push { force } => crate::engine::git::push(repo, *force).map_err(Into::into),
OpsCommand::Cp { .. }
| OpsCommand::Doctor { .. }
| OpsCommand::Pm { .. }
| OpsCommand::Branches { .. }
| OpsCommand::Wt { .. }
| OpsCommand::Shell { .. }
| OpsCommand::Auth { .. }
| OpsCommand::Queue { .. } => Err(OpsError::Message(
"ops item does not support cp/doctor/pm/branches/wt/shell/auth/queue commands"
.to_string(),
)),
}
}