@@ -223,31 +223,66 @@ function runCommand(command, args, options = {}) {
223223 return new Promise ( ( resolve , reject ) => {
224224 const child = childProcess . spawn ( command , args , {
225225 cwd : options . cwd ?? process . cwd ( ) ,
226+ detached : options . detached ?? process . platform !== "win32" ,
226227 env : options . env ?? process . env ,
227228 shell : options . shell ,
228229 stdio : options . stdio ?? [ "pipe" , "pipe" , "pipe" ] ,
229230 windowsVerbatimArguments : options . windowsVerbatimArguments ,
230231 } ) ;
231232 const stdout = createOutputCapture ( "stdout" ) ;
232233 const stderr = createOutputCapture ( "stderr" ) ;
234+ let timedOut = false ;
235+ let killTimer ;
233236 const timer = setTimeout ( ( ) => {
234- child . kill ( "SIGTERM" ) ;
235- setTimeout ( ( ) => child . kill ( "SIGKILL" ) , 1000 ) . unref ( ) ;
236- reject ( new Error ( scrub ( `command timed out: ${ command } ${ args . join ( " " ) } ` ) ) ) ;
237+ timedOut = true ;
238+ terminateProcessTree ( child , "SIGTERM" ) ;
239+ killTimer = setTimeout ( ( ) => terminateProcessTree ( child , "SIGKILL" ) , 1000 ) ;
240+ killTimer . unref ( ) ;
237241 } , timeoutMs ) ;
238- child . stdout . on ( "data" , ( chunk ) => {
242+ child . stdout ? .on ( "data" , ( chunk ) => {
239243 stdout . append ( chunk ) ;
240244 } ) ;
241- child . stderr . on ( "data" , ( chunk ) => {
245+ child . stderr ? .on ( "data" , ( chunk ) => {
242246 stderr . append ( chunk ) ;
243247 } ) ;
248+ const parentSignalHandlers = new Map ( ) ;
249+ const removeParentSignalHandlers = ( ) => {
250+ for ( const [ signal , handler ] of parentSignalHandlers ) {
251+ process . off ( signal , handler ) ;
252+ }
253+ parentSignalHandlers . clear ( ) ;
254+ } ;
255+ if ( process . platform !== "win32" && child . pid ) {
256+ for ( const signal of [ "SIGHUP" , "SIGINT" , "SIGTERM" ] ) {
257+ const handler = ( ) => {
258+ terminateProcessTree ( child , signal ) ;
259+ removeParentSignalHandlers ( ) ;
260+ process . kill ( process . pid , signal ) ;
261+ } ;
262+ parentSignalHandlers . set ( signal , handler ) ;
263+ process . once ( signal , handler ) ;
264+ }
265+ }
244266 child . on ( "error" , ( error ) => {
245267 clearTimeout ( timer ) ;
268+ if ( killTimer ) {
269+ clearTimeout ( killTimer ) ;
270+ }
271+ removeParentSignalHandlers ( ) ;
246272 reject ( error instanceof Error ? error : new Error ( formatErrorMessage ( error ) ) ) ;
247273 } ) ;
248274 child . on ( "close" , ( code , signal ) => {
249275 clearTimeout ( timer ) ;
276+ if ( killTimer ) {
277+ clearTimeout ( killTimer ) ;
278+ }
279+ removeParentSignalHandlers ( ) ;
250280 const result = { code : code ?? 0 , signal, stdout : stdout . text ( ) , stderr : stderr . text ( ) } ;
281+ if ( timedOut ) {
282+ terminateProcessTree ( child , "SIGKILL" ) ;
283+ reject ( new Error ( scrub ( `command timed out: ${ command } ${ args . join ( " " ) } ` ) ) ) ;
284+ return ;
285+ }
251286 if ( result . code !== 0 && options . allowFailure !== true ) {
252287 reject (
253288 new Error (
0 commit comments