From f5f800c533de7c26b54cb85ae172e62e41541ac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Thu, 27 Aug 2026 19:22:29 +0200 Subject: [PATCH] feat: wire backends to bundled binaries via _tool() Backends now resolve package managers from euri_mklive/bin/ first, falling back to system PATH. No more host_deps for package managers. --- euri_mklive/distros/alpine.py | 9 +++++---- euri_mklive/distros/arch.py | 14 ++++++-------- euri_mklive/distros/base.py | 18 +++++++++++++++++- euri_mklive/distros/debian.py | 14 +++++++++++--- euri_mklive/distros/fedora.py | 6 +++--- euri_mklive/distros/void.py | 10 +++++----- 6 files changed, 47 insertions(+), 24 deletions(-) diff --git a/euri_mklive/distros/alpine.py b/euri_mklive/distros/alpine.py index 577f675..114905a 100644 --- a/euri_mklive/distros/alpine.py +++ b/euri_mklive/distros/alpine.py @@ -7,7 +7,7 @@ from .base import Distro class Alpine(Distro): name = "alpine" - host_deps = ["apk-tools", "squashfs-tools", "xorriso", "mtools", "dosfstools"] + host_deps = ["squashfs-tools", "xorriso", "mtools", "dosfstools"] MIRROR = "https://dl-cdn.alpinelinux.org/alpine/latest-stable" @@ -17,7 +17,7 @@ class Alpine(Distro): init = config.get("init", "openrc") cmd = [ - "apk", + self._tool("apk"), "-X", f"{mirror}/{branch}/main", "-U", "--allow-untrusted", @@ -36,7 +36,7 @@ class Alpine(Distro): def install_packages(self, rootfs: Path, packages: list[str]) -> None: if not packages: return - self._run_chroot(rootfs, ["apk", "add", *packages]) + self._run_chroot(rootfs, [self._tool("apk"), "add", *packages]) def configure_base(self, rootfs: Path, config: dict) -> None: hostname = config.get("hostname", "euri") @@ -57,7 +57,8 @@ class Alpine(Distro): self._run_chroot(rootfs, ["rc-update", "add", svc, "default"]) def install_bootloader(self, rootfs: Path, config: dict) -> None: - self._run_chroot(rootfs, ["apk", "add", "grub", "grub-efi", "os-prober"]) + pm = self._tool("apk") + self._run_chroot(rootfs, [pm, "add", "grub", "grub-efi", "os-prober"]) self._run_chroot(rootfs, ["grub-install", "--target=i386-pc", "/dev/sda"]) self._run_chroot(rootfs, ["grub-install", "--target=x86_64-efi", "--efi-directory=/boot/efi"]) self._run_chroot(rootfs, ["grub-mkconfig", "-o", "/boot/grub/grub.cfg"]) diff --git a/euri_mklive/distros/arch.py b/euri_mklive/distros/arch.py index 28c1d21..401a67b 100644 --- a/euri_mklive/distros/arch.py +++ b/euri_mklive/distros/arch.py @@ -19,19 +19,16 @@ class Arch(Distro): base_pkgs = ["base", "base-devel", f"{init}", *config.get("base_packages", [])] if self.family == "artix": - # basestrap for Artix elogind_pkg = f"elogind-{init}" - cmd = ["basestrap", "-i", str(rootfs), *base_pkgs, elogind_pkg] + cmd = [self._tool("pacstrap"), "-i", str(rootfs), *base_pkgs, elogind_pkg] else: - # pacstrap for Arch - cmd = ["pacstrap", "-K", str(rootfs), *base_pkgs] + cmd = [self._tool("pacstrap"), "-K", str(rootfs), *base_pkgs] self._run(cmd) def install_packages(self, rootfs: Path, packages: list[str]) -> None: - # always include the package manager itself - pkgs = ["pacman", *packages] - self._run_chroot(rootfs, ["pacman", "-Syu", "--noconfirm", *pkgs]) + pm = self._tool("pacman") + self._run_chroot(rootfs, [pm, "-Syu", "--noconfirm", *packages]) def configure_base(self, rootfs: Path, config: dict) -> None: hostname = config.get("hostname", "euri") @@ -73,7 +70,8 @@ class Arch(Distro): def install_bootloader(self, rootfs: Path, config: dict) -> None: init = config.get("init", "dinit") - self._run_chroot(rootfs, ["pacman", "-S", "--noconfirm", "grub", "efibootmgr", "os-prober"]) + pm = self._tool("pacman") + self._run_chroot(rootfs, [pm, "-S", "--noconfirm", "grub", "efibootmgr", "os-prober"]) self._run_chroot(rootfs, ["grub-install", "--target=i386-pc", "/dev/sda"]) self._run_chroot(rootfs, ["grub-install", "--target=x86_64-efi", "--efi-directory=/boot/efi", "--bootloader-id=grub"]) self._run_chroot(rootfs, ["grub-mkconfig", "-o", "/boot/grub/grub.cfg"]) diff --git a/euri_mklive/distros/base.py b/euri_mklive/distros/base.py index e9f3db9..390d3fc 100644 --- a/euri_mklive/distros/base.py +++ b/euri_mklive/distros/base.py @@ -2,16 +2,18 @@ from __future__ import annotations +import shutil import subprocess from abc import ABC, abstractmethod from pathlib import Path +_BUNDLED = Path(__file__).resolve().parent.parent / "bin" + class Distro(ABC): """Base class every distro backend must implement.""" name: str - # host packages needed to bootstrap (e.g. debootstrap, pacstrap) host_deps: list[str] @abstractmethod @@ -30,6 +32,20 @@ class Distro(ABC): def install_bootloader(self, rootfs: Path, config: dict) -> None: """Install and configure the bootloader inside the rootfs.""" + @staticmethod + def _tool(name: str) -> str: + bundled = _BUNDLED / name + if bundled.is_file(): + return str(bundled) + if bundled.is_dir(): + script = bundled / name + if script.is_file(): + return str(script) + path = shutil.which(name) + if path: + return path + return name + @staticmethod def _run(cmd: list[str], **kw) -> subprocess.CompletedProcess: print(f" \033[90m$\033[0m \033[1m{' '.join(cmd)}\033[0m") diff --git a/euri_mklive/distros/debian.py b/euri_mklive/distros/debian.py index ab3bd8d..d475d8d 100644 --- a/euri_mklive/distros/debian.py +++ b/euri_mklive/distros/debian.py @@ -7,7 +7,7 @@ from .base import Distro class Debian(Distro): name = "debian" - host_deps = ["debootstrap", "squashfs-tools", "xorriso", "mtools", "dosfstools"] + host_deps = ["squashfs-tools", "xorriso", "mtools", "dosfstools"] SUITES = { "debian": "bookworm", @@ -23,12 +23,20 @@ class Debian(Distro): self.family = family def bootstrap(self, rootfs: Path, config: dict) -> None: + import os + suite = config.get("suite", self.SUITES.get(self.family, "bookworm")) mirror = config.get("mirror", self.MIRRORS.get(self.family)) variant = config.get("variant", "minbase") + debootstrap = self._tool("debootstrap") + + env = {**os.environ} + debootstrap_dir = Path(debootstrap).parent + if (debootstrap_dir / "scripts").is_dir(): + env["DEBOOTSTRAP_DIR"] = str(debootstrap_dir) cmd = [ - "debootstrap", + debootstrap, "--variant", variant, "--include", ",".join(config.get("base_packages", ["base-files", "apt"])), "--arch", config.get("arch", "amd64"), @@ -36,7 +44,7 @@ class Debian(Distro): str(rootfs), mirror, ] - self._run(cmd) + self._run(cmd, env=env) def install_packages(self, rootfs: Path, packages: list[str]) -> None: if not packages: diff --git a/euri_mklive/distros/fedora.py b/euri_mklive/distros/fedora.py index 98cde34..03e47cd 100644 --- a/euri_mklive/distros/fedora.py +++ b/euri_mklive/distros/fedora.py @@ -7,7 +7,7 @@ from .base import Distro class Fedora(Distro): name = "fedora" - host_deps = ["dnf", "squashfs-tools", "xorriso", "mtools", "dosfstools"] + host_deps = ["squashfs-tools", "xorriso", "mtools", "dosfstools"] GROUPS = { "fedora": "@core", @@ -23,7 +23,7 @@ class Fedora(Distro): group = self.GROUPS.get(self.family, "@core") cmd = [ - "dnf", + self._tool("dnf5"), "--installroot", str(rootfs), "-y", f"--releasever={release}", @@ -38,7 +38,7 @@ class Fedora(Distro): def install_packages(self, rootfs: Path, packages: list[str]) -> None: if not packages: return - self._run_chroot(rootfs, ["dnf", "-y", "install", *packages]) + self._run_chroot(rootfs, [self._tool("dnf5"), "-y", "install", *packages]) def configure_base(self, rootfs: Path, config: dict) -> None: hostname = config.get("hostname", "euri") diff --git a/euri_mklive/distros/void.py b/euri_mklive/distros/void.py index 0a258c5..773051a 100644 --- a/euri_mklive/distros/void.py +++ b/euri_mklive/distros/void.py @@ -8,7 +8,7 @@ from ..dinit import install_all class Void(Distro): name = "void" - host_deps = ["xbps-install", "squashfs-tools", "xorriso", "mtools", "dosfstools"] + host_deps = ["squashfs-tools", "xorriso", "mtools", "dosfstools"] REPOS = { "glibc": "https://repo-default.voidlinux.org/current", @@ -26,7 +26,7 @@ class Void(Distro): base_pkgs.append("dinit") cmd = [ - "xbps-install", + self._tool("xbps-install"), "-S", "-r", str(rootfs), "-R", repo, @@ -37,12 +37,12 @@ class Void(Distro): # remove runit if we're using dinit if init == "dinit": - self._run_chroot(rootfs, ["xbps-remove", "-y", "runit"]) + self._run_chroot(rootfs, [self._tool("xbps-remove"), "-y", "runit"]) def install_packages(self, rootfs: Path, packages: list[str]) -> None: if not packages: return - self._run_chroot(rootfs, ["xbps-install", "-Sy", *packages]) + self._run_chroot(rootfs, [self._tool("xbps-install"), "-Sy", *packages]) def configure_base(self, rootfs: Path, config: dict) -> None: hostname = config.get("hostname", "euri") @@ -78,7 +78,7 @@ class Void(Distro): def install_bootloader(self, rootfs: Path, config: dict) -> None: libc = config.get("libc", "musl") pkgs = ["grub", "grub-x86_64-efi", "efibootmgr"] - self._run_chroot(rootfs, ["xbps-install", "-Sy", *pkgs]) + self._run_chroot(rootfs, [self._tool("xbps-install"), "-Sy", *pkgs]) self._run_chroot(rootfs, ["grub-install", "--target=i386-pc", "/dev/sda"]) self._run_chroot(rootfs, ["grub-install", "--target=x86_64-efi", "--efi-directory=/boot/efi", "--bootloader-id=Void"]) self._run_chroot(rootfs, ["grub-mkconfig", "-o", "/boot/grub/grub.cfg"])