refactor: extract shared runCommand utility and cleanup codebase
- Create shared command utility in ts/utils/command.ts - Update index.ts, process-manager.ts, system-info.ts to use shared utility - Remove duplicate runCommand implementations from all files - Remove legacy Chrome wrapper methods (startChrome, stopChrome, isChromeRunning) - Consolidate Sway window selectors from 5 to 2 - Remove unused isobuild TypeScript files (mod.ts, deno.json, ts/index.ts) - Make getStatus() async to properly await system info - Add disk and system info sections to UI
This commit is contained in:
Binary file not shown.
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"name": "@ecobridge/eco-os-isobuild",
|
||||
"version": "0.0.1",
|
||||
"exports": "./mod.ts",
|
||||
"tasks": {
|
||||
"build": "deno run --allow-all mod.ts build",
|
||||
"clean": "deno run --allow-all mod.ts clean",
|
||||
"test-qemu": "deno run --allow-all mod.ts test-qemu"
|
||||
},
|
||||
"imports": {
|
||||
"@std/fs": "jsr:@std/fs@^1.0.0",
|
||||
"@std/path": "jsr:@std/path@^1.0.0",
|
||||
"@std/async": "jsr:@std/async@^1.0.0"
|
||||
},
|
||||
"compilerOptions": {
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/**
|
||||
* EcoOS ISO Builder
|
||||
*
|
||||
* CLI for building custom Ubuntu ISO with EcoOS daemon
|
||||
*/
|
||||
|
||||
import { build } from './ts/index.ts';
|
||||
|
||||
const command = Deno.args[0];
|
||||
|
||||
switch (command) {
|
||||
case 'build':
|
||||
await build();
|
||||
break;
|
||||
|
||||
case 'clean':
|
||||
console.log('Cleaning build artifacts...');
|
||||
try {
|
||||
await Deno.remove('./output', { recursive: true });
|
||||
await Deno.remove('./build', { recursive: true });
|
||||
console.log('Clean complete.');
|
||||
} catch {
|
||||
console.log('Nothing to clean.');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'test-qemu':
|
||||
console.log('Testing ISO in QEMU...');
|
||||
const isoPath = './output/ecoos.iso';
|
||||
try {
|
||||
await Deno.stat(isoPath);
|
||||
} catch {
|
||||
console.error('ISO not found. Run "deno task build" first.');
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
const qemu = new Deno.Command('qemu-system-x86_64', {
|
||||
args: [
|
||||
'-enable-kvm',
|
||||
'-m', '4G',
|
||||
'-cpu', 'host',
|
||||
'-smp', '2',
|
||||
'-cdrom', isoPath,
|
||||
'-boot', 'd',
|
||||
'-vga', 'virtio',
|
||||
'-display', 'gtk',
|
||||
'-device', 'usb-tablet',
|
||||
'-nic', 'user,hostfwd=tcp::3006-:3006',
|
||||
],
|
||||
stdout: 'inherit',
|
||||
stderr: 'inherit',
|
||||
});
|
||||
await qemu.spawn().status;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`
|
||||
EcoOS ISO Builder
|
||||
|
||||
Usage:
|
||||
deno task build Build the ISO
|
||||
deno task clean Clean build artifacts
|
||||
deno task test-qemu Test ISO in QEMU
|
||||
|
||||
Requirements:
|
||||
- Ubuntu 24.04+ host
|
||||
- live-build, debootstrap, xorriso installed
|
||||
- sudo access for live-build
|
||||
`);
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
/**
|
||||
* ISO Build Orchestration
|
||||
*
|
||||
* Builds the EcoOS custom Ubuntu ISO
|
||||
*/
|
||||
|
||||
import * as path from '@std/path';
|
||||
|
||||
const ROOT = path.dirname(path.dirname(path.fromFileUrl(import.meta.url)));
|
||||
const DAEMON_DIR = path.join(path.dirname(ROOT), 'ecoos_daemon');
|
||||
const BUILD_DIR = path.join(ROOT, 'build');
|
||||
const OUTPUT_DIR = path.join(ROOT, 'output');
|
||||
const CONFIG_DIR = path.join(ROOT, 'config');
|
||||
|
||||
export async function build(): Promise<void> {
|
||||
console.log('=== EcoOS ISO Builder ===\n');
|
||||
|
||||
// Step 1: Check prerequisites
|
||||
console.log('[1/7] Checking prerequisites...');
|
||||
await checkPrerequisites();
|
||||
|
||||
// Step 2: Bundle the daemon
|
||||
console.log('[2/7] Bundling ecoos_daemon...');
|
||||
await bundleDaemon();
|
||||
|
||||
// Step 3: Prepare build directory
|
||||
console.log('[3/7] Preparing build directory...');
|
||||
await prepareBuildDir();
|
||||
|
||||
// Step 4: Configure live-build
|
||||
console.log('[4/7] Configuring live-build...');
|
||||
await configureLiveBuild();
|
||||
|
||||
// Step 5: Copy package lists
|
||||
console.log('[5/7] Copying package lists...');
|
||||
await copyPackageLists();
|
||||
|
||||
// Step 6: Copy daemon and configs to chroot includes
|
||||
console.log('[6/7] Preparing chroot includes...');
|
||||
await prepareChrootIncludes();
|
||||
|
||||
// Step 7: Build ISO
|
||||
console.log('[7/7] Building ISO (this may take a while)...');
|
||||
await buildIso();
|
||||
|
||||
console.log('\n=== Build Complete ===');
|
||||
console.log(`ISO: ${OUTPUT_DIR}/ecoos.iso`);
|
||||
}
|
||||
|
||||
async function checkPrerequisites(): Promise<void> {
|
||||
const commands = ['lb', 'debootstrap', 'xorriso'];
|
||||
|
||||
for (const cmd of commands) {
|
||||
try {
|
||||
const result = await run('which', [cmd]);
|
||||
if (!result.success) {
|
||||
throw new Error(`${cmd} not found`);
|
||||
}
|
||||
} catch {
|
||||
console.error(`Missing prerequisite: ${cmd}`);
|
||||
console.error('Install with: sudo apt install live-build debootstrap xorriso');
|
||||
Deno.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' All prerequisites found.');
|
||||
}
|
||||
|
||||
async function bundleDaemon(): Promise<void> {
|
||||
// Compile the daemon to a single executable
|
||||
const bundleDir = path.join(DAEMON_DIR, 'bundle');
|
||||
await Deno.mkdir(bundleDir, { recursive: true });
|
||||
|
||||
const result = await run('deno', [
|
||||
'compile',
|
||||
'--allow-all',
|
||||
'--output', path.join(bundleDir, 'eco-daemon'),
|
||||
path.join(DAEMON_DIR, 'mod.ts'),
|
||||
], { cwd: DAEMON_DIR });
|
||||
|
||||
if (!result.success) {
|
||||
console.error('Failed to bundle daemon');
|
||||
console.error(result.stderr);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
console.log(' Daemon bundled successfully.');
|
||||
}
|
||||
|
||||
async function prepareBuildDir(): Promise<void> {
|
||||
// Clean and create build directory
|
||||
try {
|
||||
await Deno.remove(BUILD_DIR, { recursive: true });
|
||||
} catch {
|
||||
// Directory may not exist
|
||||
}
|
||||
|
||||
await Deno.mkdir(BUILD_DIR, { recursive: true });
|
||||
console.log(' Build directory prepared.');
|
||||
}
|
||||
|
||||
async function configureLiveBuild(): Promise<void> {
|
||||
// Initialize live-build config
|
||||
const result = await run('lb', ['config'], { cwd: BUILD_DIR });
|
||||
|
||||
if (!result.success) {
|
||||
console.error('Failed to initialize live-build');
|
||||
console.error(result.stderr);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
// Copy our auto/config
|
||||
const autoDir = path.join(BUILD_DIR, 'auto');
|
||||
await Deno.mkdir(autoDir, { recursive: true });
|
||||
|
||||
const configSrc = path.join(CONFIG_DIR, 'live-build', 'auto', 'config');
|
||||
const configDst = path.join(autoDir, 'config');
|
||||
|
||||
await Deno.copyFile(configSrc, configDst);
|
||||
await Deno.chmod(configDst, 0o755);
|
||||
|
||||
// Re-run lb config with our settings
|
||||
const result2 = await run('lb', ['config'], { cwd: BUILD_DIR });
|
||||
if (!result2.success) {
|
||||
console.error('Failed to configure live-build');
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
console.log(' live-build configured.');
|
||||
}
|
||||
|
||||
async function copyPackageLists(): Promise<void> {
|
||||
const srcDir = path.join(CONFIG_DIR, 'live-build', 'package-lists');
|
||||
const dstDir = path.join(BUILD_DIR, 'config', 'package-lists');
|
||||
|
||||
await Deno.mkdir(dstDir, { recursive: true });
|
||||
|
||||
for await (const entry of Deno.readDir(srcDir)) {
|
||||
if (entry.isFile && entry.name.endsWith('.list.chroot')) {
|
||||
const src = path.join(srcDir, entry.name);
|
||||
const dst = path.join(dstDir, entry.name);
|
||||
await Deno.copyFile(src, dst);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' Package lists copied.');
|
||||
}
|
||||
|
||||
async function prepareChrootIncludes(): Promise<void> {
|
||||
const includesDir = path.join(BUILD_DIR, 'config', 'includes.chroot');
|
||||
|
||||
// Create directory structure
|
||||
await Deno.mkdir(path.join(includesDir, 'opt', 'eco', 'bin'), { recursive: true });
|
||||
await Deno.mkdir(path.join(includesDir, 'opt', 'eco', 'daemon'), { recursive: true });
|
||||
await Deno.mkdir(path.join(includesDir, 'etc', 'systemd', 'system'), { recursive: true });
|
||||
|
||||
// Copy bundled daemon
|
||||
const daemonSrc = path.join(DAEMON_DIR, 'bundle', 'eco-daemon');
|
||||
const daemonDst = path.join(includesDir, 'opt', 'eco', 'bin', 'eco-daemon');
|
||||
|
||||
try {
|
||||
await Deno.copyFile(daemonSrc, daemonDst);
|
||||
await Deno.chmod(daemonDst, 0o755);
|
||||
} catch (e) {
|
||||
console.error('Failed to copy daemon bundle:', e);
|
||||
// Fall back to copying source files
|
||||
console.log(' Copying daemon source files instead...');
|
||||
await copyDir(path.join(DAEMON_DIR, 'ts'), path.join(includesDir, 'opt', 'eco', 'daemon'));
|
||||
await Deno.copyFile(
|
||||
path.join(DAEMON_DIR, 'mod.ts'),
|
||||
path.join(includesDir, 'opt', 'eco', 'daemon', 'mod.ts')
|
||||
);
|
||||
}
|
||||
|
||||
// Copy systemd service
|
||||
const serviceSrc = path.join(CONFIG_DIR, 'systemd', 'eco-daemon.service');
|
||||
const serviceDst = path.join(includesDir, 'etc', 'systemd', 'system', 'eco-daemon.service');
|
||||
await Deno.copyFile(serviceSrc, serviceDst);
|
||||
|
||||
// Copy autoinstall config
|
||||
const autoinstallDir = path.join(BUILD_DIR, 'config', 'includes.binary');
|
||||
await Deno.mkdir(autoinstallDir, { recursive: true });
|
||||
|
||||
const userDataSrc = path.join(CONFIG_DIR, 'autoinstall', 'user-data');
|
||||
const userDataDst = path.join(autoinstallDir, 'autoinstall', 'user-data');
|
||||
await Deno.mkdir(path.dirname(userDataDst), { recursive: true });
|
||||
await Deno.copyFile(userDataSrc, userDataDst);
|
||||
|
||||
// Create empty meta-data file (required for cloud-init)
|
||||
await Deno.writeTextFile(path.join(path.dirname(userDataDst), 'meta-data'), '');
|
||||
|
||||
console.log(' Chroot includes prepared.');
|
||||
}
|
||||
|
||||
async function buildIso(): Promise<void> {
|
||||
const result = await run('sudo', ['lb', 'build'], {
|
||||
cwd: BUILD_DIR,
|
||||
stdout: 'inherit',
|
||||
stderr: 'inherit',
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
console.error('ISO build failed');
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
// Move ISO to output directory
|
||||
await Deno.mkdir(OUTPUT_DIR, { recursive: true });
|
||||
|
||||
for await (const entry of Deno.readDir(BUILD_DIR)) {
|
||||
if (entry.isFile && entry.name.endsWith('.iso')) {
|
||||
const src = path.join(BUILD_DIR, entry.name);
|
||||
const dst = path.join(OUTPUT_DIR, 'ecoos.iso');
|
||||
await Deno.rename(src, dst);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function run(
|
||||
cmd: string,
|
||||
args: string[],
|
||||
options: { cwd?: string; stdout?: 'piped' | 'inherit'; stderr?: 'piped' | 'inherit' } = {}
|
||||
): Promise<{ success: boolean; stdout: string; stderr: string }> {
|
||||
const command = new Deno.Command(cmd, {
|
||||
args,
|
||||
cwd: options.cwd,
|
||||
stdout: options.stdout ?? 'piped',
|
||||
stderr: options.stderr ?? 'piped',
|
||||
});
|
||||
|
||||
const result = await command.output();
|
||||
|
||||
return {
|
||||
success: result.success,
|
||||
stdout: new TextDecoder().decode(result.stdout),
|
||||
stderr: new TextDecoder().decode(result.stderr),
|
||||
};
|
||||
}
|
||||
|
||||
async function copyDir(src: string, dst: string): Promise<void> {
|
||||
await Deno.mkdir(dst, { recursive: true });
|
||||
|
||||
for await (const entry of Deno.readDir(src)) {
|
||||
const srcPath = path.join(src, entry.name);
|
||||
const dstPath = path.join(dst, entry.name);
|
||||
|
||||
if (entry.isDirectory) {
|
||||
await copyDir(srcPath, dstPath);
|
||||
} else {
|
||||
await Deno.copyFile(srcPath, dstPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user