11import { promises as fs } from "fs" ;
22import path from "path" ;
33import { isBinaryFile } from "isbinaryfile" ;
4+ import * as readline from "readline" ;
45
56import { createClient , createConfig , type Client } from "@hey-api/client-fetch" ;
67import ora from "ora" ;
@@ -118,7 +119,7 @@ export const buildCommand: yargs.CommandModule<
118119 return `\n${ spinnerMessages . join ( "\n" ) } ` ;
119120 }
120121
121- const sandboxIds = await Promise . all (
122+ const sandboxes = await Promise . all (
122123 clusters . map ( async ( { host : cluster , slug } , index ) => {
123124 const clusterApiClient : Client = createClient (
124125 createConfig ( {
@@ -129,14 +130,6 @@ export const buildCommand: yargs.CommandModule<
129130 } ,
130131 } )
131132 ) ;
132- const sdk = new CodeSandbox ( API_KEY , {
133- baseUrl : BASE_URL ,
134- headers : {
135- "x-pitcher-manager-url" : `https://${ cluster } /api/v1` ,
136- } ,
137- } ) ;
138-
139- let sandboxId : string | undefined ;
140133
141134 try {
142135 const { hash, files : filePaths } = await hashDirectory (
@@ -146,7 +139,8 @@ export const buildCommand: yargs.CommandModule<
146139 const tag = `sha:${ shortHash } -${ slug } ` ;
147140
148141 spinner . start ( updateSpinnerMessage ( index , "Creating sandbox..." ) ) ;
149- sandboxId = await createSandbox ( {
142+
143+ const sandboxId = await createSandbox ( {
150144 apiClient : clusterApiClient ,
151145 shaTag : tag ,
152146 fromSandbox : argv . fromSandbox ,
@@ -157,6 +151,40 @@ export const buildCommand: yargs.CommandModule<
157151 : VMTier . fromName ( "Micro" ) ,
158152 } ) ;
159153
154+ return { sandboxId, filePaths, cluster } ;
155+ } catch ( error ) {
156+ spinner . fail (
157+ updateSpinnerMessage (
158+ index ,
159+ error instanceof Error
160+ ? error . message
161+ : "Unknown error occurred"
162+ )
163+ ) ;
164+ throw error ;
165+ }
166+ } )
167+ ) ;
168+
169+ const results = await Promise . allSettled (
170+ sandboxes . map ( async ( { sandboxId, filePaths, cluster } , index ) => {
171+ const clusterApiClient : Client = createClient (
172+ createConfig ( {
173+ baseUrl : BASE_URL ,
174+ headers : {
175+ Authorization : `Bearer ${ API_KEY } ` ,
176+ "x-pitcher-manager-url" : `https://${ cluster } /api/v1` ,
177+ } ,
178+ } )
179+ ) ;
180+ const sdk = new CodeSandbox ( API_KEY , {
181+ baseUrl : BASE_URL ,
182+ headers : {
183+ "x-pitcher-manager-url" : `https://${ cluster } /api/v1` ,
184+ } ,
185+ } ) ;
186+
187+ try {
160188 spinner . start (
161189 updateSpinnerMessage ( index , "Starting sandbox..." , sandboxId )
162190 ) ;
@@ -209,6 +237,7 @@ export const buildCommand: yargs.CommandModule<
209237 ? VMTier . fromName ( argv . vmTier )
210238 : VMTier . fromName ( "Micro" ) ,
211239 } ) ;
240+
212241 session = await sandbox . connect ( ) ;
213242
214243 const disposableStore = new DisposableStore ( ) ;
@@ -295,11 +324,7 @@ export const buildCommand: yargs.CommandModule<
295324 }
296325
297326 spinner . start (
298- updateSpinnerMessage (
299- index ,
300- "Creating memory snapshot..." ,
301- sandboxId
302- )
327+ updateSpinnerMessage ( index , "Creating snapshot..." , sandboxId )
303328 ) ;
304329 await sdk . sandboxes . hibernate ( sandbox . id ) ;
305330 spinner . start (
@@ -308,27 +333,75 @@ export const buildCommand: yargs.CommandModule<
308333
309334 return sandbox . id ;
310335 } catch ( error ) {
311- spinner . fail (
336+ spinner . start (
312337 updateSpinnerMessage (
313338 index ,
314- error instanceof Error
315- ? error . message
316- : "Unknown error occurred" ,
339+ "Failed, please manually verify at https://codesandbox.io/s/" +
340+ sandboxId ,
317341 sandboxId
318342 )
319343 ) ;
344+
320345 throw error ;
321346 }
322347 } )
323348 ) ;
324349
325- spinner . succeed ( `\n${ spinnerMessages . join ( "\n" ) } ` ) ;
350+ const failedSandboxes = sandboxes . filter (
351+ ( _ , index ) => results [ index ] . status === "rejected"
352+ ) ;
353+
354+ if ( failedSandboxes . length > 0 ) {
355+ spinner . info ( `\n${ spinnerMessages . join ( "\n" ) } ` ) ;
356+
357+ await waitForEnter (
358+ `\nThere was an issue preparing the sandboxes. Verify ${ failedSandboxes
359+ . map ( ( sandbox ) => sandbox . sandboxId )
360+ . join ( ", " ) } and press ENTER to create snapshot...\n`
361+ ) ;
362+
363+ failedSandboxes . forEach ( ( { sandboxId } ) => {
364+ updateSpinnerMessage (
365+ sandboxes . findIndex ( ( sandbox ) => sandbox . sandboxId === sandboxId ) ,
366+ "Creating snapshot..." ,
367+ sandboxId
368+ ) ;
369+ } ) ;
370+
371+ spinner . start ( `\n${ spinnerMessages . join ( "\n" ) } ` ) ;
372+
373+ await Promise . all (
374+ failedSandboxes . map ( async ( { sandboxId, cluster } ) => {
375+ const sdk = new CodeSandbox ( API_KEY , {
376+ baseUrl : BASE_URL ,
377+ headers : {
378+ "x-pitcher-manager-url" : `https://${ cluster } /api/v1` ,
379+ } ,
380+ } ) ;
381+
382+ await sdk . sandboxes . hibernate ( sandboxId ) ;
383+
384+ spinner . start (
385+ updateSpinnerMessage (
386+ sandboxes . findIndex (
387+ ( sandbox ) => sandbox . sandboxId === sandboxId
388+ ) ,
389+ "Snapshot created" ,
390+ sandboxId
391+ )
392+ ) ;
393+ } )
394+ ) ;
395+ spinner . succeed ( `\n${ spinnerMessages . join ( "\n" ) } ` ) ;
396+ } else {
397+ spinner . succeed ( `\n${ spinnerMessages . join ( "\n" ) } ` ) ;
398+ }
326399
327400 const data = handleResponse (
328401 await vmCreateTag ( {
329402 client : apiClient ,
330403 body : {
331- vm_ids : sandboxIds ,
404+ vm_ids : sandboxes . map ( ( sandbox ) => sandbox . sandboxId ) ,
332405 } ,
333406 } ) ,
334407 "Failed to create template"
@@ -371,6 +444,20 @@ type CreateSandboxParams = {
371444 ipcountry ?: string ;
372445} ;
373446
447+ function waitForEnter ( message : string ) {
448+ const rl = readline . createInterface ( {
449+ input : process . stdin ,
450+ output : process . stdout ,
451+ } ) ;
452+
453+ return new Promise < void > ( ( resolve ) => {
454+ rl . question ( message , ( ) => {
455+ rl . close ( ) ;
456+ resolve ( ) ;
457+ } ) ;
458+ } ) ;
459+ }
460+
374461function createAlias ( directory : string , alias : string ) {
375462 const aliasParts = alias . split ( "@" ) ;
376463
0 commit comments