diff --git a/src/github.rs b/src/github.rs index 95f7ee622..5bf6d39ce 100644 --- a/src/github.rs +++ b/src/github.rs @@ -749,6 +749,20 @@ pub struct Repository { impl Repository { const GITHUB_API_URL: &'static str = "https://api.github.com"; + pub async fn get_issue(&self, client: &GithubClient, id: u64) -> anyhow::Result { + let url = format!( + "{}/repos/{}/issues/{}", + Repository::GITHUB_API_URL, + self.full_name, + id + ); + let result = client.get(&url); + client + .json(result) + .await + .with_context(|| format!("failed to get issue from {}", url)) + } + pub async fn get_issues<'a>( &self, client: &GithubClient, diff --git a/src/handlers.rs b/src/handlers.rs index 40639fff0..b2ee07084 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -25,6 +25,7 @@ impl fmt::Display for HandlerError { mod assign; mod autolabel; +mod beta_backport; mod close; mod github_releases; mod glacier; @@ -74,6 +75,14 @@ pub async fn handle(ctx: &Context, event: &Event) -> Vec { ); } + if let Err(e) = beta_backport::handle(ctx, event).await { + log::error!( + "failed to process event {:?} with beta_backport handler: {:?}", + event, + e + ); + } + if let Some(ghr_config) = config .as_ref() .ok() diff --git a/src/handlers/beta_backport.rs b/src/handlers/beta_backport.rs new file mode 100644 index 000000000..07ae7e6b1 --- /dev/null +++ b/src/handlers/beta_backport.rs @@ -0,0 +1,52 @@ +use crate::github::{Event, IssuesAction, Label}; +use crate::handlers::Context; +use regex::Regex; + +lazy_static! { + // See https://docs.github.com/en/issues/tracking-your-work-with-issues/creating-issues/linking-a-pull-request-to-an-issue + // Max 19 digits long to prevent u64 overflow + static ref CLOSES_ISSUE: Regex = Regex::new("(close[sd]|fix(e[sd])?|resolve[sd]) #(\\d{1,19})").unwrap(); +} + +pub(crate) async fn handle( + ctx: &Context, + event: &Event, +) -> anyhow::Result<()> { + let issue_event = if let Event::Issue(event) = event { + event + } else { + return Ok(()); + }; + + if issue_event.action != IssuesAction::Opened { + return Ok(()); + } + + if issue_event.issue.pull_request.is_none() { + return Ok(()); + } + + for caps in CLOSES_ISSUE.captures_iter(&issue_event.issue.body) { + // Should never fail due to the regex + let issue_id = caps.get(1).unwrap().as_str().parse::().unwrap(); + let issue = issue_event + .repository + .get_issue(&ctx.github, issue_id) + .await?; + if issue.labels.contains(&Label { + name: "regression-from-stable-to-beta".to_string(), + }) { + let mut labels = issue_event.issue.labels().to_owned(); + labels.push(Label { + name: "beta-nominated".to_string(), + }); + issue_event + .issue + .set_labels(&ctx.github, labels) + .await?; + break; + } + } + + Ok(()) +}