Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions stylix/testbed/autoload.nix
Original file line number Diff line number Diff line change
@@ -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
];
Comment thread
trueNAHO marked this conversation as resolved.

# 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
)
119 changes: 21 additions & 98 deletions stylix/testbed/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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