Compare commits

..

3 Commits

View File

@@ -76,11 +76,10 @@ def run_command(cmd, check=True):
@contextmanager @contextmanager
def mount_pseudo_fs(mount_root): def mount_pseudo_fs(mount_root):
""" """
Context manager to bind mount /dev, /proc, /sys, /run, and efivarfs into mount_root. Context manager to bind mount /dev, /proc, /sys, and efivarfs into mount_root.
""" """
logger.info(f"Mounting pseudo-filesystems to {mount_root}...") logger.info(f"Mounting pseudo-filesystems to {mount_root}...")
# dev/pts and dev/shm are often needed by scriptlets mounts = ["dev", "proc", "sys"]
mounts = ["dev", "proc", "sys", "run", "dev/pts", "dev/shm"]
mounted_paths = [] mounted_paths = []
try: try:
@@ -90,14 +89,6 @@ def mount_pseudo_fs(mount_root):
run_command(["mount", "--bind", f"/{fs}", target]) run_command(["mount", "--bind", f"/{fs}", target])
mounted_paths.append(target) mounted_paths.append(target)
# Bind mount RPM GPG keys from host
gpg_keys_host = "/etc/pki/rpm-gpg"
if os.path.exists(gpg_keys_host):
gpg_keys_target = os.path.join(mount_root, "etc/pki/rpm-gpg")
os.makedirs(gpg_keys_target, exist_ok=True)
run_command(["mount", "--bind", gpg_keys_host, gpg_keys_target])
mounted_paths.append(gpg_keys_target)
# Mount efivarfs if it exists on the host # Mount efivarfs if it exists on the host
efivars_path = "/sys/firmware/efi/efivars" efivars_path = "/sys/firmware/efi/efivars"
if os.path.exists(efivars_path): if os.path.exists(efivars_path):
@@ -119,109 +110,10 @@ def mount_pseudo_fs(mount_root):
logger.warning(f"Failed to unmount {path}: {e}") logger.warning(f"Failed to unmount {path}: {e}")
def has_rpms(path): def install_minimal_os(mount_root, releasever="43"):
"""Checks if a directory contains any .rpm files."""
if not os.path.isdir(path):
return False
try:
for f in os.listdir(path):
if f.endswith(".rpm"):
return True
except Exception:
pass
return False
def find_iso_repo():
"""
Attempts to find a local repository on the ISO or other mounted media.
"""
possible_paths = [
"/run/install/repo",
"/run/install/source",
"/mnt/install/repo",
"/run/initramfs/live",
"/run/initramfs/isoscan",
]
def check_path(p):
if os.path.exists(os.path.join(p, "repodata")) or os.path.exists(os.path.join(p, "media.repo")):
return p
for sub in ["os", "Packages", "BaseOS", "AppStream"]:
sub_p = os.path.join(p, sub)
if os.path.exists(os.path.join(sub_p, "repodata")) or os.path.exists(os.path.join(sub_p, "media.repo")):
return sub_p
return None
# 1. Check known paths
for path in possible_paths:
res = check_path(path)
if res: return res
# 2. Check all mounted filesystems
try:
res = subprocess.run(["findmnt", "-nlo", "TARGET"], capture_output=True, text=True)
if res.returncode == 0:
for mount in res.stdout.splitlines():
if mount.startswith("/proc") or mount.startswith("/sys") or mount.startswith("/dev"):
continue
res = check_path(mount)
if res: return res
except Exception as e:
logger.debug(f"Error searching mount points: {e}")
# 3. Try to mount /dev/sr0 or /dev/cdrom
for dev in ["/dev/sr0", "/dev/cdrom"]:
try:
if os.path.exists(dev):
res = subprocess.run(["findmnt", "-n", dev], capture_output=True)
if res.returncode != 0:
try:
os.makedirs("/mnt/iso", exist_ok=True)
subprocess.run(["mount", "-o", "ro", dev, "/mnt/iso"], check=True)
res = check_path("/mnt/iso")
if res: return res
except Exception: pass
except Exception: pass
# 4. Check /run/media deeper
if os.path.exists("/run/media"):
try:
for root, dirs, files in os.walk("/run/media"):
if "repodata" in dirs or "media.repo" in files:
return root
# Limit depth for performance
if root.count(os.sep) > 5:
del dirs[:]
except Exception: pass
# 5. Last resort: any directory with .rpm files
for path in possible_paths:
if has_rpms(path): return path
for sub in ["os", "Packages", "BaseOS", "AppStream"]:
if has_rpms(os.path.join(path, sub)): return os.path.join(path, sub)
return None
def install_minimal_os(mount_root, releasever=None):
""" """
Installs minimal Fedora packages to mount_root. Installs minimal Fedora packages to mount_root.
""" """
if not releasever:
# Try to detect from host
try:
with open("/etc/os-release", "r") as f:
for line in f:
if line.startswith("VERSION_ID="):
releasever = line.split("=")[1].strip().strip('"')
break
except Exception:
pass
if not releasever:
releasever = "43" # Fallback
logger.info(f"Installing minimal Fedora {releasever} to {mount_root}...") logger.info(f"Installing minimal Fedora {releasever} to {mount_root}...")
log_os_install("INSTALL", "start", f"Target: {mount_root}, Release: {releasever}") log_os_install("INSTALL", "start", f"Target: {mount_root}, Release: {releasever}")
@@ -231,7 +123,7 @@ def install_minimal_os(mount_root, releasever=None):
"coreutils", "coreutils",
"kernel", "kernel",
"systemd", "systemd",
"systemd-boot", "systemd-boot-unsigned",
"dnf", "dnf",
"shadow-utils", "shadow-utils",
"util-linux", "util-linux",
@@ -242,39 +134,49 @@ def install_minimal_os(mount_root, releasever=None):
] ]
# Offline installation logic # Offline installation logic
iso_repo = find_iso_repo() possible_repos = [
"/run/install/repo",
# Create a temporary dnf.conf to be strictly local if repo found "/run/install/source",
dnf_conf_path = "/tmp/iridium_dnf.conf" "/mnt/install/repo",
with open(dnf_conf_path, "w") as f: "/run/initramfs/live",
f.write("[main]\ngpgcheck=0\ninstallroot_managed_by_dnf=True\n") "/run/initramfs/isoscan",
dnf_args = [
f"--config={dnf_conf_path}",
"--setopt=cachedir=/tmp/dnf-cache",
"--setopt=install_weak_deps=False",
"--nodocs",
] ]
iso_repo = None
for path in possible_repos:
if os.path.exists(os.path.join(path, "repodata")):
iso_repo = path
break
elif os.path.exists(os.path.join(path, "Packages")):
iso_repo = path
break
# Try searching in /run/media if not found
if not iso_repo and os.path.exists("/run/media"):
try:
for user in os.listdir("/run/media"):
user_path = os.path.join("/run/media", user)
if os.path.isdir(user_path):
for label in os.listdir(user_path):
label_path = os.path.join(user_path, label)
if os.path.exists(os.path.join(label_path, "repodata")):
iso_repo = label_path
break
if iso_repo: break
except Exception: pass
dnf_args = []
if iso_repo: if iso_repo:
logger.info(f"Found ISO repository at {iso_repo}. Using strictly offline mode.") logger.info(f"Found ISO repository at {iso_repo}. Using strictly offline mode.")
dnf_args += [ dnf_args = [
"--disablerepo=*", "--disablerepo=*",
f"--repofrompath=iridium-iso,{iso_repo}", f"--repofrompath=iridium-iso,{iso_repo}",
"--enablerepo=iridium-iso", "--enablerepo=iridium-iso",
"--nogpgcheck", "--cacheonly",
"--offline", # Supported by dnf5
"--setopt=iridium-iso.gpgcheck=0",
"--setopt=metadata_expire=-1",
] ]
else: else:
# Check if we are on a Live ISO but no repo found logger.warning("ISO repository not found in common locations. DNF might try to use network.")
if os.path.exists("/run/initramfs/live/LiveOS/squashfs.img"): dnf_args = []
logger.warning("Detected Fedora Live environment, but no RPM repository was found on the media.")
logger.warning("Workstation Live ISOs usually do not contain a DNF repository for offline installation.")
logger.warning("ISO repository not found. DNF will attempt to use network.")
dnf_args.append("--use-host-config")
cmd = [ cmd = [
"dnf", "dnf",
@@ -282,17 +184,15 @@ def install_minimal_os(mount_root, releasever=None):
"-y", "-y",
f"--installroot={mount_root}", f"--installroot={mount_root}",
f"--releasever={releasever}", f"--releasever={releasever}",
"--setopt=install_weak_deps=False",
"--nodocs",
] ]
if not iso_repo:
cmd.append("--use-host-config")
cmd += dnf_args + packages cmd += dnf_args + packages
# Copy resolv.conf if it exists (some scriptlets might need it for UID/GID lookups)
try:
os.makedirs(os.path.join(mount_root, "etc"), exist_ok=True)
if os.path.exists("/etc/resolv.conf"):
subprocess.run(["cp", "/etc/resolv.conf", os.path.join(mount_root, "etc/resolv.conf")])
except Exception: pass
with mount_pseudo_fs(mount_root): with mount_pseudo_fs(mount_root):
run_command(cmd) run_command(cmd)