Skip to content

Commit 65128b9

Browse files
authored
Merge pull request #336 from Kobzol/approve-non-open-prs
Disallow approving non open PRs
2 parents 43247ad + fbb8b1c commit 65128b9

5 files changed

Lines changed: 92 additions & 22 deletions

File tree

src/bors/comment.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ handled during merge and rebase. This is normal, and you should still perform st
163163
Comment::new(message)
164164
}
165165

166+
pub fn approve_non_open_pr_comment() -> Comment {
167+
Comment::new("Only open, non-draft PRs can be approved".to_string())
168+
}
169+
166170
fn list_workflows_status(workflows: &[WorkflowModel]) -> String {
167171
workflows
168172
.iter()

src/bors/handlers/pr_events.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@ async fn notify_of_edited_pr(
269269
.post_comment(
270270
pr_number,
271271
Comment::new(format!(
272-
":warning: The base branch changed to `{base_name}`, and the PR will need to be re-approved.",
272+
r#":warning: The base branch changed to `{base_name}`, and the
273+
PR will need to be re-approved."#,
273274
)),
274275
)
275276
.await
@@ -284,7 +285,8 @@ async fn notify_of_pushed_pr(
284285
.post_comment(
285286
pr_number,
286287
Comment::new(format!(
287-
r#":warning: A new commit {} was pushed to the branch, the PR will need to be re-approved."#,
288+
r#":warning: A new commit `{}` was pushed to the branch, the
289+
PR will need to be re-approved."#,
288290
head_sha
289291
)),
290292
)
@@ -345,7 +347,10 @@ mod tests {
345347

346348
insta::assert_snapshot!(
347349
tester.get_comment().await?,
348-
@":warning: The base branch changed to `beta`, and the PR will need to be re-approved."
350+
@r"
351+
:warning: The base branch changed to `beta`, and the
352+
PR will need to be re-approved.
353+
"
349354
);
350355
tester.default_pr().await.expect_unapproved();
351356
Ok(tester)
@@ -398,7 +403,10 @@ mod tests {
398403

399404
insta::assert_snapshot!(
400405
tester.get_comment().await?,
401-
@":warning: A new commit pr-1-commit-1 was pushed to the branch, the PR will need to be re-approved."
406+
@r"
407+
:warning: A new commit `pr-1-commit-1` was pushed to the branch, the
408+
PR will need to be re-approved.
409+
"
402410
);
403411
tester.default_pr().await.expect_unapproved();
404412
Ok(tester)
@@ -478,7 +486,9 @@ mod tests {
478486
pr.pr_status == PullRequestStatus::Open
479487
})
480488
.await?;
481-
tester.close_pr(default_repo_name(), pr.number.0).await?;
489+
tester
490+
.set_pr_status_closed(default_repo_name(), pr.number.0)
491+
.await?;
482492
tester
483493
.wait_for_pr(default_repo_name(), pr.number.0, |pr| {
484494
pr.pr_status == PullRequestStatus::Closed
@@ -505,7 +515,7 @@ mod tests {
505515
})
506516
.await?;
507517
tester
508-
.ready_for_review(default_repo_name(), pr.number.0)
518+
.set_pr_status_ready_for_review(default_repo_name(), pr.number.0)
509519
.await?;
510520
tester
511521
.wait_for_pr(default_repo_name(), pr.number.0, |pr| {
@@ -527,7 +537,7 @@ mod tests {
527537
})
528538
.await?;
529539
tester
530-
.convert_to_draft(default_repo_name(), pr.number.0)
540+
.set_pr_status_draft(default_repo_name(), pr.number.0)
531541
.await?;
532542
tester
533543
.wait_for_pr(default_repo_name(), pr.number.0, |pr| {
@@ -595,7 +605,9 @@ mod tests {
595605
pr.pr_status == PullRequestStatus::Open
596606
})
597607
.await?;
598-
tester.merge_pr(default_repo_name(), pr.number.0).await?;
608+
tester
609+
.set_pr_status_merged(default_repo_name(), pr.number.0)
610+
.await?;
599611
tester
600612
.wait_for_pr(default_repo_name(), pr.number.0, |pr| {
601613
pr.pr_status == PullRequestStatus::Merged

src/bors/handlers/refresh.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,9 @@ timeout = 3600
339339
.await?;
340340
tester
341341
.with_blocked_webhooks(async |tester| {
342-
tester.close_pr(default_repo_name(), pr.number.0).await
342+
tester
343+
.set_pr_status_closed(default_repo_name(), pr.number.0)
344+
.await
343345
})
344346
.await?;
345347
tester.refresh_prs().await;
@@ -359,7 +361,7 @@ timeout = 3600
359361
tester
360362
.with_blocked_webhooks(async |tester| {
361363
tester
362-
.convert_to_draft(default_repo_name(), pr.number.0)
364+
.set_pr_status_draft(default_repo_name(), pr.number.0)
363365
.await
364366
})
365367
.await?;

src/bors/handlers/review.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use std::sync::Arc;
22

33
use crate::PgDbClient;
4-
use crate::bors::Comment;
54
use crate::bors::RepositoryState;
65
use crate::bors::command::Approver;
76
use crate::bors::command::RollupMode;
7+
use crate::bors::comment::approve_non_open_pr_comment;
88
use crate::bors::handlers::has_permission;
99
use crate::bors::handlers::labels::handle_label_trigger;
1010
use crate::bors::handlers::{PullRequestData, deny_request};
11+
use crate::bors::{Comment, PullRequestStatus};
1112
use crate::database::ApprovalInfo;
1213
use crate::database::DelegatedPermission;
1314
use crate::database::TreeState;
@@ -31,6 +32,15 @@ pub(super) async fn command_approve(
3132
deny_request(&repo_state, pr.number(), author, PermissionType::Review).await?;
3233
return Ok(());
3334
};
35+
36+
if !matches!(pr.github.status, PullRequestStatus::Open) {
37+
repo_state
38+
.client
39+
.post_comment(pr.number(), approve_non_open_pr_comment())
40+
.await?;
41+
return Ok(());
42+
}
43+
3444
let approver = match approver {
3545
Approver::Myself => author.username.clone(),
3646
Approver::Specified(approver) => approver.clone(),
@@ -1040,4 +1050,46 @@ mod tests {
10401050
})
10411051
.await;
10421052
}
1053+
1054+
#[sqlx::test]
1055+
async fn approve_draft_pr(pool: sqlx::PgPool) {
1056+
run_test(pool, |mut tester| async {
1057+
tester
1058+
.set_pr_status_draft(default_repo_name(), default_pr_number())
1059+
.await?;
1060+
tester.post_comment("@bors r+").await?;
1061+
insta::assert_snapshot!(tester.get_comment().await?, @"Only open, non-draft PRs can be approved");
1062+
tester.default_pr().await.expect_unapproved();
1063+
Ok(tester)
1064+
})
1065+
.await;
1066+
}
1067+
1068+
#[sqlx::test]
1069+
async fn approve_closed_pr(pool: sqlx::PgPool) {
1070+
run_test(pool, |mut tester| async {
1071+
tester
1072+
.set_pr_status_closed(default_repo_name(), default_pr_number())
1073+
.await?;
1074+
tester.post_comment("@bors r+").await?;
1075+
insta::assert_snapshot!(tester.get_comment().await?, @"Only open, non-draft PRs can be approved");
1076+
tester.default_pr().await.expect_unapproved();
1077+
Ok(tester)
1078+
})
1079+
.await;
1080+
}
1081+
1082+
#[sqlx::test]
1083+
async fn approve_merged_pr(pool: sqlx::PgPool) {
1084+
run_test(pool, |mut tester| async {
1085+
tester
1086+
.set_pr_status_merged(default_repo_name(), default_pr_number())
1087+
.await?;
1088+
tester.post_comment("@bors r+").await?;
1089+
insta::assert_snapshot!(tester.get_comment().await?, @"Only open, non-draft PRs can be approved");
1090+
tester.default_pr().await.expect_unapproved();
1091+
Ok(tester)
1092+
})
1093+
.await;
1094+
}
10431095
}

src/tests/mocks/bors.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ impl BorsTester {
427427
Ok(pr)
428428
}
429429

430-
pub async fn close_pr(
430+
pub async fn reopen_pr(
431431
&mut self,
432432
repo_name: GithubRepoName,
433433
pr_number: u64,
@@ -438,19 +438,19 @@ impl BorsTester {
438438
let pr = repo
439439
.pull_requests
440440
.get_mut(&pr_number)
441-
.expect("PR must be opened before closing it");
442-
pr.close_pr();
441+
.expect("PR must exist before being reopened");
442+
pr.reopen_pr();
443443
pr.clone()
444444
};
445445
self.send_webhook(
446446
"pull_request",
447-
GitHubPullRequestEventPayload::new(pr.clone(), "closed", None),
447+
GitHubPullRequestEventPayload::new(pr.clone(), "reopened", None),
448448
)
449449
.await?;
450450
Ok(())
451451
}
452452

453-
pub async fn reopen_pr(
453+
pub async fn set_pr_status_closed(
454454
&mut self,
455455
repo_name: GithubRepoName,
456456
pr_number: u64,
@@ -461,19 +461,19 @@ impl BorsTester {
461461
let pr = repo
462462
.pull_requests
463463
.get_mut(&pr_number)
464-
.expect("PR must exist before being reopened");
465-
pr.reopen_pr();
464+
.expect("PR must be opened before closing it");
465+
pr.close_pr();
466466
pr.clone()
467467
};
468468
self.send_webhook(
469469
"pull_request",
470-
GitHubPullRequestEventPayload::new(pr.clone(), "reopened", None),
470+
GitHubPullRequestEventPayload::new(pr.clone(), "closed", None),
471471
)
472472
.await?;
473473
Ok(())
474474
}
475475

476-
pub async fn convert_to_draft(
476+
pub async fn set_pr_status_draft(
477477
&mut self,
478478
repo_name: GithubRepoName,
479479
pr_number: u64,
@@ -496,7 +496,7 @@ impl BorsTester {
496496
Ok(())
497497
}
498498

499-
pub async fn ready_for_review(
499+
pub async fn set_pr_status_ready_for_review(
500500
&mut self,
501501
repo_name: GithubRepoName,
502502
pr_number: u64,
@@ -519,7 +519,7 @@ impl BorsTester {
519519
Ok(())
520520
}
521521

522-
pub async fn merge_pr(
522+
pub async fn set_pr_status_merged(
523523
&mut self,
524524
repo_name: GithubRepoName,
525525
pr_number: u64,

0 commit comments

Comments
 (0)