1
+ use std:: process:: Command ;
2
+
1
3
use clap:: builder:: BoolishValueParser ;
2
4
use clap:: Parser ;
3
- use serde:: Serialize ;
5
+ use cross:: { shell:: Verbosity , CommandExt } ;
6
+ use serde:: { Deserialize , Serialize } ;
4
7
5
8
use crate :: util:: { get_matrix, gha_output, gha_print, CiTarget , ImageTarget } ;
6
9
7
10
pub ( crate ) fn run ( message : String , author : String ) -> Result < ( ) , color_eyre:: Report > {
8
11
let mut matrix: Vec < CiTarget > = get_matrix ( ) . clone ( ) ;
9
- if author == "bors[bot]" && message. starts_with ( "Try #" ) {
10
- if let Some ( ( _, args) ) = message. split_once ( ": " ) {
11
- let app = TargetMatrixArgs :: parse_from ( args. split ( ' ' ) ) ;
12
- app. filter ( & mut matrix) ;
13
- }
12
+ let ( prs, mut app) = if author == "bors[bot]" {
13
+ process_bors_message ( & message) ?
14
14
} else {
15
- gha_print ( "Running all targets." ) ;
15
+ ( vec ! [ ] , TargetMatrixArgs :: default ( ) )
16
+ } ;
17
+
18
+ if !prs. is_empty ( )
19
+ && prs. iter ( ) . try_fold ( true , |b, pr| {
20
+ Ok :: < _ , eyre:: Report > ( b && has_no_ci_target ( pr) ?)
21
+ } ) ?
22
+ {
23
+ app. none = true ;
16
24
}
17
25
26
+ app. filter ( & mut matrix) ;
27
+
18
28
let matrix = matrix
19
29
. iter ( )
20
30
. map ( |target| TargetMatrixElement {
@@ -32,12 +42,68 @@ pub(crate) fn run(message: String, author: String) -> Result<(), color_eyre::Rep
32
42
std : target. std . map ( |b| b as u8 ) ,
33
43
} )
34
44
. collect :: < Vec < _ > > ( ) ;
45
+
35
46
let json = serde_json:: to_string ( & matrix) ?;
36
47
gha_print ( & json) ;
37
48
gha_output ( "matrix" , & json) ;
38
49
Ok ( ( ) )
39
50
}
40
51
52
+ fn parse_gh_labels ( pr : & str ) -> cross:: Result < Vec < String > > {
53
+ #[ derive( Deserialize ) ]
54
+ struct PullRequest {
55
+ labels : Vec < PullRequestLabels > ,
56
+ }
57
+
58
+ #[ derive( Deserialize ) ]
59
+ struct PullRequestLabels {
60
+ name : String ,
61
+ }
62
+ eyre:: ensure!(
63
+ pr. chars( ) . all( |c| c. is_ascii_digit( ) ) ,
64
+ "pr should be a number, got {:?}" ,
65
+ pr
66
+ ) ;
67
+ let stdout = Command :: new ( "gh" )
68
+ . args ( [ "pr" , "view" , pr, "--json" , "labels" ] )
69
+ . run_and_get_stdout ( & mut Verbosity :: Quiet . into ( ) ) ?;
70
+ let pr_info: PullRequest = serde_json:: from_str ( & stdout) ?;
71
+ Ok ( pr_info. labels . into_iter ( ) . map ( |l| l. name ) . collect ( ) )
72
+ }
73
+
74
+ fn has_no_ci_target ( pr : & str ) -> cross:: Result < bool > {
75
+ Ok ( parse_gh_labels ( pr) ?. contains ( & "no-ci-targets" . to_owned ( ) ) )
76
+ }
77
+
78
+ /// Returns the pr(s) associated with this bors commit and the app to use for processing
79
+ fn process_bors_message ( message : & str ) -> cross:: Result < ( Vec < & str > , TargetMatrixArgs ) > {
80
+ if let Some ( message) = message. strip_prefix ( "Try #" ) {
81
+ let ( pr, args) = message
82
+ . split_once ( ':' )
83
+ . ok_or_else ( || eyre:: eyre!( "bors message must start with \" Try #:\" " ) ) ?;
84
+ let args = args. trim_start ( ) ;
85
+ let app = if !args. is_empty ( ) {
86
+ TargetMatrixArgs :: parse_from ( args. split ( ' ' ) )
87
+ } else {
88
+ TargetMatrixArgs :: default ( )
89
+ } ;
90
+ Ok ( ( vec ! [ pr] , app) )
91
+ } else if let Some ( message) = message. strip_prefix ( "Merge" ) {
92
+ Ok ( (
93
+ message
94
+ . lines ( )
95
+ . next ( )
96
+ . unwrap_or_default ( )
97
+ . split ( " #" )
98
+ . skip ( 1 )
99
+ . collect ( ) ,
100
+ TargetMatrixArgs :: default ( ) ,
101
+ ) )
102
+ } else {
103
+ eyre:: bail!( "unexpected bors commit message encountered" )
104
+ }
105
+ }
106
+
41
107
#[ derive( Serialize ) ]
42
108
#[ serde( rename_all = "kebab-case" ) ]
43
109
struct TargetMatrixElement < ' a > {
@@ -63,7 +129,7 @@ struct TargetMatrixElement<'a> {
63
129
std : Option < u8 > ,
64
130
}
65
131
66
- #[ derive( Parser , Debug ) ]
132
+ #[ derive( Parser , Debug , Default , PartialEq , Eq ) ]
67
133
#[ clap( no_binary_name = true ) ]
68
134
struct TargetMatrixArgs {
69
135
#[ clap( long, short, num_args = 0 ..) ]
@@ -84,7 +150,11 @@ struct TargetMatrixArgs {
84
150
85
151
impl TargetMatrixArgs {
86
152
pub fn filter ( & self , matrix : & mut Vec < CiTarget > ) {
153
+ if self == & TargetMatrixArgs :: default ( ) {
154
+ gha_print ( "Running all targets." ) ;
155
+ }
87
156
if self . none {
157
+ gha_print ( "Running no targets." ) ;
88
158
std:: mem:: take ( matrix) ;
89
159
return ;
90
160
}
@@ -196,4 +266,39 @@ mod tests {
196
266
let matrix = run ( [ "--none" ] ) ;
197
267
assert_eq ! ( & Vec :: <CiTarget >:: new( ) , & matrix) ;
198
268
}
269
+
270
+ #[ test]
271
+ fn prs ( ) {
272
+ assert_eq ! (
273
+ process_bors_message( "Merge #1337\n 1337: merge" ) . unwrap( ) . 0 ,
274
+ vec![ "1337" ]
275
+ ) ;
276
+ assert_eq ! (
277
+ process_bors_message( "Merge #1337 #42\n 1337: merge\n 42: merge 2" )
278
+ . unwrap( )
279
+ . 0 ,
280
+ vec![ "1337" , "42" ]
281
+ ) ;
282
+ assert_eq ! (
283
+ // the trailing space is intentional
284
+ process_bors_message( "Try #1337: \n " ) . unwrap( ) . 0 ,
285
+ vec![ "1337" ]
286
+ ) ;
287
+ }
288
+
289
+ #[ test]
290
+ fn full_invocation ( ) {
291
+ let ( prs, app) = process_bors_message ( "Try #1337: " ) . unwrap ( ) ;
292
+ assert_eq ! ( prs, vec![ "1337" ] ) ;
293
+ assert_eq ! ( app, TargetMatrixArgs :: default ( ) ) ;
294
+ let ( prs, app) = process_bors_message ( "Try #1337: --std 1" ) . unwrap ( ) ;
295
+ assert_eq ! ( prs, vec![ "1337" ] ) ;
296
+ assert_eq ! (
297
+ app,
298
+ TargetMatrixArgs {
299
+ std: Some ( true ) ,
300
+ ..TargetMatrixArgs :: default ( )
301
+ }
302
+ ) ;
303
+ }
199
304
}
0 commit comments