diff options
author | Alyssa Ross <hi@alyssa.is> | 2024-03-01 11:40:12 +0100 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2024-03-01 11:40:12 +0100 |
commit | bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86 (patch) | |
tree | 8eb035cbab19794f6415cc460fac7226f7a58afc /nixpkgs/nixos | |
parent | 66f707d69f1e423db5a35c2fe43b32781125a9af (diff) | |
parent | 09c1497ce5d4ed4a0edfdd44450d3048074cb300 (diff) | |
download | nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.gz nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.bz2 nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.lz nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.xz nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.zst nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.zip |
Merge branch 'nixos-unstable-small' of https://github.com/NixOS/nixpkgs
Diffstat (limited to 'nixpkgs/nixos')
60 files changed, 2124 insertions, 361 deletions
diff --git a/nixpkgs/nixos/doc/manual/development/settings-options.section.md b/nixpkgs/nixos/doc/manual/development/settings-options.section.md index 3a4800742b04..71ec9bbc8892 100644 --- a/nixpkgs/nixos/doc/manual/development/settings-options.section.md +++ b/nixpkgs/nixos/doc/manual/development/settings-options.section.md @@ -73,6 +73,34 @@ have a predefined type and string generator already declared under It returns a set with INI-specific attributes `type` and `generate` as specified [below](#pkgs-formats-result). + The type of the input is an *attrset* of sections; key-value pairs where + the key is the section name and the value is the corresponding content + which is also an *attrset* of key-value pairs for the actual key-value + mappings of the INI format. + The values of the INI atoms are subject to the above parameters (e.g. lists + may be transformed into multiple key-value pairs depending on + `listToValue`). + +`pkgs.formats.iniWithGlobalSection` { *`listsAsDuplicateKeys`* ? false, *`listToValue`* ? null, \.\.\. } + +: A function taking an attribute set with values + + `listsAsDuplicateKeys` + + : A boolean for controlling whether list values can be used to + represent duplicate INI keys + + `listToValue` + + : A function for turning a list of values into a single value. + + It returns a set with INI-specific attributes `type` and `generate` + as specified [below](#pkgs-formats-result). + The type of the input is an *attrset* of the structure + `{ sections = {}; globalSection = {}; }` where *sections* are several + sections as with *pkgs.formats.ini* and *globalSection* being just a single + attrset of key-value pairs for a single section, the global section which + preceedes the section definitions. `pkgs.formats.toml` { } diff --git a/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md b/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md index b5973c19a2c4..510019b58658 100644 --- a/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md +++ b/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md @@ -16,6 +16,8 @@ In addition to numerous new and upgraded packages, this release has the followin - `linuxPackages_testing_bcachefs` is now fully deprecated by `linuxPackages_latest`, and is therefore no longer available. +- The default kernel package has been updated from 6.1 to 6.6. All supported kernels remain available. + - NixOS now installs a stub ELF loader that prints an informative error message when users attempt to run binaries not made for NixOS. - This can be disabled through the `environment.stub-ld.enable` option. - If you use `programs.nix-ld.enable`, no changes are needed. The stub will be disabled automatically. @@ -50,6 +52,8 @@ In addition to numerous new and upgraded packages, this release has the followin } ``` +- Plasma 6 is now available and can be installed with `services.xserver.desktopManager.plasma6.enable = true;`. Plasma 5 will likely be deprecated in the next release (24.11). Note that Plasma 6 runs as Wayland by default, and the X11 session needs to be explicitly selected if necessary. + ## New Services {#sec-release-24.05-new-services} <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. --> @@ -85,8 +89,12 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m - [go-camo](https://github.com/cactus/go-camo), a secure image proxy server. Available as [services.go-camo](#opt-services.go-camo.enable). +- [Monado](https://monado.freedesktop.org/), an open source XR runtime. Available as [services.monado](#opt-services.monado.enable). + - [Clevis](https://github.com/latchset/clevis), a pluggable framework for automated decryption, used to unlock encrypted devices in initrd. Available as [boot.initrd.clevis.enable](#opt-boot.initrd.clevis.enable). +- [armagetronad](https://wiki.armagetronad.org), a mid-2000s 3D lightcycle game widely played at iD Tech Camps. You can define multiple servers using `services.armagetronad.<server>.enable`. + - [TuxClocker](https://github.com/Lurkki14/tuxclocker), a hardware control and monitoring program. Available as [programs.tuxclocker](#opt-programs.tuxclocker.enable). - [ALVR](https://github.com/alvr-org/alvr), a VR desktop streamer. Available as [programs.alvr](#opt-programs.alvr.enable) @@ -97,11 +105,13 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m - [systemd-lock-handler](https://git.sr.ht/~whynothugo/systemd-lock-handler/), a bridge between logind D-Bus events and systemd targets. Available as [services.systemd-lock-handler.enable](#opt-services.systemd-lock-handler.enable). +- [Mealie](https://nightly.mealie.io/), a self-hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in NuxtJS for a pleasant user experience for the whole family. Available as [services.mealie](#opt-services.mealie.enable) + ## Backward Incompatibilities {#sec-release-24.05-incompatibilities} <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. --> -- `himalaya` was updated to v1.0.0-beta, which introduces breaking changes. Check out the [release note](https://github.com/soywod/himalaya/releases/tag/v1.0.0-beta) for details. +- `himalaya` was updated to `v1.0.0-beta.3`, which introduces breaking changes. Check out the [release note](https://github.com/soywod/himalaya/releases/tag/v1.0.0-beta.3) for details. - The `power.ups` module now generates `upsd.conf`, `upsd.users` and `upsmon.conf` automatically from a set of new configuration options. This breaks compatibility with existing `power.ups` setups where these files were created manually. Back up these files before upgrading NixOS. @@ -277,6 +287,10 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m - Cinnamon has been updated to 6.0. Please beware that the [Wayland session](https://blog.linuxmint.com/?p=4591) is still experimental in this release. +- New `boot.loader.systemd-boot.xbootldrMountPoint` allows setting up a separate [XBOOTLDR partition](https://uapi-group.org/specifications/specs/boot_loader_specification/) to store boot files. Useful on systems with a small EFI System partition that cannot be easily repartitioned. + +- `boot.loader.systemd-boot` will now verify that `efiSysMountPoint` (and `xbootldrMountPoint` if configured) are mounted partitions. + - `services.postgresql.extraPlugins` changed its type from just a list of packages to also a function that returns such a list. For example a config line like ``services.postgresql.extraPlugins = with pkgs.postgresql_11.pkgs; [ postgis ];`` is recommended to be changed to ``services.postgresql.extraPlugins = ps: with ps; [ postgis ];``; @@ -378,6 +392,11 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m - The `mpich` package expression now requires `withPm` to be a list, e.g. `"hydra:gforker"` becomes `[ "hydra" "gforker" ]`. +- When merging systemd unit options (of type `unitOption`), + if at least one definition is a list, all those which aren't are now lifted into a list, + making it possible to accumulate definitions without resorting to `mkForce`, + hence to retain the definitions not anticipating that need. + - YouTrack is bumped to 2023.3. The update is not performed automatically, it requires manual interaction. See the YouTrack section in the manual for details. - QtMultimedia has changed its default backend to `QT_MEDIA_BACKEND=ffmpeg` (previously `gstreamer` on Linux or `darwin` on MacOS). diff --git a/nixpkgs/nixos/lib/make-disk-image.nix b/nixpkgs/nixos/lib/make-disk-image.nix index 047e72e2ac0d..da94ef16654c 100644 --- a/nixpkgs/nixos/lib/make-disk-image.nix +++ b/nixpkgs/nixos/lib/make-disk-image.nix @@ -56,6 +56,14 @@ This partition table type uses GPT and: - creates an FAT32 ESP partition from 8MiB to specified `bootSize` parameter (256MiB by default), set it bootable ; - creates an primary ext4 partition starting after the boot partition and extending to the full disk image +#### `efixbootldr` + +This partition table type uses GPT and: + +- creates an FAT32 ESP partition from 8MiB to 100MiB, set it bootable ; +- creates an FAT32 BOOT partition from 100MiB to specified `bootSize` parameter (256MiB by default), set `bls_boot` flag ; +- creates an primary ext4 partition starting after the boot partition and extending to the full disk image + #### `hybrid` This partition table type uses GPT and: @@ -111,19 +119,7 @@ To solve this, you can run `fdisk -l $image` and generate `dd if=$image of=$imag # When setting one of `user' or `group', the other needs to be set too. contents ? [] -, # Type of partition table to use; either "legacy", "efi", or "none". - # For "efi" images, the GPT partition table is used and a mandatory ESP - # partition of reasonable size is created in addition to the root partition. - # For "legacy", the msdos partition table is used and a single large root - # partition is created. - # For "legacy+gpt", the GPT partition table is used, a 1MiB no-fs partition for - # use by the bootloader is created, and a single large root partition is - # created. - # For "hybrid", the GPT partition table is used and a mandatory ESP - # partition of reasonable size is created in addition to the root partition. - # Also a legacy MBR will be present. - # For "none", no partition table is created. Enabling `installBootLoader` - # most likely fails as GRUB will probably refuse to install. +, # Type of partition table to use; described in the `Image Partitioning` section above. partitionTableType ? "legacy" , # Whether to invoke `switch-to-configuration boot` during image creation @@ -193,11 +189,11 @@ To solve this, you can run `fdisk -l $image` and generate `dd if=$image of=$imag additionalPaths ? [] }: -assert (lib.assertOneOf "partitionTableType" partitionTableType [ "legacy" "legacy+gpt" "efi" "hybrid" "none" ]); +assert (lib.assertOneOf "partitionTableType" partitionTableType [ "legacy" "legacy+gpt" "efi" "efixbootldr" "hybrid" "none" ]); assert (lib.assertMsg (fsType == "ext4" && deterministic -> rootFSUID != null) "In deterministic mode with a ext4 partition, rootFSUID must be non-null, by default, it is equal to rootGPUID."); # We use -E offset=X below, which is only supported by e2fsprogs assert (lib.assertMsg (partitionTableType != "none" -> fsType == "ext4") "to produce a partition table, we need to use -E offset flag which is support only for fsType = ext4"); -assert (lib.assertMsg (touchEFIVars -> partitionTableType == "hybrid" || partitionTableType == "efi" || partitionTableType == "legacy+gpt") "EFI variables can be used only with a partition table of type: hybrid, efi or legacy+gpt."); +assert (lib.assertMsg (touchEFIVars -> partitionTableType == "hybrid" || partitionTableType == "efi" || partitionTableType == "efixbootldr" || partitionTableType == "legacy+gpt") "EFI variables can be used only with a partition table of type: hybrid, efi, efixbootldr, or legacy+gpt."); # If only Nix store image, then: contents must be empty, configFile must be unset, and we should no install bootloader. assert (lib.assertMsg (onlyNixStore -> contents == [] && configFile == null && !installBootLoader) "In a only Nix store image, the contents must be empty, no configuration must be provided and no bootloader should be installed."); # Either both or none of {user,group} need to be set @@ -225,6 +221,7 @@ let format' = format; in let legacy = "1"; "legacy+gpt" = "2"; efi = "2"; + efixbootldr = "3"; hybrid = "3"; }.${partitionTableType}; @@ -266,6 +263,23 @@ let format' = format; in let $diskImage ''} ''; + efixbootldr = '' + parted --script $diskImage -- \ + mklabel gpt \ + mkpart ESP fat32 8MiB 100MiB \ + set 1 boot on \ + mkpart BOOT fat32 100MiB ${bootSize} \ + set 2 bls_boot on \ + mkpart ROOT ext4 ${bootSize} -1 + ${optionalString deterministic '' + sgdisk \ + --disk-guid=97FD5997-D90B-4AA3-8D16-C1723AEA73C \ + --partition-guid=1:1C06F03B-704E-4657-B9CD-681A087A2FDC \ + --partition-guid=2:970C694F-AFD0-4B99-B750-CDB7A329AB6F \ + --partition-guid=3:${rootGPUID} \ + $diskImage + ''} + ''; hybrid = '' parted --script $diskImage -- \ mklabel gpt \ @@ -436,7 +450,7 @@ let format' = format; in let diskImage=nixos.raw ${if diskSize == "auto" then '' - ${if partitionTableType == "efi" || partitionTableType == "hybrid" then '' + ${if partitionTableType == "efi" || partitionTableType == "efixbootldr" || partitionTableType == "hybrid" then '' # Add the GPT at the end gptSpace=$(( 512 * 34 * 1 )) # Normally we'd need to account for alignment and things, if bootSize @@ -570,6 +584,15 @@ let format' = format; in let ${optionalString touchEFIVars "mount -t efivarfs efivarfs /sys/firmware/efi/efivars"} ''} + ${optionalString (partitionTableType == "efixbootldr") '' + mkdir -p /mnt/{boot,efi} + mkfs.vfat -n ESP /dev/vda1 + mkfs.vfat -n BOOT /dev/vda2 + mount /dev/vda1 /mnt/efi + mount /dev/vda2 /mnt/boot + + ${optionalString touchEFIVars "mount -t efivarfs efivarfs /sys/firmware/efi/efivars"} + ''} # Install a configuration.nix mkdir -p /mnt/etc/nixos diff --git a/nixpkgs/nixos/lib/systemd-lib.nix b/nixpkgs/nixos/lib/systemd-lib.nix index c9cca619ed70..ef218e674ebf 100644 --- a/nixpkgs/nixos/lib/systemd-lib.nix +++ b/nixpkgs/nixos/lib/systemd-lib.nix @@ -378,7 +378,7 @@ in rec { ''; targetToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy enable overrideStrategy; + { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = '' [Unit] @@ -387,7 +387,7 @@ in rec { }; serviceToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy enable overrideStrategy; + { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = commonUnitText def ('' [Service] '' + (let env = cfg.globalEnvironment // def.environment; @@ -408,7 +408,7 @@ in rec { }; socketToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy enable overrideStrategy; + { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = commonUnitText def '' [Socket] ${attrsToSection def.socketConfig} @@ -418,7 +418,7 @@ in rec { }; timerToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy enable overrideStrategy; + { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = commonUnitText def '' [Timer] ${attrsToSection def.timerConfig} @@ -426,7 +426,7 @@ in rec { }; pathToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy enable overrideStrategy; + { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = commonUnitText def '' [Path] ${attrsToSection def.pathConfig} @@ -434,7 +434,7 @@ in rec { }; mountToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy enable overrideStrategy; + { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = commonUnitText def '' [Mount] ${attrsToSection def.mountConfig} @@ -442,7 +442,7 @@ in rec { }; automountToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy enable overrideStrategy; + { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = commonUnitText def '' [Automount] ${attrsToSection def.automountConfig} @@ -450,7 +450,7 @@ in rec { }; sliceToUnit = name: def: - { inherit (def) aliases wantedBy requiredBy enable overrideStrategy; + { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy; text = commonUnitText def '' [Slice] ${attrsToSection def.sliceConfig} diff --git a/nixpkgs/nixos/lib/systemd-unit-options.nix b/nixpkgs/nixos/lib/systemd-unit-options.nix index bc7880da9fe0..e4953ba72dd9 100644 --- a/nixpkgs/nixos/lib/systemd-unit-options.nix +++ b/nixpkgs/nixos/lib/systemd-unit-options.nix @@ -21,14 +21,8 @@ in rec { let defs' = filterOverrides defs; in - if isList (head defs').value - then concatMap (def: - if builtins.typeOf def.value == "list" - then def.value - else - throw "The definitions for systemd unit options should be either all lists, representing repeatable options, or all non-lists, but for the option ${showOption loc}, the definitions are a mix of list and non-list ${lib.options.showDefs defs'}" - ) defs' - + if any (def: isList def.value) defs' + then concatMap (def: toList def.value) defs' else mergeEqualOption loc defs'; }; diff --git a/nixpkgs/nixos/lib/test-driver/test_driver/driver.py b/nixpkgs/nixos/lib/test-driver/test_driver/driver.py index 786821b0cc0d..72a33e0b2d57 100644 --- a/nixpkgs/nixos/lib/test-driver/test_driver/driver.py +++ b/nixpkgs/nixos/lib/test-driver/test_driver/driver.py @@ -12,6 +12,8 @@ from test_driver.machine import Machine, NixStartScript, retry from test_driver.polling_condition import PollingCondition from test_driver.vlan import VLan +SENTINEL = object() + def get_tmp_dir() -> Path: """Returns a temporary directory that is defined by TMPDIR, TEMP, TMP or CWD @@ -187,23 +189,58 @@ class Driver: # to swallow them and prevent itself from terminating. os.kill(os.getpid(), signal.SIGTERM) - def create_machine(self, args: Dict[str, Any]) -> Machine: + def create_machine( + self, + start_command: str | dict, + *, + name: Optional[str] = None, + keep_vm_state: bool = False, + ) -> Machine: + # Legacy args handling + # FIXME: remove after 24.05 + if isinstance(start_command, dict): + if name is not None or keep_vm_state: + raise TypeError( + "Dictionary passed to create_machine must be the only argument" + ) + + args = start_command + start_command = args.pop("startCommand", SENTINEL) + + if start_command is SENTINEL: + raise TypeError( + "Dictionary passed to create_machine must contain startCommand" + ) + + if not isinstance(start_command, str): + raise TypeError( + f"startCommand must be a string, got: {repr(start_command)}" + ) + + name = args.pop("name", None) + keep_vm_state = args.pop("keep_vm_state", False) + + if args: + raise TypeError( + f"Unsupported arguments passed to create_machine: {args}" + ) + + rootlog.warning( + "Using create_machine with a single dictionary argument is deprecated, and will be removed in NixOS 24.11" + ) + # End legacy args handling + tmp_dir = get_tmp_dir() - if args.get("startCommand"): - start_command: str = args.get("startCommand", "") - cmd = NixStartScript(start_command) - name = args.get("name", cmd.machine_name) - else: - cmd = Machine.create_startcommand(args) # type: ignore - name = args.get("name", "machine") + cmd = NixStartScript(start_command) + name = name or cmd.machine_name return Machine( tmp_dir=tmp_dir, out_dir=self.out_dir, start_command=cmd, name=name, - keep_vm_state=args.get("keep_vm_state", False), + keep_vm_state=keep_vm_state, ) def serial_stdout_on(self) -> None: diff --git a/nixpkgs/nixos/lib/test-driver/test_driver/machine.py b/nixpkgs/nixos/lib/test-driver/test_driver/machine.py index 93411a4a348e..df8628bce956 100644 --- a/nixpkgs/nixos/lib/test-driver/test_driver/machine.py +++ b/nixpkgs/nixos/lib/test-driver/test_driver/machine.py @@ -208,7 +208,6 @@ class StartCommand: ), stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, shell=True, cwd=state_dir, env=self.build_environment(state_dir, shared_dir), @@ -235,77 +234,6 @@ class NixStartScript(StartCommand): return name -class LegacyStartCommand(StartCommand): - """Used in some places to create an ad-hoc machine instead of - using nix test instrumentation + module system for that purpose. - Legacy. - """ - - def __init__( - self, - netBackendArgs: Optional[str] = None, # noqa: N803 - netFrontendArgs: Optional[str] = None, # noqa: N803 - hda: Optional[Tuple[Path, str]] = None, - cdrom: Optional[str] = None, - usb: Optional[str] = None, - bios: Optional[str] = None, - qemuBinary: Optional[str] = None, # noqa: N803 - qemuFlags: Optional[str] = None, # noqa: N803 - ): - if qemuBinary is not None: - self._cmd = qemuBinary - else: - self._cmd = "qemu-kvm" - - self._cmd += " -m 384" - - # networking - net_backend = "-netdev user,id=net0" - net_frontend = "-device virtio-net-pci,netdev=net0" - if netBackendArgs is not None: - net_backend += "," + netBackendArgs - if netFrontendArgs is not None: - net_frontend += "," + netFrontendArgs - self._cmd += f" {net_backend} {net_frontend}" - - # hda - hda_cmd = "" - if hda is not None: - hda_path = hda[0].resolve() - hda_interface = hda[1] - if hda_interface == "scsi": - hda_cmd += ( - f" -drive id=hda,file={hda_path},werror=report,if=none" - " -device scsi-hd,drive=hda" - ) - else: - hda_cmd += f" -drive file={hda_path},if={hda_interface},werror=report" - self._cmd += hda_cmd - - # cdrom - if cdrom is not None: - self._cmd += f" -cdrom {cdrom}" - - # usb - usb_cmd = "" - if usb is not None: - # https://github.com/qemu/qemu/blob/master/docs/usb2.txt - usb_cmd += ( - " -device usb-ehci" - f" -drive id=usbdisk,file={usb},if=none,readonly" - " -device usb-storage,drive=usbdisk " - ) - self._cmd += usb_cmd - - # bios - if bios is not None: - self._cmd += f" -bios {bios}" - - # qemu flags - if qemuFlags is not None: - self._cmd += f" {qemuFlags}" - - class Machine: """A handle to the machine with this name, that also knows how to manage the machine lifecycle with the help of a start script / command.""" @@ -377,29 +305,6 @@ class Machine: self.booted = False self.connected = False - @staticmethod - def create_startcommand(args: Dict[str, str]) -> StartCommand: - rootlog.warning( - "Using legacy create_startcommand(), " - "please use proper nix test vm instrumentation, instead " - "to generate the appropriate nixos test vm qemu startup script" - ) - hda = None - if args.get("hda"): - hda_arg: str = args.get("hda", "") - hda_arg_path: Path = Path(hda_arg) - hda = (hda_arg_path, args.get("hdaInterface", "")) - return LegacyStartCommand( - netBackendArgs=args.get("netBackendArgs"), - netFrontendArgs=args.get("netFrontendArgs"), - hda=hda, - cdrom=args.get("cdrom"), - usb=args.get("usb"), - bios=args.get("bios"), - qemuBinary=args.get("qemuBinary"), - qemuFlags=args.get("qemuFlags"), - ) - def is_up(self) -> bool: return self.booted and self.connected diff --git a/nixpkgs/nixos/lib/test-script-prepend.py b/nixpkgs/nixos/lib/test-script-prepend.py index 15e59ce01047..976992ea0015 100644 --- a/nixpkgs/nixos/lib/test-script-prepend.py +++ b/nixpkgs/nixos/lib/test-script-prepend.py @@ -26,6 +26,17 @@ class PollingConditionProtocol(Protocol): raise Exception("This is just type information for the Nix test driver") +class CreateMachineProtocol(Protocol): + def __call__( + self, + start_command: str | dict, + *, + name: Optional[str] = None, + keep_vm_state: bool = False, + ) -> Machine: + raise Exception("This is just type information for the Nix test driver") + + start_all: Callable[[], None] subtest: Callable[[str], ContextManager[None]] retry: RetryProtocol @@ -34,7 +45,7 @@ machines: List[Machine] vlans: List[VLan] driver: Driver log: Logger -create_machine: Callable[[Dict[str, Any]], Machine] +create_machine: CreateMachineProtocol run_tests: Callable[[], None] join_all: Callable[[], None] serial_stdout_off: Callable[[], None] diff --git a/nixpkgs/nixos/lib/testing/nixos-test-base.nix b/nixpkgs/nixos/lib/testing/nixos-test-base.nix index 59e6e3843367..d76a25361f8c 100644 --- a/nixpkgs/nixos/lib/testing/nixos-test-base.nix +++ b/nixpkgs/nixos/lib/testing/nixos-test-base.nix @@ -16,7 +16,11 @@ in # The human version (e.g. 21.05-pre) is left as is, because it is useful # for external modules that test with e.g. testers.nixosTest and rely on that # version number. - config.system.nixos.revision = mkForce "constant-nixos-revision"; + config.system.nixos = { + revision = mkForce "constant-nixos-revision"; + versionSuffix = mkForce "test"; + label = mkForce "test"; + }; } ]; diff --git a/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix b/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix new file mode 100644 index 000000000000..11118db3aae2 --- /dev/null +++ b/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix @@ -0,0 +1,46 @@ +# This module defines a NixOS installation CD that contains Plasma 6. + +{ pkgs, ... }: + +{ + imports = [ ./installation-cd-graphical-calamares.nix ]; + + isoImage.edition = "plasma6"; + + services.xserver = { + desktopManager.plasma6.enable = true; + + # Automatically login as nixos. + displayManager = { + sddm.enable = true; + autoLogin = { + enable = true; + user = "nixos"; + }; + }; + }; + + environment.systemPackages = [ + # FIXME: using Qt5 builds of Maliit as upstream has not ported to Qt6 yet + pkgs.maliit-framework + pkgs.maliit-keyboard + ]; + + system.activationScripts.installerDesktop = let + + # Comes from documentation.nix when xserver and nixos.enable are true. + manualDesktopFile = "/run/current-system/sw/share/applications/nixos-manual.desktop"; + + homeDir = "/home/nixos/"; + desktopDir = homeDir + "Desktop/"; + + in '' + mkdir -p ${desktopDir} + chown nixos ${homeDir} ${desktopDir} + + ln -sfT ${manualDesktopFile} ${desktopDir + "nixos-manual.desktop"} + ln -sfT ${pkgs.gparted}/share/applications/gparted.desktop ${desktopDir + "gparted.desktop"} + ln -sfT ${pkgs.calamares-nixos}/share/applications/io.calamares.calamares.desktop ${desktopDir + "io.calamares.calamares.desktop"} + ''; + +} diff --git a/nixpkgs/nixos/modules/installer/netboot/netboot.nix b/nixpkgs/nixos/modules/installer/netboot/netboot.nix index a50f22cbe471..028a2d74041e 100644 --- a/nixpkgs/nixos/modules/installer/netboot/netboot.nix +++ b/nixpkgs/nixos/modules/installer/netboot/netboot.nix @@ -62,19 +62,12 @@ with lib; }; fileSystems."/nix/store" = mkImageMediaOverride - { fsType = "overlay"; - device = "overlay"; - options = [ - "lowerdir=/nix/.ro-store" - "upperdir=/nix/.rw-store/store" - "workdir=/nix/.rw-store/work" - ]; - - depends = [ - "/nix/.ro-store" - "/nix/.rw-store/store" - "/nix/.rw-store/work" - ]; + { overlay = { + lowerdir = [ "/nix/.ro-store" ]; + upperdir = "/nix/.rw-store/store"; + workdir = "/nix/.rw-store/work"; + }; + neededForBoot = true; }; boot.initrd.availableKernelModules = [ "squashfs" "overlay" ]; diff --git a/nixpkgs/nixos/modules/module-list.nix b/nixpkgs/nixos/modules/module-list.nix index 5d82c6de77e2..08144140b830 100644 --- a/nixpkgs/nixos/modules/module-list.nix +++ b/nixpkgs/nixos/modules/module-list.nix @@ -512,6 +512,7 @@ ./services/editors/infinoted.nix ./services/finance/odoo.nix ./services/games/archisteamfarm.nix + ./services/games/armagetronad.nix ./services/games/crossfire-server.nix ./services/games/deliantra-server.nix ./services/games/factorio.nix @@ -549,6 +550,7 @@ ./services/hardware/lcd.nix ./services/hardware/lirc.nix ./services/hardware/nvidia-container-toolkit-cdi-generator + ./services/hardware/monado.nix ./services/hardware/nvidia-optimus.nix ./services/hardware/openrgb.nix ./services/hardware/pcscd.nix @@ -1324,6 +1326,7 @@ ./services/web-apps/mastodon.nix ./services/web-apps/matomo.nix ./services/web-apps/mattermost.nix + ./services/web-apps/mealie.nix ./services/web-apps/mediawiki.nix ./services/web-apps/meme-bingo-web.nix ./services/web-apps/microbin.nix diff --git a/nixpkgs/nixos/modules/programs/gnupg.nix b/nixpkgs/nixos/modules/programs/gnupg.nix index 8f82de033666..179d2de87cc5 100644 --- a/nixpkgs/nixos/modules/programs/gnupg.nix +++ b/nixpkgs/nixos/modules/programs/gnupg.nix @@ -15,6 +15,7 @@ let defaultPinentryFlavor = if xserverCfg.desktopManager.lxqt.enable || xserverCfg.desktopManager.plasma5.enable + || xserverCfg.desktopManager.plasma6.enable || xserverCfg.desktopManager.deepin.enable then "qt" else if xserverCfg.desktopManager.xfce.enable then diff --git a/nixpkgs/nixos/modules/programs/wayland/sway.nix b/nixpkgs/nixos/modules/programs/wayland/sway.nix index 57ee629b2881..ca2503ae5da7 100644 --- a/nixpkgs/nixos/modules/programs/wayland/sway.nix +++ b/nixpkgs/nixos/modules/programs/wayland/sway.nix @@ -119,10 +119,10 @@ in { extraPackages = mkOption { type = with types; listOf package; default = with pkgs; [ - swaylock swayidle foot dmenu + swaylock swayidle foot dmenu wmenu ]; defaultText = literalExpression '' - with pkgs; [ swaylock swayidle foot dmenu ]; + with pkgs; [ swaylock swayidle foot dmenu wmenu ]; ''; example = literalExpression '' with pkgs; [ diff --git a/nixpkgs/nixos/modules/security/pam.nix b/nixpkgs/nixos/modules/security/pam.nix index ed03254cb5ee..b87e22b23980 100644 --- a/nixpkgs/nixos/modules/security/pam.nix +++ b/nixpkgs/nixos/modules/security/pam.nix @@ -96,6 +96,10 @@ let pamOpts = { config, name, ... }: let cfg = config; in let config = parentConfig; in { + imports = [ + (lib.mkRenamedOptionModule [ "enableKwallet" ] [ "kwallet" "enable" ]) + ]; + options = { name = mkOption { @@ -462,16 +466,23 @@ let ''; }; - enableKwallet = mkOption { - default = false; - type = types.bool; - description = lib.mdDoc '' - If enabled, pam_wallet will attempt to automatically unlock the - user's default KDE wallet upon login. If the user has no wallet named - "kdewallet", or the login password does not match their wallet - password, KDE will prompt separately after login. - ''; + kwallet = { + enable = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + If enabled, pam_wallet will attempt to automatically unlock the + user's default KDE wallet upon login. If the user has no wallet named + "kdewallet", or the login password does not match their wallet + password, KDE will prompt separately after login. + ''; + }; + + package = mkPackageOption pkgs.plasma5Packages "kwallet-pam" { + pkgsText = "pkgs.plasma5Packages"; + }; }; + sssdStrictAccess = mkOption { default = false; type = types.bool; @@ -686,7 +697,7 @@ let (config.security.pam.enableEcryptfs || config.security.pam.enableFscrypt || cfg.pamMount - || cfg.enableKwallet + || cfg.kwallet.enable || cfg.enableGnomeKeyring || config.services.intune.enable || cfg.googleAuthenticator.enable @@ -711,9 +722,7 @@ let { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; settings = { disable_interactive = true; }; } - { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; settings = { - kwalletd = "${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5"; - }; } + { name = "kwallet"; enable = cfg.kwallet.enable; control = "optional"; modulePath = "${cfg.kwallet.package}/lib/security/pam_kwallet5.so"; } { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; } { name = "intune"; enable = config.services.intune.enable; control = "optional"; modulePath = "${pkgs.intune-portal}/lib/security/pam_intune.so"; } { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = { @@ -848,9 +857,7 @@ let order = "user,group,default"; debug = true; }; } - { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; settings = { - kwalletd = "${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5"; - }; } + { name = "kwallet"; enable = cfg.kwallet.enable; control = "optional"; modulePath = "${cfg.kwallet.package}/lib/security/pam_kwallet5.so"; } { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; settings = { auto_start = true; }; } diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix index aa24c0842bab..8f3ad78d50ce 100644 --- a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix +++ b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix @@ -293,6 +293,18 @@ in { assertion = (cfg.alsa.enable || cfg.pulse.enable) -> cfg.audio.enable; message = "Using PipeWire's ALSA/PulseAudio compatibility layers requires running PipeWire as the sound server. Set `services.pipewire.audio.enable` to true."; } + { + assertion = builtins.length + (builtins.attrNames + ( + lib.filterAttrs + (name: value: + lib.hasPrefix "pipewire/" name || name == "pipewire" + ) + config.environment.etc + )) == 1; + message = "Using `environment.etc.\"pipewire<...>\"` directly is no longer supported in 24.05. Use `services.pipewire.extraConfig` or `services.pipewire.configPackages` instead."; + } ]; environment.systemPackages = [ cfg.package ] diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/wireplumber.nix b/nixpkgs/nixos/modules/services/desktops/pipewire/wireplumber.nix index dc4d726d7632..99aea8facb16 100644 --- a/nixpkgs/nixos/modules/services/desktops/pipewire/wireplumber.nix +++ b/nixpkgs/nixos/modules/services/desktops/pipewire/wireplumber.nix @@ -56,13 +56,13 @@ in -- PipeWire is not used for audio, so prevent it from grabbing audio devices alsa_monitor.enable = function() end ''; - systemwideConfigPkg = pkgs.writeTextDir "wireplumber/main.lua.d/80-systemwide.lua" '' + systemwideConfigPkg = pkgs.writeTextDir "share/wireplumber/main.lua.d/80-systemwide.lua" '' -- When running system-wide, these settings need to be disabled (they -- use functions that aren't available on the system dbus). alsa_monitor.properties["alsa.reserve"] = false default_access.properties["enable-flatpak-portal"] = false ''; - systemwideBluetoothConfigPkg = pkgs.writeTextDir "wireplumber/bluetooth.lua.d/80-systemwide.lua" '' + systemwideBluetoothConfigPkg = pkgs.writeTextDir "share/wireplumber/bluetooth.lua.d/80-systemwide.lua" '' -- When running system-wide, logind-integration needs to be disabled. bluez_monitor.properties["with-logind"] = false ''; @@ -98,6 +98,18 @@ in assertion = !config.hardware.bluetooth.hsphfpd.enable; message = "Using WirePlumber conflicts with hsphfpd, as it provides the same functionality. `hardware.bluetooth.hsphfpd.enable` needs be set to false"; } + { + assertion = builtins.length + (builtins.attrNames + ( + lib.filterAttrs + (name: value: + lib.hasPrefix "wireplumber/" name || name == "wireplumber" + ) + config.environment.etc + )) == 1; + message = "Using `environment.etc.\"wireplumber<...>\"` directly is no longer supported in 24.05. Use `services.wireplumber.configPackages` instead."; + } ]; environment.systemPackages = [ cfg.package ]; diff --git a/nixpkgs/nixos/modules/services/games/armagetronad.nix b/nixpkgs/nixos/modules/services/games/armagetronad.nix new file mode 100644 index 000000000000..f79818e0e53b --- /dev/null +++ b/nixpkgs/nixos/modules/services/games/armagetronad.nix @@ -0,0 +1,268 @@ +{ config, lib, pkgs, ... }: +let + inherit (lib) mkEnableOption mkIf mkOption mkMerge literalExpression; + inherit (lib) mapAttrsToList filterAttrs unique recursiveUpdate types; + + mkValueStringArmagetron = with lib; v: + if isInt v then toString v + else if isFloat v then toString v + else if isString v then v + else if true == v then "1" + else if false == v then "0" + else if null == v then "" + else throw "unsupported type: ${builtins.typeOf v}: ${(lib.generators.toPretty {} v)}"; + + settingsFormat = pkgs.formats.keyValue { + mkKeyValue = lib.generators.mkKeyValueDefault + { + mkValueString = mkValueStringArmagetron; + } " "; + listsAsDuplicateKeys = true; + }; + + cfg = config.services.armagetronad; + enabledServers = lib.filterAttrs (n: v: v.enable) cfg.servers; + nameToId = serverName: "armagetronad-${serverName}"; + getStateDirectory = serverName: "armagetronad/${serverName}"; + getServerRoot = serverName: "/var/lib/${getStateDirectory serverName}"; +in +{ + options = { + services.armagetronad = { + servers = mkOption { + description = lib.mdDoc "Armagetron server definitions."; + default = { }; + type = types.attrsOf (types.submodule { + options = { + enable = mkEnableOption (lib.mdDoc "armagetronad"); + + package = lib.mkPackageOptionMD pkgs "armagetronad-dedicated" { + example = '' + pkgs.armagetronad."0.2.9-sty+ct+ap".dedicated + ''; + extraDescription = '' + Ensure that you use a derivation which contains the path `bin/armagetronad-dedicated`. + ''; + }; + + host = mkOption { + type = types.str; + default = "0.0.0.0"; + description = lib.mdDoc "Host to listen on. Used for SERVER_IP."; + }; + + port = mkOption { + type = types.port; + default = 4534; + description = lib.mdDoc "Port to listen on. Used for SERVER_PORT."; + }; + + dns = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc "DNS address to use for this server. Optional."; + }; + + openFirewall = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc "Set to true to open the configured UDP port for Armagetron Advanced."; + }; + + name = mkOption { + type = types.str; + description = "The name of this server."; + }; + + settings = mkOption { + type = settingsFormat.type; + default = { }; + description = lib.mdDoc '' + Armagetron Advanced server rules configuration. Refer to: + <https://wiki.armagetronad.org/index.php?title=Console_Commands> + or `armagetronad-dedicated --doc` for a list. + + This attrset is used to populate `settings_custom.cfg`; see: + <https://wiki.armagetronad.org/index.php/Configuration_Files> + ''; + example = literalExpression '' + { + CYCLE_RUBBER = 40; + } + ''; + }; + + roundSettings = mkOption { + type = settingsFormat.type; + default = { }; + description = lib.mdDoc '' + Armagetron Advanced server per-round configuration. Refer to: + <https://wiki.armagetronad.org/index.php?title=Console_Commands> + or `armagetronad-dedicated --doc` for a list. + + This attrset is used to populate `everytime.cfg`; see: + <https://wiki.armagetronad.org/index.php/Configuration_Files> + ''; + example = literalExpression '' + { + SAY = [ + "Hosted on NixOS" + "https://nixos.org" + "iD Tech High Rubber rul3z!! Happy New Year 2008!!1" + ]; + } + ''; + }; + }; + }); + }; + }; + }; + + config = mkIf (enabledServers != { }) { + systemd.tmpfiles.settings = mkMerge (mapAttrsToList + (serverName: serverCfg: + let + serverId = nameToId serverName; + serverRoot = getServerRoot serverName; + serverInfo = ( + { + SERVER_IP = serverCfg.host; + SERVER_PORT = serverCfg.port; + SERVER_NAME = serverCfg.name; + } // (lib.optionalAttrs (serverCfg.dns != null) { SERVER_DNS = serverCfg.dns; }) + ); + customSettings = serverCfg.settings; + everytimeSettings = serverCfg.roundSettings; + + serverInfoCfg = settingsFormat.generate "server_info.${serverName}.cfg" serverInfo; + customSettingsCfg = settingsFormat.generate "settings_custom.${serverName}.cfg" customSettings; + everytimeSettingsCfg = settingsFormat.generate "everytime.${serverName}.cfg" everytimeSettings; + in + { + "10-armagetronad-${serverId}" = { + "${serverRoot}/data" = { + d = { + group = serverId; + user = serverId; + mode = "0750"; + }; + }; + "${serverRoot}/settings" = { + d = { + group = serverId; + user = serverId; + mode = "0750"; + }; + }; + "${serverRoot}/var" = { + d = { + group = serverId; + user = serverId; + mode = "0750"; + }; + }; + "${serverRoot}/resource" = { + d = { + group = serverId; + user = serverId; + mode = "0750"; + }; + }; + "${serverRoot}/input" = { + "f+" = { + group = serverId; + user = serverId; + mode = "0640"; + }; + }; + "${serverRoot}/settings/server_info.cfg" = { + "L+" = { + argument = "${serverInfoCfg}"; + }; + }; + "${serverRoot}/settings/settings_custom.cfg" = { + "L+" = { + argument = "${customSettingsCfg}"; + }; + }; + "${serverRoot}/settings/everytime.cfg" = { + "L+" = { + argument = "${everytimeSettingsCfg}"; + }; + }; + }; + } + ) + enabledServers + ); + + systemd.services = mkMerge (mapAttrsToList + (serverName: serverCfg: + let + serverId = nameToId serverName; + in + { + "armagetronad-${serverName}" = { + description = "Armagetron Advanced Dedicated Server for ${serverName}"; + wants = [ "basic.target" ]; + after = [ "basic.target" "network.target" "multi-user.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = + let + serverRoot = getServerRoot serverName; + in + { + Type = "simple"; + StateDirectory = getStateDirectory serverName; + ExecStart = "${lib.getExe serverCfg.package} --daemon --input ${serverRoot}/input --userdatadir ${serverRoot}/data --userconfigdir ${serverRoot}/settings --vardir ${serverRoot}/var --autoresourcedir ${serverRoot}/resource"; + Restart = "on-failure"; + CapabilityBoundingSet = ""; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RestrictNamespaces = true; + RestrictSUIDSGID = true; + User = serverId; + Group = serverId; + }; + }; + }) + enabledServers + ); + + networking.firewall.allowedUDPPorts = + unique (mapAttrsToList (serverName: serverCfg: serverCfg.port) (filterAttrs (serverName: serverCfg: serverCfg.openFirewall) enabledServers)); + + users.users = mkMerge (mapAttrsToList + (serverName: serverCfg: + { + ${nameToId serverName} = { + group = nameToId serverName; + description = "Armagetron Advanced dedicated user for server ${serverName}"; + isSystemUser = true; + }; + }) + enabledServers + ); + + users.groups = mkMerge (mapAttrsToList + (serverName: serverCfg: + { + ${nameToId serverName} = { }; + }) + enabledServers + ); + }; +} diff --git a/nixpkgs/nixos/modules/services/hardware/monado.nix b/nixpkgs/nixos/modules/services/hardware/monado.nix new file mode 100644 index 000000000000..9f9c6c39a0b4 --- /dev/null +++ b/nixpkgs/nixos/modules/services/hardware/monado.nix @@ -0,0 +1,102 @@ +{ config +, lib +, pkgs +, ... +}: +let + inherit (lib) mkDefault mkEnableOption mkIf mkOption mkPackageOption types; + + cfg = config.services.monado; + +in +{ + options.services.monado = { + enable = mkEnableOption "Monado user service"; + + package = mkPackageOption pkgs "monado" { }; + + defaultRuntime = mkOption { + type = types.bool; + description = '' + Whether to enable Monado as the default OpenXR runtime on the system. + + Note that applications can bypass this option by setting an active + runtime in a writable XDG_CONFIG_DIRS location like `~/.config`. + ''; + default = false; + example = true; + }; + + highPriority = mkEnableOption "high priority capability for monado-service" + // mkOption { default = true; }; + }; + + config = mkIf cfg.enable { + security.wrappers."monado-service" = mkIf cfg.highPriority { + setuid = false; + owner = "root"; + group = "root"; + # cap_sys_nice needed for asynchronous reprojection + capabilities = "cap_sys_nice+eip"; + source = lib.getExe' cfg.package "monado-service"; + }; + + services.udev.packages = with pkgs; [ xr-hardware ]; + + systemd.user = { + services.monado = { + description = "Monado XR runtime service module"; + requires = [ "monado.socket" ]; + conflicts = [ "monado-dev.service" ]; + + unitConfig.ConditionUser = "!root"; + + environment = { + # Default options + # https://gitlab.freedesktop.org/monado/monado/-/blob/4548e1738591d0904f8db4df8ede652ece889a76/src/xrt/targets/service/monado.in.service#L12 + XRT_COMPOSITOR_LOG = mkDefault "debug"; + XRT_PRINT_OPTIONS = mkDefault "on"; + IPC_EXIT_ON_DISCONNECT = mkDefault "off"; + }; + + serviceConfig = { + ExecStart = + if cfg.highPriority + then "${config.security.wrapperDir}/monado-service" + else lib.getExe' cfg.package "monado-service"; + Restart = "no"; + }; + + restartTriggers = [ cfg.package ]; + }; + + sockets.monado = { + description = "Monado XR service module connection socket"; + conflicts = [ "monado-dev.service" ]; + + unitConfig.ConditionUser = "!root"; + + socketConfig = { + ListenStream = "%t/monado_comp_ipc"; + RemoveOnStop = true; + + # If Monado crashes while starting up, we want to close incoming OpenXR connections + FlushPending = true; + }; + + restartTriggers = [ cfg.package ]; + + wantedBy = [ "sockets.target" ]; + }; + }; + + environment.systemPackages = [ cfg.package ]; + environment.pathsToLink = [ "/share/openxr" ]; + + environment.etc."xdg/openxr/1/active_runtime.json" = mkIf cfg.defaultRuntime { + source = "${cfg.package}/share/openxr/1/openxr_monado.json"; + }; + }; + + meta.maintainers = with lib.maintainers; [ Scrumplex ]; +} diff --git a/nixpkgs/nixos/modules/services/misc/ollama.nix b/nixpkgs/nixos/modules/services/misc/ollama.nix index d9359d2b5cd4..3ac3beb4de07 100644 --- a/nixpkgs/nixos/modules/services/misc/ollama.nix +++ b/nixpkgs/nixos/modules/services/misc/ollama.nix @@ -1,27 +1,44 @@ -{ config, lib, pkgs, ... }: let +{ config, lib, pkgs, ... }: +let + inherit (lib) types; cfg = config.services.ollama; - -in { - + ollamaPackage = cfg.package.override { + inherit (cfg) acceleration; + linuxPackages = config.boot.kernelPackages // { + nvidia_x11 = config.hardware.nvidia.package; + }; + }; +in +{ options = { services.ollama = { enable = lib.mkEnableOption ( lib.mdDoc "Server for local large language models" ); listenAddress = lib.mkOption { - type = lib.types.str; + type = types.str; default = "127.0.0.1:11434"; description = lib.mdDoc '' Specifies the bind address on which the ollama server HTTP interface listens. ''; }; + acceleration = lib.mkOption { + type = types.nullOr (types.enum [ "rocm" "cuda" ]); + default = null; + example = "rocm"; + description = lib.mdDoc '' + Specifies the interface to use for hardware acceleration. + + - `rocm`: supported by modern AMD GPUs + - `cuda`: supported by modern NVIDIA GPUs + ''; + }; package = lib.mkPackageOption pkgs "ollama" { }; }; }; config = lib.mkIf cfg.enable { - systemd = { services.ollama = { wantedBy = [ "multi-user.target" ]; @@ -33,7 +50,7 @@ in { OLLAMA_HOST = cfg.listenAddress; }; serviceConfig = { - ExecStart = "${lib.getExe cfg.package} serve"; + ExecStart = "${lib.getExe ollamaPackage} serve"; WorkingDirectory = "/var/lib/ollama"; StateDirectory = [ "ollama" ]; DynamicUser = true; @@ -41,10 +58,8 @@ in { }; }; - environment.systemPackages = [ cfg.package ]; - + environment.systemPackages = [ ollamaPackage ]; }; - meta.maintainers = with lib.maintainers; [ onny ]; - + meta.maintainers = with lib.maintainers; [ abysssol onny ]; } diff --git a/nixpkgs/nixos/modules/services/misc/paperless.nix b/nixpkgs/nixos/modules/services/misc/paperless.nix index 1256d8315c8b..ab042e4b6ee2 100644 --- a/nixpkgs/nixos/modules/services/misc/paperless.nix +++ b/nixpkgs/nixos/modules/services/misc/paperless.nix @@ -307,6 +307,9 @@ in Restart = "on-failure"; }; environment = env; + # Allow the consumer to access the private /tmp directory of the server. + # This is required to support consuming files via a local folder. + unitConfig.JoinsNamespaceOf = "paperless-task-queue.service"; }; systemd.services.paperless-web = { diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nut.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nut.nix index 1c86b48b4509..e58a394456a3 100644 --- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nut.nix +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nut.nix @@ -36,6 +36,17 @@ in provisioned outside of Nix store. ''; }; + nutVariables = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + List of NUT variable names to monitor. + + If no variables are set, all numeric variables will be exported automatically. + See the [upstream docs](https://github.com/DRuggeri/nut_exporter?tab=readme-ov-file#variables-and-information) + for more information. + ''; + }; }; serviceOpts = { script = '' @@ -44,7 +55,9 @@ in ${pkgs.prometheus-nut-exporter}/bin/nut_exporter \ --nut.server=${cfg.nutServer} \ --web.listen-address="${cfg.listenAddress}:${toString cfg.port}" \ - ${optionalString (cfg.nutUser != "") "--nut.username=${cfg.nutUser}"} + ${optionalString (cfg.nutUser != "") "--nut.username=${cfg.nutUser}"} \ + ${optionalString (cfg.nutVariables != []) "--nut.vars_enable=${concatStringsSep "," cfg.nutVariables}"} \ + ${concatStringsSep " " cfg.extraFlags} ''; }; } diff --git a/nixpkgs/nixos/modules/services/networking/dhcpcd.nix b/nixpkgs/nixos/modules/services/networking/dhcpcd.nix index 266a7ea1435e..8d5ac02ba88b 100644 --- a/nixpkgs/nixos/modules/services/networking/dhcpcd.nix +++ b/nixpkgs/nixos/modules/services/networking/dhcpcd.nix @@ -13,6 +13,8 @@ let enableDHCP = config.networking.dhcpcd.enable && (config.networking.useDHCP || any (i: i.useDHCP == true) interfaces); + enableNTPService = (config.services.ntp.enable || config.services.ntpd-rs.enable || config.services.openntpd.enable || config.services.chrony.enable); + # Don't start dhcpcd on explicitly configured interfaces or on # interfaces that are part of a bridge, bond or sit device. ignoredInterfaces = @@ -89,20 +91,22 @@ let ${cfg.extraConfig} ''; - exitHook = pkgs.writeText "dhcpcd.exit-hook" - '' + exitHook = pkgs.writeText "dhcpcd.exit-hook" '' + ${optionalString enableNTPService '' if [ "$reason" = BOUND -o "$reason" = REBOOT ]; then - # Restart ntpd. We need to restart it to make sure that it - # will actually do something: if ntpd cannot resolve the - # server hostnames in its config file, then it will never do - # anything ever again ("couldn't resolve ..., giving up on - # it"), so we silently lose time synchronisation. This also - # applies to openntpd. - /run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd.service openntpd.service chronyd.service ntpd-rs.service || true + # Restart ntpd. We need to restart it to make sure that it will actually do something: + # if ntpd cannot resolve the server hostnames in its config file, then it will never do + # anything ever again ("couldn't resolve ..., giving up on it"), so we silently lose + # time synchronisation. This also applies to openntpd. + ${optionalString config.services.ntp.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd.service || true"} + ${optionalString config.services.ntpd-rs.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd-rs.service || true"} + ${optionalString config.services.openntpd.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart openntpd.service || true"} + ${optionalString config.services.chrony.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart chronyd.service || true"} fi + ''} - ${cfg.runHook} - ''; + ${cfg.runHook} + ''; in @@ -232,7 +236,7 @@ in wants = [ "network.target" ]; before = [ "network-online.target" ]; - restartTriggers = [ exitHook ]; + restartTriggers = optional (enableNTPService || cfg.runHook != "") [ exitHook ]; # Stopping dhcpcd during a reconfiguration is undesirable # because it brings down the network interfaces configured by @@ -261,7 +265,9 @@ in environment.systemPackages = [ dhcpcd ]; - environment.etc."dhcpcd.exit-hook".source = exitHook; + environment.etc."dhcpcd.exit-hook" = mkIf (enableNTPService || cfg.runHook != "") { + source = exitHook; + }; powerManagement.resumeCommands = mkIf config.systemd.services.dhcpcd.enable '' diff --git a/nixpkgs/nixos/modules/services/networking/mosquitto.nix b/nixpkgs/nixos/modules/services/networking/mosquitto.nix index ad9eefb42252..4a08f5ed2370 100644 --- a/nixpkgs/nixos/modules/services/networking/mosquitto.nix +++ b/nixpkgs/nixos/modules/services/networking/mosquitto.nix @@ -177,17 +177,6 @@ let '' ++ hashedLines)); - makeACLFile = idx: users: supplement: - pkgs.writeText "mosquitto-acl-${toString idx}.conf" - (concatStringsSep - "\n" - (flatten [ - supplement - (mapAttrsToList - (n: u: [ "user ${n}" ] ++ map (t: "topic ${t}") u.acl) - users) - ])); - authPluginOptions = with types; submodule { options = { plugin = mkOption { @@ -342,7 +331,7 @@ let formatListener = idx: listener: [ "listener ${toString listener.port} ${toString listener.address}" - "acl_file ${makeACLFile idx listener.users listener.acl}" + "acl_file /etc/mosquitto/acl-${toString idx}.conf" ] ++ optional (! listener.omitPasswordAuth) "password_file ${cfg.dataDir}/passwd-${toString idx}" ++ formatFreeform {} listener.settings @@ -698,6 +687,27 @@ in cfg.listeners); }; + environment.etc = listToAttrs ( + imap0 + (idx: listener: { + name = "mosquitto/acl-${toString idx}.conf"; + value = { + user = config.users.users.mosquitto.name; + group = config.users.users.mosquitto.group; + mode = "0400"; + text = (concatStringsSep + "\n" + (flatten [ + listener.acl + (mapAttrsToList + (n: u: [ "user ${n}" ] ++ map (t: "topic ${t}") u.acl) + listener.users) + ])); + }; + }) + cfg.listeners + ); + users.users.mosquitto = { description = "Mosquitto MQTT Broker Daemon owner"; group = "mosquitto"; diff --git a/nixpkgs/nixos/modules/services/networking/searx.nix b/nixpkgs/nixos/modules/services/networking/searx.nix index 938d585e3179..5bbf875f0d57 100644 --- a/nixpkgs/nixos/modules/services/networking/searx.nix +++ b/nixpkgs/nixos/modules/services/networking/searx.nix @@ -213,7 +213,7 @@ in serviceConfig = { User = "searx"; Group = "searx"; - ExecStart = "${cfg.package}/bin/searx-run"; + ExecStart = lib.getExe cfg.package; } // optionalAttrs (cfg.environmentFile != null) { EnvironmentFile = builtins.toPath cfg.environmentFile; }; environment = { diff --git a/nixpkgs/nixos/modules/services/networking/unbound.nix b/nixpkgs/nixos/modules/services/networking/unbound.nix index 616b32f11797..8438e472e11e 100644 --- a/nixpkgs/nixos/modules/services/networking/unbound.nix +++ b/nixpkgs/nixos/modules/services/networking/unbound.nix @@ -24,12 +24,24 @@ let confNoServer = concatStringsSep "\n" ((mapAttrsToList (toConf "") (builtins.removeAttrs cfg.settings [ "server" ])) ++ [""]); confServer = concatStringsSep "\n" (mapAttrsToList (toConf " ") (builtins.removeAttrs cfg.settings.server [ "define-tag" ])); - confFile = pkgs.writeText "unbound.conf" '' + confFileUnchecked = pkgs.writeText "unbound.conf" '' server: ${optionalString (cfg.settings.server.define-tag != "") (toOption " " "define-tag" cfg.settings.server.define-tag)} ${confServer} ${confNoServer} ''; + confFile = if cfg.checkconf then pkgs.runCommandLocal "unbound-checkconf" { } '' + cp ${confFileUnchecked} unbound.conf + + # fake stateDir which is not accesible in the sandbox + mkdir -p $PWD/state + sed -i unbound.conf \ + -e '/auto-trust-anchor-file/d' \ + -e "s|${cfg.stateDir}|$PWD/state|" + ${cfg.package}/bin/unbound-checkconf unbound.conf + + cp ${confFileUnchecked} $out + '' else confFileUnchecked; rootTrustAnchorFile = "${cfg.stateDir}/root.key"; @@ -62,6 +74,17 @@ in { description = lib.mdDoc "Directory holding all state for unbound to run."; }; + checkconf = mkOption { + type = types.bool; + default = !cfg.settings ? include; + defaultText = "!config.services.unbound.settings ? include"; + description = lib.mdDoc '' + Wether to check the resulting config file with unbound checkconf for syntax errors. + + If settings.include is used, then this options is disabled, as the import can likely not be resolved at build time. + ''; + }; + resolveLocalQueries = mkOption { type = types.bool; default = true; diff --git a/nixpkgs/nixos/modules/services/security/kanidm.nix b/nixpkgs/nixos/modules/services/security/kanidm.nix index c659d93b4087..9d074c3027d0 100644 --- a/nixpkgs/nixos/modules/services/security/kanidm.nix +++ b/nixpkgs/nixos/modules/services/security/kanidm.nix @@ -132,6 +132,28 @@ in default = "WriteReplica"; type = lib.types.enum [ "WriteReplica" "WriteReplicaNoUI" "ReadOnlyReplica" ]; }; + online_backup = { + path = lib.mkOption { + description = lib.mdDoc "Path to the output directory for backups."; + type = lib.types.path; + default = "/var/lib/kanidm/backups"; + }; + schedule = lib.mkOption { + description = lib.mdDoc "The schedule for backups in cron format."; + type = lib.types.str; + default = "00 22 * * *"; + }; + versions = lib.mkOption { + description = lib.mdDoc '' + Number of backups to keep. + + The default is set to `0`, in order to disable backups by default. + ''; + type = lib.types.ints.unsigned; + default = 0; + example = 7; + }; + }; }; }; default = { }; @@ -233,6 +255,14 @@ in environment.systemPackages = lib.mkIf cfg.enableClient [ cfg.package ]; + systemd.tmpfiles.settings."10-kanidm" = { + ${cfg.serverSettings.online_backup.path}.d = { + mode = "0700"; + user = "kanidm"; + group = "kanidm"; + }; + }; + systemd.services.kanidm = lib.mkIf cfg.enableServer { description = "kanidm identity management daemon"; wantedBy = [ "multi-user.target" ]; @@ -253,6 +283,8 @@ in BindPaths = [ # To create the socket "/run/kanidmd:/run/kanidmd" + # To store backups + cfg.serverSettings.online_backup.path ]; AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; diff --git a/nixpkgs/nixos/modules/services/web-apps/mealie.nix b/nixpkgs/nixos/modules/services/web-apps/mealie.nix new file mode 100644 index 000000000000..8bb7542c6b56 --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/mealie.nix @@ -0,0 +1,79 @@ +{ config, lib, pkgs, ...}: +let + cfg = config.services.mealie; + pkg = cfg.package; +in +{ + options.services.mealie = { + enable = lib.mkEnableOption "Mealie, a recipe manager and meal planner"; + + package = lib.mkPackageOption pkgs "mealie" { }; + + listenAddress = lib.mkOption { + type = lib.types.str; + default = "0.0.0.0"; + description = "Address on which the service should listen."; + }; + + port = lib.mkOption { + type = lib.types.port; + default = 9000; + description = "Port on which to serve the Mealie service."; + }; + + settings = lib.mkOption { + type = with lib.types; attrsOf anything; + default = {}; + description = lib.mdDoc '' + Configuration of the Mealie service. + + See [the mealie documentation](https://nightly.mealie.io/documentation/getting-started/installation/backend-config/) for available options and default values. + + In addition to the official documentation, you can set {env}`MEALIE_LOG_FILE`. + ''; + example = { + ALLOW_SIGNUP = "false"; + }; + }; + + credentialsFile = lib.mkOption { + type = with lib.types; nullOr path; + default = null; + example = "/run/secrets/mealie-credentials.env"; + description = '' + File containing credentials used in mealie such as {env}`POSTGRES_PASSWORD` + or sensitive LDAP options. + + Expects the format of an `EnvironmentFile=`, as described by {manpage}`systemd.exec(5)`. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.mealie = { + description = "Mealie, a self hosted recipe manager and meal planner"; + + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + + environment = { + PRODUCTION = "true"; + ALEMBIC_CONFIG_FILE="${pkg}/config/alembic.ini"; + API_PORT = toString cfg.port; + DATA_DIR = "/var/lib/mealie"; + CRF_MODEL_PATH = "/var/lib/mealie/model.crfmodel"; + } // (builtins.mapAttrs (_: val: toString val) cfg.settings); + + serviceConfig = { + DynamicUser = true; + User = "mealie"; + ExecStartPre = "${pkg}/libexec/init_db"; + ExecStart = "${lib.getExe pkg} -b ${cfg.listenAddress}:${builtins.toString cfg.port}"; + EnvironmentFile = lib.mkIf (cfg.credentialsFile != null) cfg.credentialsFile; + StateDirectory = "mealie"; + StandardOutput="journal"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/default.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/default.nix index 66cb4ee29c0a..ecb8d1e91bde 100644 --- a/nixpkgs/nixos/modules/services/x11/desktop-managers/default.nix +++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/default.nix @@ -18,7 +18,7 @@ in # determines the default: later modules (if enabled) are preferred. # E.g., if Plasma 5 is enabled, it supersedes xterm. imports = [ - ./none.nix ./xterm.nix ./phosh.nix ./xfce.nix ./plasma5.nix ./lumina.nix + ./none.nix ./xterm.nix ./phosh.nix ./xfce.nix ./plasma5.nix ./plasma6.nix ./lumina.nix ./lxqt.nix ./enlightenment.nix ./gnome.nix ./retroarch.nix ./kodi.nix ./mate.nix ./pantheon.nix ./surf-display.nix ./cde.nix ./cinnamon.nix ./budgie.nix ./deepin.nix diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix index 0eb492ce4684..7645b3070369 100644 --- a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix +++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix @@ -362,7 +362,7 @@ in security.pam.services.kde = { allowNullPassword = true; }; - security.pam.services.login.enableKwallet = true; + security.pam.services.login.kwallet.enable = true; systemd.user.services = { plasma-early-setup = mkIf cfg.runUsingSystemd { diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma6.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma6.nix new file mode 100644 index 000000000000..bc246b1af278 --- /dev/null +++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma6.nix @@ -0,0 +1,276 @@ +{ + config, + lib, + pkgs, + utils, + ... +}: let + xcfg = config.services.xserver; + cfg = xcfg.desktopManager.plasma6; + + inherit (pkgs) kdePackages; + inherit (lib) literalExpression mkDefault mkIf mkOption mkPackageOptionMD types; +in { + options = { + services.xserver.desktopManager.plasma6 = { + enable = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Enable the Plasma 6 (KDE 6) desktop environment."; + }; + + enableQt5Integration = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc "Enable Qt 5 integration (theming, etc). Disable for a pure Qt 6 system."; + }; + + notoPackage = mkPackageOptionMD pkgs "Noto fonts - used for UI by default" { + default = ["noto-fonts"]; + example = "noto-fonts-lgc-plus"; + }; + }; + + environment.plasma6.excludePackages = mkOption { + description = lib.mdDoc "List of default packages to exclude from the configuration"; + type = types.listOf types.package; + default = []; + example = literalExpression "[ pkgs.kdePackages.elisa ]"; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = cfg.enable -> !config.services.xserver.desktopManager.plasma5.enable; + message = "Cannot enable plasma5 and plasma6 at the same time!"; + } + ]; + + qt.enable = true; + environment.systemPackages = with kdePackages; let + requiredPackages = [ + # Hack? To make everything run on Wayland + qtwayland + # Needed to render SVG icons + qtsvg + + # Frameworks with globally loadable bits + frameworkintegration # provides Qt plugin + kauth # provides helper service + kcoreaddons # provides extra mime type info + kded # provides helper service + kfilemetadata # provides Qt plugins + kguiaddons # provides geo URL handlers + kiconthemes # provides Qt plugins + kimageformats # provides Qt plugins + kio # provides helper service + a bunch of other stuff + kpackage # provides kpackagetool tool + kservice # provides kbuildsycoca6 tool + kwallet # provides helper service + kwallet-pam # provides helper service + kwalletmanager # provides KCMs and stuff + plasma-activities # provides plasma-activities-cli tool + solid # provides solid-hardware6 tool + phonon-vlc # provides Phonon plugin + + # Core Plasma parts + kwin + pkgs.xwayland + + kscreen + libkscreen + + kscreenlocker + + kactivitymanagerd + kde-cli-tools + kglobalacceld + kwrited # wall message proxy, not to be confused with kwrite + + milou + polkit-kde-agent-1 + + plasma-desktop + plasma-workspace + + # Crash handler + drkonqi + + # Application integration + libplasma # provides Kirigami platform theme + plasma-integration # provides Qt platform theme + kde-gtk-config + + # Artwork + themes + breeze + breeze-icons + breeze-gtk + ocean-sound-theme + plasma-workspace-wallpapers + pkgs.hicolor-icon-theme # fallback icons + qqc2-breeze-style + qqc2-desktop-style + + # misc Plasma extras + kdeplasma-addons + + pkgs.xdg-user-dirs # recommended upstream + + # Plasma utilities + kmenuedit + + kinfocenter + plasma-systemmonitor + ksystemstats + libksysguard + + spectacle + systemsettings + + # Gear + baloo + dolphin + dolphin-plugins + ffmpegthumbs + kdegraphics-thumbnailers + kde-inotify-survey + kio-admin + kio-extras + kio-fuse + ]; + optionalPackages = [ + plasma-browser-integration + konsole + (lib.getBin qttools) # Expose qdbus in PATH + + ark + elisa + gwenview + okular + kate + khelpcenter + print-manager + ]; + in + requiredPackages + ++ utils.removePackagesByName optionalPackages config.environment.plasma6.excludePackages + ++ lib.optionals config.services.xserver.desktopManager.plasma6.enableQt5Integration [ + breeze.qt5 + plasma-integration.qt5 + pkgs.plasma5Packages.kwayland-integration + kio-extras-kf5 + ] + # Optional hardware support features + ++ lib.optionals config.hardware.bluetooth.enable [bluedevil bluez-qt pkgs.openobex pkgs.obexftp] + ++ lib.optional config.networking.networkmanager.enable plasma-nm + ++ lib.optional config.hardware.pulseaudio.enable plasma-pa + ++ lib.optional config.services.pipewire.pulse.enable plasma-pa + ++ lib.optional config.powerManagement.enable powerdevil + ++ lib.optional config.services.colord.enable colord-kde + ++ lib.optional config.services.hardware.bolt.enable plasma-thunderbolt + ++ lib.optionals config.services.samba.enable [kdenetwork-filesharing pkgs.samba] + ++ lib.optional config.services.xserver.wacom.enable wacomtablet + ++ lib.optional config.services.flatpak.enable flatpak-kcm; + + environment.pathsToLink = [ + # FIXME: modules should link subdirs of `/share` rather than relying on this + "/share" + "/libexec" # for drkonqi + ]; + + environment.etc."X11/xkb".source = xcfg.xkb.dir; + + # Add ~/.config/kdedefaults to XDG_CONFIG_DIRS for shells, since Plasma sets that. + # FIXME: maybe we should append to XDG_CONFIG_DIRS in /etc/set-environment instead? + environment.sessionVariables.XDG_CONFIG_DIRS = ["$HOME/.config/kdedefaults"]; + + # Needed for things that depend on other store.kde.org packages to install correctly, + # notably Plasma look-and-feel packages (a.k.a. Global Themes) + # + # FIXME: this is annoyingly impure and should really be fixed at source level somehow, + # but kpackage is a library so we can't just wrap the one thing invoking it and be done. + # This also means things won't work for people not on Plasma, but at least this way it + # works for SOME people. + environment.sessionVariables.KPACKAGE_DEP_RESOLVERS_PATH = "${kdePackages.frameworkintegration.out}/libexec/kf6/kpackagehandlers"; + + # Enable GTK applications to load SVG icons + services.xserver.gdk-pixbuf.modulePackages = [pkgs.librsvg]; + + fonts.packages = [cfg.notoPackage pkgs.hack-font]; + fonts.fontconfig.defaultFonts = { + monospace = ["Hack" "Noto Sans Mono"]; + sansSerif = ["Noto Sans"]; + serif = ["Noto Serif"]; + }; + + programs.ssh.askPassword = mkDefault "${kdePackages.ksshaskpass.out}/bin/ksshaskpass"; + + # Enable helpful DBus services. + services.accounts-daemon.enable = true; + # when changing an account picture the accounts-daemon reads a temporary file containing the image which systemsettings5 may place under /tmp + systemd.services.accounts-daemon.serviceConfig.PrivateTmp = false; + + services.power-profiles-daemon.enable = mkDefault true; + services.system-config-printer.enable = mkIf config.services.printing.enable (mkDefault true); + services.udisks2.enable = true; + services.upower.enable = config.powerManagement.enable; + services.xserver.libinput.enable = mkDefault true; + + # Extra UDEV rules used by Solid + services.udev.packages = [ + # libmtp has "bin", "dev", "out" outputs. UDEV rules file is in "out". + pkgs.libmtp.out + pkgs.media-player-info + ]; + + # Set up Dr. Konqi as crash handler + systemd.packages = [kdePackages.drkonqi]; + systemd.services."drkonqi-coredump-processor@".wantedBy = ["systemd-coredump@.service"]; + + xdg.portal.enable = true; + xdg.portal.extraPortals = [kdePackages.xdg-desktop-portal-kde]; + xdg.portal.configPackages = mkDefault [kdePackages.xdg-desktop-portal-kde]; + services.pipewire.enable = mkDefault true; + + services.xserver.displayManager = { + sessionPackages = [kdePackages.plasma-workspace]; + defaultSession = mkDefault "plasma"; + }; + services.xserver.displayManager.sddm = { + package = kdePackages.sddm; + theme = mkDefault "breeze"; + extraPackages = with kdePackages; [ + breeze-icons + kirigami + plasma5support + qtsvg + qtvirtualkeyboard + ]; + }; + + security.pam.services = { + login.kwallet = { + enable = true; + package = kdePackages.kwallet-pam; + }; + kde.kwallet = { + enable = true; + package = kdePackages.kwallet-pam; + }; + kde-fingerprint = lib.mkIf config.services.fprintd.enable { fprintAuth = true; }; + kde-smartcard = lib.mkIf config.security.pam.p11.enable { p11Auth = true; }; + }; + + programs.dconf.enable = true; + + programs.firefox.nativeMessagingHosts.packages = [kdePackages.plasma-browser-integration]; + + programs.chromium = { + enablePlasmaBrowserIntegration = true; + plasmaBrowserIntegrationPackage = pkgs.kdePackages.plasma-browser-integration; + }; + + programs.kdeconnect.package = kdePackages.kdeconnect-kde; + }; +} diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix index 0576619cc8d2..5b7f4bc58d80 100644 --- a/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix +++ b/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix @@ -7,7 +7,10 @@ let cfg = dmcfg.sddm; xEnv = config.systemd.services.display-manager.environment; - sddm = cfg.package; + sddm = cfg.package.override(old: { + withWayland = cfg.wayland.enable; + extraPackages = old.extraPackages or [] ++ cfg.extraPackages; + }); iniFmt = pkgs.formats.ini { }; @@ -140,6 +143,15 @@ in ''; }; + extraPackages = mkOption { + type = types.listOf types.package; + default = []; + defaultText = "[]"; + description = lib.mdDoc '' + Extra Qt plugins / QML libraries to add to the environment. + ''; + }; + autoNumlock = mkOption { type = types.bool; default = false; @@ -211,7 +223,7 @@ in keymap_variant = xcfg.xkb.variant; keymap_options = xcfg.xkb.options; }; - }; in "${pkgs.weston}/bin/weston --shell=fullscreen-shell.so -c ${westonIni}"; + }; in "${pkgs.weston}/bin/weston --shell=kiosk -c ${westonIni}"; description = lib.mdDoc "Command used to start the selected compositor"; }; }; @@ -235,15 +247,7 @@ in } ]; - services.xserver.displayManager.job = { - environment = { - # Load themes from system environment - QT_PLUGIN_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtPluginPrefix; - QML2_IMPORT_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtQmlPrefix; - }; - - execCmd = "exec /run/current-system/sw/bin/sddm"; - }; + services.xserver.displayManager.job.execCmd = "exec /run/current-system/sw/bin/sddm"; security.pam.services = { sddm.text = '' diff --git a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py index a9978d7adf80..258cf622a894 100644 --- a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py +++ b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py @@ -17,6 +17,9 @@ from dataclasses import dataclass # These values will be replaced with actual values during the package build EFI_SYS_MOUNT_POINT = "@efiSysMountPoint@" +BOOT_MOUNT_POINT = "@bootMountPoint@" +LOADER_CONF = f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf" # Always stored on the ESP +NIXOS_DIR = "@nixosDir@" TIMEOUT = "@timeout@" EDITOR = "@editor@" == "1" CONSOLE_MODE = "@consoleMode@" @@ -28,6 +31,7 @@ CONFIGURATION_LIMIT = int("@configurationLimit@") CAN_TOUCH_EFI_VARIABLES = "@canTouchEfiVariables@" GRACEFUL = "@graceful@" COPY_EXTRA_FILES = "@copyExtraFiles@" +CHECK_MOUNTPOINTS = "@checkMountpoints@" @dataclass class BootSpec: @@ -87,7 +91,7 @@ def generation_conf_filename(profile: str | None, generation: int, specialisatio def write_loader_conf(profile: str | None, generation: int, specialisation: str | None) -> None: - with open(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf.tmp", 'w') as f: + with open(f"{LOADER_CONF}.tmp", 'w') as f: if TIMEOUT != "": f.write(f"timeout {TIMEOUT}\n") f.write("default %s\n" % generation_conf_filename(profile, generation, specialisation)) @@ -96,7 +100,7 @@ def write_loader_conf(profile: str | None, generation: int, specialisation: str f.write(f"console-mode {CONSOLE_MODE}\n") f.flush() os.fsync(f.fileno()) - os.rename(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf.tmp", f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf") + os.rename(f"{LOADER_CONF}.tmp", LOADER_CONF) def get_bootspec(profile: str | None, generation: int) -> BootSpec: @@ -126,9 +130,9 @@ def copy_from_file(file: str, dry_run: bool = False) -> str: store_file_path = os.path.realpath(file) suffix = os.path.basename(store_file_path) store_dir = os.path.basename(os.path.dirname(store_file_path)) - efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix) + efi_file_path = f"{NIXOS_DIR}/{store_dir}-{suffix}.efi" if not dry_run: - copy_if_not_exists(store_file_path, f"{EFI_SYS_MOUNT_POINT}%s" % (efi_file_path)) + copy_if_not_exists(store_file_path, f"{BOOT_MOUNT_POINT}{efi_file_path}") return efi_file_path def write_entry(profile: str | None, generation: int, specialisation: str | None, @@ -145,7 +149,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None try: if bootspec.initrdSecrets is not None: - subprocess.check_call([bootspec.initrdSecrets, f"{EFI_SYS_MOUNT_POINT}%s" % (initrd)]) + subprocess.check_call([bootspec.initrdSecrets, f"{BOOT_MOUNT_POINT}%s" % (initrd)]) except subprocess.CalledProcessError: if current: print("failed to create initrd secrets!", file=sys.stderr) @@ -155,7 +159,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None f'for "{title} - Configuration {generation}", an older generation', file=sys.stderr) print("note: this is normal after having removed " "or renamed a file in `boot.initrd.secrets`", file=sys.stderr) - entry_file = f"{EFI_SYS_MOUNT_POINT}/loader/entries/%s" % ( + entry_file = f"{BOOT_MOUNT_POINT}/loader/entries/%s" % ( generation_conf_filename(profile, generation, specialisation)) tmp_path = "%s.tmp" % (entry_file) kernel_params = "init=%s " % bootspec.init @@ -202,14 +206,14 @@ def get_generations(profile: str | None = None) -> list[SystemIdentifier]: def remove_old_entries(gens: list[SystemIdentifier]) -> None: - rex_profile = re.compile(r"^" + re.escape(EFI_SYS_MOUNT_POINT) + "/loader/entries/nixos-(.*)-generation-.*\.conf$") - rex_generation = re.compile(r"^" + re.escape(EFI_SYS_MOUNT_POINT) + "/loader/entries/nixos.*-generation-([0-9]+)(-specialisation-.*)?\.conf$") + rex_profile = re.compile(r"^" + re.escape(BOOT_MOUNT_POINT) + "/loader/entries/nixos-(.*)-generation-.*\.conf$") + rex_generation = re.compile(r"^" + re.escape(BOOT_MOUNT_POINT) + "/loader/entries/nixos.*-generation-([0-9]+)(-specialisation-.*)?\.conf$") known_paths = [] for gen in gens: bootspec = get_bootspec(gen.profile, gen.generation) known_paths.append(copy_from_file(bootspec.kernel, True)) known_paths.append(copy_from_file(bootspec.initrd, True)) - for path in glob.iglob(f"{EFI_SYS_MOUNT_POINT}/loader/entries/nixos*-generation-[1-9]*.conf"): + for path in glob.iglob(f"{BOOT_MOUNT_POINT}/loader/entries/nixos*-generation-[1-9]*.conf"): if rex_profile.match(path): prof = rex_profile.sub(r"\1", path) else: @@ -220,11 +224,18 @@ def remove_old_entries(gens: list[SystemIdentifier]) -> None: continue if not (prof, gen_number, None) in gens: os.unlink(path) - for path in glob.iglob(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/*"): + for path in glob.iglob(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/*"): if not path in known_paths and not os.path.isdir(path): os.unlink(path) +def cleanup_esp() -> None: + for path in glob.iglob(f"{EFI_SYS_MOUNT_POINT}/loader/entries/nixos*"): + os.unlink(path) + if os.path.isdir(f"{EFI_SYS_MOUNT_POINT}/{NIXOS_DIR}"): + shutil.rmtree(f"{EFI_SYS_MOUNT_POINT}/{NIXOS_DIR}") + + def get_profiles() -> list[str]: if os.path.isdir("/nix/var/nix/profiles/system-profiles/"): return [x @@ -255,6 +266,9 @@ def install_bootloader(args: argparse.Namespace) -> None: # flags to pass to bootctl install/update bootctl_flags = [] + if BOOT_MOUNT_POINT != EFI_SYS_MOUNT_POINT: + bootctl_flags.append(f"--boot-path={BOOT_MOUNT_POINT}") + if CAN_TOUCH_EFI_VARIABLES != "1": bootctl_flags.append("--no-variables") @@ -263,8 +277,8 @@ def install_bootloader(args: argparse.Namespace) -> None: if os.getenv("NIXOS_INSTALL_BOOTLOADER") == "1": # bootctl uses fopen() with modes "wxe" and fails if the file exists. - if os.path.exists(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf"): - os.unlink(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf") + if os.path.exists(LOADER_CONF): + os.unlink(LOADER_CONF) subprocess.check_call([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"] + bootctl_flags + ["install"]) else: @@ -291,13 +305,15 @@ def install_bootloader(args: argparse.Namespace) -> None: 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"]) - os.makedirs(f"{EFI_SYS_MOUNT_POINT}/efi/nixos", exist_ok=True) - os.makedirs(f"{EFI_SYS_MOUNT_POINT}/loader/entries", 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) gens = get_generations() for profile in get_profiles(): gens += get_generations(profile) + remove_old_entries(gens) + for gen in gens: try: bootspec = get_bootspec(gen.profile, gen.generation) @@ -315,9 +331,15 @@ def install_bootloader(args: argparse.Namespace) -> None: else: raise e - for root, _, files in os.walk(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files", topdown=False): - relative_root = root.removeprefix(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files").removeprefix("/") - actual_root = os.path.join(f"{EFI_SYS_MOUNT_POINT}", relative_root) + if BOOT_MOUNT_POINT != EFI_SYS_MOUNT_POINT: + # Cleanup any entries in ESP if xbootldrMountPoint is set. + # If the user later unsets xbootldrMountPoint, entries in XBOOTLDR will not be cleaned up + # automatically, as we don't have information about the mount point anymore. + cleanup_esp() + + for root, _, files in os.walk(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/.extra-files", topdown=False): + relative_root = root.removeprefix(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/.extra-files").removeprefix("/") + actual_root = os.path.join(f"{BOOT_MOUNT_POINT}", relative_root) for file in files: actual_file = os.path.join(actual_root, file) @@ -330,7 +352,7 @@ def install_bootloader(args: argparse.Namespace) -> None: os.rmdir(actual_root) os.rmdir(root) - os.makedirs(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files", exist_ok=True) + os.makedirs(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/.extra-files", exist_ok=True) subprocess.check_call(COPY_EXTRA_FILES) @@ -340,6 +362,8 @@ def main() -> None: parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help=f"The default {DISTRO_NAME} config to boot") args = parser.parse_args() + subprocess.check_call(CHECK_MOUNTPOINTS) + try: install_bootloader(args) finally: @@ -347,9 +371,14 @@ def main() -> None: # it can leave the system in an unbootable state, when a crash/outage # happens shortly after an update. To decrease the likelihood of this # event sync the efi filesystem after each update. - rc = libc.syncfs(os.open(f"{EFI_SYS_MOUNT_POINT}", os.O_RDONLY)) + rc = libc.syncfs(os.open(f"{BOOT_MOUNT_POINT}", os.O_RDONLY)) if rc != 0: - print(f"could not sync {EFI_SYS_MOUNT_POINT}: {os.strerror(rc)}", file=sys.stderr) + print(f"could not sync {BOOT_MOUNT_POINT}: {os.strerror(rc)}", file=sys.stderr) + + if BOOT_MOUNT_POINT != EFI_SYS_MOUNT_POINT: + rc = libc.syncfs(os.open(EFI_SYS_MOUNT_POINT, os.O_RDONLY)) + if rc != 0: + print(f"could not sync {EFI_SYS_MOUNT_POINT}: {os.strerror(rc)}", file=sys.stderr) if __name__ == '__main__': diff --git a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix index ea4553b8208f..645b764760da 100644 --- a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix +++ b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix @@ -7,7 +7,7 @@ let efi = config.boot.loader.efi; - systemdBootBuilder = pkgs.substituteAll { + systemdBootBuilder = pkgs.substituteAll rec { src = ./systemd-boot-builder.py; isExecutable = true; @@ -28,33 +28,48 @@ let inherit (efi) efiSysMountPoint canTouchEfiVariables; + bootMountPoint = if cfg.xbootldrMountPoint != null + then cfg.xbootldrMountPoint + else efi.efiSysMountPoint; + + nixosDir = "/EFI/nixos"; + inherit (config.system.nixos) distroName; memtest86 = optionalString cfg.memtest86.enable pkgs.memtest86plus; netbootxyz = optionalString cfg.netbootxyz.enable pkgs.netbootxyz-efi; + checkMountpoints = pkgs.writeShellScript "check-mountpoints" '' + fail() { + echo "$1 = '$2' is not a mounted partition. Is the path configured correctly?" >&2 + exit 1 + } + ${pkgs.util-linuxMinimal}/bin/findmnt ${efiSysMountPoint} > /dev/null || fail efiSysMountPoint ${efiSysMountPoint} + ${lib.optionalString + (cfg.xbootldrMountPoint != null) + "${pkgs.util-linuxMinimal}/bin/findmnt ${cfg.xbootldrMountPoint} > /dev/null || fail xbootldrMountPoint ${cfg.xbootldrMountPoint}"} + ''; + copyExtraFiles = pkgs.writeShellScript "copy-extra-files" '' empty_file=$(${pkgs.coreutils}/bin/mktemp) ${concatStrings (mapAttrsToList (n: v: '' - ${pkgs.coreutils}/bin/install -Dp "${v}" "${efi.efiSysMountPoint}/"${escapeShellArg n} - ${pkgs.coreutils}/bin/install -D $empty_file "${efi.efiSysMountPoint}/efi/nixos/.extra-files/"${escapeShellArg n} + ${pkgs.coreutils}/bin/install -Dp "${v}" "${bootMountPoint}/"${escapeShellArg n} + ${pkgs.coreutils}/bin/install -D $empty_file "${bootMountPoint}/${nixosDir}/.extra-files/"${escapeShellArg n} '') cfg.extraFiles)} ${concatStrings (mapAttrsToList (n: v: '' - ${pkgs.coreutils}/bin/install -Dp "${pkgs.writeText n v}" "${efi.efiSysMountPoint}/loader/entries/"${escapeShellArg n} - ${pkgs.coreutils}/bin/install -D $empty_file "${efi.efiSysMountPoint}/efi/nixos/.extra-files/loader/entries/"${escapeShellArg n} + ${pkgs.coreutils}/bin/install -Dp "${pkgs.writeText n v}" "${bootMountPoint}/loader/entries/"${escapeShellArg n} + ${pkgs.coreutils}/bin/install -D $empty_file "${bootMountPoint}/${nixosDir}/.extra-files/loader/entries/"${escapeShellArg n} '') cfg.extraEntries)} ''; }; - checkedSystemdBootBuilder = pkgs.runCommand "systemd-boot" { - nativeBuildInputs = [ pkgs.mypy ]; - } '' + checkedSystemdBootBuilder = pkgs.runCommand "systemd-boot" { } '' mkdir -p $out/bin install -m755 ${systemdBootBuilder} $out/bin/systemd-boot-builder - mypy \ + ${lib.getExe pkgs.buildPackages.mypy} \ --no-implicit-optional \ --disallow-untyped-calls \ --disallow-untyped-defs \ @@ -101,6 +116,18 @@ in { ''; }; + xbootldrMountPoint = mkOption { + default = null; + type = types.nullOr types.str; + description = lib.mdDoc '' + Where the XBOOTLDR partition is mounted. + + If set, this partition will be used as $BOOT to store boot loader entries and extra files + instead of the EFI partition. As per the bootloader specification, it is recommended that + the EFI and XBOOTLDR partitions be mounted at `/efi` and `/boot`, respectively. + ''; + }; + configurationLimit = mkOption { default = null; example = 120; @@ -110,7 +137,7 @@ in { Useful to prevent boot partition running out of disk space. `null` means no limit i.e. all generations - that were not garbage collected yet. + that have not been garbage collected yet. ''; }; @@ -202,7 +229,7 @@ in { ''; description = lib.mdDoc '' Any additional entries you want added to the `systemd-boot` menu. - These entries will be copied to {file}`/boot/loader/entries`. + These entries will be copied to {file}`$BOOT/loader/entries`. Each attribute name denotes the destination file name, and the corresponding attribute value is the contents of the entry. @@ -219,9 +246,9 @@ in { { "efi/memtest86/memtest.efi" = "''${pkgs.memtest86plus}/memtest.efi"; } ''; description = lib.mdDoc '' - A set of files to be copied to {file}`/boot`. + A set of files to be copied to {file}`$BOOT`. Each attribute name denotes the destination file name in - {file}`/boot`, while the corresponding + {file}`$BOOT`, while the corresponding attribute value specifies the source file. ''; }; @@ -246,6 +273,18 @@ in { config = mkIf cfg.enable { assertions = [ { + assertion = (hasPrefix "/" efi.efiSysMountPoint); + message = "The ESP mount point '${efi.efiSysMountPoint}' must be an absolute path"; + } + { + assertion = cfg.xbootldrMountPoint == null || (hasPrefix "/" cfg.xbootldrMountPoint); + message = "The XBOOTLDR mount point '${cfg.xbootldrMountPoint}' must be an absolute path"; + } + { + assertion = cfg.xbootldrMountPoint != efi.efiSysMountPoint; + message = "The XBOOTLDR mount point '${cfg.xbootldrMountPoint}' cannot be the same as the ESP mount point '${efi.efiSysMountPoint}'"; + } + { assertion = (config.boot.kernelPackages.kernel.features or { efiBootStub = true; }) ? efiBootStub; message = "This kernel does not support the EFI boot stub"; } diff --git a/nixpkgs/nixos/modules/system/boot/networkd.nix b/nixpkgs/nixos/modules/system/boot/networkd.nix index a7399bd55e77..88d6a2ded873 100644 --- a/nixpkgs/nixos/modules/system/boot/networkd.nix +++ b/nixpkgs/nixos/modules/system/boot/networkd.nix @@ -647,9 +647,9 @@ let "BatmanAdvanced" ]) # Note: For DHCP the values both, none, v4, v6 are deprecated - (assertValueOneOf "DHCP" ["yes" "no" "ipv4" "ipv6"]) + (assertValueOneOf "DHCP" (boolValues ++ ["ipv4" "ipv6"])) (assertValueOneOf "DHCPServer" boolValues) - (assertValueOneOf "LinkLocalAddressing" ["yes" "no" "ipv4" "ipv6" "fallback" "ipv4-fallback"]) + (assertValueOneOf "LinkLocalAddressing" (boolValues ++ ["ipv4" "ipv6" "fallback" "ipv4-fallback"])) (assertValueOneOf "IPv6LinkLocalAddressGenerationMode" ["eui64" "none" "stable-privacy" "random"]) (assertValueOneOf "IPv4LLRoute" boolValues) (assertValueOneOf "DefaultRouteOnDevice" boolValues) diff --git a/nixpkgs/nixos/modules/system/boot/plymouth.nix b/nixpkgs/nixos/modules/system/boot/plymouth.nix index b041b8951fa3..16bca40993ae 100644 --- a/nixpkgs/nixos/modules/system/boot/plymouth.nix +++ b/nixpkgs/nixos/modules/system/boot/plymouth.nix @@ -186,6 +186,8 @@ in # module might come from a theme cp ${themesEnv}/lib/plymouth/*.so $out cp ${plymouth}/lib/plymouth/renderers/*.so $out/renderers + # useless in the initrd, and adds several megabytes to the closure + rm $out/renderers/x11.so ''; "/etc/plymouth/themes".source = pkgs.runCommand "plymouth-initrd-themes" {} '' # Check if the actual requested theme is here @@ -271,6 +273,8 @@ in # module might come from a theme cp ${themesEnv}/lib/plymouth/*.so $out/lib/plymouth cp ${plymouth}/lib/plymouth/renderers/*.so $out/lib/plymouth/renderers + # useless in the initrd, and adds several megabytes to the closure + rm $out/lib/plymouth/renderers/x11.so mkdir -p $out/share/plymouth/themes cp ${plymouth}/share/plymouth/plymouthd.defaults $out/share/plymouth diff --git a/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix b/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix index 03ef00e5683c..271d8f86d0e6 100644 --- a/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix +++ b/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix @@ -52,7 +52,7 @@ in { # See: https://github.com/NixOS/nixpkgs/issues/213408 pkgs.substitute { src = "${systemd}/example/sysctl.d/50-coredump.conf"; - replacements = [ + substitutions = [ "--replace" "${systemd}" "${pkgs.symlinkJoin { name = "systemd"; paths = [ systemd ]; }}" diff --git a/nixpkgs/nixos/modules/system/boot/systemd/repart.nix b/nixpkgs/nixos/modules/system/boot/systemd/repart.nix index 3be744acd0b3..6cc387cb6f43 100644 --- a/nixpkgs/nixos/modules/system/boot/systemd/repart.nix +++ b/nixpkgs/nixos/modules/system/boot/systemd/repart.nix @@ -10,6 +10,20 @@ let "repart.d" format (lib.mapAttrs (_n: v: { Partition = v; }) cfg.partitions); + + partitionAssertions = lib.mapAttrsToList (fileName: definition: + let + maxLabelLength = 36; # GPT_LABEL_MAX defined in systemd's gpt.h + labelLength = builtins.stringLength definition.Label; + in + { + assertion = definition ? Label -> maxLabelLength >= labelLength; + message = '' + The partition label '${definition.Label}' defined for '${fileName}' is ${toString labelLength} + characters long, but the maximum label length supported by systemd is ${toString maxLabelLength}. + ''; + } + ) cfg.partitions; in { options = { @@ -81,7 +95,7 @@ in 'boot.initrd.systemd.repart.enable' requires 'boot.initrd.systemd.enable' to be enabled. ''; } - ]; + ] ++ partitionAssertions; # systemd-repart uses loopback devices for partition creation boot.initrd.availableKernelModules = lib.optional initrdCfg.enable "loop"; diff --git a/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix b/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix index 98df6a40e8a1..58aca3fdbd4f 100644 --- a/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix +++ b/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix @@ -347,24 +347,12 @@ in removeLinuxDRM = lib.mkOption { type = types.bool; default = false; - description = lib.mdDoc '' - Linux 6.2 dropped some kernel symbols required on aarch64 required by zfs. - Enabling this option will bring them back to allow this kernel version. - Note that in some jurisdictions this may be illegal as it might be considered - removing copyright protection from the code. - See https://www.ifross.org/?q=en/artikel/ongoing-dispute-over-value-exportsymbolgpl-function for further information. - - If configure your kernel package with `zfs.latestCompatibleLinuxPackages`, you will need to also pass removeLinuxDRM to that package like this: + description = '' + Patch the kernel to change symbols needed by ZFS from + EXPORT_SYMBOL_GPL to EXPORT_SYMBOL. - ``` - { pkgs, ... }: { - boot.kernelPackages = (pkgs.zfs.override { - removeLinuxDRM = pkgs.hostPlatform.isAarch64; - }).latestCompatibleLinuxPackages; - - boot.zfs.removeLinuxDRM = true; - } - ``` + Currently has no effect, but may again in future if a kernel + update breaks ZFS due to symbols being newly changed to GPL. ''; }; }; @@ -588,7 +576,7 @@ in kernelParams = lib.optionals (!config.boot.zfs.allowHibernation) [ "nohibernate" ]; extraModulePackages = [ - (cfgZfs.modulePackage.override { inherit (cfgZfs) removeLinuxDRM; }) + cfgZfs.modulePackage ]; }; @@ -725,21 +713,6 @@ in services.udev.packages = [ cfgZfs.package ]; # to hook zvol naming, etc. systemd.packages = [ cfgZfs.package ]; - # Export kernel_neon_* symbols again. - # This change is necessary until ZFS figures out a solution - # with upstream or in their build system to fill the gap for - # this symbol. - # In the meantime, we restore what was once a working piece of code - # in the kernel. - boot.kernelPatches = lib.optional (cfgZfs.removeLinuxDRM && pkgs.stdenv.hostPlatform.system == "aarch64-linux") { - name = "export-neon-symbols-as-gpl"; - patch = pkgs.fetchpatch { - url = "https://github.com/torvalds/linux/commit/aaeca98456431a8d9382ecf48ac4843e252c07b3.patch"; - hash = "sha256-L2g4G1tlWPIi/QRckMuHDcdWBcKpObSWSRTvbHRIwIk="; - revert = true; - }; - }; - systemd.services = let createImportService' = pool: createImportService { inherit pool; diff --git a/nixpkgs/nixos/modules/virtualisation/podman/default.nix b/nixpkgs/nixos/modules/virtualisation/podman/default.nix index 5a99dc8a1bb9..a97739054216 100644 --- a/nixpkgs/nixos/modules/virtualisation/podman/default.nix +++ b/nixpkgs/nixos/modules/virtualisation/podman/default.nix @@ -216,9 +216,11 @@ in requires = [ "podman.service" ]; }; + systemd.services.podman.environment = config.networking.proxy.envVars; systemd.sockets.podman.wantedBy = [ "sockets.target" ]; systemd.sockets.podman.socketConfig.SocketGroup = "podman"; + systemd.user.services.podman.environment = config.networking.proxy.envVars; systemd.user.sockets.podman.wantedBy = [ "sockets.target" ]; systemd.timers.podman-prune.timerConfig = lib.mkIf cfg.autoPrune.enable { diff --git a/nixpkgs/nixos/release-combined.nix b/nixpkgs/nixos/release-combined.nix index 7700441b1d6b..459700514ffc 100644 --- a/nixpkgs/nixos/release-combined.nix +++ b/nixpkgs/nixos/release-combined.nix @@ -142,7 +142,6 @@ in rec { (onFullSupported "nixos.tests.networking.networkd.virtual") (onFullSupported "nixos.tests.networking.networkd.vlan") (onFullSupported "nixos.tests.systemd-networkd-ipv6-prefix-delegation") - (onFullSupported "nixos.tests.nfs3.simple") (onFullSupported "nixos.tests.nfs4.simple") (onSystems ["x86_64-linux"] "nixos.tests.oci-containers.podman") (onFullSupported "nixos.tests.openssh") diff --git a/nixpkgs/nixos/release-small.nix b/nixpkgs/nixos/release-small.nix index 6204dc731ad9..cac20b63925f 100644 --- a/nixpkgs/nixos/release-small.nix +++ b/nixpkgs/nixos/release-small.nix @@ -43,7 +43,7 @@ in rec { login misc nat - nfs3 + nfs4 openssh php predictable-interface-names @@ -125,7 +125,7 @@ in rec { "nixos.tests.misc" "nixos.tests.nat.firewall" "nixos.tests.nat.standalone" - "nixos.tests.nfs3.simple" + "nixos.tests.nfs4.simple" "nixos.tests.openssh" "nixos.tests.php.fpm" "nixos.tests.php.pcre" diff --git a/nixpkgs/nixos/release.nix b/nixpkgs/nixos/release.nix index 2acc5ade7848..ff60b0b79f6d 100644 --- a/nixpkgs/nixos/release.nix +++ b/nixpkgs/nixos/release.nix @@ -177,6 +177,12 @@ in rec { inherit system; }); + iso_plasma6 = forMatchingSystems supportedSystems (system: makeIso { + module = ./modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix; + type = "plasma6"; + inherit system; + }); + iso_gnome = forMatchingSystems supportedSystems (system: makeIso { module = ./modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix; type = "gnome"; diff --git a/nixpkgs/nixos/tests/all-tests.nix b/nixpkgs/nixos/tests/all-tests.nix index 1144a5611dcf..231767ca2b97 100644 --- a/nixpkgs/nixos/tests/all-tests.nix +++ b/nixpkgs/nixos/tests/all-tests.nix @@ -128,6 +128,7 @@ in { appliance-repart-image = runTest ./appliance-repart-image.nix; apparmor = handleTest ./apparmor.nix {}; archi = handleTest ./archi.nix {}; + armagetronad = handleTest ./armagetronad.nix {}; atd = handleTest ./atd.nix {}; atop = handleTest ./atop.nix {}; atuin = handleTest ./atuin.nix {}; @@ -497,6 +498,7 @@ in { lxd = pkgs.recurseIntoAttrs (handleTest ./lxd { inherit handleTestOn; }); lxd-image-server = handleTest ./lxd-image-server.nix {}; #logstash = handleTest ./logstash.nix {}; + lomiri-system-settings = handleTest ./lomiri-system-settings.nix {}; lorri = handleTest ./lorri/default.nix {}; maddy = discoverTests (import ./maddy { inherit handleTest; }); maestral = handleTest ./maestral.nix {}; @@ -516,6 +518,7 @@ in { matrix-synapse = handleTest ./matrix/synapse.nix {}; matrix-synapse-workers = handleTest ./matrix/synapse-workers.nix {}; mattermost = handleTest ./mattermost.nix {}; + mealie = handleTest ./mealie.nix {}; mediamtx = handleTest ./mediamtx.nix {}; mediatomb = handleTest ./mediatomb.nix {}; mediawiki = handleTest ./mediawiki.nix {}; @@ -535,6 +538,7 @@ in { mobilizon = handleTest ./mobilizon.nix {}; mod_perl = handleTest ./mod_perl.nix {}; molly-brown = handleTest ./molly-brown.nix {}; + monado = handleTest ./monado.nix {}; monica = handleTest ./web-apps/monica.nix {}; mongodb = handleTest ./mongodb.nix {}; moodle = handleTest ./moodle.nix {}; @@ -692,6 +696,7 @@ in { plantuml-server = handleTest ./plantuml-server.nix {}; plasma-bigscreen = handleTest ./plasma-bigscreen.nix {}; plasma5 = handleTest ./plasma5.nix {}; + plasma6 = handleTest ./plasma6.nix {}; plasma5-systemd-start = handleTest ./plasma5-systemd-start.nix {}; plausible = handleTest ./plausible.nix {}; please = handleTest ./please.nix {}; diff --git a/nixpkgs/nixos/tests/armagetronad.nix b/nixpkgs/nixos/tests/armagetronad.nix new file mode 100644 index 000000000000..ff2841dedd21 --- /dev/null +++ b/nixpkgs/nixos/tests/armagetronad.nix @@ -0,0 +1,272 @@ +import ./make-test-python.nix ({ pkgs, ...} : + +let + user = "alice"; + + client = + { pkgs, ... }: + + { imports = [ ./common/user-account.nix ./common/x11.nix ]; + hardware.opengl.driSupport = true; + virtualisation.memorySize = 256; + environment = { + systemPackages = [ pkgs.armagetronad ]; + variables.XAUTHORITY = "/home/${user}/.Xauthority"; + }; + test-support.displayManager.auto.user = user; + }; + +in { + name = "armagetronad"; + meta = with pkgs.lib.maintainers; { + maintainers = [ numinit ]; + }; + + enableOCR = true; + + nodes = + { + server = { + services.armagetronad.servers = { + high-rubber = { + enable = true; + name = "Smoke Test High Rubber Server"; + port = 4534; + settings = { + SERVER_OPTIONS = "High Rubber server made to run smoke tests."; + CYCLE_RUBBER = 40; + SIZE_FACTOR = 0.5; + }; + roundSettings = { + SAY = [ + "NixOS Smoke Test Server" + "https://nixos.org" + ]; + }; + }; + sty = { + enable = true; + name = "Smoke Test sty+ct+ap Server"; + package = pkgs.armagetronad."0.2.9-sty+ct+ap".dedicated; + port = 4535; + settings = { + SERVER_OPTIONS = "sty+ct+ap server made to run smoke tests."; + CYCLE_RUBBER = 20; + SIZE_FACTOR = 0.5; + }; + roundSettings = { + SAY = [ + "NixOS Smoke Test sty+ct+ap Server" + "https://nixos.org" + ]; + }; + }; + trunk = { + enable = true; + name = "Smoke Test trunk Server"; + package = pkgs.armagetronad."0.4".dedicated; + port = 4536; + settings = { + SERVER_OPTIONS = "0.4 server made to run smoke tests."; + CYCLE_RUBBER = 20; + SIZE_FACTOR = 0.5; + }; + roundSettings = { + SAY = [ + "NixOS Smoke Test 0.4 Server" + "https://nixos.org" + ]; + }; + }; + }; + }; + + client1 = client; + client2 = client; + }; + + testScript = let + xdo = name: text: let + xdoScript = pkgs.writeText "${name}.xdo" text; + in "${pkgs.xdotool}/bin/xdotool ${xdoScript}"; + in + '' + import shlex + import threading + from collections import namedtuple + + class Client(namedtuple('Client', ('node', 'name'))): + def send(self, *keys): + for key in keys: + self.node.send_key(key) + + def send_on(self, text, *keys): + self.node.wait_for_text(text) + self.send(*keys) + + Server = namedtuple('Server', ('node', 'name', 'address', 'port', 'welcome', 'attacker', 'victim', 'coredump_delay')) + + # Clients and their in-game names + clients = ( + Client(client1, 'Arduino'), + Client(client2, 'SmOoThIcE') + ) + + # Server configs. + servers = ( + Server(server, 'high-rubber', 'server', 4534, 'NixOS Smoke Test Server', 'SmOoThIcE', 'Arduino', 8), + Server(server, 'sty', 'server', 4535, 'NixOS Smoke Test sty+ct+ap Server', 'Arduino', 'SmOoThIcE', 8), + Server(server, 'trunk', 'server', 4536, 'NixOS Smoke Test 0.4 Server', 'Arduino', 'SmOoThIcE', 8) + ) + + """ + Runs a command as the client user. + """ + def run(cmd): + return "su - ${user} -c " + shlex.quote(cmd) + + screenshot_idx = 1 + + """ + Takes screenshots on all clients. + """ + def take_screenshots(screenshot_idx): + for client in clients: + client.node.screenshot(f"screen_{client.name}_{screenshot_idx}") + return screenshot_idx + 1 + + # Wait for the servers to come up. + start_all() + for srv in servers: + srv.node.wait_for_unit(f"armagetronad-{srv.name}") + srv.node.wait_until_succeeds(f"ss --numeric --udp --listening | grep -q {srv.port}") + + # Make sure console commands work through the named pipe we created. + for srv in servers: + srv.node.succeed( + f"echo 'say Testing!' >> /var/lib/armagetronad/{srv.name}/input" + ) + srv.node.succeed( + f"echo 'say Testing again!' >> /var/lib/armagetronad/{srv.name}/input" + ) + srv.node.wait_until_succeeds( + f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: Testing!'" + ) + srv.node.wait_until_succeeds( + f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: Testing again!'" + ) + + """ + Sets up a client, waiting for the given barrier on completion. + """ + def client_setup(client, servers, barrier): + client.node.wait_for_x() + + # Configure Armagetron. + client.node.succeed( + run("mkdir -p ~/.armagetronad/var"), + run(f"echo 'PLAYER_1 {client.name}' >> ~/.armagetronad/var/autoexec.cfg") + ) + for idx, srv in enumerate(servers): + client.node.succeed( + run(f"echo 'BOOKMARK_{idx+1}_ADDRESS {srv.address}' >> ~/.armagetronad/var/autoexec.cfg"), + run(f"echo 'BOOKMARK_{idx+1}_NAME {srv.name}' >> ~/.armagetronad/var/autoexec.cfg"), + run(f"echo 'BOOKMARK_{idx+1}_PORT {srv.port}' >> ~/.armagetronad/var/autoexec.cfg") + ) + + # Start Armagetron. + client.node.succeed(run("ulimit -c unlimited; armagetronad >&2 & disown")) + client.node.wait_until_succeeds( + run( + "${xdo "create_new_win-select_main_window" '' + search --onlyvisible --name "Armagetron Advanced" + windowfocus --sync + windowactivate --sync + ''}" + ) + ) + + # Get through the tutorial. + client.send_on('Language Settings', 'ret') + client.send_on('First Setup', 'ret') + client.send_on('Welcome to Armagetron Advanced', 'ret') + client.send_on('round 1', 'esc') + client.send_on('Menu', 'up', 'up', 'ret') + client.send_on('We hope you', 'ret') + client.send_on('Armagetron Advanced', 'ret') + client.send_on('Play Game', 'ret') + + # Online > LAN > Network Setup > Mates > Server Bookmarks + client.send_on('Multiplayer', 'down', 'down', 'down', 'down', 'ret') + + barrier.wait() + + # Get to the Server Bookmarks screen on both clients. This takes a while so do it asynchronously. + barrier = threading.Barrier(3, timeout=120) + for client in clients: + threading.Thread(target=client_setup, args=(client, servers, barrier)).start() + barrier.wait() + + # Main testing loop. Iterates through each server bookmark and connects to them in sequence. + # Assumes that the game is currently on the Server Bookmarks screen. + for srv in servers: + screenshot_idx = take_screenshots(screenshot_idx) + + # Connect both clients at once, one second apart. + for client in clients: + client.send('ret') + client.node.sleep(1) + + # Wait for clients to connect + for client in clients: + srv.node.wait_until_succeeds( + f"journalctl -u armagetronad-{srv.name} -e | grep -q '{client.name}.*entered the game'" + ) + + # Wait for the match to start + srv.node.wait_until_succeeds( + f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: {srv.welcome}'" + ) + srv.node.wait_until_succeeds( + f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: https://nixos.org'" + ) + srv.node.wait_until_succeeds( + f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Go (round 1 of 10)'" + ) + + # Wait a bit + srv.node.sleep(srv.coredump_delay) + + # Turn the attacker player's lightcycle left + attacker = next(client for client in clients if client.name == srv.attacker) + victim = next(client for client in clients if client.name == srv.victim) + attacker.send('left') + screenshot_idx = take_screenshots(screenshot_idx) + + # Wait for coredump. + srv.node.wait_until_succeeds( + f"journalctl -u armagetronad-{srv.name} -e | grep -q '{attacker.name} core dumped {victim.name}'" + ) + screenshot_idx = take_screenshots(screenshot_idx) + + # Disconnect both clients from the server + for client in clients: + client.send('esc') + client.send_on('Menu', 'up', 'up', 'ret') + srv.node.wait_until_succeeds( + f"journalctl -u armagetronad-{srv.name} -e | grep -q '{client.name}.*left the game'" + ) + + # Next server. + for client in clients: + client.send_on('Server Bookmarks', 'down') + + # Stop the servers + for srv in servers: + srv.node.succeed( + f"systemctl stop armagetronad-{srv.name}" + ) + srv.node.wait_until_fails(f"ss --numeric --udp --listening | grep -q {srv.port}") + ''; + +}) diff --git a/nixpkgs/nixos/tests/boot.nix b/nixpkgs/nixos/tests/boot.nix index ec2a9f6527c9..56f72dddf526 100644 --- a/nixpkgs/nixos/tests/boot.nix +++ b/nixpkgs/nixos/tests/boot.nix @@ -4,10 +4,41 @@ }: with import ../lib/testing-python.nix { inherit system pkgs; }; -with pkgs.lib; let - qemu-common = import ../lib/qemu-common.nix { inherit (pkgs) lib pkgs; }; + lib = pkgs.lib; + qemu-common = import ../lib/qemu-common.nix { inherit lib pkgs; }; + + mkStartCommand = { + memory ? 2048, + cdrom ? null, + usb ? null, + pxe ? null, + uboot ? false, + uefi ? false, + extraFlags ? [], + }: let + qemu = qemu-common.qemuBinary pkgs.qemu_test; + + flags = [ + "-m" (toString memory) + "-netdev" ("user,id=net0" + (lib.optionalString (pxe != null) ",tftp=${pxe},bootfile=netboot.ipxe")) + "-device" ("virtio-net-pci,netdev=net0" + (lib.optionalString (pxe != null && uefi) ",romfile=${pkgs.ipxe}/ipxe.efirom")) + ] ++ lib.optionals (cdrom != null) [ + "-cdrom" cdrom + ] ++ lib.optionals (usb != null) [ + "-device" "usb-ehci" + "-drive" "id=usbdisk,file=${usb},if=none,readonly" + "-device" "usb-storage,drive=usbdisk" + ] ++ lib.optionals (pxe != null) [ + "-boot" "order=n" + ] ++ lib.optionals uefi [ + "-drive" "if=pflash,format=raw,unit=0,readonly=on,file=${pkgs.OVMF.firmware}" + "-drive" "if=pflash,format=raw,unit=1,readonly=on,file=${pkgs.OVMF.variables}" + ] ++ extraFlags; + + flagsStr = lib.concatStringsSep " " flags; + in "${qemu} ${flagsStr}"; iso = (import ../lib/eval-config.nix { @@ -28,21 +59,16 @@ let ]; }).config.system.build.sdImage; - pythonDict = params: "\n {\n ${concatStringsSep ",\n " (mapAttrsToList (name: param: "\"${name}\": \"${param}\"") params)},\n }\n"; - - makeBootTest = name: extraConfig: + makeBootTest = name: config: let - machineConfig = pythonDict ({ - qemuBinary = qemu-common.qemuBinary pkgs.qemu_test; - qemuFlags = "-m 768"; - } // extraConfig); + startCommand = mkStartCommand config; in makeTest { name = "boot-" + name; nodes = { }; testScript = '' - machine = create_machine(${machineConfig}) + machine = create_machine("${startCommand}") machine.start() machine.wait_for_unit("multi-user.target") machine.succeed("nix store verify --no-trust -r --option experimental-features nix-command /run/current-system") @@ -73,43 +99,35 @@ let config.system.build.netbootIpxeScript ]; }; - machineConfig = pythonDict ({ - qemuBinary = qemu-common.qemuBinary pkgs.qemu_test; - qemuFlags = "-boot order=n -m 2000"; - netBackendArgs = "tftp=${ipxeBootDir},bootfile=netboot.ipxe"; + startCommand = mkStartCommand ({ + pxe = ipxeBootDir; } // extraConfig); in makeTest { name = "boot-netboot-" + name; nodes = { }; testScript = '' - machine = create_machine(${machineConfig}) + machine = create_machine("${startCommand}") machine.start() machine.wait_for_unit("multi-user.target") machine.shutdown() ''; }; - uefiBinary = { - x86_64-linux = "${pkgs.OVMF.fd}/FV/OVMF.fd"; - aarch64-linux = "${pkgs.OVMF.fd}/FV/QEMU_EFI.fd"; - }.${pkgs.stdenv.hostPlatform.system}; in { uefiCdrom = makeBootTest "uefi-cdrom" { + uefi = true; cdrom = "${iso}/iso/${iso.isoName}"; - bios = uefiBinary; }; uefiUsb = makeBootTest "uefi-usb" { + uefi = true; usb = "${iso}/iso/${iso.isoName}"; - bios = uefiBinary; }; uefiNetboot = makeNetbootTest "uefi" { - bios = uefiBinary; - # Custom ROM is needed for EFI PXE boot. I failed to understand exactly why, because QEMU should still use iPXE for EFI. - netFrontendArgs = "romfile=${pkgs.ipxe}/ipxe.efirom"; + uefi = true; }; -} // optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") { +} // lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") { biosCdrom = makeBootTest "bios-cdrom" { cdrom = "${iso}/iso/${iso.isoName}"; }; @@ -124,9 +142,12 @@ in { sdImage = "${sd}/sd-image/${sd.imageName}"; mutableImage = "/tmp/linked-image.qcow2"; - machineConfig = pythonDict { - bios = "${pkgs.ubootQemuX86}/u-boot.rom"; - qemuFlags = "-m 768 -machine type=pc,accel=tcg -drive file=${mutableImage},if=ide,format=qcow2"; + startCommand = mkStartCommand { + extraFlags = [ + "-bios" "${pkgs.ubootQemuX86}/u-boot.rom" + "-machine" "type=pc,accel=tcg" + "-drive" "file=${mutableImage},if=virtio" + ]; }; in makeTest { name = "boot-uboot-extlinux"; @@ -138,11 +159,14 @@ in { if os.system("qemu-img create -f qcow2 -F raw -b ${sdImage} ${mutableImage}") != 0: raise RuntimeError("Could not create mutable linked image") - machine = create_machine(${machineConfig}) + machine = create_machine("${startCommand}") machine.start() machine.wait_for_unit("multi-user.target") machine.succeed("nix store verify -r --no-trust --option experimental-features nix-command /run/current-system") machine.shutdown() ''; + + # kernel can't find rootfs after boot - investigate? + meta.broken = true; }; } diff --git a/nixpkgs/nixos/tests/common/ec2.nix b/nixpkgs/nixos/tests/common/ec2.nix index 1a64c464039b..82922102f07b 100644 --- a/nixpkgs/nixos/tests/common/ec2.nix +++ b/nixpkgs/nixos/tests/common/ec2.nix @@ -61,7 +61,7 @@ with pkgs.lib; + " $QEMU_OPTS" ) - machine = create_machine({"startCommand": start_command}) + machine = create_machine(start_command) try: '' + indentLines script + '' finally: diff --git a/nixpkgs/nixos/tests/consul.nix b/nixpkgs/nixos/tests/consul.nix index 6233234ff083..c819312068dc 100644 --- a/nixpkgs/nixos/tests/consul.nix +++ b/nixpkgs/nixos/tests/consul.nix @@ -42,6 +42,8 @@ let ]; networking.firewall = firewallSettings; + nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "consul" ]; + services.consul = { enable = true; inherit webUi; @@ -65,6 +67,8 @@ let ]; networking.firewall = firewallSettings; + nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "consul" ]; + services.consul = assert builtins.elem thisConsensusServerHost allConsensusServerHosts; { diff --git a/nixpkgs/nixos/tests/docker-tools.nix b/nixpkgs/nixos/tests/docker-tools.nix index f252eb9ff61e..7d91076600f9 100644 --- a/nixpkgs/nixos/tests/docker-tools.nix +++ b/nixpkgs/nixos/tests/docker-tools.nix @@ -58,6 +58,20 @@ let ''; config.Cmd = [ "${pkgs.coreutils}/bin/stat" "-c" "%u:%g" "/testfile" ]; }; + + nonRootTestImage = + pkgs.dockerTools.streamLayeredImage rec { + name = "non-root-test"; + tag = "latest"; + uid = 1000; + gid = 1000; + uname = "user"; + gname = "user"; + config = { + User = "user"; + Cmd = [ "${pkgs.coreutils}/bin/stat" "-c" "%u:%g" "${pkgs.coreutils}/bin/stat" ]; + }; + }; in { name = "docker-tools"; meta = with pkgs.lib.maintainers; { @@ -181,7 +195,7 @@ in { ): docker.succeed( "docker load --input='${examples.bashLayeredWithUser}'", - "docker run -u somebody --rm ${examples.bashLayeredWithUser.imageName} ${pkgs.bash}/bin/bash -c 'test 555 == $(stat --format=%a /nix) && test 555 == $(stat --format=%a /nix/store)'", + "docker run -u somebody --rm ${examples.bashLayeredWithUser.imageName} ${pkgs.bash}/bin/bash -c 'test 755 == $(stat --format=%a /nix) && test 755 == $(stat --format=%a /nix/store)'", "docker rmi ${examples.bashLayeredWithUser.imageName}", ) @@ -604,5 +618,11 @@ in { "${chownTestImage} | docker load", "docker run --rm ${chownTestImage.imageName} | diff /dev/stdin <(echo 12345:12345)" ) + + with subtest("streamLayeredImage: with non-root user"): + docker.succeed( + "${nonRootTestImage} | docker load", + "docker run --rm ${chownTestImage.imageName} | diff /dev/stdin <(echo 12345:12345)" + ) ''; }) diff --git a/nixpkgs/nixos/tests/incus/container.nix b/nixpkgs/nixos/tests/incus/container.nix index 0f42d16f133d..eb00429e53fe 100644 --- a/nixpkgs/nixos/tests/incus/container.nix +++ b/nixpkgs/nixos/tests/incus/container.nix @@ -5,6 +5,8 @@ let configuration = { # Building documentation makes the test unnecessarily take a longer time: documentation.enable = lib.mkForce false; + + boot.kernel.sysctl."net.ipv4.ip_forward" = "1"; } // extra; }; @@ -40,6 +42,12 @@ in with machine.nested("Waiting for instance to start and be usable"): retry(instance_is_up) + def check_sysctl(instance): + with subtest("systemd sysctl settings are applied"): + machine.succeed(f"incus exec {instance} -- systemctl status systemd-sysctl") + sysctl = machine.succeed(f"incus exec {instance} -- sysctl net.ipv4.ip_forward").strip().split(" ")[-1] + assert "1" == sysctl, f"systemd-sysctl configuration not correctly applied, {sysctl} != 1" + machine.wait_for_unit("incus.service") # no preseed should mean no service @@ -83,6 +91,7 @@ in with subtest("lxc-container generator configures plain container"): # reuse the existing container to save some time machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf") + check_sysctl("container") with subtest("lxc-container generator configures nested container"): machine.execute("incus delete --force container") @@ -94,6 +103,8 @@ in target = machine.succeed("incus exec container readlink -- -f /run/systemd/system/systemd-binfmt.service").strip() assert target == "/dev/null", "lxc generator did not correctly mask /run/systemd/system/systemd-binfmt.service" + check_sysctl("container") + with subtest("lxc-container generator configures privileged container"): machine.execute("incus delete --force container") machine.succeed("incus launch nixos container --config security.privileged=true") @@ -101,5 +112,7 @@ in retry(instance_is_up) machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf") + + check_sysctl("container") ''; }) diff --git a/nixpkgs/nixos/tests/installer.nix b/nixpkgs/nixos/tests/installer.nix index b6cb6a0c6d45..97bb7f8def59 100644 --- a/nixpkgs/nixos/tests/installer.nix +++ b/nixpkgs/nixos/tests/installer.nix @@ -83,46 +83,34 @@ let , postInstallCommands, preBootCommands, postBootCommands, extraConfig , testSpecialisationConfig, testFlakeSwitch, clevisTest, clevisFallbackTest }: - let iface = "virtio"; - isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi); - bios = if pkgs.stdenv.isAarch64 then "QEMU_EFI.fd" else "OVMF.fd"; + let + qemu-common = import ../lib/qemu-common.nix { inherit (pkgs) lib pkgs; }; + isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi); + qemu = qemu-common.qemuBinary pkgs.qemu_test; in if !isEfi && !pkgs.stdenv.hostPlatform.isx86 then '' machine.succeed("true") '' else '' + import os import subprocess - tpm_folder = os.environ['NIX_BUILD_TOP'] - def assemble_qemu_flags(): - flags = "-cpu max" - ${if (system == "x86_64-linux" || system == "i686-linux") - then ''flags += " -m 1024"'' - else ''flags += " -m 768 -enable-kvm -machine virt,gic-version=host"'' - } - ${optionalString clevisTest ''flags += f" -chardev socket,id=chrtpm,path={tpm_folder}/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0"''} - ${optionalString clevisTest ''flags += " -device virtio-net-pci,netdev=vlan1,mac=52:54:00:12:11:02 -netdev vde,id=vlan1,sock=\"$QEMU_VDE_SOCKET_1\""''} - return flags + tpm_folder = os.environ['NIX_BUILD_TOP'] - qemu_flags = {"qemuFlags": assemble_qemu_flags()} + startcommand = "${qemu} -m 2048" - import os + ${optionalString clevisTest '' + startcommand += f" -chardev socket,id=chrtpm,path={tpm_folder}/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0" + startcommand += " -device virtio-net-pci,netdev=vlan1,mac=52:54:00:12:11:02 -netdev vde,id=vlan1,sock=\"$QEMU_VDE_SOCKET_1\"" + ''} + ${optionalString isEfi '' + startcommand +=" -drive if=pflash,format=raw,unit=0,readonly=on,file=${pkgs.OVMF.firmware} -drive if=pflash,format=raw,unit=1,readonly=on,file=${pkgs.OVMF.variables}" + ''} image_dir = machine.state_dir disk_image = os.path.join(image_dir, "machine.qcow2") - - hd_flags = { - "hdaInterface": "${iface}", - "hda": disk_image, - } - ${optionalString isEfi '' - hd_flags.update( - bios="${pkgs.OVMF.fd}/FV/${bios}" - )'' - } - default_flags = {**hd_flags, **qemu_flags} - + startcommand += f" -drive file={disk_image},if=virtio,werror=report" def create_machine_named(name): - return create_machine({**default_flags, "name": name}) + return create_machine(startcommand, name=name) class Tpm: def __init__(self): @@ -471,7 +459,7 @@ let # builds stuff in the VM, needs more juice virtualisation.diskSize = 8 * 1024; virtualisation.cores = 8; - virtualisation.memorySize = 1536; + virtualisation.memorySize = 2048; boot.initrd.systemd.enable = systemdStage1; diff --git a/nixpkgs/nixos/tests/k3s/default.nix b/nixpkgs/nixos/tests/k3s/default.nix index e168f8233c76..512dc06ee77e 100644 --- a/nixpkgs/nixos/tests/k3s/default.nix +++ b/nixpkgs/nixos/tests/k3s/default.nix @@ -6,6 +6,11 @@ let allK3s = lib.filterAttrs (n: _: lib.strings.hasPrefix "k3s_" n) pkgs; in { + # Testing K3s with Etcd backend + etcd = lib.mapAttrs (_: k3s: import ./etcd.nix { + inherit system pkgs k3s; + inherit (pkgs) etcd; + }) allK3s; # Run a single node k3s cluster and verify a pod can run single-node = lib.mapAttrs (_: k3s: import ./single-node.nix { inherit system pkgs k3s; }) allK3s; # Run a multi-node k3s cluster and verify pod networking works across nodes diff --git a/nixpkgs/nixos/tests/k3s/etcd.nix b/nixpkgs/nixos/tests/k3s/etcd.nix new file mode 100644 index 000000000000..d6e9a294adb1 --- /dev/null +++ b/nixpkgs/nixos/tests/k3s/etcd.nix @@ -0,0 +1,100 @@ +import ../make-test-python.nix ({ pkgs, lib, k3s, etcd, ... }: + +{ + name = "${k3s.name}-etcd"; + + nodes = { + + etcd = { ... }: { + services.etcd = { + enable = true; + openFirewall = true; + listenClientUrls = [ "http://192.168.1.1:2379" "http://127.0.0.1:2379" ]; + listenPeerUrls = [ "http://192.168.1.1:2380" ]; + initialAdvertisePeerUrls = [ "http://192.168.1.1:2380" ]; + initialCluster = [ "etcd=http://192.168.1.1:2380" ]; + }; + networking = { + useDHCP = false; + defaultGateway = "192.168.1.1"; + interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [ + { address = "192.168.1.1"; prefixLength = 24; } + ]; + }; + }; + + k3s = { pkgs, ... }: { + environment.systemPackages = with pkgs; [ jq ]; + # k3s uses enough resources the default vm fails. + virtualisation.memorySize = 1536; + virtualisation.diskSize = 4096; + + services.k3s = { + enable = true; + role = "server"; + extraFlags = builtins.toString [ + "--datastore-endpoint=\"http://192.168.1.1:2379\"" + "--disable" "coredns" + "--disable" "local-storage" + "--disable" "metrics-server" + "--disable" "servicelb" + "--disable" "traefik" + "--node-ip" "192.168.1.2" + ]; + }; + + networking = { + firewall = { + allowedTCPPorts = [ 2379 2380 6443 ]; + allowedUDPPorts = [ 8472 ]; + }; + useDHCP = false; + defaultGateway = "192.168.1.2"; + interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [ + { address = "192.168.1.2"; prefixLength = 24; } + ]; + }; + }; + + }; + + testScript = '' + with subtest("should start etcd"): + etcd.start() + etcd.wait_for_unit("etcd.service") + + with subtest("should wait for etcdctl endpoint status to succeed"): + etcd.wait_until_succeeds("etcdctl endpoint status") + + with subtest("should start k3s"): + k3s.start() + k3s.wait_for_unit("k3s") + + with subtest("should test if kubectl works"): + k3s.wait_until_succeeds("k3s kubectl get node") + + with subtest("should wait for service account to show up; takes a sec"): + k3s.wait_until_succeeds("k3s kubectl get serviceaccount default") + + with subtest("should create a sample secret object"): + k3s.succeed("k3s kubectl create secret generic nixossecret --from-literal thesecret=abacadabra") + + with subtest("should check if secret is correct"): + k3s.wait_until_succeeds("[[ $(kubectl get secrets nixossecret -o json | jq -r .data.thesecret | base64 -d) == abacadabra ]]") + + with subtest("should have a secret in database"): + etcd.wait_until_succeeds("[[ $(etcdctl get /registry/secrets/default/nixossecret | head -c1 | wc -c) -ne 0 ]]") + + with subtest("should delete the secret"): + k3s.succeed("k3s kubectl delete secret nixossecret") + + with subtest("should not have a secret in database"): + etcd.wait_until_fails("[[ $(etcdctl get /registry/secrets/default/nixossecret | head -c1 | wc -c) -ne 0 ]]") + + with subtest("should shutdown k3s and etcd"): + k3s.shutdown() + etcd.shutdown() + ''; + + meta.maintainers = etcd.meta.maintainers ++ k3s.meta.maintainers; +}) diff --git a/nixpkgs/nixos/tests/kernel-generic.nix b/nixpkgs/nixos/tests/kernel-generic.nix index 0dcab39f3fad..9714a94382ee 100644 --- a/nixpkgs/nixos/tests/kernel-generic.nix +++ b/nixpkgs/nixos/tests/kernel-generic.nix @@ -30,7 +30,6 @@ let linux_5_10_hardened linux_5_15_hardened linux_6_1_hardened - linux_6_5_hardened linux_6_6_hardened linux_6_7_hardened linux_rt_5_4 diff --git a/nixpkgs/nixos/tests/lomiri-system-settings.nix b/nixpkgs/nixos/tests/lomiri-system-settings.nix new file mode 100644 index 000000000000..867fc14797e7 --- /dev/null +++ b/nixpkgs/nixos/tests/lomiri-system-settings.nix @@ -0,0 +1,99 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "lomiri-system-settings-standalone"; + meta.maintainers = lib.teams.lomiri.members; + + nodes.machine = { config, pkgs, ... }: { + imports = [ + ./common/x11.nix + ]; + + services.xserver.enable = true; + + environment = { + systemPackages = with pkgs.lomiri; [ + suru-icon-theme + lomiri-system-settings + ]; + variables = { + UITK_ICON_THEME = "suru"; + }; + }; + + i18n.supportedLocales = [ "all" ]; + + fonts.packages = with pkgs; [ + # Intended font & helps with OCR + ubuntu_font_family + ]; + + services.upower.enable = true; + }; + + enableOCR = true; + + testScript = let + settingsPages = [ + # Base pages + { name = "wifi"; type = "internal"; element = "networks"; } + { name = "bluetooth"; type = "internal"; element = "discoverable|None detected"; } + # only text we can really look for with VPN is on a button, OCR on CI struggles with it + { name = "vpn"; type = "internal"; element = "Add|Manual|Configuration"; skipOCR = true; } + { name = "appearance"; type = "internal"; element = "Background image|blur effects"; } + { name = "desktop"; type = "internal"; element = "workspaces|Icon size"; } + { name = "sound"; type = "internal"; element = "Silent Mode|Message sound"; } + { name = "language"; type = "internal"; element = "Display language|External keyboard"; } + { name = "notification"; type = "internal"; element = "Apps that notify"; } + { name = "gestures"; type = "internal"; element = "Edge drag"; } + { name = "mouse"; type = "internal"; element = "Cursor speed|Wheel scrolling speed"; } + { name = "timedate"; type = "internal"; element = "Time zone|Set the time and date"; } + + # External plugins + { name = "security-privacy"; type = "external"; element = "Locking|unlocking|permissions"; elementLocalised = "Sperren|Entsperren|Berechtigungen"; } + ]; + in + '' + machine.wait_for_x() + + with subtest("lomiri system settings launches"): + machine.execute("lomiri-system-settings >&2 &") + machine.wait_for_text("System Settings") + machine.screenshot("lss_open") + + # Move focus to start of plugins list for following list of tests + machine.send_key("tab") + machine.send_key("tab") + machine.screenshot("lss_focus") + + # tab through & open all sub-menus, to make sure none of them fail + '' + (lib.strings.concatMapStringsSep "\n" (page: '' + machine.send_key("tab") + machine.send_key("kp_enter") + '' + + lib.optionalString (!(page.skipOCR or false)) '' + with subtest("lomiri system settings ${page.name} works"): + machine.wait_for_text(r"(${page.element})") + machine.screenshot("lss_page_${page.name}") + '') settingsPages) + '' + + machine.execute("pkill -f lomiri-system-settings") + + with subtest("lomiri system settings localisation works"): + machine.execute("env LANG=de_DE.UTF-8 lomiri-system-settings >&2 &") + machine.wait_for_text("Systemeinstellungen") + machine.screenshot("lss_localised_open") + + # Move focus to start of plugins list for following list of tests + machine.send_key("tab") + machine.send_key("tab") + machine.screenshot("lss_focus_localised") + + '' + (lib.strings.concatMapStringsSep "\n" (page: '' + machine.send_key("tab") + machine.send_key("kp_enter") + '' + lib.optionalString (page.type == "external") '' + with subtest("lomiri system settings ${page.name} localisation works"): + machine.wait_for_text(r"(${page.elementLocalised})") + machine.screenshot("lss_localised_page_${page.name}") + '') settingsPages) + '' + ''; +}) diff --git a/nixpkgs/nixos/tests/mealie.nix b/nixpkgs/nixos/tests/mealie.nix new file mode 100644 index 000000000000..88f749c71294 --- /dev/null +++ b/nixpkgs/nixos/tests/mealie.nix @@ -0,0 +1,24 @@ +import ./make-test-python.nix ({ pkgs, ...} : + +{ + name = "mealie"; + meta = with pkgs.lib.maintainers; { + maintainers = [ litchipi ]; + }; + + nodes = { + server = { + services.mealie = { + enable = true; + port = 9001; + }; + }; + }; + + testScript = '' + start_all() + server.wait_for_unit("mealie.service") + server.wait_for_open_port(9001) + server.succeed("curl --fail http://localhost:9001") + ''; +}) diff --git a/nixpkgs/nixos/tests/monado.nix b/nixpkgs/nixos/tests/monado.nix new file mode 100644 index 000000000000..8368950951e7 --- /dev/null +++ b/nixpkgs/nixos/tests/monado.nix @@ -0,0 +1,39 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "monado"; + + nodes.machine = + { pkgs, ... }: + + { + hardware.opengl.enable = true; + users.users.alice = { + isNormalUser = true; + uid = 1000; + }; + + services.monado = { + enable = true; + defaultRuntime = true; + }; + # Stop Monado from probing for any hardware + systemd.user.services.monado.environment.SIMULATED_ENABLE = "1"; + + environment.systemPackages = with pkgs; [ openxr-loader ]; + }; + + testScript = { nodes, ... }: + let + userId = toString nodes.machine.users.users.alice.uid; + runtimePath = "/run/user/${userId}"; + in + '' + machine.succeed("loginctl enable-linger alice") + machine.wait_for_unit("user@${userId}.service") + + machine.wait_for_unit("monado.socket", "alice") + machine.systemctl("start monado.service", "alice") + machine.wait_for_unit("monado.service", "alice") + + machine.succeed("su -- alice -c env XDG_RUNTIME_DIR=${runtimePath} openxr_runtime_list") + ''; +}) diff --git a/nixpkgs/nixos/tests/plasma6.nix b/nixpkgs/nixos/tests/plasma6.nix new file mode 100644 index 000000000000..ec5b3f24ef74 --- /dev/null +++ b/nixpkgs/nixos/tests/plasma6.nix @@ -0,0 +1,64 @@ +import ./make-test-python.nix ({ pkgs, ...} : + +{ + name = "plasma6"; + meta = with pkgs.lib.maintainers; { + maintainers = [ k900 ]; + }; + + nodes.machine = { ... }: + + { + imports = [ ./common/user-account.nix ]; + services.xserver.enable = true; + services.xserver.displayManager.sddm.enable = true; + # FIXME: this should be testing Wayland + services.xserver.displayManager.defaultSession = "plasmax11"; + services.xserver.desktopManager.plasma6.enable = true; + environment.plasma6.excludePackages = [ pkgs.kdePackages.elisa ]; + services.xserver.displayManager.autoLogin = { + enable = true; + user = "alice"; + }; + }; + + testScript = { nodes, ... }: let + user = nodes.machine.users.users.alice; + xdo = "${pkgs.xdotool}/bin/xdotool"; + in '' + with subtest("Wait for login"): + start_all() + machine.wait_for_file("/tmp/xauth_*") + machine.succeed("xauth merge /tmp/xauth_*") + + with subtest("Check plasmashell started"): + machine.wait_until_succeeds("pgrep plasmashell") + machine.wait_for_window("^Desktop ") + + with subtest("Check that KDED is running"): + machine.succeed("pgrep kded6") + + with subtest("Ensure Elisa is not installed"): + machine.fail("which elisa") + + machine.succeed("su - ${user.name} -c 'xauth merge /tmp/xauth_*'") + + with subtest("Run Dolphin"): + machine.execute("su - ${user.name} -c 'DISPLAY=:0.0 dolphin >&2 &'") + machine.wait_for_window(" Dolphin") + + with subtest("Run Konsole"): + machine.execute("su - ${user.name} -c 'DISPLAY=:0.0 konsole >&2 &'") + machine.wait_for_window("Konsole") + + with subtest("Run systemsettings"): + machine.execute("su - ${user.name} -c 'DISPLAY=:0.0 systemsettings >&2 &'") + machine.wait_for_window("Settings") + + with subtest("Wait to get a screenshot"): + machine.execute( + "${xdo} key Alt+F1 sleep 10" + ) + machine.screenshot("screen") + ''; +}) diff --git a/nixpkgs/nixos/tests/searx.nix b/nixpkgs/nixos/tests/searx.nix index 2f808cb65266..02a88f690db7 100644 --- a/nixpkgs/nixos/tests/searx.nix +++ b/nixpkgs/nixos/tests/searx.nix @@ -36,7 +36,7 @@ import ./make-test-python.nix ({ pkgs, ...} : }; # fancy setup: run in uWSGI and use nginx as proxy - nodes.fancy = { ... }: { + nodes.fancy = { config, ... }: { imports = [ ../modules/profiles/minimal.nix ]; services.searx = { @@ -65,7 +65,7 @@ import ./make-test-python.nix ({ pkgs, ...} : include ${pkgs.nginx}/conf/uwsgi_params; uwsgi_pass unix:/run/searx/uwsgi.sock; ''; - locations."/searx/static/".alias = "${pkgs.searx}/share/static/"; + locations."/searx/static/".alias = "${config.services.searx.package}/share/static/"; }; # allow nginx access to the searx socket @@ -108,7 +108,7 @@ import ./make-test-python.nix ({ pkgs, ...} : "${pkgs.curl}/bin/curl --fail http://localhost/searx >&2" ) fancy.succeed( - "${pkgs.curl}/bin/curl --fail http://localhost/searx/static/themes/oscar/js/bootstrap.min.js >&2" + "${pkgs.curl}/bin/curl --fail http://localhost/searx/static/themes/simple/js/leaflet.js >&2" ) ''; }) diff --git a/nixpkgs/nixos/tests/systemd-boot.nix b/nixpkgs/nixos/tests/systemd-boot.nix index ce3245f3d862..1b7e83253e59 100644 --- a/nixpkgs/nixos/tests/systemd-boot.nix +++ b/nixpkgs/nixos/tests/systemd-boot.nix @@ -14,6 +14,72 @@ let boot.loader.efi.canTouchEfiVariables = true; environment.systemPackages = [ pkgs.efibootmgr ]; }; + + commonXbootldr = { config, lib, pkgs, ... }: + let + diskImage = import ../lib/make-disk-image.nix { + inherit config lib pkgs; + label = "nixos"; + format = "qcow2"; + partitionTableType = "efixbootldr"; + touchEFIVars = true; + installBootLoader = true; + }; + in + { + imports = [ common ]; + virtualisation.useBootLoader = lib.mkForce false; # Only way to tell qemu-vm not to create the default system image + virtualisation.directBoot.enable = false; # But don't direct boot either because we're testing systemd-boot + + system.build.diskImage = diskImage; # Use custom disk image with an XBOOTLDR partition + virtualisation.efi.variables = "${diskImage}/efi-vars.fd"; + + virtualisation.useDefaultFilesystems = false; # Needs custom setup for `diskImage` + virtualisation.bootPartition = null; + virtualisation.fileSystems = { + "/" = { + device = "/dev/vda3"; + fsType = "ext4"; + }; + "/boot" = { + device = "/dev/vda2"; + fsType = "vfat"; + noCheck = true; + }; + "/efi" = { + device = "/dev/vda1"; + fsType = "vfat"; + noCheck = true; + }; + }; + + boot.loader.systemd-boot.enable = true; + boot.loader.efi.efiSysMountPoint = "/efi"; + boot.loader.systemd-boot.xbootldrMountPoint = "/boot"; + }; + + customDiskImage = nodes: '' + import os + import subprocess + import tempfile + + tmp_disk_image = tempfile.NamedTemporaryFile() + + subprocess.run([ + "${nodes.machine.virtualisation.qemu.package}/bin/qemu-img", + "create", + "-f", + "qcow2", + "-b", + "${nodes.machine.system.build.diskImage}/nixos.qcow2", + "-F", + "qcow2", + tmp_disk_image.name, + ]) + + # Set NIX_DISK_IMAGE so that the qemu script finds the right disk image. + os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name + ''; in { basic = makeTest { @@ -65,6 +131,32 @@ in ''; }; + basicXbootldr = makeTest { + name = "systemd-boot-xbootldr"; + meta.maintainers = with pkgs.lib.maintainers; [ sdht0 ]; + + nodes.machine = commonXbootldr; + + testScript = { nodes, ... }: '' + ${customDiskImage nodes} + + machine.start() + machine.wait_for_unit("multi-user.target") + + machine.succeed("test -e /efi/EFI/systemd/systemd-bootx64.efi") + machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf") + + # Ensure we actually booted using systemd-boot + # Magic number is the vendor UUID used by systemd-boot. + machine.succeed( + "test -e /sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" + ) + + # "bootctl install" should have created an EFI entry + machine.succeed('efibootmgr | grep "Linux Boot Manager"') + ''; + }; + # Check that specialisations create corresponding boot entries. specialisation = makeTest { name = "systemd-boot-specialisation"; @@ -184,6 +276,29 @@ in ''; }; + entryFilenameXbootldr = makeTest { + name = "systemd-boot-entry-filename-xbootldr"; + meta.maintainers = with pkgs.lib.maintainers; [ sdht0 ]; + + nodes.machine = { pkgs, lib, ... }: { + imports = [ commonXbootldr ]; + boot.loader.systemd-boot.memtest86.enable = true; + boot.loader.systemd-boot.memtest86.entryFilename = "apple.conf"; + }; + + testScript = { nodes, ... }: '' + ${customDiskImage nodes} + + machine.start() + 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/EFI/memtest86/memtest.efi") + ''; + }; + extraEntries = makeTest { name = "systemd-boot-extra-entries"; meta.maintainers = with pkgs.lib.maintainers; [ Enzime julienmalka ]; |