@@ -13,7 +13,7 @@ use std::{
1313 process:: Command ,
1414} ;
1515
16- use anyhow:: Context as _;
16+ use anyhow:: { anyhow , Context as _} ;
1717use audit:: { audit_cell_arguments, audit_config_arguments} ;
1818use td_util:: command:: { create_at_file_arg, with_command} ;
1919use tracing:: info;
@@ -63,34 +63,63 @@ impl Buck2 {
6363 command
6464 }
6565
66+ fn kill_daemon ( & self ) {
67+ let mut command = self . command ( ) ;
68+ command. arg ( "kill" ) ;
69+ let _ = command. status ( ) ;
70+ }
71+
72+ fn run_output_with_retry < F > ( & self , mut make_command : F ) -> anyhow:: Result < std:: process:: Output >
73+ where
74+ F : FnMut ( ) -> Command ,
75+ {
76+ const MAX_ATTEMPTS : usize = 2 ;
77+ for attempt in 0 ..MAX_ATTEMPTS {
78+ let command = make_command ( ) ;
79+ let res = with_command ( command, |mut command| Ok ( command. output ( ) ?) ) ?;
80+ if res. status . success ( ) {
81+ return Ok ( res) ;
82+ }
83+
84+ let stderr = String :: from_utf8_lossy ( & res. stderr ) . to_string ( ) ;
85+ if attempt + 1 < MAX_ATTEMPTS && should_retry_buck2_daemon ( & stderr) {
86+ tracing:: warn!(
87+ "buck2 daemon connection failed; retrying after kill (attempt {}/{})" ,
88+ attempt + 1 ,
89+ MAX_ATTEMPTS
90+ ) ;
91+ self . kill_daemon ( ) ;
92+ continue ;
93+ }
94+
95+ return Err ( anyhow ! ( "Buck2 stderr: {}" , stderr) ) ;
96+ }
97+
98+ Err ( anyhow ! ( "Buck2 failed after {} attempts" , MAX_ATTEMPTS ) )
99+ }
100+
66101 pub fn root ( & mut self ) -> anyhow:: Result < PathBuf > {
67102 Ok ( self . root . clone ( ) . expect ( "buck root unset" ) )
68103 }
69104
70105 pub fn cells ( & mut self ) -> anyhow:: Result < String > {
71- let mut command = self . command ( ) ;
72- command. args ( audit_cell_arguments ( ) ) ;
73- command. current_dir ( self . root ( ) ?) ;
74- let res = with_command ( command, |mut command| {
75- let res = command. output ( ) ?;
76- res. status . exit_result ( ) . with_context ( || {
77- format ! ( "Buck2 stderr: {}" , String :: from_utf8_lossy( & res. stderr) )
78- } ) ?;
79- Ok ( res)
106+ let root = self . root ( ) ?;
107+ let res = self . run_output_with_retry ( || {
108+ let mut command = self . command ( ) ;
109+ command. args ( audit_cell_arguments ( ) ) ;
110+ command. current_dir ( & root) ;
111+ command
80112 } ) ?;
81113 Ok ( String :: from_utf8 ( res. stdout ) ?)
82114 }
83115
84116 pub fn audit_config ( & mut self ) -> anyhow:: Result < String > {
85- let mut command = self . command ( ) ;
86- command. args ( audit_config_arguments ( ) ) ;
87- command. current_dir ( self . root ( ) ?) ;
88- let res = with_command ( command, |mut command| {
89- let res = command. output ( ) ?;
90- res. status . exit_result ( ) . with_context ( || {
91- format ! ( "Buck2 stderr: {}" , String :: from_utf8_lossy( & res. stderr) )
92- } ) ?;
93- Ok ( res)
117+ let root = self . root ( ) ?;
118+ let res = self . run_output_with_retry ( || {
119+ let mut command = self . command ( ) ;
120+ command. args ( audit_config_arguments ( ) ) ;
121+ command. current_dir ( & root) ;
122+ command
94123 } ) ?;
95124 Ok ( String :: from_utf8 ( res. stdout ) ?)
96125 }
@@ -119,17 +148,18 @@ impl Buck2 {
119148
120149 let ( _file, at_file) = create_at_file_arg ( targets, "\n " ) ?;
121150
122- let mut command = self . command ( ) ;
123- command
124- . args ( targets_arguments ( ) )
125- . arg ( "--output" )
126- . arg ( output)
127- . arg ( at_file)
128- . args ( extra_args) ;
129-
130- with_command ( command, |mut command| {
131- Ok ( command. status ( ) ?. exit_result ( ) ?)
132- } )
151+ let res = self . run_output_with_retry ( || {
152+ let mut command = self . command ( ) ;
153+ command
154+ . args ( targets_arguments ( ) )
155+ . arg ( "--output" )
156+ . arg ( output)
157+ . arg ( at_file. clone ( ) )
158+ . args ( extra_args) ;
159+ command
160+ } ) ?;
161+ res. status . exit_result ( ) . context ( "buck2 targets failed" ) ?;
162+ Ok ( ( ) )
133163 }
134164
135165 pub fn owners (
@@ -141,28 +171,31 @@ impl Buck2 {
141171
142172 let ( _file, at_file) = create_at_file_arg ( changes, "\n " ) ?;
143173
144- let mut command = self . command ( ) ;
145- command
146- . arg ( "uquery" )
147- . arg ( "--json" )
148- . arg ( "owner(\" %s\" )" )
149- . arg ( at_file)
150- . args ( extra_args) ;
151- command. current_dir ( self . root ( ) ?) ;
152-
153- info ! ( "Running owners query: {:?}" , command) ;
154-
155- let res = with_command ( command, |mut command| {
156- let res = command. output ( ) ?;
157- res. status . exit_result ( ) . with_context ( || {
158- format ! ( "Buck2 stderr: {}" , String :: from_utf8_lossy( & res. stderr) )
159- } ) ?;
160- Ok ( res)
174+ let root = self . root ( ) ?;
175+ let res = self . run_output_with_retry ( || {
176+ let mut command = self . command ( ) ;
177+ command
178+ . arg ( "uquery" )
179+ . arg ( "--json" )
180+ . arg ( "owner(\" %s\" )" )
181+ . arg ( & at_file)
182+ . args ( extra_args) ;
183+ command. current_dir ( & root) ;
184+ command
161185 } ) ?;
186+
187+ info ! ( "Running owners query" ) ;
162188 Ok ( String :: from_utf8 ( res. stdout ) ?)
163189 }
164190}
165191
192+ fn should_retry_buck2_daemon ( stderr : & str ) -> bool {
193+ let lower = stderr. to_ascii_lowercase ( ) ;
194+ lower. contains ( "failed to connect to buck daemon" )
195+ || lower. contains ( "no buckd.info timed out" )
196+ || lower. contains ( "starting new buck2 daemon" )
197+ }
198+
166199pub fn targets_arguments ( ) -> & ' static [ & ' static str ] {
167200 & [
168201 "targets" ,
0 commit comments