1+ use std:: process:: Command ;
2+
13use clap:: builder:: BoolishValueParser ;
24use clap:: Parser ;
3- use serde:: Serialize ;
5+ use cross:: { shell:: Verbosity , CommandExt } ;
6+ use serde:: { Deserialize , Serialize } ;
47
58use crate :: util:: { get_matrix, gha_output, gha_print, CiTarget , ImageTarget } ;
69
710pub ( crate ) fn run ( message : String , author : String ) -> Result < ( ) , color_eyre:: Report > {
811 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) ?
1414 } 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 ;
1624 }
1725
26+ app. filter ( & mut matrix) ;
27+
1828 let matrix = matrix
1929 . iter ( )
2030 . map ( |target| TargetMatrixElement {
@@ -32,12 +42,68 @@ pub(crate) fn run(message: String, author: String) -> Result<(), color_eyre::Rep
3242 std : target. std . map ( |b| b as u8 ) ,
3343 } )
3444 . collect :: < Vec < _ > > ( ) ;
45+
3546 let json = serde_json:: to_string ( & matrix) ?;
3647 gha_print ( & json) ;
3748 gha_output ( "matrix" , & json) ;
3849 Ok ( ( ) )
3950}
4051
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+
41107#[ derive( Serialize ) ]
42108#[ serde( rename_all = "kebab-case" ) ]
43109struct TargetMatrixElement < ' a > {
@@ -63,7 +129,7 @@ struct TargetMatrixElement<'a> {
63129 std : Option < u8 > ,
64130}
65131
66- #[ derive( Parser , Debug ) ]
132+ #[ derive( Parser , Debug , Default , PartialEq , Eq ) ]
67133#[ clap( no_binary_name = true ) ]
68134struct TargetMatrixArgs {
69135 #[ clap( long, short, num_args = 0 ..) ]
@@ -84,7 +150,11 @@ struct TargetMatrixArgs {
84150
85151impl TargetMatrixArgs {
86152 pub fn filter ( & self , matrix : & mut Vec < CiTarget > ) {
153+ if self == & TargetMatrixArgs :: default ( ) {
154+ gha_print ( "Running all targets." ) ;
155+ }
87156 if self . none {
157+ gha_print ( "Running no targets." ) ;
88158 std:: mem:: take ( matrix) ;
89159 return ;
90160 }
@@ -196,4 +266,39 @@ mod tests {
196266 let matrix = run ( [ "--none" ] ) ;
197267 assert_eq ! ( & Vec :: <CiTarget >:: new( ) , & matrix) ;
198268 }
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+ }
199304}
0 commit comments