@@ -67,6 +67,59 @@ export class CDKDeployer implements BackendDeployer {
6767 return this . tryInvokeCdk ( InvokableCommand . DESTROY , backendId , [ '--force' ] ) ;
6868 } ;
6969
70+ /**
71+ * Wrapper for the child process executor. Helps in unit testing as node:test framework
72+ * doesn't have capabilities to mock exported functions like `execa` as of right now.
73+ */
74+ executeChildProcess = async (
75+ command : string ,
76+ commandArgs : string [ ] ,
77+ options : { printStdout : boolean } = { printStdout : true }
78+ ) => {
79+ // We let the stdout and stdin inherit and streamed to parent process but pipe
80+ // the stderr and use it to throw on failure. This is to prevent actual
81+ // actionable errors being hidden among the stdout. Moreover execa errors are
82+ // useless when calling CLIs unless you made execa calling error.
83+ let aggregatedStderr = '' ;
84+ const aggregatorStderrStream = new stream . Writable ( ) ;
85+ aggregatorStderrStream . _write = function ( chunk , encoding , done ) {
86+ aggregatedStderr += chunk ;
87+ done ( ) ;
88+ } ;
89+
90+ const childProcess = execa ( command , commandArgs , {
91+ stdin : 'inherit' ,
92+ stdout : 'pipe' ,
93+ stderr : 'pipe' ,
94+
95+ // Piping the output by default strips off the color. This is a workaround to
96+ // preserve the color being piped to parent process.
97+ extendEnv : true ,
98+ env : { FORCE_COLOR : '1' } ,
99+ } ) ;
100+ childProcess . stderr ?. pipe ( aggregatorStderrStream ) ;
101+
102+ if ( options ?. printStdout ) {
103+ childProcess . stdout ?. pipe ( process . stdout ) ;
104+ }
105+
106+ const cdkOutput = { deploymentTimes : { } } ;
107+ if ( childProcess . stdout ) {
108+ await this . populateCDKOutputFromStdout ( cdkOutput , childProcess . stdout ) ;
109+ }
110+
111+ try {
112+ await childProcess ;
113+ return cdkOutput ;
114+ } catch ( error ) {
115+ // swallow execa error which is most of the time noise (basically child exited with exit code...)
116+ // bubbling this up to customers add confusion (Customers don't need to know we are running IPC calls
117+ // and their exit codes printed while sandbox continue to run). Hence we explicitly don't pass error in the cause
118+ // rather throw the entire stderr for clients to figure out what to do with it.
119+ throw new Error ( aggregatedStderr ) ;
120+ }
121+ } ;
122+
70123 private invokeTsc = async ( deployProps ?: DeployProps ) => {
71124 if ( ! deployProps ?. validateAppSources ) {
72125 return ;
@@ -172,59 +225,6 @@ export class CDKDeployer implements BackendDeployer {
172225 return await this . executeChildProcess ( 'npx' , cdkCommandArgs ) ;
173226 } ;
174227
175- /**
176- * Wrapper for the child process executor. Helps in unit testing as node:test framework
177- * doesn't have capabilities to mock exported functions like `execa` as of right now.
178- */
179- executeChildProcess = async (
180- command : string ,
181- commandArgs : string [ ] ,
182- options : { printStdout : boolean } = { printStdout : true }
183- ) => {
184- // We let the stdout and stdin inherit and streamed to parent process but pipe
185- // the stderr and use it to throw on failure. This is to prevent actual
186- // actionable errors being hidden among the stdout. Moreover execa errors are
187- // useless when calling CLIs unless you made execa calling error.
188- let aggregatedStderr = '' ;
189- const aggregatorStderrStream = new stream . Writable ( ) ;
190- aggregatorStderrStream . _write = function ( chunk , encoding , done ) {
191- aggregatedStderr += chunk ;
192- done ( ) ;
193- } ;
194-
195- const childProcess = execa ( command , commandArgs , {
196- stdin : 'inherit' ,
197- stdout : 'pipe' ,
198- stderr : 'pipe' ,
199-
200- // Piping the output by default strips off the color. This is a workaround to
201- // preserve the color being piped to parent process.
202- extendEnv : true ,
203- env : { FORCE_COLOR : '1' } ,
204- } ) ;
205- childProcess . stderr ?. pipe ( aggregatorStderrStream ) ;
206-
207- if ( options ?. printStdout ) {
208- childProcess . stdout ?. pipe ( process . stdout ) ;
209- }
210-
211- const cdkOutput = { deploymentTimes : { } } ;
212- if ( childProcess . stdout ) {
213- await this . populateCDKOutputFromStdout ( cdkOutput , childProcess . stdout ) ;
214- }
215-
216- try {
217- await childProcess ;
218- return cdkOutput ;
219- } catch ( error ) {
220- // swallow execa error which is most of the time noise (basically child exited with exit code...)
221- // bubbling this up to customers add confusion (Customers don't need to know we are running IPC calls
222- // and their exit codes printed while sandbox continue to run). Hence we explicitly don't pass error in the cause
223- // rather throw the entire stderr for clients to figure out what to do with it.
224- throw new Error ( aggregatedStderr ) ;
225- }
226- } ;
227-
228228 private populateCDKOutputFromStdout = async (
229229 output : DeployResult | DestroyResult ,
230230 stdout : stream . Readable
0 commit comments