Files
euri-mklive/euri_mklive/cli.py
T
c-ludenberg 3c5a357cb4 euri-mklive: universal CLI ISO builder
- any distro from any distro (debian, ubuntu, arch, artix, void, alpine, fedora)
- YAML config for distro, arch, init, packages, users, services, bootloader
- each distro backend knows how to bootstrap itself
- squashfs + xorriso for ISO generation
- sexy CLI with banner and colored output
- examples for Artix+Euri and Project Horizon
2026-08-26 19:01:19 +02:00

90 lines
3.0 KiB
Python

"""euri-mklive — Universal CLI ISO builder."""
from __future__ import annotations
import sys
from pathlib import Path
import click
from . import __version__
from .config import load
from .rootfs import build, cleanup
from .iso import generate
from .distros import REGISTRY
BANNER = r"""
\033[1;36m
██╗███╗ ██╗████████╗██╗ ██████╗
██║████╗ ██║╚══██╔══╝██║ ██╔══██╗
██║██╔██╗ ██║ ██║ ██║ ██████╔╝
██║██║╚██╗██║ ██║ ██║ ██╔══██╗
██║██║ ╚████║ ██║ ███████╗██║ ██║
╚═╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
\033[0m
\033[1m universal iso builder\033[0m
\033[90m any distro, from any distro\033[0m
"""
SUPPORTED = ", ".join(sorted(REGISTRY.keys()))
@click.group(invoke_without_command=True)
@click.version_option(__version__, prog_name="euri-mklive")
@click.pass_context
def cli(ctx: click.Context) -> None:
"""\033[1mBuild bootable ISOs for any Linux distribution.\033[0m"""
if ctx.invoked_subcommand is None:
click.echo(BANNER)
click.echo(f" \033[90msupported distros:\033[0m {SUPPORTED}")
click.echo()
click.echo(ctx.get_help())
@cli.command()
@click.argument("config", type=click.Path(exists=True, path_type=Path))
@click.option("-o", "--output", type=click.Path(path_type=Path), default=".", help="Output directory")
@click.option("--keep-rootfs", is_flag=True, help="Don't remove rootfs after ISO generation")
def build_iso(config: Path, output: Path, keep_rootfs: bool) -> None:
"""Build an ISO from a YAML config file."""
click.echo(BANNER)
cfg = load(config)
output.mkdir(parents=True, exist_ok=True)
distro = cfg["distro"]
arch = cfg["arch"]
init = cfg.get("init", "dinit")
pkgs = len(cfg.get("packages", []))
click.echo(f" \033[36mdistro:\033[0m {distro}")
click.echo(f" \033[36march:\033[0m {arch}")
click.echo(f" \033[36minit:\033[0m {init}")
click.echo(f" \033[36mpackages:\033[0m {pkgs}")
click.echo()
try:
rootfs = build(cfg, output)
iso = generate(rootfs, cfg, output)
if not keep_rootfs:
cleanup(rootfs)
click.echo(f"\n\033[90mRootfs cleaned up.\033[0m")
click.echo(f"\n\033[1;32mDone! ISO ready at {iso}\033[0m")
except Exception as e:
click.echo(f"\n\033[1;31mError: {e}\033[0m", err=True)
sys.exit(1)
@cli.command()
def supported() -> None:
"""List all supported distros."""
click.echo(BANNER)
click.echo(" \033[1mSupported distros:\033[0m")
for name, cls in sorted(REGISTRY.items()):
click.echo(f" \033[36m{name:<15}\033[0m {cls.__doc__ or ''}")
click.echo()
click.echo(" \033[90mEach distro can be built FROM any other distro.\033[0m")