import subprocess import logging import os logger = logging.getLogger(__name__) def run_command(cmd, check=True): logger.info(f"Running command: {' '.join(cmd)}") try: result = subprocess.run(cmd, check=check, capture_output=True, text=True) return result except subprocess.CalledProcessError as e: logger.error(f"Command failed: {e.stderr}") raise def install_minimal_os(mount_root, releasever="41"): """ Installs minimal Fedora packages to mount_root. """ logger.info(f"Installing minimal Fedora {releasever} to {mount_root}...") packages = [ "basesystem", "bash", "coreutils", "kernel", "systemd", "dnf", "grub2-efi-x64", "shim-x64", "efibootmgr", "passwd", "rootfiles", "vim-minimal" ] # Fedora 41 (or whatever is current) # We use --releasever to ensure dnf knows which version to fetch cmd = [ "dnf", "install", "-y", f"--installroot={mount_root}", f"--releasever={releasever}", "--setopt=install_weak_deps=False", "--nodocs" ] + packages run_command(cmd) logger.info("Base system installation complete.") def configure_system(mount_root, partition_info): """ Basic configuration: fstab and grub. """ logger.info("Configuring system...") # 1. Generate fstab # We can get UUIDs using blkid def get_uuid(dev): res = run_command(["blkid", "-s", "UUID", "-o", "value", dev]) return res.stdout.strip() root_uuid = get_uuid(partition_info["root"]) efi_uuid = get_uuid(partition_info["efi"]) swap_uuid = get_uuid(partition_info["swap"]) fstab_content = f""" UUID={root_uuid} / ext4 defaults 1 1 UUID={efi_uuid} /boot/efi vfat defaults 0 2 UUID={swap_uuid} none swap defaults 0 0 """ with open(os.path.join(mount_root, "etc/fstab"), "w") as f: f.write(fstab_content) # 2. Configure GRUB # This is tricky because we need to chroot or use --boot-directory # Simplest for "barely bootable shell" is to try running grub2-mkconfig inside chroot # Need to bind mount dev, proc, sys for chroot for dev in ["dev", "proc", "sys"]: target = os.path.join(mount_root, dev) run_command(["mount", "--bind", f"/{dev}", target]) try: # grub2-mkconfig -o /boot/grub2/grub.cfg (or wherever Fedora puts it) # On UEFI Fedora: /boot/efi/EFI/fedora/grub.cfg usually just redirects to /boot/grub2/grub.cfg # Let's just do both or standard one. chroot_cmd = ["chroot", mount_root, "grub2-mkconfig", "-o", "/boot/grub2/grub.cfg"] run_command(chroot_cmd) # Also need to make sure EFI boot entry is there, but shim/grub2 packages usually handle it in %post? # Maybe not in --installroot. finally: # Unmount binds for dev in ["sys", "proc", "dev"]: run_command(["umount", os.path.join(mount_root, dev)]) logger.info("System configuration complete.")