@@ -225,8 +225,9 @@ function promptSnapshotName(): string | null {
225225}
226226
227227// --- Build Logic ---
228- // This is the core of the feature. It creates a temporary volume,
229- // boots a sandbox, installs everything, then snapshots the result.
228+ // This is the core of the feature. It creates a volume, boots a
229+ // sandbox, installs everything, then snapshots the result.
230+ // The volume is kept because the snapshot depends on it.
230231
231232async function buildSnapshot (
232233 client : Client ,
@@ -241,7 +242,7 @@ async function buildSnapshot(
241242 verbose : boolean ;
242243 } ,
243244) : Promise < void > {
244- // A unique name for the temporary volume so it doesn't clash with anything
245+ // A unique name for the build volume so it doesn't clash with anything
245246 const volumeSlug = `qs-temp-${ Date . now ( ) } ` ;
246247
247248 // In verbose mode, command output goes straight to the terminal.
@@ -268,29 +269,44 @@ async function buildSnapshot(
268269 return `[${ currentStep } /${ totalSteps } ] ${ label } ` ;
269270 } ;
270271
271- // Step 1: Create a temporary volume based on Debian 13
272- spinner . message = "Creating temporary volume..." ;
272+ spinner . message = "Creating volume..." ;
273273 spinner . start ( ) ;
274- const volume = await client . volumes . create ( {
275- slug : volumeSlug ,
276- capacity : options . capacity ,
277- region : options . region ,
278- from : "builtin:debian-13" ,
279- } ) ;
274+ let volume ;
275+ try {
276+ volume = await client . volumes . create ( {
277+ slug : volumeSlug ,
278+ capacity : options . capacity ,
279+ region : options . region ,
280+ from : "builtin:debian-13" ,
281+ } ) ;
282+ } catch ( e ) {
283+ spinner . stop ( ) ;
284+ throw new Error ( `Failed to create volume: ${ e } ` ) ;
285+ }
280286 spinner . stop ( ) ;
281287 console . log ( `${ green ( "✔" ) } Volume created` ) ;
282288
283289 // Boot a sandbox using this volume as its root filesystem.
284290 // The sandbox is short-lived (10m timeout) — just long enough to install.
285291 spinner . message = "Booting sandbox..." ;
286292 spinner . start ( ) ;
287- const sandbox = await Sandbox . create ( {
288- token : options . token ,
289- org : options . org ,
290- timeout : "10m" ,
291- region : options . region ,
292- root : volume . id ,
293- } ) ;
293+ let sandbox ;
294+ try {
295+ sandbox = await Sandbox . create ( {
296+ token : options . token ,
297+ org : options . org ,
298+ timeout : "10m" ,
299+ region : options . region ,
300+ root : volume . id ,
301+ } ) ;
302+ } catch ( e ) {
303+ spinner . stop ( ) ;
304+ throw new Error (
305+ `Failed to boot sandbox: ${ e } \n` +
306+ ` Volume '${ volumeSlug } ' was created but is now unused.\n` +
307+ ` You can delete it with: deno sandbox volumes delete ${ volumeSlug } ` ,
308+ ) ;
309+ }
294310 spinner . stop ( ) ;
295311 console . log ( `${ green ( "✔" ) } Sandbox booted` ) ;
296312
@@ -330,17 +346,26 @@ async function buildSnapshot(
330346 }
331347
332348 // Setup commands are optional — if one fails we warn but keep going
349+ const failedCommands : string [ ] = [ ] ;
333350 for ( const cmd of options . setupCommands ) {
334351 spinner . message = step ( `Running: ${ cmd } ` ) ;
335352 spinner . start ( ) ;
336353 const setupOk = await runInSandbox ( sandbox , cmd ) ;
337354 spinner . stop ( ) ;
338355 if ( ! setupOk ) {
339356 console . log ( `${ yellow ( "⚠" ) } Setup command failed: ${ cmd } ` ) ;
357+ failedCommands . push ( cmd ) ;
340358 } else {
341359 console . log ( `${ green ( "✔" ) } ${ cmd } ` ) ;
342360 }
343361 }
362+ if ( failedCommands . length > 0 ) {
363+ console . log ( ) ;
364+ console . log (
365+ `${ yellow ( "⚠" ) } ${ failedCommands . length } setup command(s) failed. ` +
366+ "The snapshot will be incomplete." ,
367+ ) ;
368+ }
344369 } finally {
345370 // We use kill() instead of close() because close() only disconnects
346371 // the client while the sandbox continues running server-side with
0 commit comments