@@ -17,22 +17,50 @@ interface SpawnAsync {
1717 status : number | null ;
1818}
1919
20- export function spawnAsync (
21- cmd : string ,
22- options ?:
23- | childProcess . SpawnOptionsWithoutStdio
24- | childProcess . SpawnOptionsWithStdioTuple < childProcess . StdioPipe , childProcess . StdioPipe , childProcess . StdioPipe > ,
25- input ?: string ,
26- ) : Promise < SpawnAsync > {
27- const start = Date . now ( ) ;
20+ interface SpawnOptions {
21+ timeout ?: number ;
22+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23+ env ?: any ;
24+ cwd ?: string ;
25+ }
26+
27+ export function spawnAsync ( cmd : string , options ?: SpawnOptions , input ?: string ) : Promise < SpawnAsync > {
28+ const timeoutMs = options ?. timeout || 60_000 * 5 ;
2829
2930 return new Promise < SpawnAsync > ( resolve => {
3031 const cp = childProcess . spawn ( cmd , { shell : true , ...options } ) ;
3132
33+ // Ensure we properly time out after max. 5 min per command
34+ let timeout : ReturnType < typeof setTimeout > | undefined = setTimeout ( ( ) => {
35+ console . log ( `Command "${ cmd } " timed out after 5 minutes.` ) ;
36+ cp . kill ( ) ;
37+ end ( null , `ETDIMEDOUT: Process timed out after ${ timeoutMs } ms.` ) ;
38+ } , timeoutMs ) ;
39+
3240 const stderr : unknown [ ] = [ ] ;
3341 const stdout : string [ ] = [ ] ;
3442 let error : Error | undefined ;
3543
44+ function end ( status : number | null , errorMessage ?: string ) : void {
45+ // This means we already ended
46+ if ( ! timeout ) {
47+ return ;
48+ }
49+
50+ if ( ! error && errorMessage ) {
51+ error = new Error ( errorMessage ) ;
52+ }
53+
54+ clearTimeout ( timeout ) ;
55+ timeout = undefined ;
56+ resolve ( {
57+ stdout : stdout . join ( '' ) ,
58+ stderr : stderr . join ( '' ) ,
59+ error : error || ( status !== 0 ? new Error ( `Process exited with status ${ status } ` ) : undefined ) ,
60+ status,
61+ } ) ;
62+ }
63+
3664 cp . stdout . on ( 'data' , data => {
3765 stdout . push ( data ? ( data as object ) . toString ( ) : '' ) ;
3866 } ) ;
@@ -46,19 +74,7 @@ export function spawnAsync(
4674 } ) ;
4775
4876 cp . on ( 'close' , status => {
49- const end = Date . now ( ) ;
50-
51- // We manually mark this as timed out if the process takes too long
52- if ( ! error && status === 1 && options ?. timeout && end >= start + options . timeout ) {
53- error = new Error ( `ETDIMEDOUT: Process timed out after ${ options . timeout } ms.` ) ;
54- }
55-
56- resolve ( {
57- stdout : stdout . join ( '' ) ,
58- stderr : stderr . join ( '' ) ,
59- error : error || ( status !== 0 ? new Error ( `Process exited with status ${ status } ` ) : undefined ) ,
60- status,
61- } ) ;
77+ end ( status ) ;
6278 } ) ;
6379
6480 if ( input ) {
0 commit comments