Merge pull request #323421 from Mic92/systemd-boot-builder

This commit is contained in:
Julien Malka
2024-07-07 19:36:09 +02:00
committed by GitHub

View File

@@ -12,7 +12,7 @@ import subprocess
import sys import sys
import warnings import warnings
import json import json
from typing import NamedTuple, Dict, List from typing import NamedTuple, Any
from dataclasses import dataclass from dataclasses import dataclass
# These values will be replaced with actual values during the package build # These values will be replaced with actual values during the package build
@@ -21,7 +21,7 @@ BOOT_MOUNT_POINT = "@bootMountPoint@"
LOADER_CONF = f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf" # Always stored on the ESP LOADER_CONF = f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf" # Always stored on the ESP
NIXOS_DIR = "@nixosDir@" NIXOS_DIR = "@nixosDir@"
TIMEOUT = "@timeout@" TIMEOUT = "@timeout@"
EDITOR = "@editor@" == "1" EDITOR = "@editor@" == "1" # noqa: PLR0133
CONSOLE_MODE = "@consoleMode@" CONSOLE_MODE = "@consoleMode@"
BOOTSPEC_TOOLS = "@bootspecTools@" BOOTSPEC_TOOLS = "@bootspecTools@"
DISTRO_NAME = "@distroName@" DISTRO_NAME = "@distroName@"
@@ -38,17 +38,22 @@ class BootSpec:
init: str init: str
initrd: str initrd: str
kernel: str kernel: str
kernelParams: List[str] kernelParams: list[str] # noqa: N815
label: str label: str
system: str system: str
toplevel: str toplevel: str
specialisations: Dict[str, "BootSpec"] specialisations: dict[str, "BootSpec"]
sortKey: str sortKey: str # noqa: N815
initrdSecrets: str | None = None initrdSecrets: str | None = None # noqa: N815
libc = ctypes.CDLL("libc.so.6") libc = ctypes.CDLL("libc.so.6")
FILE = None | int
def run(cmd: list[str], stdout: FILE = None) -> subprocess.CompletedProcess[str]:
return subprocess.run(cmd, check=True, text=True, stdout=stdout)
class SystemIdentifier(NamedTuple): class SystemIdentifier(NamedTuple):
profile: str | None profile: str | None
generation: int generation: int
@@ -112,17 +117,20 @@ def get_bootspec(profile: str | None, generation: int) -> BootSpec:
boot_json_f = open(boot_json_path, 'r') boot_json_f = open(boot_json_path, 'r')
bootspec_json = json.load(boot_json_f) bootspec_json = json.load(boot_json_f)
else: else:
boot_json_str = subprocess.check_output([ boot_json_str = run(
f"{BOOTSPEC_TOOLS}/bin/synthesize", [
"--version", f"{BOOTSPEC_TOOLS}/bin/synthesize",
"1", "--version",
system_directory, "1",
"/dev/stdout"], system_directory,
universal_newlines=True) "/dev/stdout",
],
stdout=subprocess.PIPE,
).stdout
bootspec_json = json.loads(boot_json_str) bootspec_json = json.loads(boot_json_str)
return bootspec_from_json(bootspec_json) return bootspec_from_json(bootspec_json)
def bootspec_from_json(bootspec_json: Dict) -> BootSpec: def bootspec_from_json(bootspec_json: dict[str, Any]) -> BootSpec:
specialisations = bootspec_json['org.nixos.specialisation.v1'] specialisations = bootspec_json['org.nixos.specialisation.v1']
specialisations = {k: bootspec_from_json(v) for k, v in specialisations.items()} specialisations = {k: bootspec_from_json(v) for k, v in specialisations.items()}
systemdBootExtension = bootspec_json.get('org.nixos.systemd-boot', {}) systemdBootExtension = bootspec_json.get('org.nixos.systemd-boot', {})
@@ -157,7 +165,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None
try: try:
if bootspec.initrdSecrets is not None: if bootspec.initrdSecrets is not None:
subprocess.check_call([bootspec.initrdSecrets, f"{BOOT_MOUNT_POINT}%s" % (initrd)]) run([bootspec.initrdSecrets, f"{BOOT_MOUNT_POINT}%s" % (initrd)])
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
if current: if current:
print("failed to create initrd secrets!", file=sys.stderr) print("failed to create initrd secrets!", file=sys.stderr)
@@ -192,13 +200,17 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None
def get_generations(profile: str | None = None) -> list[SystemIdentifier]: def get_generations(profile: str | None = None) -> list[SystemIdentifier]:
gen_list = subprocess.check_output([ gen_list = run(
f"{NIX}/bin/nix-env", [
"--list-generations", f"{NIX}/bin/nix-env",
"-p", "--list-generations",
"/nix/var/nix/profiles/%s" % ("system-profiles/" + profile if profile else "system")], "-p",
universal_newlines=True) "/nix/var/nix/profiles/%s"
gen_lines = gen_list.split('\n') % ("system-profiles/" + profile if profile else "system"),
],
stdout=subprocess.PIPE,
).stdout
gen_lines = gen_list.split("\n")
gen_lines.pop() gen_lines.pop()
configurationLimit = CONFIGURATION_LIMIT configurationLimit = CONFIGURATION_LIMIT
@@ -230,10 +242,10 @@ def remove_old_entries(gens: list[SystemIdentifier]) -> None:
gen_number = int(rex_generation.sub(r"\1", path)) gen_number = int(rex_generation.sub(r"\1", path))
except ValueError: except ValueError:
continue continue
if not (prof, gen_number, None) in gens: if (prof, gen_number, None) not in gens:
os.unlink(path) os.unlink(path)
for path in glob.iglob(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/*"): for path in glob.iglob(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/*"):
if not path in known_paths and not os.path.isdir(path): if path not in known_paths and not os.path.isdir(path):
os.unlink(path) os.unlink(path)
@@ -263,9 +275,7 @@ def install_bootloader(args: argparse.Namespace) -> None:
# be there on newly installed systems, so let's generate one so that # be there on newly installed systems, so let's generate one so that
# bootctl can find it and we can also pass it to write_entry() later. # bootctl can find it and we can also pass it to write_entry() later.
cmd = [f"{SYSTEMD}/bin/systemd-machine-id-setup", "--print"] cmd = [f"{SYSTEMD}/bin/systemd-machine-id-setup", "--print"]
machine_id = subprocess.run( machine_id = run(cmd, stdout=subprocess.PIPE).stdout.rstrip()
cmd, text=True, check=True, stdout=subprocess.PIPE
).stdout.rstrip()
if os.getenv("NIXOS_INSTALL_GRUB") == "1": if os.getenv("NIXOS_INSTALL_GRUB") == "1":
warnings.warn("NIXOS_INSTALL_GRUB env var deprecated, use NIXOS_INSTALL_BOOTLOADER", DeprecationWarning) warnings.warn("NIXOS_INSTALL_GRUB env var deprecated, use NIXOS_INSTALL_BOOTLOADER", DeprecationWarning)
@@ -288,11 +298,20 @@ def install_bootloader(args: argparse.Namespace) -> None:
if os.path.exists(LOADER_CONF): if os.path.exists(LOADER_CONF):
os.unlink(LOADER_CONF) os.unlink(LOADER_CONF)
subprocess.check_call([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"] + bootctl_flags + ["install"]) run(
[f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"]
+ bootctl_flags
+ ["install"]
)
else: else:
# Update bootloader to latest if needed # Update bootloader to latest if needed
available_out = subprocess.check_output([f"{SYSTEMD}/bin/bootctl", "--version"], universal_newlines=True).split()[2] available_out = run(
installed_out = subprocess.check_output([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}", "status"], universal_newlines=True) [f"{SYSTEMD}/bin/bootctl", "--version"], stdout=subprocess.PIPE
).stdout.split()[2]
installed_out = run(
[f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}", "status"],
stdout=subprocess.PIPE,
).stdout
# See status_binaries() in systemd bootctl.c for code which generates this # See status_binaries() in systemd bootctl.c for code which generates this
installed_match = re.search(r"^\W+File:.*/EFI/(?:BOOT|systemd)/.*\.efi \(systemd-boot ([\d.]+[^)]*)\)$", installed_match = re.search(r"^\W+File:.*/EFI/(?:BOOT|systemd)/.*\.efi \(systemd-boot ([\d.]+[^)]*)\)$",
@@ -311,7 +330,11 @@ def install_bootloader(args: argparse.Namespace) -> None:
if installed_version < available_version: if installed_version < available_version:
print("updating systemd-boot from %s to %s" % (installed_version, available_version)) print("updating systemd-boot from %s to %s" % (installed_version, available_version))
subprocess.check_call([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"] + bootctl_flags + ["update"]) run(
[f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"]
+ bootctl_flags
+ ["update"]
)
os.makedirs(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}", exist_ok=True) os.makedirs(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}", exist_ok=True)
os.makedirs(f"{BOOT_MOUNT_POINT}/loader/entries", exist_ok=True) os.makedirs(f"{BOOT_MOUNT_POINT}/loader/entries", exist_ok=True)
@@ -362,7 +385,7 @@ def install_bootloader(args: argparse.Namespace) -> None:
os.makedirs(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/.extra-files", exist_ok=True) os.makedirs(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/.extra-files", exist_ok=True)
subprocess.check_call(COPY_EXTRA_FILES) run([COPY_EXTRA_FILES])
def main() -> None: def main() -> None:
@@ -370,7 +393,7 @@ def main() -> None:
parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help=f"The default {DISTRO_NAME} config to boot") parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help=f"The default {DISTRO_NAME} config to boot")
args = parser.parse_args() args = parser.parse_args()
subprocess.check_call(CHECK_MOUNTPOINTS) run([CHECK_MOUNTPOINTS])
try: try:
install_bootloader(args) install_bootloader(args)