mirror of
https://github.com/moons-14/dotfiles.git
synced 2026-10-06 03:08:11 +09:00
nix registory
This commit is contained in:
@@ -24,4 +24,8 @@
|
|||||||
|
|
||||||
!/hosts/
|
!/hosts/
|
||||||
|
|
||||||
|
!/libs/
|
||||||
|
|
||||||
!/modules/
|
!/modules/
|
||||||
|
|
||||||
|
!/tests/
|
||||||
|
|||||||
Generated
+22
@@ -570,6 +570,27 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nix-darwin": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1783744694,
|
||||||
|
"narHash": "sha256-2cp6N3rrwnGYLTx9l6N+NI+kwrCWxvJUbj5WJhvB29A=",
|
||||||
|
"owner": "nix-darwin",
|
||||||
|
"repo": "nix-darwin",
|
||||||
|
"rev": "c3e90c89649b07d1a96e4b9dd6cd0d6e44b91a74",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-darwin",
|
||||||
|
"ref": "nix-darwin-26.05",
|
||||||
|
"repo": "nix-darwin",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nix-index-database": {
|
"nix-index-database": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -962,6 +983,7 @@
|
|||||||
"lanzaboote": "lanzaboote",
|
"lanzaboote": "lanzaboote",
|
||||||
"llm-agents": "llm-agents",
|
"llm-agents": "llm-agents",
|
||||||
"niri-flake": "niri-flake",
|
"niri-flake": "niri-flake",
|
||||||
|
"nix-darwin": "nix-darwin",
|
||||||
"nix-index-database": "nix-index-database",
|
"nix-index-database": "nix-index-database",
|
||||||
"nixos-hardware": "nixos-hardware",
|
"nixos-hardware": "nixos-hardware",
|
||||||
"nixos-wsl": "nixos-wsl",
|
"nixos-wsl": "nixos-wsl",
|
||||||
|
|||||||
@@ -11,6 +11,11 @@
|
|||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nix-darwin = {
|
||||||
|
url = "github:nix-darwin/nix-darwin/nix-darwin-26.05";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
# Hardware / Platform
|
# Hardware / Platform
|
||||||
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
|
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
|
||||||
nixos-wsl.url = "github:nix-community/NixOS-WSL";
|
nixos-wsl.url = "github:nix-community/NixOS-WSL";
|
||||||
|
|||||||
@@ -2,5 +2,6 @@
|
|||||||
imports = [
|
imports = [
|
||||||
./formatter.nix
|
./formatter.nix
|
||||||
./git-hooks.nix
|
./git-hooks.nix
|
||||||
|
./registry.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
dotfilesLib = import ../libs {
|
||||||
|
inherit inputs lib;
|
||||||
|
root = ../.;
|
||||||
|
};
|
||||||
|
hostSpecsPath = ../hosts/default.nix;
|
||||||
|
hostSpecs =
|
||||||
|
if builtins.pathExists hostSpecsPath then
|
||||||
|
let
|
||||||
|
value = import hostSpecsPath;
|
||||||
|
in
|
||||||
|
if builtins.isFunction value then
|
||||||
|
value (
|
||||||
|
builtins.intersectAttrs (builtins.functionArgs value) {
|
||||||
|
inherit inputs lib;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else
|
||||||
|
value
|
||||||
|
else
|
||||||
|
{ };
|
||||||
|
configurations = dotfilesLib.hosts.mkConfigurations hostSpecs;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
flake = {
|
||||||
|
inherit (configurations) darwinConfigurations nixosConfigurations;
|
||||||
|
lib = dotfilesLib;
|
||||||
|
};
|
||||||
|
|
||||||
|
perSystem =
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
checks.registry = import ../tests/registry.nix {
|
||||||
|
inherit inputs lib pkgs;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
inputs,
|
||||||
|
lib ? inputs.nixpkgs.lib,
|
||||||
|
root,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
registry = import ./registry.nix {
|
||||||
|
inherit inputs lib;
|
||||||
|
modulesRoot = root + "/modules";
|
||||||
|
};
|
||||||
|
hosts = import ./hosts.nix {
|
||||||
|
inherit inputs lib registry;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit hosts registry;
|
||||||
|
}
|
||||||
+168
@@ -0,0 +1,168 @@
|
|||||||
|
{
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
registry,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
ensure =
|
||||||
|
condition: message: value:
|
||||||
|
if condition then value else throw "host registry: ${message}";
|
||||||
|
|
||||||
|
isLinux = system: lib.hasSuffix "-linux" system;
|
||||||
|
isDarwin = system: lib.hasSuffix "-darwin" system;
|
||||||
|
|
||||||
|
hostFile =
|
||||||
|
spec: name:
|
||||||
|
let
|
||||||
|
path = spec.path + "/${name}";
|
||||||
|
in
|
||||||
|
if builtins.pathExists path then path else null;
|
||||||
|
|
||||||
|
selectedUnits =
|
||||||
|
spec:
|
||||||
|
[ "users.${spec.user}" ]
|
||||||
|
++ map (name: "profiles.${name}") (spec.profiles or [ ])
|
||||||
|
++ map (name: "applications.${name}") (spec.applications or [ ])
|
||||||
|
++ (spec.units or [ ]);
|
||||||
|
|
||||||
|
validateSpec =
|
||||||
|
name: spec:
|
||||||
|
ensure (builtins.isAttrs spec) "${name}: host specification must be an attribute set" (
|
||||||
|
ensure (spec ? system && builtins.isString spec.system) "${name}: system is required" (
|
||||||
|
ensure (isLinux spec.system || isDarwin spec.system)
|
||||||
|
"${name}: unsupported system '${spec.system}'; expected a Linux NixOS or Darwin system"
|
||||||
|
(
|
||||||
|
ensure (spec ? user && builtins.isString spec.user && spec.user != "") "${name}: user is required" (
|
||||||
|
ensure (spec ? path && builtins.pathExists spec.path)
|
||||||
|
"${name}: path must name an existing host directory"
|
||||||
|
(
|
||||||
|
ensure
|
||||||
|
(lib.all
|
||||||
|
(field: builtins.isList (spec.${field} or [ ]) && lib.all builtins.isString (spec.${field} or [ ]))
|
||||||
|
[
|
||||||
|
"profiles"
|
||||||
|
"applications"
|
||||||
|
"units"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
"${name}: profiles, applications, and units must be lists of strings"
|
||||||
|
(ensure (builtins.isBool (spec.homeManager or true)) "${name}: homeManager must be a boolean" spec)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
mkSpecialArgs = name: spec: {
|
||||||
|
inherit inputs registry;
|
||||||
|
inherit (spec) system;
|
||||||
|
hostName = name;
|
||||||
|
primaryUser = spec.user;
|
||||||
|
};
|
||||||
|
|
||||||
|
mkHomeManagerModule =
|
||||||
|
name: spec: selected:
|
||||||
|
let
|
||||||
|
homePath = hostFile spec "home.nix";
|
||||||
|
homeModules = [
|
||||||
|
(registry.mkModule { class = "home"; })
|
||||||
|
(registry.mkSelectionModule selected)
|
||||||
|
]
|
||||||
|
++ lib.optional (homePath != null) homePath;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ inputs.home-manager.nixosModules.home-manager ];
|
||||||
|
|
||||||
|
home-manager = {
|
||||||
|
useGlobalPkgs = true;
|
||||||
|
useUserPackages = true;
|
||||||
|
extraSpecialArgs = mkSpecialArgs name spec;
|
||||||
|
users.${spec.user}.imports = homeModules;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
mkDarwinHomeManagerModule =
|
||||||
|
name: spec: selected:
|
||||||
|
let
|
||||||
|
homePath = hostFile spec "home.nix";
|
||||||
|
homeModules = [
|
||||||
|
(registry.mkModule { class = "home"; })
|
||||||
|
(registry.mkSelectionModule selected)
|
||||||
|
]
|
||||||
|
++ lib.optional (homePath != null) homePath;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ inputs.home-manager.darwinModules.home-manager ];
|
||||||
|
|
||||||
|
home-manager = {
|
||||||
|
useGlobalPkgs = true;
|
||||||
|
useUserPackages = true;
|
||||||
|
extraSpecialArgs = mkSpecialArgs name spec;
|
||||||
|
users.${spec.user}.imports = homeModules;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
mkNixos =
|
||||||
|
name: rawSpec:
|
||||||
|
let
|
||||||
|
spec = validateSpec name rawSpec;
|
||||||
|
selected = registry.validateUnitIds (selectedUnits spec);
|
||||||
|
nixosPath = hostFile spec "nixos.nix";
|
||||||
|
modules = [
|
||||||
|
(registry.mkModule { class = "nixos"; })
|
||||||
|
(registry.mkSelectionModule selected)
|
||||||
|
{ networking.hostName = lib.mkDefault name; }
|
||||||
|
]
|
||||||
|
++ lib.optional (spec.homeManager or true) (mkHomeManagerModule name spec selected)
|
||||||
|
++ lib.optional (nixosPath != null) nixosPath;
|
||||||
|
in
|
||||||
|
inputs.nixpkgs.lib.nixosSystem {
|
||||||
|
inherit (spec) system;
|
||||||
|
specialArgs = mkSpecialArgs name spec;
|
||||||
|
inherit modules;
|
||||||
|
};
|
||||||
|
|
||||||
|
mkDarwin =
|
||||||
|
name: rawSpec:
|
||||||
|
let
|
||||||
|
spec = validateSpec name rawSpec;
|
||||||
|
selected = registry.validateUnitIds (selectedUnits spec);
|
||||||
|
darwinPath = hostFile spec "darwin.nix";
|
||||||
|
modules = [
|
||||||
|
(registry.mkModule { class = "darwin"; })
|
||||||
|
(registry.mkSelectionModule selected)
|
||||||
|
{ networking.hostName = lib.mkDefault name; }
|
||||||
|
]
|
||||||
|
++ lib.optional (spec.homeManager or true) (mkDarwinHomeManagerModule name spec selected)
|
||||||
|
++ lib.optional (darwinPath != null) darwinPath;
|
||||||
|
in
|
||||||
|
ensure (inputs ? nix-darwin) "${name}: the nix-darwin input is required" (
|
||||||
|
inputs.nix-darwin.lib.darwinSystem {
|
||||||
|
inherit (spec) system;
|
||||||
|
specialArgs = mkSpecialArgs name spec;
|
||||||
|
inherit modules;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
mkConfigurations =
|
||||||
|
hostSpecs:
|
||||||
|
let
|
||||||
|
validated = lib.mapAttrs validateSpec hostSpecs;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
nixosConfigurations = lib.mapAttrs mkNixos (
|
||||||
|
lib.filterAttrs (_: spec: isLinux spec.system) validated
|
||||||
|
);
|
||||||
|
darwinConfigurations = lib.mapAttrs mkDarwin (
|
||||||
|
lib.filterAttrs (_: spec: isDarwin spec.system) validated
|
||||||
|
);
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit
|
||||||
|
mkConfigurations
|
||||||
|
mkDarwin
|
||||||
|
mkNixos
|
||||||
|
selectedUnits
|
||||||
|
;
|
||||||
|
}
|
||||||
@@ -0,0 +1,324 @@
|
|||||||
|
{
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
modulesRoot,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
reservedFiles = {
|
||||||
|
common = "common.nix";
|
||||||
|
nixos = "nixos.nix";
|
||||||
|
darwin = "darwin.nix";
|
||||||
|
home = "home.nix";
|
||||||
|
meta = "meta.nix";
|
||||||
|
};
|
||||||
|
|
||||||
|
isFile = kind: kind == "regular" || kind == "symlink";
|
||||||
|
|
||||||
|
ensure =
|
||||||
|
condition: message: value:
|
||||||
|
if condition then value else throw "unit registry: ${message}";
|
||||||
|
|
||||||
|
callWithAvailableArgs =
|
||||||
|
value: availableArgs:
|
||||||
|
if builtins.isFunction value then
|
||||||
|
value (builtins.intersectAttrs (builtins.functionArgs value) availableArgs)
|
||||||
|
else
|
||||||
|
value;
|
||||||
|
|
||||||
|
pathFor =
|
||||||
|
relativePath:
|
||||||
|
if relativePath == [ ] then
|
||||||
|
modulesRoot
|
||||||
|
else
|
||||||
|
modulesRoot + "/${lib.concatStringsSep "/" relativePath}";
|
||||||
|
|
||||||
|
entryIsFile = entries: name: builtins.hasAttr name entries && isFile entries.${name};
|
||||||
|
|
||||||
|
normalizeMeta =
|
||||||
|
unit:
|
||||||
|
let
|
||||||
|
metaPath = unit.fragments.meta;
|
||||||
|
importedValue =
|
||||||
|
if metaPath == null then
|
||||||
|
{ }
|
||||||
|
else
|
||||||
|
callWithAvailableArgs (import metaPath) {
|
||||||
|
inherit inputs lib unit;
|
||||||
|
};
|
||||||
|
imported =
|
||||||
|
ensure (builtins.isAttrs importedValue) "${unit.id}: meta.nix must return an attribute set"
|
||||||
|
importedValue;
|
||||||
|
allowedKeys = [
|
||||||
|
"description"
|
||||||
|
"includes"
|
||||||
|
"imports"
|
||||||
|
];
|
||||||
|
unknownKeys = lib.filter (name: !(builtins.elem name allowedKeys)) (builtins.attrNames imported);
|
||||||
|
description = imported.description or null;
|
||||||
|
includes = imported.includes or [ ];
|
||||||
|
imports = imported.imports or { };
|
||||||
|
allowedImportKeys = [
|
||||||
|
"nixos"
|
||||||
|
"darwin"
|
||||||
|
"home"
|
||||||
|
];
|
||||||
|
unknownImportKeys =
|
||||||
|
if builtins.isAttrs imports then
|
||||||
|
lib.filter (name: !(builtins.elem name allowedImportKeys)) (builtins.attrNames imports)
|
||||||
|
else
|
||||||
|
[ ];
|
||||||
|
normalized = {
|
||||||
|
inherit description includes;
|
||||||
|
imports = {
|
||||||
|
nixos = imports.nixos or [ ];
|
||||||
|
darwin = imports.darwin or [ ];
|
||||||
|
home = imports.home or [ ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
ensure (unknownKeys == [ ])
|
||||||
|
"${unit.id}: meta.nix has unsupported keys: ${lib.concatStringsSep ", " unknownKeys}"
|
||||||
|
(
|
||||||
|
ensure (description == null || builtins.isString description)
|
||||||
|
"${unit.id}: meta.description must be a string"
|
||||||
|
(
|
||||||
|
ensure (builtins.isList includes && lib.all builtins.isString includes)
|
||||||
|
"${unit.id}: meta.includes must be a list of fully qualified unit IDs"
|
||||||
|
(
|
||||||
|
ensure (lib.unique includes == includes) "${unit.id}: meta.includes contains duplicate unit IDs" (
|
||||||
|
ensure (builtins.isAttrs imports) "${unit.id}: meta.imports must be an attribute set" (
|
||||||
|
ensure (unknownImportKeys == [ ])
|
||||||
|
"${unit.id}: meta.imports has unsupported classes: ${lib.concatStringsSep ", " unknownImportKeys}"
|
||||||
|
(
|
||||||
|
ensure (lib.all builtins.isList [
|
||||||
|
normalized.imports.nixos
|
||||||
|
normalized.imports.darwin
|
||||||
|
normalized.imports.home
|
||||||
|
]) "${unit.id}: every meta.imports.<class> value must be a list" normalized
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
makeUnit =
|
||||||
|
relativePath: entries:
|
||||||
|
let
|
||||||
|
directory = pathFor relativePath;
|
||||||
|
id = lib.concatStringsSep "." relativePath;
|
||||||
|
fragments = lib.mapAttrs (
|
||||||
|
_class: fileName: if entryIsFile entries fileName then directory + "/${fileName}" else null
|
||||||
|
) reservedFiles;
|
||||||
|
baseUnit = {
|
||||||
|
inherit
|
||||||
|
id
|
||||||
|
directory
|
||||||
|
fragments
|
||||||
|
relativePath
|
||||||
|
;
|
||||||
|
optionPath = [ "my" ] ++ relativePath ++ [ "enable" ];
|
||||||
|
kind = builtins.head relativePath;
|
||||||
|
name = lib.last relativePath;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
lib.optionalAttrs (builtins.length relativePath > 2 && builtins.head relativePath == "profiles")
|
||||||
|
{
|
||||||
|
group = builtins.elemAt relativePath 1;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
ensure (relativePath != [ ]) "the modules root cannot itself be a unit" (
|
||||||
|
ensure (lib.all (component: component != "" && !(lib.hasInfix "." component)) relativePath)
|
||||||
|
"${id}: path components must be non-empty and must not contain dots"
|
||||||
|
(baseUnit // { meta = normalizeMeta baseUnit; })
|
||||||
|
);
|
||||||
|
|
||||||
|
walk =
|
||||||
|
relativePath:
|
||||||
|
let
|
||||||
|
directory = pathFor relativePath;
|
||||||
|
entries = builtins.readDir directory;
|
||||||
|
hasReservedFile = lib.any (fileName: entryIsFile entries fileName) (
|
||||||
|
builtins.attrValues reservedFiles
|
||||||
|
);
|
||||||
|
childDirectories = lib.filter (name: entries.${name} == "directory") (builtins.attrNames entries);
|
||||||
|
current = lib.optional hasReservedFile (makeUnit relativePath entries);
|
||||||
|
children = lib.concatMap (name: walk (relativePath ++ [ name ])) childDirectories;
|
||||||
|
in
|
||||||
|
current ++ children;
|
||||||
|
|
||||||
|
discoveredUnits =
|
||||||
|
ensure (builtins.pathExists modulesRoot) "modules root does not exist: ${toString modulesRoot}"
|
||||||
|
(walk [ ]);
|
||||||
|
unitsById = builtins.listToAttrs (map (unit: lib.nameValuePair unit.id unit) discoveredUnits);
|
||||||
|
|
||||||
|
dependencyValidation = lib.foldl' (
|
||||||
|
valid: unit:
|
||||||
|
lib.foldl' (
|
||||||
|
inner: includedId:
|
||||||
|
if builtins.hasAttr includedId unitsById then
|
||||||
|
inner
|
||||||
|
else
|
||||||
|
throw "unit registry: ${unit.id} includes missing unit '${includedId}'"
|
||||||
|
) valid unit.meta.includes
|
||||||
|
) true discoveredUnits;
|
||||||
|
|
||||||
|
units = builtins.seq dependencyValidation unitsById;
|
||||||
|
unitIds = builtins.attrNames units;
|
||||||
|
|
||||||
|
getUnit =
|
||||||
|
id: if builtins.hasAttr id units then units.${id} else throw "unit registry: unknown unit '${id}'";
|
||||||
|
|
||||||
|
validateUnitIds =
|
||||||
|
ids:
|
||||||
|
ensure (
|
||||||
|
builtins.isList ids && lib.all builtins.isString ids
|
||||||
|
) "selected units must be a list of strings" (map (id: builtins.seq (getUnit id) id) ids);
|
||||||
|
|
||||||
|
optionDefinitions = lib.foldl' lib.recursiveUpdate { } (
|
||||||
|
map (
|
||||||
|
unit:
|
||||||
|
lib.setAttrByPath unit.optionPath (
|
||||||
|
lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description =
|
||||||
|
if unit.meta.description == null then
|
||||||
|
"Whether to enable the ${unit.id} unit."
|
||||||
|
else
|
||||||
|
"Whether to enable ${unit.meta.description}.";
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) discoveredUnits
|
||||||
|
);
|
||||||
|
|
||||||
|
enabled = config: unit: lib.getAttrFromPath unit.optionPath config;
|
||||||
|
|
||||||
|
enableUnit = id: lib.setAttrByPath (getUnit id).optionPath true;
|
||||||
|
|
||||||
|
includeConfig =
|
||||||
|
config: unit: lib.mkIf (enabled config unit) (lib.mkMerge (map enableUnit unit.meta.includes));
|
||||||
|
|
||||||
|
fragmentClasses = {
|
||||||
|
nixos = [
|
||||||
|
"common"
|
||||||
|
"nixos"
|
||||||
|
];
|
||||||
|
darwin = [
|
||||||
|
"common"
|
||||||
|
"darwin"
|
||||||
|
];
|
||||||
|
home = [ "home" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
applyFragment =
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
fragmentPath,
|
||||||
|
options,
|
||||||
|
specialArgs,
|
||||||
|
unit,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
fragment = import fragmentPath;
|
||||||
|
directArgs = specialArgs // {
|
||||||
|
inherit
|
||||||
|
config
|
||||||
|
lib
|
||||||
|
options
|
||||||
|
specialArgs
|
||||||
|
unit
|
||||||
|
;
|
||||||
|
};
|
||||||
|
fragmentArgSpec = builtins.functionArgs fragment;
|
||||||
|
fragmentArgs = builtins.listToAttrs (
|
||||||
|
lib.concatMap (
|
||||||
|
name:
|
||||||
|
if builtins.hasAttr name directArgs then
|
||||||
|
[ (lib.nameValuePair name directArgs.${name}) ]
|
||||||
|
else if fragmentArgSpec.${name} then
|
||||||
|
[ ]
|
||||||
|
else
|
||||||
|
[ (lib.nameValuePair name config._module.args.${name}) ]
|
||||||
|
) (builtins.attrNames fragmentArgSpec)
|
||||||
|
);
|
||||||
|
resultValue = if builtins.isFunction fragment then fragment fragmentArgs else fragment;
|
||||||
|
result =
|
||||||
|
ensure (builtins.isAttrs resultValue)
|
||||||
|
"${unit.id}: ${builtins.baseNameOf fragmentPath} must return an attribute set"
|
||||||
|
resultValue;
|
||||||
|
forbiddenKeys = lib.filter (name: builtins.hasAttr name result) [
|
||||||
|
"imports"
|
||||||
|
"options"
|
||||||
|
"config"
|
||||||
|
];
|
||||||
|
in
|
||||||
|
ensure (forbiddenKeys == [ ])
|
||||||
|
"${unit.id}: ${builtins.baseNameOf fragmentPath} is a configuration fragment and cannot define top-level ${lib.concatStringsSep ", " forbiddenKeys}"
|
||||||
|
result;
|
||||||
|
|
||||||
|
externalImports = class: lib.concatMap (unit: unit.meta.imports.${class}) discoveredUnits;
|
||||||
|
|
||||||
|
mkModule =
|
||||||
|
{ class }:
|
||||||
|
ensure (builtins.hasAttr class fragmentClasses) "unsupported module class '${class}'" (
|
||||||
|
builtins.seq dependencyValidation (
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
options,
|
||||||
|
specialArgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
fragmentConfigs = lib.concatMap (
|
||||||
|
unit:
|
||||||
|
lib.filter (value: value != null) (
|
||||||
|
map (
|
||||||
|
fragmentClass:
|
||||||
|
let
|
||||||
|
fragmentPath = unit.fragments.${fragmentClass};
|
||||||
|
in
|
||||||
|
if fragmentPath == null then
|
||||||
|
null
|
||||||
|
else
|
||||||
|
lib.mkIf (enabled config unit) (applyFragment {
|
||||||
|
inherit
|
||||||
|
config
|
||||||
|
fragmentPath
|
||||||
|
options
|
||||||
|
specialArgs
|
||||||
|
unit
|
||||||
|
;
|
||||||
|
})
|
||||||
|
) fragmentClasses.${class}
|
||||||
|
)
|
||||||
|
) discoveredUnits;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = externalImports class;
|
||||||
|
options = optionDefinitions;
|
||||||
|
config = lib.mkMerge ((map (includeConfig config) discoveredUnits) ++ fragmentConfigs);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
mkSelectionModule =
|
||||||
|
selectedIds:
|
||||||
|
let
|
||||||
|
checkedIds = validateUnitIds (lib.unique selectedIds);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = lib.mkMerge (map enableUnit checkedIds);
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit
|
||||||
|
getUnit
|
||||||
|
mkModule
|
||||||
|
mkSelectionModule
|
||||||
|
unitIds
|
||||||
|
units
|
||||||
|
validateUnitIds
|
||||||
|
;
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
includes = [ "applications.missing" ];
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
imports = [ ];
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
system.stateVersion = 6;
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
fileSystems."/" = {
|
||||||
|
device = "/dev/null";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
boot.loader.grub.enable = false;
|
||||||
|
system.stateVersion = "26.05";
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
marker ? "host",
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
test.systemValues = [ "common:${marker}" ];
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
test.systemValues = [ "darwin" ];
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
test.homeValues = [ "home" ];
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
description = "alpha test application";
|
||||||
|
|
||||||
|
includes = [ "services.nested" ];
|
||||||
|
|
||||||
|
imports.nixos = [
|
||||||
|
{
|
||||||
|
options.test = {
|
||||||
|
external = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "external-default";
|
||||||
|
};
|
||||||
|
systemValues = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
imports.darwin = [
|
||||||
|
{
|
||||||
|
options.test.systemValues = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
imports.home = [
|
||||||
|
{
|
||||||
|
options.test.homeValues = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
test.systemValues = [ "nixos" ];
|
||||||
|
test.external = "set-by-nixos-fragment";
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
test.homeValues = [ "beta-home" ];
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
thisFileMustNotBeDiscovered = true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
includes = [
|
||||||
|
"applications.alpha"
|
||||||
|
"applications.beta"
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
imports.nixos = [
|
||||||
|
{
|
||||||
|
options.test.nested = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
test.nested = true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
home = {
|
||||||
|
username = "test";
|
||||||
|
homeDirectory = if pkgs.stdenv.hostPlatform.isDarwin then "/Users/test" else "/home/test";
|
||||||
|
stateVersion = "26.05";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
{
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
registry = import ../libs/registry.nix {
|
||||||
|
inherit inputs lib;
|
||||||
|
modulesRoot = ./fixtures/registry/modules;
|
||||||
|
};
|
||||||
|
|
||||||
|
hostLib = import ../libs/hosts.nix {
|
||||||
|
inherit inputs lib registry;
|
||||||
|
};
|
||||||
|
|
||||||
|
baseModule = {
|
||||||
|
options = {
|
||||||
|
home = {
|
||||||
|
username = lib.mkOption { type = lib.types.str; };
|
||||||
|
homeDirectory = lib.mkOption { type = lib.types.str; };
|
||||||
|
stateVersion = lib.mkOption { type = lib.types.str; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
eval =
|
||||||
|
class: selected:
|
||||||
|
lib.evalModules {
|
||||||
|
specialArgs = {
|
||||||
|
inherit pkgs;
|
||||||
|
marker = "special-arg";
|
||||||
|
};
|
||||||
|
modules = [
|
||||||
|
baseModule
|
||||||
|
(registry.mkModule { inherit class; })
|
||||||
|
(registry.mkSelectionModule selected)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
nixos = eval "nixos" [ "profiles.interface.test" ];
|
||||||
|
darwin = eval "darwin" [ "applications.alpha" ];
|
||||||
|
home = eval "home" [ "profiles.interface.test" ];
|
||||||
|
|
||||||
|
nixosHost = hostLib.mkNixos "registry-test" {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
user = "test";
|
||||||
|
path = ./fixtures/registry/hosts;
|
||||||
|
profiles = [ "interface.test" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
darwinHost = hostLib.mkDarwin "registry-test-darwin" {
|
||||||
|
system = "aarch64-darwin";
|
||||||
|
user = "test";
|
||||||
|
path = ./fixtures/registry/hosts;
|
||||||
|
applications = [ "alpha" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
missingUnit = builtins.tryEval (
|
||||||
|
builtins.deepSeq (registry.validateUnitIds [ "applications.missing" ]) true
|
||||||
|
);
|
||||||
|
|
||||||
|
invalidDependencyRegistry = import ../libs/registry.nix {
|
||||||
|
inherit inputs lib;
|
||||||
|
modulesRoot = ./fixtures/registry-invalid-dependency/modules;
|
||||||
|
};
|
||||||
|
missingDependency = builtins.tryEval (builtins.deepSeq invalidDependencyRegistry.unitIds true);
|
||||||
|
|
||||||
|
invalidFragmentRegistry = import ../libs/registry.nix {
|
||||||
|
inherit inputs lib;
|
||||||
|
modulesRoot = ./fixtures/registry-invalid-fragment/modules;
|
||||||
|
};
|
||||||
|
invalidFragmentEvaluation = lib.evalModules {
|
||||||
|
modules = [
|
||||||
|
(invalidFragmentRegistry.mkModule { class = "home"; })
|
||||||
|
(invalidFragmentRegistry.mkSelectionModule [ "applications.broken" ])
|
||||||
|
];
|
||||||
|
};
|
||||||
|
invalidFragment = builtins.tryEval (builtins.deepSeq invalidFragmentEvaluation.config true);
|
||||||
|
|
||||||
|
assertions =
|
||||||
|
assert
|
||||||
|
registry.unitIds == [
|
||||||
|
"applications.alpha"
|
||||||
|
"applications.beta"
|
||||||
|
"profiles.interface.test"
|
||||||
|
"services.nested"
|
||||||
|
"users.test"
|
||||||
|
];
|
||||||
|
assert !(builtins.elem "namespace" registry.unitIds);
|
||||||
|
assert nixos.config.my.profiles.interface.test.enable;
|
||||||
|
assert nixos.config.my.applications.alpha.enable;
|
||||||
|
assert nixos.config.my.applications.beta.enable;
|
||||||
|
assert nixos.config.my.services.nested.enable;
|
||||||
|
assert nixos.config.test.nested;
|
||||||
|
assert
|
||||||
|
nixos.config.test.systemValues == [
|
||||||
|
"common:special-arg"
|
||||||
|
"nixos"
|
||||||
|
];
|
||||||
|
assert nixos.config.test.external == "set-by-nixos-fragment";
|
||||||
|
assert
|
||||||
|
darwin.config.test.systemValues == [
|
||||||
|
"common:special-arg"
|
||||||
|
"darwin"
|
||||||
|
];
|
||||||
|
assert
|
||||||
|
home.config.test.homeValues == [
|
||||||
|
"home"
|
||||||
|
"beta-home"
|
||||||
|
];
|
||||||
|
assert nixosHost.config.my.profiles.interface.test.enable;
|
||||||
|
assert nixosHost.config.home-manager.users.test.my.applications.alpha.enable;
|
||||||
|
assert
|
||||||
|
nixosHost.config.home-manager.users.test.test.homeValues == [
|
||||||
|
"home"
|
||||||
|
"beta-home"
|
||||||
|
];
|
||||||
|
assert darwinHost.config.my.applications.alpha.enable;
|
||||||
|
assert darwinHost.config.home-manager.users.test.my.applications.alpha.enable;
|
||||||
|
assert darwinHost.config.home-manager.users.test.test.homeValues == [ "home" ];
|
||||||
|
assert
|
||||||
|
darwinHost.config.test.systemValues == [
|
||||||
|
"common:host"
|
||||||
|
"darwin"
|
||||||
|
];
|
||||||
|
assert !missingUnit.success;
|
||||||
|
assert !missingDependency.success;
|
||||||
|
assert !invalidFragment.success;
|
||||||
|
true;
|
||||||
|
in
|
||||||
|
assert assertions;
|
||||||
|
pkgs.runCommand "unit-registry-evaluation-tests" { } ''
|
||||||
|
touch "$out"
|
||||||
|
''
|
||||||
Reference in New Issue
Block a user