2026-02-05 18:05:06 -05:00

228 lines
6.5 KiB
Python

"""GRUB bootloader installation and configuration."""
import re
import shutil
from pathlib import Path
from .utils import info, success, error, warn, run, emerge
def emerge_grub() -> None:
"""Install GRUB bootloader."""
info("=== Installing GRUB ===")
emerge("sys-boot/grub")
success("GRUB installed.")
def install_grub_efi() -> None:
"""Install GRUB to EFI system partition."""
info("=== Installing GRUB to EFI ===")
run(
"grub-install",
"--target=x86_64-efi",
"--efi-directory=/boot",
"--bootloader-id=Gentoo",
)
success("GRUB installed to EFI partition.")
def configure_grub() -> None:
"""Configure GRUB for LUKS + NVIDIA."""
info("=== Configuring GRUB ===")
grub_default = Path("/etc/default/grub")
if not grub_default.exists():
error("/etc/default/grub not found")
return
content = grub_default.read_text()
changes_made = False
# Settings to apply (use regex for robust matching of commented/uncommented lines)
settings = {
"GRUB_CMDLINE_LINUX_DEFAULT": '"nvidia_drm.modeset=1 acpi_backlight=native"',
"GRUB_ENABLE_CRYPTODISK": "y",
"GRUB_GFXMODE": "1920x1080",
}
for key, value in settings.items():
full_setting = f'{key}={value}'
pattern = rf'^#?\s*{key}=.*$'
if re.search(pattern, content, re.MULTILINE):
# Replace existing line (commented or not)
new_content = re.sub(pattern, full_setting, content, flags=re.MULTILINE)
if new_content != content:
content = new_content
changes_made = True
else:
# Append if not found at all
content += f"\n{full_setting}"
changes_made = True
if changes_made:
grub_default.write_text(content)
success("GRUB configured for LUKS + NVIDIA + HiDPI")
print(" - GRUB_CMDLINE_LINUX_DEFAULT: nvidia_drm.modeset=1 acpi_backlight=native")
print(" - GRUB_ENABLE_CRYPTODISK: y")
print(" - GRUB_GFXMODE: 1920x1080")
else:
info("GRUB already configured")
def generate_grub_config() -> None:
"""Generate GRUB configuration."""
info("=== Generating GRUB Config ===")
run("grub-mkconfig", "-o", "/boot/grub/grub.cfg")
success("GRUB config generated.")
def copy_dracut_crypt_config(source_dir: Path) -> None:
"""Copy LUKS dracut configuration."""
info("=== Copying Dracut Crypt Config ===")
dracut_src = source_dir / "dracut.conf.d" / "crypt.conf"
dracut_dst = Path("/etc/dracut.conf.d")
dracut_dst.mkdir(parents=True, exist_ok=True)
if dracut_src.exists():
shutil.copy2(dracut_src, dracut_dst / "crypt.conf")
success("Copied crypt.conf to /etc/dracut.conf.d/")
else:
# Create default if not in source
crypt_conf = dracut_dst / "crypt.conf"
crypt_conf.write_text(
"# LUKS support for encrypted root\n"
'add_dracutmodules+=" crypt "\n'
)
success("Created default /etc/dracut.conf.d/crypt.conf")
def verify_initramfs() -> None:
"""Verify initramfs has required modules for boot."""
info("=== Verifying Initramfs ===")
# Find latest initramfs
boot = Path("/boot")
initramfs_files = sorted(boot.glob("initramfs-*.img"), reverse=True)
if not initramfs_files:
error("No initramfs found in /boot")
return
initramfs = initramfs_files[0]
info(f"Checking {initramfs.name}...")
result = run("lsinitrd", str(initramfs), capture=True, check=False)
if not result.ok or not result.stdout:
error("Could not inspect initramfs")
return
output = result.stdout
# Check for crypt/LUKS modules
print()
info("LUKS/Crypt modules:")
crypt_found = False
for keyword in ["crypt", "luks", "dm-crypt"]:
matches = [line for line in output.split("\n") if keyword in line.lower()]
if matches:
crypt_found = True
for match in matches[:5]:
print(f" {match}")
if crypt_found:
success("LUKS support found in initramfs")
else:
error("WARNING: No LUKS/crypt modules found!")
print(" Boot from encrypted root may fail.")
# Check for NVIDIA modules
print()
info("NVIDIA modules:")
nvidia_found = False
nvidia_modules = [line for line in output.split("\n") if "nvidia" in line.lower()]
if nvidia_modules:
nvidia_found = True
for module in nvidia_modules[:5]:
print(f" {module}")
success("NVIDIA modules found in initramfs")
else:
warn("No NVIDIA modules in initramfs (may be OK if not using early KMS)")
# Summary
print()
if crypt_found and nvidia_found:
success("Initramfs verification PASSED - LUKS and NVIDIA modules present")
elif crypt_found:
success("Initramfs verification PASSED - system should boot (NVIDIA optional)")
else:
error("Initramfs verification FAILED - fix before rebooting!")
def rebuild_initramfs() -> None:
"""Rebuild initramfs with current configuration."""
info("=== Rebuilding Initramfs ===")
# Find installed kernel version
modules_dir = Path("/lib/modules")
if not modules_dir.exists():
error("No kernel modules found. Install kernel first.")
return
kernels = sorted(modules_dir.iterdir(), reverse=True)
if not kernels:
error("No kernel versions found in /lib/modules")
return
kernel_version = kernels[0].name
info(f"Rebuilding initramfs for kernel {kernel_version}")
run("dracut", "--force", f"/boot/initramfs-{kernel_version}.img", kernel_version)
success("Initramfs rebuilt.")
def setup_bootloader(source_dir: Path | None = None) -> None:
"""Full bootloader setup workflow."""
if source_dir is None:
source_dir = Path("/root/gentoo")
# Ensure dracut crypt config is in place
copy_dracut_crypt_config(source_dir)
print()
emerge_grub()
print()
install_grub_efi()
print()
configure_grub()
print()
generate_grub_config()
print()
# Verify initramfs
verify_initramfs()
print()
success("=== Bootloader Setup Complete ===")
print()
info("Pre-reboot checklist:")
print(" 1. Verify /etc/fstab is correct")
print(" 2. Verify /etc/conf.d/dmcrypt has swap configured")
print(" 3. Ensure root password is set")
print(" 4. Ensure user account exists")
print()
info("Ready to reboot!")
print(" exit # Leave chroot")
print(" reboot # Restart system")