Skip to content

Commit 0a33cb0

Browse files
feat: implement r+, r= and r-
1 parent 2ab9b1a commit 0a33cb0

File tree

16 files changed

+504
-41
lines changed

16 files changed

+504
-41
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Add down migration script here
2+
ALTER TABLE pull_request DROP COLUMN approved_by;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- Add up migration script here
2+
ALTER TABLE pull_request ADD COLUMN approved_by TEXT;
3+

rust-bors.example.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ timeout = 3600
1010
# - try_failed: Try build has failed
1111
# (Optional)
1212
[labels]
13+
approve = ["+approved"]
1314
try = ["+foo", "-bar"]
1415
try_succeed = ["+foobar", "+foo", "+baz"]
1516
try_failed = []

src/bors/command/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,21 @@ pub enum Parent {
1111
Last,
1212
}
1313

14+
#[derive(Clone, Debug, PartialEq)]
15+
pub enum Approver {
16+
/// The approver is the same as the comment author.
17+
Myself,
18+
/// The approver is specified by the user.
19+
Specified(String),
20+
}
21+
1422
/// Bors command specified by a user.
1523
#[derive(Debug, PartialEq)]
1624
pub enum BorsCommand {
25+
/// Approve a commit.
26+
Approve(Approver),
27+
/// Unapprove a commit.
28+
Unapprove,
1729
/// Print help
1830
Help,
1931
/// Ping the bot.

src/bors/command/parser.rs

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use std::collections::HashSet;
44

5-
use crate::bors::command::{BorsCommand, Parent};
5+
use crate::bors::command::{Approver, BorsCommand, Parent};
66
use crate::github::CommitSha;
77

88
#[derive(Debug, PartialEq)]
@@ -40,8 +40,14 @@ impl CommandParser {
4040
text: &'a str,
4141
) -> Vec<Result<BorsCommand, CommandParseError<'a>>> {
4242
// The order of the parsers in the vector is important
43-
let parsers: Vec<for<'b> fn(&'b str, &[CommandPart<'b>]) -> ParseResult<'b>> =
44-
vec![parser_help, parser_ping, parser_try_cancel, parser_try];
43+
let parsers: Vec<for<'b> fn(&'b str, &[CommandPart<'b>]) -> ParseResult<'b>> = vec![
44+
parse_self_approve,
45+
parse_unapprove,
46+
parser_help,
47+
parser_ping,
48+
parser_try_cancel,
49+
parser_try,
50+
];
4551

4652
text.lines()
4753
.filter_map(|line| match line.find(&self.prefix) {
@@ -62,7 +68,11 @@ impl CommandParser {
6268
}
6369
Some(Err(CommandParseError::UnknownCommand(command)))
6470
}
71+
// for `@bors r=user1`
6572
CommandPart::KeyValue { .. } => {
73+
if let Some(result) = parse_approve_on_behalf(&parts) {
74+
return Some(result);
75+
}
6676
Some(Err(CommandParseError::MissingCommand))
6777
}
6878
}
@@ -108,6 +118,41 @@ fn parse_parts(input: &str) -> Result<Vec<CommandPart>, CommandParseError> {
108118

109119
/// Parsers
110120
121+
/// Parses "@bors r+"
122+
fn parse_self_approve<'a>(command: &'a str, _parts: &[CommandPart<'a>]) -> ParseResult<'a> {
123+
if command == "r+" {
124+
Some(Ok(BorsCommand::Approve(Approver::Myself)))
125+
} else {
126+
None
127+
}
128+
}
129+
130+
/// Parses "@bors r=<username>".
131+
fn parse_approve_on_behalf<'a>(parts: &[CommandPart<'a>]) -> ParseResult<'a> {
132+
if let Some(CommandPart::KeyValue { key, value }) = parts.first() {
133+
if *key != "r" {
134+
Some(Err(CommandParseError::UnknownArg(key)))
135+
} else if value.is_empty() {
136+
return Some(Err(CommandParseError::MissingArgValue { arg: "r" }));
137+
} else {
138+
Some(Ok(BorsCommand::Approve(Approver::Specified(
139+
value.to_string(),
140+
))))
141+
}
142+
} else {
143+
Some(Err(CommandParseError::MissingArgValue { arg: "r" }))
144+
}
145+
}
146+
147+
/// Parses "@bors r-"
148+
fn parse_unapprove<'a>(command: &'a str, _parts: &[CommandPart<'a>]) -> ParseResult<'a> {
149+
if command == "r-" {
150+
Some(Ok(BorsCommand::Unapprove))
151+
} else {
152+
None
153+
}
154+
}
155+
111156
/// Parses "@bors help".
112157
fn parser_help<'a>(command: &'a str, _parts: &[CommandPart<'a>]) -> ParseResult<'a> {
113158
if command == "help" {
@@ -199,7 +244,7 @@ fn parser_try_cancel<'a>(command: &'a str, parts: &[CommandPart<'a>]) -> ParseRe
199244
#[cfg(test)]
200245
mod tests {
201246
use crate::bors::command::parser::{CommandParseError, CommandParser};
202-
use crate::bors::command::{BorsCommand, Parent};
247+
use crate::bors::command::{Approver, BorsCommand, Parent};
203248
use crate::github::CommitSha;
204249

205250
#[test]
@@ -242,6 +287,38 @@ mod tests {
242287
assert!(matches!(cmds[0], Err(CommandParseError::DuplicateArg("a"))));
243288
}
244289

290+
#[test]
291+
fn parse_default_approve() {
292+
let cmds = parse_commands("@bors r+");
293+
assert_eq!(cmds.len(), 1);
294+
assert!(matches!(
295+
cmds[0],
296+
Ok(BorsCommand::Approve(Approver::Myself))
297+
));
298+
}
299+
300+
#[test]
301+
fn parse_approve_on_behalf() {
302+
let cmds = parse_commands("@bors r=user1");
303+
assert_eq!(cmds.len(), 1);
304+
insta::assert_debug_snapshot!(cmds[0], @r###"
305+
Ok(
306+
Approve(
307+
Specified(
308+
"user1",
309+
),
310+
),
311+
)
312+
"###);
313+
}
314+
315+
#[test]
316+
fn parse_unapprove() {
317+
let cmds = parse_commands("@bors r-");
318+
assert_eq!(cmds.len(), 1);
319+
assert!(matches!(cmds[0], Ok(BorsCommand::Unapprove)));
320+
}
321+
245322
#[test]
246323
fn parse_help() {
247324
let cmds = parse_commands("@bors help");

src/bors/handlers/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::bors::event::{BorsGlobalEvent, BorsRepositoryEvent, PullRequestCommen
88
use crate::bors::handlers::help::command_help;
99
use crate::bors::handlers::ping::command_ping;
1010
use crate::bors::handlers::refresh::refresh_repository;
11+
use crate::bors::handlers::review::{command_approve, command_unapprove};
1112
use crate::bors::handlers::trybuild::{command_try_build, command_try_cancel, TRY_BRANCH_NAME};
1213
use crate::bors::handlers::workflow::{
1314
handle_check_suite_completed, handle_workflow_completed, handle_workflow_started,
@@ -22,6 +23,7 @@ mod help;
2223
mod labels;
2324
mod ping;
2425
mod refresh;
26+
mod review;
2527
mod trybuild;
2628
mod workflow;
2729

@@ -179,6 +181,18 @@ async fn handle_comment(
179181
let repo = Arc::clone(&repo);
180182
let database = Arc::clone(&database);
181183
let result = match command {
184+
BorsCommand::Approve(approver) => {
185+
let span = tracing::info_span!("Approve");
186+
command_approve(repo, database, &pull_request, &comment.author, &approver)
187+
.instrument(span)
188+
.await
189+
}
190+
BorsCommand::Unapprove => {
191+
let span = tracing::info_span!("Unapprove");
192+
command_unapprove(repo, database, &pull_request, &comment.author)
193+
.instrument(span)
194+
.await
195+
}
182196
BorsCommand::Help => {
183197
let span = tracing::info_span!("Help");
184198
command_help(repo, &pull_request).instrument(span).await

0 commit comments

Comments
 (0)