diff --git a/src/config.rs b/src/config.rs index 244c35ef..b9853552 100644 --- a/src/config.rs +++ b/src/config.rs @@ -32,6 +32,9 @@ pub(crate) struct Config { pub(crate) github_releases: Option, pub(crate) review_submitted: Option, pub(crate) review_requested: Option, + pub(crate) converted_to_draft: Option, + pub(crate) ready_for_review: Option, + pub(crate) closed_pr: Option, pub(crate) shortcut: Option, pub(crate) note: Option, pub(crate) mentions: Option, @@ -209,6 +212,12 @@ pub(crate) struct AutolabelLabelConfig { #[serde(default)] pub(crate) new_pr: bool, #[serde(default)] + pub(crate) new_draft_pr: bool, + #[serde(default)] + pub(crate) reopened_pr: bool, + #[serde(default)] + pub(crate) reopened_draft_pr: bool, + #[serde(default)] pub(crate) new_issue: bool, } @@ -289,6 +298,27 @@ pub(crate) struct ReviewRequestedConfig { pub(crate) add_labels: Vec, } +#[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct ConvertedToDraftConfig { + pub(crate) remove_labels: Vec, + pub(crate) add_labels: Vec, +} + +#[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct ReadyForReviewConfig { + pub(crate) remove_labels: Vec, + pub(crate) add_labels: Vec, +} + +#[derive(PartialEq, Eq, Debug, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct ClosedPrConfig { + pub(crate) remove_labels: Vec, + pub(crate) add_labels: Vec, +} + pub(crate) async fn get( gh: &GithubClient, repo: &Repository, diff --git a/src/handlers.rs b/src/handlers.rs index 814113b3..0a7d3347 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -26,6 +26,8 @@ impl fmt::Display for HandlerError { mod assign; mod autolabel; mod close; +mod closed_pr; +mod converted_to_draft; pub mod docs_update; mod github_releases; mod glacier; @@ -39,6 +41,7 @@ mod notification; mod notify_zulip; mod ping; mod prioritize; +mod ready_for_review; mod relabel; mod review_requested; mod review_submitted; @@ -167,6 +170,9 @@ issue_handlers! { no_merges, notify_zulip, review_requested, + converted_to_draft, + ready_for_review, + closed_pr, validate_config, } diff --git a/src/handlers/autolabel.rs b/src/handlers/autolabel.rs index 2a107849..26a00428 100644 --- a/src/handlers/autolabel.rs +++ b/src/handlers/autolabel.rs @@ -26,7 +26,10 @@ pub(super) async fn parse_input( // FIXME: This will re-apply labels after a push that the user had tried to // remove. Not much can be done about that currently; the before/after on // synchronize may be straddling a rebase, which will break diff generation. - if event.action == IssuesAction::Opened || event.action == IssuesAction::Synchronize { + if matches!( + &event.action, + IssuesAction::Opened | IssuesAction::Reopened | IssuesAction::Synchronize + ) { let files = event .issue .diff(&ctx.github) @@ -69,21 +72,32 @@ pub(super) async fn parse_input( name: label.to_owned(), }); } - if cfg.new_pr && event.action == IssuesAction::Opened { - autolabels.push(Label { - name: label.to_owned(), - }); - } } - if event.issue.pull_request.is_none() - && cfg.new_issue - && event.action == IssuesAction::Opened + if event.issue.is_pr() + && matches!(event.action, IssuesAction::Opened) + && ((cfg.new_pr && !event.issue.draft) || (cfg.new_draft_pr && event.issue.draft)) { autolabels.push(Label { name: label.to_owned(), }); } + + if event.issue.is_pr() + && matches!(event.action, IssuesAction::Reopened) + && ((cfg.reopened_pr && !event.issue.draft) + || (cfg.reopened_draft_pr && event.issue.draft)) + { + autolabels.push(Label { + name: label.to_owned(), + }); + } + + if !event.issue.is_pr() && cfg.new_issue && event.action == IssuesAction::Opened { + autolabels.push(Label { + name: label.to_owned(), + }); + } } if !autolabels.is_empty() { diff --git a/src/handlers/closed_pr.rs b/src/handlers/closed_pr.rs new file mode 100644 index 00000000..1ad15480 --- /dev/null +++ b/src/handlers/closed_pr.rs @@ -0,0 +1,44 @@ +use crate::config::ClosedPrConfig; +use crate::github::{IssuesAction, IssuesEvent, Label}; +use crate::handlers::Context; + +pub(crate) struct ClosedPrInput {} + +pub(crate) async fn parse_input( + _ctx: &Context, + event: &IssuesEvent, + _config: Option<&ClosedPrConfig>, +) -> Result, String> { + // PR author requests a review from one of the assignees + + match &event.action { + IssuesAction::Closed if event.issue.is_pr() => Ok(Some(ClosedPrInput {})), + _ => Ok(None), + } +} + +pub(crate) async fn handle_input( + ctx: &Context, + config: &ClosedPrConfig, + event: &IssuesEvent, + ClosedPrInput {}: ClosedPrInput, +) -> anyhow::Result<()> { + event + .issue + .add_labels( + &ctx.github, + config + .add_labels + .iter() + .cloned() + .map(|name| Label { name }) + .collect(), + ) + .await?; + + for label in &config.remove_labels { + event.issue.remove_label(&ctx.github, label).await?; + } + + Ok(()) +} diff --git a/src/handlers/converted_to_draft.rs b/src/handlers/converted_to_draft.rs new file mode 100644 index 00000000..ef16f001 --- /dev/null +++ b/src/handlers/converted_to_draft.rs @@ -0,0 +1,44 @@ +use crate::config::ConvertedToDraftConfig; +use crate::github::{IssuesAction, IssuesEvent, Label}; +use crate::handlers::Context; + +pub(crate) struct ConvertedToDraftInput {} + +pub(crate) async fn parse_input( + _ctx: &Context, + event: &IssuesEvent, + _config: Option<&ConvertedToDraftConfig>, +) -> Result, String> { + // PR author requests a review from one of the assignees + + match &event.action { + IssuesAction::ConvertedToDraft => Ok(Some(ConvertedToDraftInput {})), + _ => Ok(None), + } +} + +pub(crate) async fn handle_input( + ctx: &Context, + config: &ConvertedToDraftConfig, + event: &IssuesEvent, + ConvertedToDraftInput {}: ConvertedToDraftInput, +) -> anyhow::Result<()> { + event + .issue + .add_labels( + &ctx.github, + config + .add_labels + .iter() + .cloned() + .map(|name| Label { name }) + .collect(), + ) + .await?; + + for label in &config.remove_labels { + event.issue.remove_label(&ctx.github, label).await?; + } + + Ok(()) +} diff --git a/src/handlers/ready_for_review.rs b/src/handlers/ready_for_review.rs new file mode 100644 index 00000000..929ebd2a --- /dev/null +++ b/src/handlers/ready_for_review.rs @@ -0,0 +1,44 @@ +use crate::config::ReadyForReviewConfig; +use crate::github::{IssuesAction, IssuesEvent, Label}; +use crate::handlers::Context; + +pub(crate) struct ReadyForReviewInput {} + +pub(crate) async fn parse_input( + _ctx: &Context, + event: &IssuesEvent, + _config: Option<&ReadyForReviewConfig>, +) -> Result, String> { + // PR author requests a review from one of the assignees + + match &event.action { + IssuesAction::ReadyForReview => Ok(Some(ReadyForReviewInput {})), + _ => Ok(None), + } +} + +pub(crate) async fn handle_input( + ctx: &Context, + config: &ReadyForReviewConfig, + event: &IssuesEvent, + ReadyForReviewInput {}: ReadyForReviewInput, +) -> anyhow::Result<()> { + event + .issue + .add_labels( + &ctx.github, + config + .add_labels + .iter() + .cloned() + .map(|name| Label { name }) + .collect(), + ) + .await?; + + for label in &config.remove_labels { + event.issue.remove_label(&ctx.github, label).await?; + } + + Ok(()) +}