From 1fc22894545f5adf915e245b3c3e92639fd70f64 Mon Sep 17 00:00:00 2001 From: Matt Sturgeon Date: Wed, 25 Jun 2025 01:13:25 +0100 Subject: [PATCH] stylix: extract testbed modules to `autoload.nix` (#1520) Evaluated the set of testbed modules in a separate file from where we create the testbed NixOS systems. The first file (autoload.nix) has no dependency on any of Stylix's flake inputs. The second file (default.nix) takes the matrix as a parameter, then imports each module into a NixOS system, and wrapper script. This will enable a partitioned flake to evaluate the set of testbeds without needing to fully convert them into NixOS systems with dependencies on dev-inputs. Link: https://github.com/nix-community/stylix/pull/1520 Reviewed-by: Flameopathic <64027365+Flameopathic@users.noreply.github.com> Reviewed-by: awwpotato Reviewed-by: NAHO <90870942+trueNAHO@users.noreply.github.com> --- stylix/testbed/autoload.nix | 118 +++++++++++++++++++++++++++++++++++ stylix/testbed/default.nix | 119 +++++++----------------------------- 2 files changed, 139 insertions(+), 98 deletions(-) create mode 100644 stylix/testbed/autoload.nix diff --git a/stylix/testbed/autoload.nix b/stylix/testbed/autoload.nix new file mode 100644 index 00000000..29f5f1a2 --- /dev/null +++ b/stylix/testbed/autoload.nix @@ -0,0 +1,118 @@ +{ + pkgs, + lib, + testbedFieldSeparator ? ":", +}: +let + isEnabled = pkgs.callPackage ./is-enabled.nix { }; + + # Describes all testbed modules loaded from the modules directory. + # A list of attrsets, each containing: { module, path, name } + testbeds = lib.pipe ../../modules [ + builtins.readDir + builtins.attrNames + (builtins.concatMap ( + module: + let + testbeds = ../../modules/${module}/testbeds; + files = lib.optionalAttrs (builtins.pathExists testbeds) ( + builtins.readDir testbeds + ); + in + lib.mapAttrsToList ( + testbed: type: + let + path = testbeds + "/${testbed}"; + pathStr = toString path; + in + if type != "regular" then + throw "${pathStr} must be regular: ${type}" + + else if !lib.hasSuffix ".nix" testbed then + throw "testbed must be a Nix file: ${pathStr}" + + else if testbed == ".nix" then + throw "testbed must have a name: ${pathStr}" + + else + { + inherit module path; + name = lib.removeSuffix ".nix" testbed; + } + ) files + )) + ]; + + # Import all the testbed themes + themes = lib.pipe ./themes [ + builtins.readDir + (lib.filterAttrs (name: _: lib.strings.hasSuffix ".nix" name)) + (builtins.mapAttrs ( + name: type: + lib.throwIfNot (type == "regular") + "Unexpected filetype in testbed themes: ${toString ./themes/${name}} is a ${type}." + ./themes/${name} + )) + (lib.mapAttrs' (name: lib.nameValuePair (lib.strings.removeSuffix ".nix" name))) + ]; + + # Construct a NixOS module for the testbed+theme combination. + # + # If the testbed module is enabled, returns an attrset containing it: + # { «name» = «modele»; } + # + # Returns an empty attrset if the testbed module is not enabled. + # { } + makeTestbedModule = + testbed: themeName: themeModule: + let + joinFields = builtins.concatStringsSep testbedFieldSeparator; + + fields = + map + ( + field: + lib.throwIf (lib.hasInfix testbedFieldSeparator field) + "testbed field must not contain the '${testbedFieldSeparator}' testbed field separator: ${field}" + field + ) + [ + "testbed" + testbed.name + themeName + ]; + + name = joinFields fields; + in + lib.optionalAttrs (isEnabled testbed.path) { + ${name} = { + # Unique key for de-duplication + key = joinFields [ + (toString ./.) + name + ]; + + # Location displayed in errors + _file = name; + + # Avoid accidental imports, e.g. in submodules or home-manager + _class = "nixos"; + + imports = [ + testbed.path + themeModule + ]; + + config.system.name = name; + }; + }; + + # Generates a copy of each testbed for each of the imported themes. + # I.e. a testbeds*themes matrix + makeTestbeds = testbed: lib.mapAttrsToList (makeTestbedModule testbed) themes; +in +# Testbeds are merged using lib.attrsets.unionOfDisjoint to throw an error if +# any name collides. +builtins.foldl' lib.attrsets.unionOfDisjoint { } ( + builtins.concatMap makeTestbeds testbeds +) diff --git a/stylix/testbed/default.nix b/stylix/testbed/default.nix index bcb61484..68014ca0 100644 --- a/stylix/testbed/default.nix +++ b/stylix/testbed/default.nix @@ -2,63 +2,13 @@ pkgs, inputs, lib, - testbedFieldSeparator ? ":", + modules ? import ./autoload.nix { inherit pkgs lib; }, }: let - isEnabled = pkgs.callPackage ./is-enabled.nix { }; - - autoload = lib.pipe ../../modules [ - builtins.readDir - builtins.attrNames - (builtins.concatMap ( - module: - let - testbeds = ../../modules/${module}/testbeds; - files = lib.optionalAttrs (builtins.pathExists testbeds) ( - builtins.readDir testbeds - ); - in - lib.mapAttrsToList ( - testbed: type: - let - path = testbeds + "/${testbed}"; - pathStr = toString path; - in - if type != "regular" then - throw "${pathStr} must be regular: ${type}" - - else if !lib.hasSuffix ".nix" testbed then - throw "testbed must be a Nix file: ${pathStr}" - - else if testbed == ".nix" then - throw "testbed must have a name: ${pathStr}" - - else - { - inherit module path; - name = lib.removeSuffix ".nix" testbed; - } - ) files - )) - ]; - makeTestbed = - testbed: themeName: themeModule: + name: testbed: let - name = - lib.concatMapStringsSep testbedFieldSeparator - ( - field: - lib.throwIf (lib.hasInfix testbedFieldSeparator field) - "testbed field must not contain the '${testbedFieldSeparator}' testbed field separator: ${field}" - field - ) - [ - "testbed" - testbed.name - themeName - ]; system = lib.nixosSystem { inherit (pkgs) system; @@ -69,54 +19,27 @@ let ./modules/application.nix inputs.self.nixosModules.stylix inputs.home-manager.nixosModules.home-manager - testbed.path - themeModule - { system.name = name; } + testbed ]; }; - - script = pkgs.writeShellApplication { - inherit name; - text = '' - cleanup() { - if rm --recursive "$directory"; then - printf '%s\n' 'Virtualisation disk image removed.' - fi - } - - # We create a temporary directory rather than a temporary file, since - # temporary files are created empty and are not valid disk images. - directory="$(mktemp --directory)" - trap cleanup EXIT - - NIX_DISK_IMAGE="$directory/nixos.qcow2" \ - ${lib.getExe system.config.system.build.vm} - ''; - }; in - lib.optionalAttrs (isEnabled testbed.path) { - ${name} = script; + pkgs.writeShellApplication { + inherit name; + text = '' + cleanup() { + if rm --recursive "$directory"; then + printf '%s\n' 'Virtualisation disk image removed.' + fi + } + + # We create a temporary directory rather than a temporary file, since + # temporary files are created empty and are not valid disk images. + directory="$(mktemp --directory)" + trap cleanup EXIT + + NIX_DISK_IMAGE="$directory/nixos.qcow2" \ + ${lib.getExe system.config.system.build.vm} + ''; }; - - # Import all the testbed themes - themes = lib.pipe ./themes [ - builtins.readDir - (lib.filterAttrs (name: _: lib.strings.hasSuffix ".nix" name)) - (builtins.mapAttrs ( - name: type: - lib.throwIfNot (type == "regular") - "Unexpected filetype in testbed themes: ${toString ./themes/${name}} is a ${type}." - ./themes/${name} - )) - (lib.mapAttrs' (name: lib.nameValuePair (lib.strings.removeSuffix ".nix" name))) - ]; - - # This generates a copy of each testbed for each of the imported themes. - makeTestbeds = testbed: lib.mapAttrsToList (makeTestbed testbed) themes; - in -# Testbeds are merged using lib.attrsets.unionOfDisjoint to throw an error if -# testbed names collide. -builtins.foldl' lib.attrsets.unionOfDisjoint { } ( - builtins.concatMap makeTestbeds autoload -) +builtins.mapAttrs makeTestbed modules