feat(core): Initial project scaffold and implementation: Deno CLI, ISO tooling, cloud-init generation, packaging and installer scripts
This commit is contained in:
123
ts/classes/iso-packer.ts
Normal file
123
ts/classes/iso-packer.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* ISO Packer
|
||||
* Repacks a directory into a bootable ISO using xorriso
|
||||
*/
|
||||
|
||||
import { log } from '../logging.ts';
|
||||
|
||||
export class IsoPacker {
|
||||
/**
|
||||
* Pack a directory into an ISO
|
||||
*/
|
||||
async pack(sourceDir: string, outputIso: string, volumeLabel?: string): Promise<void> {
|
||||
log.info(`Creating ISO from ${sourceDir}...`);
|
||||
|
||||
const label = volumeLabel || 'UBUNTU_CUSTOM';
|
||||
|
||||
// Use xorriso to create a bootable ISO
|
||||
const command = new Deno.Command('xorriso', {
|
||||
args: [
|
||||
'-as',
|
||||
'mkisofs',
|
||||
'-r',
|
||||
'-V',
|
||||
label,
|
||||
'-o',
|
||||
outputIso,
|
||||
'-J',
|
||||
'-joliet-long',
|
||||
'-cache-inodes',
|
||||
'-isohybrid-mbr',
|
||||
'/usr/lib/ISOLINUX/isohdpfx.bin',
|
||||
'-b',
|
||||
'isolinux/isolinux.bin',
|
||||
'-c',
|
||||
'isolinux/boot.cat',
|
||||
'-boot-load-size',
|
||||
'4',
|
||||
'-boot-info-table',
|
||||
'-no-emul-boot',
|
||||
'-eltorito-alt-boot',
|
||||
'-e',
|
||||
'boot/grub/efi.img',
|
||||
'-no-emul-boot',
|
||||
'-isohybrid-gpt-basdat',
|
||||
sourceDir,
|
||||
],
|
||||
stdout: 'piped',
|
||||
stderr: 'piped',
|
||||
});
|
||||
|
||||
const process = command.spawn();
|
||||
const { code, stderr } = await process.output();
|
||||
|
||||
if (code !== 0) {
|
||||
const errorText = new TextDecoder().decode(stderr);
|
||||
throw new Error(`Failed to create ISO: ${errorText}`);
|
||||
}
|
||||
|
||||
log.success(`ISO created successfully at ${outputIso}`);
|
||||
|
||||
// Make it hybrid bootable (USB compatible)
|
||||
await this.makeIsohybrid(outputIso);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the ISO hybrid bootable (USB-compatible)
|
||||
*/
|
||||
private async makeIsohybrid(isoPath: string): Promise<void> {
|
||||
try {
|
||||
log.info('Making ISO hybrid bootable...');
|
||||
|
||||
const command = new Deno.Command('isohybrid', {
|
||||
args: ['--uefi', isoPath],
|
||||
stdout: 'piped',
|
||||
stderr: 'piped',
|
||||
});
|
||||
|
||||
const { code } = await command.output();
|
||||
|
||||
if (code === 0) {
|
||||
log.success('ISO is now USB-bootable');
|
||||
} else {
|
||||
log.warn('isohybrid failed, ISO may not be USB-bootable');
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||
log.warn(`isohybrid not available: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if required tools are installed
|
||||
*/
|
||||
async checkDependencies(): Promise<boolean> {
|
||||
try {
|
||||
const xorrisoCmd = new Deno.Command('xorriso', {
|
||||
args: ['--version'],
|
||||
stdout: 'null',
|
||||
stderr: 'null',
|
||||
});
|
||||
|
||||
const { code } = await xorrisoCmd.output();
|
||||
return code === 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure dependencies are installed
|
||||
*/
|
||||
async ensureDependencies(): Promise<void> {
|
||||
const hasXorriso = await this.checkDependencies();
|
||||
|
||||
if (!hasXorriso) {
|
||||
log.error('xorriso is not installed!');
|
||||
log.info('Install xorriso:');
|
||||
log.info(' Ubuntu/Debian: sudo apt install xorriso syslinux-utils');
|
||||
log.info(' macOS: brew install xorriso syslinux');
|
||||
throw new Error('Missing dependency: xorriso');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user