Skip to content

Commit b47cc09

Browse files
committed
Do not allow approving WIP PRs
1 parent 85435f6 commit b47cc09

File tree

5 files changed

+50
-5
lines changed

5 files changed

+50
-5
lines changed

src/bors/comment.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,15 @@ pub fn approve_non_open_pr_comment() -> Comment {
218218
Comment::new("Only open, non-draft PRs can be approved".to_string())
219219
}
220220

221+
pub fn approve_wip_title(keyword: &str) -> Comment {
222+
Comment::new(format!(
223+
r":clipboard: Looks like this PR is still in progress, ignoring approval.
224+
225+
Hint: Remove **{keyword}** from this PR's title when it is ready for review.
226+
"
227+
))
228+
}
229+
221230
pub fn delegate_try_builds_comment(delegatee: &str, bot_prefix: &CommandPrefix) -> Comment {
222231
Comment::new(format!(
223232
r":v: @{delegatee}, you can now perform try builds on this pull request!

src/bors/handlers/review.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::bors::RepositoryState;
55
use crate::bors::command::RollupMode;
66
use crate::bors::command::{Approver, CommandPrefix};
77
use crate::bors::comment::{
8-
approve_non_open_pr_comment, delegate_comment, delegate_try_builds_comment,
8+
approve_non_open_pr_comment, approve_wip_title, delegate_comment, delegate_try_builds_comment,
99
};
1010
use crate::bors::handlers::has_permission;
1111
use crate::bors::handlers::labels::handle_label_trigger;
@@ -62,13 +62,25 @@ pub(super) async fn command_approve(
6262
notify_of_approval(&repo_state, pr, approver.as_str()).await
6363
}
6464

65+
/// Keywords that will prevent an approval if they appear in the PR's title.
66+
/// They are checked in a case-insensitive manner.
67+
const WIP_KEYWORDS: &[&str] = &["wip", "[do not merge]"];
68+
6569
/// Check that the given PR can be approved in its current state.
6670
/// Returns `Ok(Some(comment))` if it **cannot** be approved; the comment should be sent to the
6771
/// pull request.
6872
async fn check_pr_approval_validity(pr: &PullRequestData) -> anyhow::Result<Option<Comment>> {
73+
// Check PR status
6974
if !matches!(pr.github.status, PullRequestStatus::Open) {
7075
return Ok(Some(approve_non_open_pr_comment()));
7176
}
77+
78+
// Check WIP title
79+
let title = pr.github.title.to_lowercase();
80+
if let Some(wip_kw) = WIP_KEYWORDS.iter().find(|kw| title.contains(*kw)) {
81+
return Ok(Some(approve_wip_title(wip_kw)));
82+
}
83+
7284
Ok(None)
7385
}
7486

@@ -298,6 +310,7 @@ async fn notify_of_delegation(
298310
#[cfg(test)]
299311
mod tests {
300312
use crate::database::{DelegatedPermission, TreeState};
313+
use crate::tests::BorsTester;
301314
use crate::{
302315
bors::{
303316
RollupMode,
@@ -1123,4 +1136,24 @@ mod tests {
11231136
})
11241137
.await;
11251138
}
1139+
1140+
#[sqlx::test]
1141+
async fn approve_wip_pr(pool: sqlx::PgPool) {
1142+
run_test(pool, async |tester: &mut BorsTester| {
1143+
tester
1144+
.edit_pr(default_repo_name(), default_pr_number(), |pr| {
1145+
pr.title = "[do not merge] CI experiments".to_string();
1146+
})
1147+
.await?;
1148+
tester.post_comment("@bors r+").await?;
1149+
insta::assert_snapshot!(tester.get_comment().await?, @r"
1150+
:clipboard: Looks like this PR is still in progress, ignoring approval.
1151+
1152+
Hint: Remove **[do not merge]** from this PR's title when it is ready for review.
1153+
");
1154+
tester.default_pr().await.expect_unapproved();
1155+
Ok(())
1156+
})
1157+
.await;
1158+
}
11261159
}

src/bors/handlers/trybuild.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ It fixes so many issues, sir."
562562

563563
insta::assert_snapshot!(tester.get_branch_commit_message(&tester.try_branch()), @r"
564564
Auto merge of rust-lang/borstest#1 - pr-1, r=<try>
565-
PR #1
565+
Title of PR 1
566566
");
567567
Ok(())
568568
})
@@ -591,7 +591,7 @@ try-job: Bar
591591

592592
insta::assert_snapshot!(tester.get_branch_commit_message(&tester.try_branch()), @r"
593593
Auto merge of rust-lang/borstest#1 - pr-1, r=<try>
594-
PR #1
594+
Title of PR 1
595595
596596
try-job: Foo
597597
try-job: Bar
@@ -620,7 +620,7 @@ try-job: Bar
620620

621621
insta::assert_snapshot!(tester.get_branch_commit_message(&tester.try_branch()), @r"
622622
Auto merge of rust-lang/borstest#1 - pr-1, r=<try>
623-
PR #1
623+
Title of PR 1
624624
625625
626626
try-job: Baz

src/tests/mocks/pull_request.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,13 @@ impl From<PullRequest> for GitHubPullRequest {
214214
closed_at,
215215
assignees,
216216
description,
217+
title,
217218
} = pr;
218219
GitHubPullRequest {
219220
user: author.clone().into(),
220221
url: "https://test.com".to_string(),
221222
id: number.0 + 1000,
222-
title: format!("PR #{number}"),
223+
title,
223224
body: description,
224225
mergeable_state,
225226
draft: status == PullRequestStatus::Draft,

src/tests/mocks/repository.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub struct PullRequest {
5555
pub closed_at: Option<DateTime<Utc>>,
5656
pub assignees: Vec<User>,
5757
pub description: String,
58+
pub title: String,
5859
}
5960

6061
impl PullRequest {
@@ -78,6 +79,7 @@ impl PullRequest {
7879
closed_at: None,
7980
assignees: Vec::new(),
8081
description: format!("Description of PR {number}"),
82+
title: format!("Title of PR {number}"),
8183
}
8284
}
8385
}

0 commit comments

Comments
 (0)