Loading nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py +10 −1 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ class BootSpec: system: str toplevel: str specialisations: Dict[str, "BootSpec"] sortKey: str initrdSecrets: str | None = None Loading Loading @@ -73,6 +74,7 @@ def system_dir(profile: str | None, generation: int, specialisation: str | None) return d BOOT_ENTRY = """title {title} sort-key {sort_key} version Generation {generation} {description} linux {kernel} initrd {initrd} Loading Loading @@ -123,7 +125,13 @@ def get_bootspec(profile: str | None, generation: int) -> BootSpec: def bootspec_from_json(bootspec_json: Dict) -> BootSpec: specialisations = bootspec_json['org.nixos.specialisation.v1'] specialisations = {k: bootspec_from_json(v) for k, v in specialisations.items()} return BootSpec(**bootspec_json['org.nixos.bootspec.v1'], specialisations=specialisations) systemdBootExtension = bootspec_json.get('org.nixos.systemd-boot', {}) sortKey = systemdBootExtension.get('sortKey', 'nixos') return BootSpec( **bootspec_json['org.nixos.bootspec.v1'], specialisations=specialisations, sortKey=sortKey ) def copy_from_file(file: str, dry_run: bool = False) -> str: Loading Loading @@ -170,6 +178,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None with open(tmp_path, 'w') as f: f.write(BOOT_ENTRY.format(title=title, sort_key=bootspec.sortKey, generation=generation, kernel=kernel, initrd=initrd, Loading nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix +62 −11 Original line number Diff line number Diff line Loading @@ -87,6 +87,16 @@ in { imports = [ (mkRenamedOptionModule [ "boot" "loader" "gummiboot" "enable" ] [ "boot" "loader" "systemd-boot" "enable" ]) (lib.mkChangedOptionModule [ "boot" "loader" "systemd-boot" "memtest86" "entryFilename" ] [ "boot" "loader" "systemd-boot" "memtest86" "sortKey" ] (config: lib.strings.removeSuffix ".conf" config.boot.loader.systemd-boot.memtest86.entryFilename) ) (lib.mkChangedOptionModule [ "boot" "loader" "systemd-boot" "netbootxyz" "entryFilename" ] [ "boot" "loader" "systemd-boot" "netbootxyz" "sortKey" ] (config: lib.strings.removeSuffix ".conf" config.boot.loader.systemd-boot.netbootxyz.entryFilename) ) ]; options.boot.loader.systemd-boot = { Loading @@ -102,6 +112,35 @@ in { ''; }; sortKey = mkOption { default = "nixos"; type = lib.types.str; description = '' The sort key used for the NixOS bootloader entries. This key determines sorting relative to non-NixOS entries. See also https://uapi-group.org/specifications/specs/boot_loader_specification/#sorting This option can also be used to control the sorting of NixOS specialisations. By default, specialisations inherit the sort key of their parent generation and will have the same value for both the sort-key and the version (i.e. the generation number), systemd-boot will therefore sort them based on their file name, meaning that in your boot menu you will have each main generation directly followed by its specialisations sorted alphabetically by their names. If you want a different ordering for a specialisation, you can override its sort-key which will cause the specialisation to be uncoupled from its parent generation. It will then be sorted by its new sort-key just like any other boot entry. The sort-key is stored in the generation's bootspec, which means that generations keep their sort-keys even if the original definition of the generation was removed from the NixOS configuration. It also means that updating the sort-key will only affect new generations, while old ones will keep the sort-key that they were originally built with. ''; }; editor = mkOption { default = true; Loading Loading @@ -184,13 +223,15 @@ in { ''; }; entryFilename = mkOption { default = "memtest86.conf"; sortKey = mkOption { default = "o_memtest86"; type = types.str; description = lib.mdDoc '' `systemd-boot` orders the menu entries by the config file names, `systemd-boot` orders the menu entries by their sort keys, so if you want something to appear after all the NixOS entries, it should start with {file}`o` or onwards. See also {option}`boot.loader.systemd-boot.sortKey`. ''; }; }; Loading @@ -207,13 +248,15 @@ in { ''; }; entryFilename = mkOption { default = "o_netbootxyz.conf"; sortKey = mkOption { default = "o_netbootxyz"; type = types.str; description = lib.mdDoc '' `systemd-boot` orders the menu entries by the config file names, `systemd-boot` orders the menu entries by their sort keys, so if you want something to appear after all the NixOS entries, it should start with {file}`o` or onwards. See also {option}`boot.loader.systemd-boot.sortKey`. ''; }; }; Loading @@ -225,6 +268,7 @@ in { { "memtest86.conf" = ''' title Memtest86+ efi /efi/memtest86/memtest.efi sort-key z_memtest '''; } ''; description = lib.mdDoc '' Loading @@ -233,9 +277,10 @@ in { Each attribute name denotes the destination file name, and the corresponding attribute value is the contents of the entry. `systemd-boot` orders the menu entries by the config file names, so if you want something to appear after all the NixOS entries, it should start with {file}`o` or onwards. To control the ordering of the entry in the boot menu, use the sort-key field, see https://uapi-group.org/specifications/specs/boot_loader_specification/#sorting and {option}`boot.loader.systemd-boot.sortKey`. ''; }; Loading Loading @@ -328,19 +373,25 @@ in { boot.loader.systemd-boot.extraEntries = mkMerge [ (mkIf cfg.memtest86.enable { "${cfg.memtest86.entryFilename}" = '' "memtest86.conf" = '' title Memtest86+ efi /efi/memtest86/memtest.efi sort-key ${cfg.memtest86.sortKey} ''; }) (mkIf cfg.netbootxyz.enable { "${cfg.netbootxyz.entryFilename}" = '' "netbootxyz.conf" = '' title netboot.xyz efi /efi/netbootxyz/netboot.xyz.efi sort-key ${cfg.netbootxyz.sortKey} ''; }) ]; boot.bootspec.extensions."org.nixos.systemd-boot" = { inherit (config.boot.loader.systemd-boot) sortKey; }; system = { build.installBootLoader = finalSystemdBootBuilder; Loading nixos/tests/systemd-boot.nix +16 −12 Original line number Diff line number Diff line Loading @@ -93,6 +93,7 @@ in machine.wait_for_unit("multi-user.target") machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf") machine.succeed("grep 'sort-key nixos' /boot/loader/entries/nixos-generation-1.conf") # Ensure we actually booted using systemd-boot # Magic number is the vendor UUID used by systemd-boot. Loading Loading @@ -166,7 +167,9 @@ in nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; specialisation.something.configuration = {}; specialisation.something.configuration = { boot.loader.systemd-boot.sortKey = "something"; }; }; testScript = '' Loading @@ -179,6 +182,9 @@ in machine.succeed( "grep -q 'title NixOS (something)' /boot/loader/entries/nixos-generation-1-specialisation-something.conf" ) machine.succeed( "grep 'sort-key something' /boot/loader/entries/nixos-generation-1-specialisation-something.conf" ) ''; }; Loading Loading @@ -256,25 +262,25 @@ in }; testScript = '' machine.succeed("test -e /boot/loader/entries/o_netbootxyz.conf") machine.succeed("test -e /boot/loader/entries/netbootxyz.conf") machine.succeed("test -e /boot/efi/netbootxyz/netboot.xyz.efi") ''; }; entryFilename = makeTest { name = "systemd-boot-entry-filename"; memtestSortKey = makeTest { name = "systemd-boot-memtest-sortkey"; meta.maintainers = with pkgs.lib.maintainers; [ Enzime julienmalka ]; nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.systemd-boot.memtest86.enable = true; boot.loader.systemd-boot.memtest86.entryFilename = "apple.conf"; boot.loader.systemd-boot.memtest86.sortKey = "apple"; }; testScript = '' machine.fail("test -e /boot/loader/entries/memtest86.conf") machine.succeed("test -e /boot/loader/entries/apple.conf") machine.succeed("test -e /boot/loader/entries/memtest86.conf") machine.succeed("test -e /boot/efi/memtest86/memtest.efi") machine.succeed("grep 'sort-key apple' /boot/loader/entries/memtest86.conf") ''; }; Loading @@ -285,7 +291,6 @@ in nodes.machine = { pkgs, lib, ... }: { imports = [ commonXbootldr ]; boot.loader.systemd-boot.memtest86.enable = true; boot.loader.systemd-boot.memtest86.entryFilename = "apple.conf"; }; testScript = { nodes, ... }: '' Loading @@ -295,8 +300,7 @@ in machine.wait_for_unit("multi-user.target") machine.succeed("test -e /efi/EFI/systemd/systemd-bootx64.efi") machine.fail("test -e /boot/loader/entries/memtest86.conf") machine.succeed("test -e /boot/loader/entries/apple.conf") machine.succeed("test -e /boot/loader/entries/memtest86.conf") machine.succeed("test -e /boot/EFI/memtest86/memtest.efi") ''; }; Loading Loading @@ -388,9 +392,9 @@ in machine.succeed("${finalSystem}/bin/switch-to-configuration boot") machine.fail("test -e /boot/efi/fruits/tomato.efi") machine.fail("test -e /boot/efi/nixos/.extra-files/efi/fruits/tomato.efi") machine.succeed("test -e /boot/loader/entries/o_netbootxyz.conf") machine.succeed("test -e /boot/loader/entries/netbootxyz.conf") machine.succeed("test -e /boot/efi/netbootxyz/netboot.xyz.efi") machine.succeed("test -e /boot/efi/nixos/.extra-files/loader/entries/o_netbootxyz.conf") machine.succeed("test -e /boot/efi/nixos/.extra-files/loader/entries/netbootxyz.conf") machine.succeed("test -e /boot/efi/nixos/.extra-files/efi/netbootxyz/netboot.xyz.efi") ''; }; Loading Loading
nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py +10 −1 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ class BootSpec: system: str toplevel: str specialisations: Dict[str, "BootSpec"] sortKey: str initrdSecrets: str | None = None Loading Loading @@ -73,6 +74,7 @@ def system_dir(profile: str | None, generation: int, specialisation: str | None) return d BOOT_ENTRY = """title {title} sort-key {sort_key} version Generation {generation} {description} linux {kernel} initrd {initrd} Loading Loading @@ -123,7 +125,13 @@ def get_bootspec(profile: str | None, generation: int) -> BootSpec: def bootspec_from_json(bootspec_json: Dict) -> BootSpec: specialisations = bootspec_json['org.nixos.specialisation.v1'] specialisations = {k: bootspec_from_json(v) for k, v in specialisations.items()} return BootSpec(**bootspec_json['org.nixos.bootspec.v1'], specialisations=specialisations) systemdBootExtension = bootspec_json.get('org.nixos.systemd-boot', {}) sortKey = systemdBootExtension.get('sortKey', 'nixos') return BootSpec( **bootspec_json['org.nixos.bootspec.v1'], specialisations=specialisations, sortKey=sortKey ) def copy_from_file(file: str, dry_run: bool = False) -> str: Loading Loading @@ -170,6 +178,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None with open(tmp_path, 'w') as f: f.write(BOOT_ENTRY.format(title=title, sort_key=bootspec.sortKey, generation=generation, kernel=kernel, initrd=initrd, Loading
nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix +62 −11 Original line number Diff line number Diff line Loading @@ -87,6 +87,16 @@ in { imports = [ (mkRenamedOptionModule [ "boot" "loader" "gummiboot" "enable" ] [ "boot" "loader" "systemd-boot" "enable" ]) (lib.mkChangedOptionModule [ "boot" "loader" "systemd-boot" "memtest86" "entryFilename" ] [ "boot" "loader" "systemd-boot" "memtest86" "sortKey" ] (config: lib.strings.removeSuffix ".conf" config.boot.loader.systemd-boot.memtest86.entryFilename) ) (lib.mkChangedOptionModule [ "boot" "loader" "systemd-boot" "netbootxyz" "entryFilename" ] [ "boot" "loader" "systemd-boot" "netbootxyz" "sortKey" ] (config: lib.strings.removeSuffix ".conf" config.boot.loader.systemd-boot.netbootxyz.entryFilename) ) ]; options.boot.loader.systemd-boot = { Loading @@ -102,6 +112,35 @@ in { ''; }; sortKey = mkOption { default = "nixos"; type = lib.types.str; description = '' The sort key used for the NixOS bootloader entries. This key determines sorting relative to non-NixOS entries. See also https://uapi-group.org/specifications/specs/boot_loader_specification/#sorting This option can also be used to control the sorting of NixOS specialisations. By default, specialisations inherit the sort key of their parent generation and will have the same value for both the sort-key and the version (i.e. the generation number), systemd-boot will therefore sort them based on their file name, meaning that in your boot menu you will have each main generation directly followed by its specialisations sorted alphabetically by their names. If you want a different ordering for a specialisation, you can override its sort-key which will cause the specialisation to be uncoupled from its parent generation. It will then be sorted by its new sort-key just like any other boot entry. The sort-key is stored in the generation's bootspec, which means that generations keep their sort-keys even if the original definition of the generation was removed from the NixOS configuration. It also means that updating the sort-key will only affect new generations, while old ones will keep the sort-key that they were originally built with. ''; }; editor = mkOption { default = true; Loading Loading @@ -184,13 +223,15 @@ in { ''; }; entryFilename = mkOption { default = "memtest86.conf"; sortKey = mkOption { default = "o_memtest86"; type = types.str; description = lib.mdDoc '' `systemd-boot` orders the menu entries by the config file names, `systemd-boot` orders the menu entries by their sort keys, so if you want something to appear after all the NixOS entries, it should start with {file}`o` or onwards. See also {option}`boot.loader.systemd-boot.sortKey`. ''; }; }; Loading @@ -207,13 +248,15 @@ in { ''; }; entryFilename = mkOption { default = "o_netbootxyz.conf"; sortKey = mkOption { default = "o_netbootxyz"; type = types.str; description = lib.mdDoc '' `systemd-boot` orders the menu entries by the config file names, `systemd-boot` orders the menu entries by their sort keys, so if you want something to appear after all the NixOS entries, it should start with {file}`o` or onwards. See also {option}`boot.loader.systemd-boot.sortKey`. ''; }; }; Loading @@ -225,6 +268,7 @@ in { { "memtest86.conf" = ''' title Memtest86+ efi /efi/memtest86/memtest.efi sort-key z_memtest '''; } ''; description = lib.mdDoc '' Loading @@ -233,9 +277,10 @@ in { Each attribute name denotes the destination file name, and the corresponding attribute value is the contents of the entry. `systemd-boot` orders the menu entries by the config file names, so if you want something to appear after all the NixOS entries, it should start with {file}`o` or onwards. To control the ordering of the entry in the boot menu, use the sort-key field, see https://uapi-group.org/specifications/specs/boot_loader_specification/#sorting and {option}`boot.loader.systemd-boot.sortKey`. ''; }; Loading Loading @@ -328,19 +373,25 @@ in { boot.loader.systemd-boot.extraEntries = mkMerge [ (mkIf cfg.memtest86.enable { "${cfg.memtest86.entryFilename}" = '' "memtest86.conf" = '' title Memtest86+ efi /efi/memtest86/memtest.efi sort-key ${cfg.memtest86.sortKey} ''; }) (mkIf cfg.netbootxyz.enable { "${cfg.netbootxyz.entryFilename}" = '' "netbootxyz.conf" = '' title netboot.xyz efi /efi/netbootxyz/netboot.xyz.efi sort-key ${cfg.netbootxyz.sortKey} ''; }) ]; boot.bootspec.extensions."org.nixos.systemd-boot" = { inherit (config.boot.loader.systemd-boot) sortKey; }; system = { build.installBootLoader = finalSystemdBootBuilder; Loading
nixos/tests/systemd-boot.nix +16 −12 Original line number Diff line number Diff line Loading @@ -93,6 +93,7 @@ in machine.wait_for_unit("multi-user.target") machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf") machine.succeed("grep 'sort-key nixos' /boot/loader/entries/nixos-generation-1.conf") # Ensure we actually booted using systemd-boot # Magic number is the vendor UUID used by systemd-boot. Loading Loading @@ -166,7 +167,9 @@ in nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; specialisation.something.configuration = {}; specialisation.something.configuration = { boot.loader.systemd-boot.sortKey = "something"; }; }; testScript = '' Loading @@ -179,6 +182,9 @@ in machine.succeed( "grep -q 'title NixOS (something)' /boot/loader/entries/nixos-generation-1-specialisation-something.conf" ) machine.succeed( "grep 'sort-key something' /boot/loader/entries/nixos-generation-1-specialisation-something.conf" ) ''; }; Loading Loading @@ -256,25 +262,25 @@ in }; testScript = '' machine.succeed("test -e /boot/loader/entries/o_netbootxyz.conf") machine.succeed("test -e /boot/loader/entries/netbootxyz.conf") machine.succeed("test -e /boot/efi/netbootxyz/netboot.xyz.efi") ''; }; entryFilename = makeTest { name = "systemd-boot-entry-filename"; memtestSortKey = makeTest { name = "systemd-boot-memtest-sortkey"; meta.maintainers = with pkgs.lib.maintainers; [ Enzime julienmalka ]; nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.systemd-boot.memtest86.enable = true; boot.loader.systemd-boot.memtest86.entryFilename = "apple.conf"; boot.loader.systemd-boot.memtest86.sortKey = "apple"; }; testScript = '' machine.fail("test -e /boot/loader/entries/memtest86.conf") machine.succeed("test -e /boot/loader/entries/apple.conf") machine.succeed("test -e /boot/loader/entries/memtest86.conf") machine.succeed("test -e /boot/efi/memtest86/memtest.efi") machine.succeed("grep 'sort-key apple' /boot/loader/entries/memtest86.conf") ''; }; Loading @@ -285,7 +291,6 @@ in nodes.machine = { pkgs, lib, ... }: { imports = [ commonXbootldr ]; boot.loader.systemd-boot.memtest86.enable = true; boot.loader.systemd-boot.memtest86.entryFilename = "apple.conf"; }; testScript = { nodes, ... }: '' Loading @@ -295,8 +300,7 @@ in machine.wait_for_unit("multi-user.target") machine.succeed("test -e /efi/EFI/systemd/systemd-bootx64.efi") machine.fail("test -e /boot/loader/entries/memtest86.conf") machine.succeed("test -e /boot/loader/entries/apple.conf") machine.succeed("test -e /boot/loader/entries/memtest86.conf") machine.succeed("test -e /boot/EFI/memtest86/memtest.efi") ''; }; Loading Loading @@ -388,9 +392,9 @@ in machine.succeed("${finalSystem}/bin/switch-to-configuration boot") machine.fail("test -e /boot/efi/fruits/tomato.efi") machine.fail("test -e /boot/efi/nixos/.extra-files/efi/fruits/tomato.efi") machine.succeed("test -e /boot/loader/entries/o_netbootxyz.conf") machine.succeed("test -e /boot/loader/entries/netbootxyz.conf") machine.succeed("test -e /boot/efi/netbootxyz/netboot.xyz.efi") machine.succeed("test -e /boot/efi/nixos/.extra-files/loader/entries/o_netbootxyz.conf") machine.succeed("test -e /boot/efi/nixos/.extra-files/loader/entries/netbootxyz.conf") machine.succeed("test -e /boot/efi/nixos/.extra-files/efi/netbootxyz/netboot.xyz.efi") ''; }; Loading