@@ -5,7 +5,8 @@ use crate::bors::RepositoryState;
55use crate :: bors:: command:: RollupMode ;
66use crate :: bors:: command:: { Approver , CommandPrefix } ;
77use crate :: bors:: comment:: {
8- approve_non_open_pr_comment, approve_wip_title, delegate_comment, delegate_try_builds_comment,
8+ approve_blocking_labels_present, approve_non_open_pr_comment, approve_wip_title,
9+ delegate_comment, delegate_try_builds_comment,
910} ;
1011use crate :: bors:: handlers:: has_permission;
1112use crate :: bors:: handlers:: labels:: handle_label_trigger;
@@ -38,7 +39,7 @@ pub(super) async fn command_approve(
3839 return Ok ( ( ) ) ;
3940 } ;
4041
41- if let Some ( error_comment) = check_pr_approval_validity ( pr) . await ? {
42+ if let Some ( error_comment) = check_pr_approval_validity ( pr, & repo_state ) . await ? {
4243 repo_state
4344 . client
4445 . post_comment ( pr. number ( ) , error_comment)
@@ -69,7 +70,10 @@ const WIP_KEYWORDS: &[&str] = &["wip", "[do not merge]"];
6970/// Check that the given PR can be approved in its current state.
7071/// Returns `Ok(Some(comment))` if it **cannot** be approved; the comment should be sent to the
7172/// pull request.
72- async fn check_pr_approval_validity ( pr : & PullRequestData ) -> anyhow:: Result < Option < Comment > > {
73+ async fn check_pr_approval_validity (
74+ pr : & PullRequestData ,
75+ repo : & RepositoryState ,
76+ ) -> anyhow:: Result < Option < Comment > > {
7377 // Check PR status
7478 if !matches ! ( pr. github. status, PullRequestStatus :: Open ) {
7579 return Ok ( Some ( approve_non_open_pr_comment ( ) ) ) ;
@@ -81,6 +85,24 @@ async fn check_pr_approval_validity(pr: &PullRequestData) -> anyhow::Result<Opti
8185 return Ok ( Some ( approve_wip_title ( wip_kw) ) ) ;
8286 }
8387
88+ // Check blocking labels
89+ let config = repo. config . load ( ) ;
90+ let blocking_labels: Vec < & str > = pr
91+ . github
92+ . labels
93+ . iter ( )
94+ . map ( |label| label. as_str ( ) )
95+ . filter ( |label| {
96+ config
97+ . labels_blocking_approval
98+ . iter ( )
99+ . any ( |blocking_label| blocking_label == label)
100+ } )
101+ . collect ( ) ;
102+ if !blocking_labels. is_empty ( ) {
103+ return Ok ( Some ( approve_blocking_labels_present ( & blocking_labels) ) ) ;
104+ }
105+
84106 Ok ( None )
85107}
86108
@@ -1102,7 +1124,7 @@ mod tests {
11021124 . set_pr_status_draft ( default_repo_name ( ) , default_pr_number ( ) )
11031125 . await ?;
11041126 tester. post_comment ( "@bors r+" ) . await ?;
1105- insta:: assert_snapshot!( tester. get_comment( ) . await ?, @"Only open, non-draft PRs can be approved" ) ;
1127+ insta:: assert_snapshot!( tester. get_comment( ) . await ?, @":clipboard: Only open, non-draft PRs can be approved. " ) ;
11061128 tester. default_pr ( ) . await . expect_unapproved ( ) ;
11071129 Ok ( ( ) )
11081130 } )
@@ -1116,7 +1138,7 @@ mod tests {
11161138 . set_pr_status_closed ( default_repo_name ( ) , default_pr_number ( ) )
11171139 . await ?;
11181140 tester. post_comment ( "@bors r+" ) . await ?;
1119- insta:: assert_snapshot!( tester. get_comment( ) . await ?, @"Only open, non-draft PRs can be approved" ) ;
1141+ insta:: assert_snapshot!( tester. get_comment( ) . await ?, @":clipboard: Only open, non-draft PRs can be approved. " ) ;
11201142 tester. default_pr ( ) . await . expect_unapproved ( ) ;
11211143 Ok ( ( ) )
11221144 } )
@@ -1130,7 +1152,7 @@ mod tests {
11301152 . set_pr_status_merged ( default_repo_name ( ) , default_pr_number ( ) )
11311153 . await ?;
11321154 tester. post_comment ( "@bors r+" ) . await ?;
1133- insta:: assert_snapshot!( tester. get_comment( ) . await ?, @"Only open, non-draft PRs can be approved" ) ;
1155+ insta:: assert_snapshot!( tester. get_comment( ) . await ?, @":clipboard: Only open, non-draft PRs can be approved. " ) ;
11341156 tester. default_pr ( ) . await . expect_unapproved ( ) ;
11351157 Ok ( ( ) )
11361158 } )
@@ -1156,4 +1178,56 @@ mod tests {
11561178 } )
11571179 . await ;
11581180 }
1181+
1182+ #[ sqlx:: test]
1183+ async fn approve_pr_with_blocked_label ( pool : sqlx:: PgPool ) {
1184+ BorsBuilder :: new ( pool)
1185+ . github ( GitHubState :: default ( ) . with_default_config (
1186+ r#"
1187+ labels_blocking_approval = ["proposed-final-comment-period"]
1188+ "# ,
1189+ ) )
1190+ . run_test ( async |tester : & mut BorsTester | {
1191+ tester
1192+ . edit_pr ( default_repo_name ( ) , default_pr_number ( ) , |pr| {
1193+ pr. labels = vec ! [
1194+ "S-waiting-on-review" . to_string( ) ,
1195+ "proposed-final-comment-period" . to_string( ) ,
1196+ ] ;
1197+ } )
1198+ . await ?;
1199+ tester. post_comment ( "@bors r+" ) . await ?;
1200+ insta:: assert_snapshot!( tester. get_comment( ) . await ?, @":clipboard: This PR cannot be approved because it currently has the following label: `proposed-final-comment-period`." ) ;
1201+ tester. default_pr ( ) . await . expect_unapproved ( ) ;
1202+ Ok ( ( ) )
1203+ } )
1204+ . await ;
1205+ }
1206+
1207+ #[ sqlx:: test]
1208+ async fn approve_pr_with_blocked_labels ( pool : sqlx:: PgPool ) {
1209+ BorsBuilder :: new ( pool)
1210+ . github ( GitHubState :: default ( ) . with_default_config (
1211+ r#"
1212+ labels_blocking_approval = ["proposed-final-comment-period", "final-comment-period"]
1213+ "# ,
1214+ ) )
1215+ . run_test ( async |tester : & mut BorsTester | {
1216+ tester
1217+ . edit_pr ( default_repo_name ( ) , default_pr_number ( ) , |pr| {
1218+ pr. labels = vec ! [
1219+ "S-waiting-on-review" . to_string( ) ,
1220+ "proposed-final-comment-period" . to_string( ) ,
1221+ "final-comment-period" . to_string( ) ,
1222+ "S-blocked" . to_string( ) ,
1223+ ] ;
1224+ } )
1225+ . await ?;
1226+ tester. post_comment ( "@bors r+" ) . await ?;
1227+ insta:: assert_snapshot!( tester. get_comment( ) . await ?, @":clipboard: This PR cannot be approved because it currently has the following labels: `proposed-final-comment-period`, `final-comment-period`." ) ;
1228+ tester. default_pr ( ) . await . expect_unapproved ( ) ;
1229+ Ok ( ( ) )
1230+ } )
1231+ . await ;
1232+ }
11591233}
0 commit comments