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

Merge pull request #4 from tsirysndr/feat/gentoo

feat: add support for gentoo linux distribution

authored by tsiry-sandratraina.com and committed by

GitHub bdade28e f4a4d633

Waiting for spindle ...
+120 -2
+20
main.ts
··· 32 import * as volumes from "./src/subcommands/volume.ts"; 33 import { 34 constructFedoraImageURL, 35 constructNixOSImageURL, 36 createDriveImageIfNeeded, 37 downloadIso, ··· 225 isoPath = yield* downloadIso(fedoraImageURL, options); 226 } else { 227 isoPath = basename(fedoraImageURL); 228 } 229 } 230 }
··· 32 import * as volumes from "./src/subcommands/volume.ts"; 33 import { 34 constructFedoraImageURL, 35 + constructGentooImageURL, 36 constructNixOSImageURL, 37 createDriveImageIfNeeded, 38 downloadIso, ··· 226 isoPath = yield* downloadIso(fedoraImageURL, options); 227 } else { 228 isoPath = basename(fedoraImageURL); 229 + } 230 + } 231 + 232 + const gentooImageURL = yield* pipe( 233 + constructGentooImageURL(input), 234 + Effect.catchAll(() => Effect.succeed(null)), 235 + ); 236 + 237 + if (gentooImageURL) { 238 + const cached = yield* pipe( 239 + basename(gentooImageURL), 240 + fileExists, 241 + Effect.flatMap(() => Effect.succeed(true)), 242 + Effect.catchAll(() => Effect.succeed(false)), 243 + ); 244 + if (!cached) { 245 + isoPath = yield* downloadIso(gentooImageURL, options); 246 + } else { 247 + isoPath = basename(gentooImageURL); 248 } 249 } 250 }
+4
src/constants.ts
··· 20 21 export const FEDORA_IMG_URL: string = 22 `https://download.fedoraproject.org/pub/fedora/linux/releases/43/Server/${Deno.build.arch}/images/Fedora-Server-Guest-Generic-43-1.6.${Deno.build.arch}.qcow2`;
··· 20 21 export const FEDORA_IMG_URL: string = 22 `https://download.fedoraproject.org/pub/fedora/linux/releases/43/Server/${Deno.build.arch}/images/Fedora-Server-Guest-Generic-43-1.6.${Deno.build.arch}.qcow2`; 23 + 24 + export const GENTOO_IMG_URL: string = Deno.build.arch === "aarch64" 25 + ? "https://distfiles.gentoo.org/releases/arm64/autobuilds/20251116T233105Z/di-arm64-console-20251116T233105Z.qcow2" 26 + : "https://distfiles.gentoo.org/releases/amd64/autobuilds/20251116T161545Z/di-amd64-console-20251116T161545Z.qcow2";
+49
src/utils.ts
··· 9 FEDORA_COREOS_DEFAULT_VERSION, 10 FEDORA_COREOS_IMG_URL, 11 FEDORA_IMG_URL, 12 LOGS_DIR, 13 NIXOS_DEFAULT_VERSION, 14 NIXOS_ISO_URL, ··· 339 return []; 340 }); 341 342 export const runQemu = (isoPath: string | null, options: Options) => 343 Effect.gen(function* () { 344 const macAddress = yield* generateRandomMacAddress(); ··· 350 const firmwareFiles = yield* setupFirmwareFilesIfNeeded(); 351 let coreosArgs: string[] = yield* setupCoreOSArgs(isoPath || options.image); 352 let fedoraArgs: string[] = yield* setupFedoraArgs(isoPath || options.image); 353 354 if (coreosArgs.length > 0 && !isoPath) { 355 coreosArgs = coreosArgs.slice(2); ··· 357 358 if (fedoraArgs.length > 0 && !isoPath) { 359 fedoraArgs = []; 360 } 361 362 const qemuArgs = [ ··· 387 ...firmwareFiles, 388 ...coreosArgs, 389 ...fedoraArgs, 390 ..._.compact( 391 options.image && [ 392 "-drive", ··· 704 }), 705 ); 706 };
··· 9 FEDORA_COREOS_DEFAULT_VERSION, 10 FEDORA_COREOS_IMG_URL, 11 FEDORA_IMG_URL, 12 + GENTOO_IMG_URL, 13 LOGS_DIR, 14 NIXOS_DEFAULT_VERSION, 15 NIXOS_ISO_URL, ··· 340 return []; 341 }); 342 343 + export const setupGentooArgs = (imagePath?: string | null) => 344 + Effect.sync(() => { 345 + if ( 346 + imagePath && 347 + imagePath.endsWith(".qcow2") && 348 + imagePath.startsWith( 349 + `di-${Deno.build.arch === "aarch64" ? "arm64" : "amd64"}-console-`, 350 + ) 351 + ) { 352 + return ["-drive", `file=${imagePath},format=qcow2,if=virtio`]; 353 + } 354 + 355 + return []; 356 + }); 357 + 358 export const runQemu = (isoPath: string | null, options: Options) => 359 Effect.gen(function* () { 360 const macAddress = yield* generateRandomMacAddress(); ··· 366 const firmwareFiles = yield* setupFirmwareFilesIfNeeded(); 367 let coreosArgs: string[] = yield* setupCoreOSArgs(isoPath || options.image); 368 let fedoraArgs: string[] = yield* setupFedoraArgs(isoPath || options.image); 369 + let gentooArgs: string[] = yield* setupGentooArgs(isoPath || options.image); 370 371 if (coreosArgs.length > 0 && !isoPath) { 372 coreosArgs = coreosArgs.slice(2); ··· 374 375 if (fedoraArgs.length > 0 && !isoPath) { 376 fedoraArgs = []; 377 + } 378 + 379 + if (gentooArgs.length > 0 && !isoPath) { 380 + gentooArgs = []; 381 } 382 383 const qemuArgs = [ ··· 408 ...firmwareFiles, 409 ...coreosArgs, 410 ...fedoraArgs, 411 + ...gentooArgs, 412 ..._.compact( 413 options.image && [ 414 "-drive", ··· 726 }), 727 ); 728 }; 729 + 730 + export const constructGentooImageURL = ( 731 + image: string, 732 + ): Effect.Effect<string, InvalidImageNameError, never> => { 733 + // detect with regex if image matches genroo pattern: gentoo-20251116T161545Z or gentoo 734 + const gentooRegex = /^(gentoo)(-(\d{8}T\d{6}Z))?$/; 735 + const match = image.match(gentooRegex); 736 + if (match?.[3]) { 737 + return Effect.succeed( 738 + GENTOO_IMG_URL.replaceAll("20251116T161545Z", match[3]).replaceAll( 739 + "20251116T233105Z", 740 + match[3], 741 + ), 742 + ); 743 + } 744 + 745 + if (match) { 746 + return Effect.succeed(GENTOO_IMG_URL); 747 + } 748 + 749 + return Effect.fail( 750 + new InvalidImageNameError({ 751 + image, 752 + cause: "Image name does not match Gentoo naming conventions.", 753 + }), 754 + ); 755 + };
+47 -2
src/utils_test.ts
··· 1 import { assertEquals } from "@std/assert"; 2 import { Effect, pipe } from "effect"; 3 - import { FEDORA_COREOS_IMG_URL, NIXOS_ISO_URL } from "./constants.ts"; 4 - import { constructCoreOSImageURL, constructNixOSImageURL } from "./utils.ts"; 5 6 Deno.test("Test Default Fedora CoreOS Image URL", () => { 7 const url = Effect.runSync( ··· 86 87 assertEquals(url, null); 88 });
··· 1 import { assertEquals } from "@std/assert"; 2 import { Effect, pipe } from "effect"; 3 + import { 4 + FEDORA_COREOS_IMG_URL, 5 + GENTOO_IMG_URL, 6 + NIXOS_ISO_URL, 7 + } from "./constants.ts"; 8 + import { 9 + constructCoreOSImageURL, 10 + constructGentooImageURL, 11 + constructNixOSImageURL, 12 + } from "./utils.ts"; 13 14 Deno.test("Test Default Fedora CoreOS Image URL", () => { 15 const url = Effect.runSync( ··· 94 95 assertEquals(url, null); 96 }); 97 + 98 + Deno.test("Test valid Gentoo Image Name", () => { 99 + const url = Effect.runSync( 100 + pipe( 101 + constructGentooImageURL("gentoo-20251116T161545Z"), 102 + Effect.catchAll((_error) => Effect.succeed(null as string | null)), 103 + ), 104 + ); 105 + 106 + const arch = Deno.build.arch === "aarch64" ? "arm64" : "amd64"; 107 + assertEquals( 108 + url, 109 + `https://distfiles.gentoo.org/releases/${arch}/autobuilds/20251116T161545Z/di-${arch}-console-20251116T161545Z.qcow2`, 110 + ); 111 + }); 112 + 113 + Deno.test("Test valid Gentoo Image Name", () => { 114 + const url = Effect.runSync( 115 + pipe( 116 + constructGentooImageURL("gentoo"), 117 + Effect.catchAll((_error) => Effect.succeed(null as string | null)), 118 + ), 119 + ); 120 + 121 + assertEquals(url, GENTOO_IMG_URL); 122 + }); 123 + 124 + Deno.test("Test invalid Gentoo Image Name", () => { 125 + const url = Effect.runSync( 126 + pipe( 127 + constructGentooImageURL("gentoo-latest"), 128 + Effect.catchAll((_error) => Effect.succeed(null as string | null)), 129 + ), 130 + ); 131 + 132 + assertEquals(url, null); 133 + });