A Docker-like CLI and HTTP API for managing headless VMs

Refactor code for improved readability and consistency by adjusting formatting and removing unnecessary line breaks in run.ts, utils.ts, and volumes.ts.

+49 -57
+22 -25
src/subcommands/run.ts
··· 7 7 import { type Options, runQemu, validateImage } from "../utils.ts"; 8 8 import { createVolume, getVolume } from "../volumes.ts"; 9 9 10 - const pullImageOnMissing = ( 11 - name: string, 12 - ): Effect.Effect<Image, Error, never> => 10 + const pullImageOnMissing = (name: string): Effect.Effect<Image, Error, never> => 13 11 pipe( 14 12 getImage(name), 15 13 Effect.flatMap((img) => { ··· 21 19 pullImage(name), 22 20 Effect.flatMap(() => getImage(name)), 23 21 Effect.flatMap((pulledImg) => 24 - pulledImg ? Effect.succeed(pulledImg) : Effect.fail( 25 - new PullImageError({ cause: "Failed to pull image" }), 26 - ) 27 - ), 22 + pulledImg 23 + ? Effect.succeed(pulledImg) 24 + : Effect.fail(new PullImageError({ cause: "Failed to pull image" })) 25 + ) 28 26 ); 29 - }), 27 + }) 30 28 ); 31 29 32 30 const createVolumeIfNeeded = ( 33 - image: Image, 31 + image: Image 34 32 ): Effect.Effect<[Image, Volume?], Error, never> => 35 33 parseFlags(Deno.args).flags.volume 36 34 ? Effect.gen(function* () { 37 - const volumeName = parseFlags(Deno.args).flags.volume as string; 38 - const volume = yield* getVolume(volumeName); 39 - if (volume) { 40 - return [image, volume]; 41 - } 42 - const newVolume = yield* createVolume(volumeName, image); 43 - return [image, newVolume]; 44 - }) 35 + const volumeName = parseFlags(Deno.args).flags.volume as string; 36 + const volume = yield* getVolume(volumeName); 37 + if (volume) { 38 + return [image, volume]; 39 + } 40 + const newVolume = yield* createVolume(volumeName, image); 41 + return [image, newVolume]; 42 + }) 45 43 : Effect.succeed([image]); 46 44 47 45 const runImage = ([image, volume]: [Image, Volume?]) => ··· 56 54 options.image = volume.path; 57 55 options.install = true; 58 56 options.diskFormat = "qcow2"; 57 + options.volume = undefined; 59 58 } 60 59 61 60 yield* runQemu(null, options); 62 61 }); 63 62 64 - export default async function ( 65 - image: string, 66 - ): Promise<void> { 63 + export default async function (image: string): Promise<void> { 67 64 await Effect.runPromise( 68 65 pipe( 69 66 Effect.promise(() => setupOrasBinary()), ··· 76 73 console.error(`Failed to run image: ${error.cause} ${image}`); 77 74 Deno.exit(1); 78 75 }) 79 - ), 80 - ), 76 + ) 77 + ) 81 78 ); 82 79 } 83 80 84 81 function mergeFlags(image: Image): Options { 85 82 const { flags } = parseFlags(Deno.args); 86 83 return { 87 - cpu: (flags.cpu || flags.c) ? (flags.cpu || flags.c) : "host", 88 - cpus: (flags.cpus || flags.C) ? (flags.cpus || flags.C) : 2, 89 - memory: (flags.memory || flags.m) ? (flags.memory || flags.m) : "2G", 84 + cpu: flags.cpu || flags.c ? flags.cpu || flags.c : "host", 85 + cpus: flags.cpus || flags.C ? flags.cpus || flags.C : 2, 86 + memory: flags.memory || flags.m ? flags.memory || flags.m : "2G", 90 87 image: image.path, 91 88 bridge: flags.bridge || flags.b, 92 89 portForward: flags.portForward || flags.p,
+5 -3
src/utils.ts
··· 333 333 : "qemu-system-x86_64"; 334 334 335 335 const firmwareFiles = yield* setupFirmwareFilesIfNeeded(); 336 - const coreosArgs: string[] = yield* setupCoreOSArgs( 337 - isoPath || options.image 338 - ); 336 + let coreosArgs: string[] = yield* setupCoreOSArgs(isoPath || options.image); 337 + 338 + if (coreosArgs.length > 0) { 339 + coreosArgs = coreosArgs.slice(2); 340 + } 339 341 340 342 const qemuArgs = [ 341 343 ..._.compact([options.bridge && qemu]),
+22 -29
src/volumes.ts
··· 19 19 }); 20 20 21 21 export const getVolume = ( 22 - id: string, 22 + id: string 23 23 ): Effect.Effect<Volume | undefined, VolumeError, never> => 24 24 Effect.tryPromise({ 25 25 try: () => ··· 27 27 .selectFrom("volumes") 28 28 .selectAll() 29 29 .where((eb) => 30 - eb.or([ 31 - eb("name", "=", id), 32 - eb("id", "=", id), 33 - eb("path", "=", id), 34 - ]) 30 + eb.or([eb("name", "=", id), eb("id", "=", id), eb("path", "=", id)]) 35 31 ) 36 32 .executeTakeFirst(), 37 33 catch: (error) => ··· 41 37 }); 42 38 43 39 export const saveVolume = ( 44 - volume: Volume, 40 + volume: Volume 45 41 ): Effect.Effect<InsertResult[], VolumeError, never> => 46 42 Effect.tryPromise({ 47 - try: () => 48 - ctx.db.insertInto("volumes") 49 - .values(volume) 50 - .execute(), 43 + try: () => ctx.db.insertInto("volumes").values(volume).execute(), 51 44 catch: (error) => 52 45 new VolumeError({ 53 46 message: error instanceof Error ? error.message : String(error), ··· 55 48 }); 56 49 57 50 export const deleteVolume = ( 58 - id: string, 51 + id: string 59 52 ): Effect.Effect<DeleteResult[], VolumeError, never> => 60 53 Effect.tryPromise({ 61 54 try: () => 62 - ctx.db.deleteFrom("volumes").where((eb) => 63 - eb.or([ 64 - eb("name", "=", id), 65 - eb("id", "=", id), 66 - ]) 67 - ).execute(), 55 + ctx.db 56 + .deleteFrom("volumes") 57 + .where((eb) => eb.or([eb("name", "=", id), eb("id", "=", id)])) 58 + .execute(), 68 59 catch: (error) => 69 60 new VolumeError({ 70 61 message: error instanceof Error ? error.message : String(error), ··· 74 65 export const createVolume = ( 75 66 name: string, 76 67 baseImage: Image, 77 - size?: string, 68 + size?: string 78 69 ): Effect.Effect<Volume, VolumeError, never> => 79 70 Effect.tryPromise({ 80 71 try: async () => { ··· 86 77 args: [ 87 78 "create", 88 79 "-F", 89 - "raw", 80 + baseImage.path.endsWith(".qcow2") ? "qcow2" : "raw", 90 81 "-f", 91 82 "qcow2", 92 83 "-b", ··· 96 87 ], 97 88 stdout: "inherit", 98 89 stderr: "inherit", 99 - }) 100 - .spawn(); 90 + }).spawn(); 101 91 const status = await qemu.status; 102 92 if (!status.success) { 103 93 throw new Error( 104 - `Failed to create volume: qemu-img exited with code ${status.code}`, 94 + `Failed to create volume: qemu-img exited with code ${status.code}` 105 95 ); 106 96 } 107 97 } 108 98 109 - ctx.db.insertInto("volumes").values({ 110 - id: createId(), 111 - name, 112 - path, 113 - baseImageId: baseImage.id, 114 - }).execute(); 99 + ctx.db 100 + .insertInto("volumes") 101 + .values({ 102 + id: createId(), 103 + name, 104 + path, 105 + baseImageId: baseImage.id, 106 + }) 107 + .execute(); 115 108 const volume = await ctx.db 116 109 .selectFrom("volumes") 117 110 .selectAll()