use std::time::Duration;
use tracing::{info, warn};
use super::{poll_github_pr_checks, GoalGithubPrClient};
use crate::runtime::goal::review::dispatcher::AggregateReviewVerdict;
const CI_POLL_TIMEOUT: Duration = Duration::from_secs(6 * 60);
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AutoMergeAction {
Merged { commit_sha: Option<String> },
BlockedOnCi,
BlockedOnReview { reason: String },
BlockedOnProtection { reason: String },
SkippedNotAutoPolicy,
Error { reason: String },
}
#[derive(Debug)]
pub struct AutoMergeContext<'a> {
pub slice_id: &'a str,
pub goal_id: &'a str,
pub pr_url: &'a str,
}
pub async fn attempt_auto_merge(
client: &mut (dyn GoalGithubPrClient + Send + Sync),
verdict: &AggregateReviewVerdict,
ctx: AutoMergeContext<'_>,
) -> AutoMergeAction {
if !verdict.all_passed {
let reason = verdict
.blocking_reason
.clone()
.unwrap_or_else(|| "review verdict failed".to_string());
warn!(
slice_id = %ctx.slice_id,
goal_id = %ctx.goal_id,
pr_url = %ctx.pr_url,
reason = %reason,
"auto-merge blocked on review"
);
return AutoMergeAction::BlockedOnReview { reason };
}
match poll_github_pr_checks(ctx.pr_url, CI_POLL_TIMEOUT).await {
Ok(true) => {}
Ok(false) => {
warn!(
slice_id = %ctx.slice_id,
goal_id = %ctx.goal_id,
pr_url = %ctx.pr_url,
"auto-merge blocked on CI: checks not green"
);
return AutoMergeAction::BlockedOnCi;
}
Err(e) => {
warn!(
slice_id = %ctx.slice_id,
goal_id = %ctx.goal_id,
pr_url = %ctx.pr_url,
error = %e,
"auto-merge blocked on CI: polling error"
);
return AutoMergeAction::BlockedOnCi;
}
}
match client.merge_pr(ctx.pr_url).await {
Ok(mutation) => {
info!(
slice_id = %ctx.slice_id,
goal_id = %ctx.goal_id,
pr_url = %ctx.pr_url,
"auto-merge succeeded"
);
AutoMergeAction::Merged {
commit_sha: mutation.url,
}
}
Err(e) => {
let reason = format!("gh pr merge failed: {e}");
warn!(
slice_id = %ctx.slice_id,
goal_id = %ctx.goal_id,
pr_url = %ctx.pr_url,
error = %e,
"auto-merge failed"
);
AutoMergeAction::Error { reason }
}
}
}