@@ -6,14 +6,8 @@ use crate::github::{
6
6
use crate :: load:: SiteCtxt ;
7
7
8
8
use hashbrown:: HashMap ;
9
- use regex:: Regex ;
10
9
use std:: sync:: Arc ;
11
10
12
- lazy_static:: lazy_static! {
13
- static ref BODY_TIMER_BUILD : Regex =
14
- Regex :: new( r"(?:\W|^)@rust-timer\s+build\s+(\w+)(?:\W|$)(?:include=(\S+))?\s*(?:exclude=(\S+))?\s*(?:runs=(\d+))?" ) . unwrap( ) ;
15
- }
16
-
17
11
pub async fn handle_github (
18
12
request : github:: Request ,
19
13
ctxt : Arc < SiteCtxt > ,
@@ -143,13 +137,30 @@ async fn handle_rust_timer(
143
137
return Ok ( github:: Response ) ;
144
138
}
145
139
146
- for captures in build_captures ( & comment. body ) . map ( |( _, captures) | captures) {
147
- let include = captures. get ( 2 ) . map ( |v| v. as_str ( ) ) ;
148
- let exclude = captures. get ( 3 ) . map ( |v| v. as_str ( ) ) ;
149
- let runs = captures. get ( 4 ) . and_then ( |v| v. as_str ( ) . parse :: < i32 > ( ) . ok ( ) ) ;
150
- {
151
- let conn = ctxt. conn ( ) . await ;
152
- conn. queue_pr ( issue. number , include, exclude, runs) . await ;
140
+ let build_cmds: Vec < _ > = parse_build_commands ( & comment. body ) . collect ( ) ;
141
+ let mut valid_build_cmds = vec ! [ ] ;
142
+ let mut errors = String :: new ( ) ;
143
+ for cmd in build_cmds {
144
+ match cmd {
145
+ Ok ( cmd) => valid_build_cmds. push ( cmd) ,
146
+ Err ( error) => errors. push_str ( & format ! ( "Cannot parse build command: {error}\n " ) ) ,
147
+ }
148
+ }
149
+ if !errors. is_empty ( ) {
150
+ main_client. post_comment ( issue. number , errors) . await ;
151
+ return Ok ( github:: Response ) ;
152
+ }
153
+
154
+ {
155
+ let conn = ctxt. conn ( ) . await ;
156
+ for command in & valid_build_cmds {
157
+ conn. queue_pr (
158
+ issue. number ,
159
+ command. params . include ,
160
+ command. params . exclude ,
161
+ command. params . runs ,
162
+ )
163
+ . await ;
153
164
}
154
165
}
155
166
@@ -158,7 +169,7 @@ async fn handle_rust_timer(
158
169
main_client,
159
170
ci_client,
160
171
issue. number ,
161
- build_captures ( & comment . body ) . map ( |( commit , _ ) | commit ) ,
172
+ valid_build_cmds . iter ( ) . map ( |c| c . sha ) ,
162
173
)
163
174
. await ?;
164
175
@@ -181,6 +192,22 @@ fn parse_queue_command(body: &str) -> Option<Result<QueueCommand, String>> {
181
192
Some ( Ok ( QueueCommand { params } ) )
182
193
}
183
194
195
+ /// Parses all occurrences of a `@rust-timer build <shared-args>` command in the input string.
196
+ fn parse_build_commands ( body : & str ) -> impl Iterator < Item = Result < BuildCommand , String > > {
197
+ get_command_lines ( body, "build" ) . map ( |line| {
198
+ let mut iter = line. splitn ( 2 , ' ' ) ;
199
+ let Some ( sha) = iter. next ( ) . filter ( |s| !s. is_empty ( ) && !s. contains ( '=' ) ) else {
200
+ return Err ( "Missing SHA in build command" . to_string ( ) ) ;
201
+ } ;
202
+
203
+ let sha = sha. trim_start_matches ( "https://github.com/rust-lang/rust/commit/" ) ;
204
+ let args = iter. next ( ) . unwrap_or ( "" ) ;
205
+ let args = parse_command_arguments ( args) ?;
206
+ let params = parse_benchmark_parameters ( args) ?;
207
+ Ok ( BuildCommand { sha, params } )
208
+ } )
209
+ }
210
+
184
211
fn get_command_lines < ' a : ' b , ' b > (
185
212
body : & ' a str ,
186
213
command : & ' b str ,
@@ -192,7 +219,7 @@ fn get_command_lines<'a: 'b, 'b>(
192
219
. map ( |index| line[ index + prefix. len ( ) ..] . trim ( ) )
193
220
} )
194
221
. filter_map ( move |line| line. strip_prefix ( command) )
195
- . map ( move |l| l. trim ( ) )
222
+ . map ( move |l| l. trim_start ( ) )
196
223
}
197
224
198
225
fn parse_benchmark_parameters < ' a > (
@@ -246,27 +273,19 @@ struct QueueCommand<'a> {
246
273
params : BenchmarkParameters < ' a > ,
247
274
}
248
275
276
+ #[ derive( Debug ) ]
277
+ struct BuildCommand < ' a > {
278
+ sha : & ' a str ,
279
+ params : BenchmarkParameters < ' a > ,
280
+ }
281
+
249
282
#[ derive( Debug ) ]
250
283
struct BenchmarkParameters < ' a > {
251
284
include : Option < & ' a str > ,
252
285
exclude : Option < & ' a str > ,
253
286
runs : Option < i32 > ,
254
287
}
255
288
256
- /// Run the `@rust-timer build` regex over the comment message extracting the commit and the other captures
257
- fn build_captures ( comment_body : & str ) -> impl Iterator < Item = ( & str , regex:: Captures ) > {
258
- BODY_TIMER_BUILD
259
- . captures_iter ( comment_body)
260
- . filter_map ( |captures| {
261
- captures. get ( 1 ) . map ( |m| {
262
- let commit = m
263
- . as_str ( )
264
- . trim_start_matches ( "https://github.com/rust-lang/rust/commit/" ) ;
265
- ( commit, captures)
266
- } )
267
- } )
268
- }
269
-
270
289
pub async fn get_authorized_users ( ) -> Result < Vec < u64 > , String > {
271
290
let url = format ! ( "{}/permissions/perf.json" , :: rust_team_data:: v1:: BASE_URL ) ;
272
291
let client = reqwest:: Client :: new ( ) ;
@@ -288,32 +307,62 @@ mod tests {
288
307
use super :: * ;
289
308
290
309
#[ test]
291
- fn captures_all_shas ( ) {
292
- let comment_body = r#"
293
- Going to do perf runs for a few of these:
294
-
295
- @rust-timer build 5832462aa1d9373b24ace96ad2c50b7a18af9952 (https://github.com/rust-lang/rust/pull/100307)
296
- @rust-timer build 23936af287657fa4148aeab40cc2a780810fae52 (https://github.com/rust-lang/rust/pull/100392)
297
- "# ;
298
- let shas = build_captures ( comment_body)
299
- . map ( |( c, _) | c)
300
- . collect :: < Vec < _ > > ( ) ;
301
- assert_eq ! (
302
- shas,
303
- & [
304
- "5832462aa1d9373b24ace96ad2c50b7a18af9952" ,
305
- "23936af287657fa4148aeab40cc2a780810fae52"
306
- ]
307
- ) ;
310
+ fn build_command_missing ( ) {
311
+ assert ! ( get_build_commands( "" ) . is_empty( ) ) ;
312
+ }
313
+
314
+ #[ test]
315
+ fn build_unknown_command ( ) {
316
+ assert ! ( get_build_commands( "@rust-timer foo" ) . is_empty( ) ) ;
308
317
}
309
318
310
319
#[ test]
311
- fn command_missing ( ) {
320
+ fn build_command_missing_sha ( ) {
321
+ insta:: assert_compact_debug_snapshot!( get_build_commands( "@rust-timer build" ) ,
322
+ @r###"[Err("Missing SHA in build command")]"### ) ;
323
+ }
324
+
325
+ #[ test]
326
+ fn build_command ( ) {
327
+ insta:: assert_compact_debug_snapshot!( get_build_commands( "@rust-timer build 5832462aa1d9373b24ace96ad2c50b7a18af9952" ) ,
328
+ @r###"[Ok(BuildCommand { sha: "5832462aa1d9373b24ace96ad2c50b7a18af9952", params: BenchmarkParameters { include: None, exclude: None, runs: None } })]"### ) ;
329
+ }
330
+
331
+ #[ test]
332
+ fn build_command_multiple ( ) {
333
+ insta:: assert_compact_debug_snapshot!( get_build_commands( r#"
334
+ @rust-timer build 5832462aa1d9373b24ace96ad2c50b7a18af9952
335
+ @rust-timer build 23936af287657fa4148aeab40cc2a780810fae52
336
+ "# ) ,
337
+ @r###"[Ok(BuildCommand { sha: "5832462aa1d9373b24ace96ad2c50b7a18af9952", params: BenchmarkParameters { include: None, exclude: None, runs: None } }), Ok(BuildCommand { sha: "23936af287657fa4148aeab40cc2a780810fae52", params: BenchmarkParameters { include: None, exclude: None, runs: None } })]"### ) ;
338
+ }
339
+
340
+ #[ test]
341
+ fn build_command_unknown_arg ( ) {
342
+ insta:: assert_compact_debug_snapshot!( get_build_commands( "@rust-timer build foo=bar" ) ,
343
+ @r###"[Err("Missing SHA in build command")]"### ) ;
344
+ }
345
+
346
+ #[ test]
347
+ fn build_command_complex ( ) {
348
+ insta:: assert_compact_debug_snapshot!( get_build_commands( " @rust-timer build sha123456 exclude=baz include=foo,bar runs=4" ) ,
349
+ @r###"[Ok(BuildCommand { sha: "sha123456", params: BenchmarkParameters { include: Some("foo,bar"), exclude: Some("baz"), runs: Some(4) } })]"### ) ;
350
+ }
351
+
352
+ #[ test]
353
+ fn build_command_link ( ) {
354
+ insta:: assert_compact_debug_snapshot!( get_build_commands( r#"
355
+ @rust-timer build https://github.com/rust-lang/rust/commit/323f521bc6d8f2b966ba7838a3f3ee364e760b7e"# ) ,
356
+ @r###"[Ok(BuildCommand { sha: "323f521bc6d8f2b966ba7838a3f3ee364e760b7e", params: BenchmarkParameters { include: None, exclude: None, runs: None } })]"### ) ;
357
+ }
358
+
359
+ #[ test]
360
+ fn queue_command_missing ( ) {
312
361
assert ! ( parse_queue_command( "" ) . is_none( ) ) ;
313
362
}
314
363
315
364
#[ test]
316
- fn unknown_command ( ) {
365
+ fn queue_unknown_command ( ) {
317
366
assert ! ( parse_queue_command( "@rust-timer foo" ) . is_none( ) ) ;
318
367
}
319
368
@@ -388,4 +437,19 @@ Going to do perf runs for a few of these:
388
437
insta:: assert_compact_debug_snapshot!( parse_queue_command( "@rust-timer queue runs=3 exclude=c,a include=b" ) ,
389
438
@r###"Some(Ok(QueueCommand { params: BenchmarkParameters { include: Some("b"), exclude: Some("c,a"), runs: Some(3) } }))"### ) ;
390
439
}
440
+
441
+ #[ test]
442
+ fn queue_command_multiline ( ) {
443
+ insta:: assert_compact_debug_snapshot!( parse_queue_command( r#"Ok, this looks good now.
444
+ Let's do a perf run quickly and then we can merge it.
445
+
446
+ @bors try @rust-timer queue include=foo,bar
447
+
448
+ Otherwise LGTM."# ) ,
449
+ @r###"Some(Ok(QueueCommand { params: BenchmarkParameters { include: Some("foo,bar"), exclude: None, runs: None } }))"### ) ;
450
+ }
451
+
452
+ fn get_build_commands ( body : & str ) -> Vec < Result < BuildCommand , String > > {
453
+ parse_build_commands ( body) . collect ( )
454
+ }
391
455
}
0 commit comments