From 2377b0269a2033eac36958bc3cc733801063d6fe Mon Sep 17 00:00:00 2001 From: N0VA Date: Tue, 3 Feb 2026 16:28:55 +0100 Subject: [PATCH] Implement minimal OS and Bootloader installation logic --- iridium_installer/backend/disk.py | 25 +++++++ iridium_installer/backend/os_install.py | 97 +++++++++++++++++++++++++ iridium_installer/main.py | 44 ++++++++--- 3 files changed, 157 insertions(+), 9 deletions(-) create mode 100644 iridium_installer/backend/os_install.py diff --git a/iridium_installer/backend/disk.py b/iridium_installer/backend/disk.py index 2bec226..bd9ac8e 100644 --- a/iridium_installer/backend/disk.py +++ b/iridium_installer/backend/disk.py @@ -74,3 +74,28 @@ def auto_partition_disk(disk_device): "swap": swap_part, "root": root_part } + +def mount_partitions(partition_info, mount_root="/mnt"): + """ + Mounts the partitions into mount_root. + partition_info is the dict returned by auto_partition_disk. + """ + import os + + # 1. Mount Root + if not os.path.exists(mount_root): + os.makedirs(mount_root) + + run_command(["mount", partition_info["root"], mount_root]) + + # 2. Mount EFI + efi_mount = os.path.join(mount_root, "boot/efi") + if not os.path.exists(efi_mount): + os.makedirs(efi_mount, exist_ok=True) + + run_command(["mount", partition_info["efi"], efi_mount]) + + # 3. Enable Swap (optional, but might as well) + run_command(["swapon", partition_info["swap"]]) + + logger.info(f"Partitions mounted at {mount_root}") diff --git a/iridium_installer/backend/os_install.py b/iridium_installer/backend/os_install.py new file mode 100644 index 0000000..91f48c8 --- /dev/null +++ b/iridium_installer/backend/os_install.py @@ -0,0 +1,97 @@ +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.") diff --git a/iridium_installer/main.py b/iridium_installer/main.py index 2704b42..dc30e48 100644 --- a/iridium_installer/main.py +++ b/iridium_installer/main.py @@ -16,20 +16,46 @@ def main(): "--partition-disk", help="Automatically partition the specified disk (WARNING: DESTROYS DATA)", ) + parser.add_argument( + "--full-install", + help="Run a full minimal installation on the specified disk (WARNING: DESTROYS DATA)", + ) args = parser.parse_args() - if args.partition_disk: - from .backend.disk import auto_partition_disk + if args.partition_disk or args.full_install: + from .backend.disk import auto_partition_disk, mount_partitions + from .backend.os_install import install_minimal_os, configure_system + + target = args.partition_disk or args.full_install try: - print(f"Starting partitioning on {args.partition_disk}...") - result = auto_partition_disk(args.partition_disk) - print("Partitioning successful!") - print(f"EFI: {result['efi']}") - print(f"Swap: {result['swap']}") - print(f"Root: {result['root']}") + print(f"Starting installation on {target}...") + + print("Step 1: Partitioning...") + parts = auto_partition_disk(target) + + if args.full_install: + print("Step 2: Mounting...") + mount_root = "/mnt" + mount_partitions(parts, mount_root) + + print("Step 3: Installing OS (this may take a while)...") + install_minimal_os(mount_root) + + print("Step 4: Configuring Bootloader...") + configure_system(mount_root, parts) + + print("Installation complete! You can now reboot.") + else: + print("Partitioning successful!") + print(f"EFI: {parts['efi']}") + print(f"Swap: {parts['swap']}") + print(f"Root: {parts['root']}") + return 0 except Exception as e: - print(f"Partitioning failed: {e}", file=sys.stderr) + print(f"Installation failed: {e}", file=sys.stderr) + import traceback + traceback.print_exc() return 1 import gi