Modular, context-aware and aspect-oriented dendritic Nix configurations.
Discussions: https://oeiuwq.zulipchat.com/join/nqp26cd4kngon6mo3ncgnuap/
den.oeiuwq.com
configurations
den
dendritic
nix
aspect
oriented
1{
2 inputs,
3 lib,
4 config,
5 ...
6}:
7let
8 inherit (config) den;
9
10 hostsOption = lib.mkOption {
11 description = "den hosts definition";
12 default = { };
13 type = lib.types.attrsOf systemType;
14 };
15
16 systemType = lib.types.submodule (
17 { name, ... }:
18 {
19 freeformType = lib.types.attrsOf (hostType name);
20 }
21 );
22
23 hostType =
24 system:
25 lib.types.submodule (
26 { name, config, ... }:
27 {
28 freeformType = lib.types.attrsOf lib.types.anything;
29 options = {
30 name = strOpt "host configuration name" name;
31 hostName = strOpt "Network hostname" config.name;
32 system = strOpt "platform system" system;
33 class = strOpt "os-configuration nix class for host" (
34 if lib.hasSuffix "darwin" config.system then "darwin" else "nixos"
35 );
36 aspect = strOpt "main aspect name of <class>" config.name;
37 description = strOpt "host description" "${config.class}.${config.hostName}@${config.system}";
38 users = lib.mkOption {
39 description = "user accounts";
40 default = { };
41 type = lib.types.attrsOf userType;
42 };
43 instantiate = lib.mkOption {
44 description = ''
45 Function used to instantiate the OS configuration.
46
47 Depending on class, defaults to:
48 `darwin`: inputs.darwin.lib.darwinSystem
49 `nixos`: inputs.nixpkgs.lib.nixosSystem
50 `systemManager`: inputs.system-manager.lib.makeSystemConfig
51
52 Set explicitly if you need:
53
54 - a custom input name, eg, nixos-unstable.
55 - adding specialArgs when absolutely required.
56 '';
57 example = lib.literalExpression "inputs.nixpkgs.lib.nixosSystem";
58 type = lib.types.unspecified;
59 default =
60 {
61 nixos = inputs.nixpkgs.lib.nixosSystem;
62 darwin = inputs.darwin.lib.darwinSystem;
63 systemManager = inputs.system-manager.lib.makeSystemConfig;
64 }
65 .${config.class};
66 };
67 intoAttr = lib.mkOption {
68 description = ''
69 Flake attr where to add the named result of this configuration.
70 flake.<intoAttr>.<name>
71
72 Depending on class, defaults to:
73 `darwin`: darwinConfigurations
74 `nixos`: nixosConfigurations
75 `systemManager`: systemConfigs
76 '';
77 example = lib.literalExpression ''"nixosConfigurations"'';
78 type = lib.types.str;
79 default =
80 {
81 nixos = "nixosConfigurations";
82 darwin = "darwinConfigurations";
83 systemManager = "systemConfigs";
84 }
85 .${config.class};
86 };
87 mainModule = lib.mkOption {
88 internal = true;
89 visible = false;
90 readOnly = true;
91 type = lib.types.deferredModule;
92 default = mainModule config "OS" "host";
93 };
94 };
95 }
96 );
97
98 userType = lib.types.submodule (
99 { name, config, ... }:
100 {
101 freeformType = lib.types.attrsOf lib.types.anything;
102 options = {
103 name = strOpt "user configuration name" name;
104 userName = strOpt "user account name" config.name;
105 class = strOpt "home management nix class" "homeManager";
106 aspect = strOpt "main aspect name" config.name;
107 };
108 }
109 );
110
111 strOpt =
112 description: default:
113 lib.mkOption {
114 type = lib.types.str;
115 inherit description default;
116 };
117
118 homesOption = lib.mkOption {
119 description = "den standalone home-manager configurations";
120 default = { };
121 type = lib.types.attrsOf homeSystemType;
122 };
123
124 homeSystemType = lib.types.submodule (
125 { name, ... }:
126 {
127 freeformType = lib.types.attrsOf (homeType name);
128 }
129 );
130
131 homeType =
132 system:
133 lib.types.submodule (
134 { name, config, ... }:
135 {
136 freeformType = lib.types.attrsOf lib.types.anything;
137 options = {
138 name = strOpt "home configuration name" name;
139 userName = strOpt "user account name" config.name;
140 system = strOpt "platform system" system;
141 class = strOpt "home management nix class" "homeManager";
142 aspect = strOpt "main aspect name" config.name;
143 description = strOpt "home description" "home.${config.userName}@${config.system}";
144 instantiate = lib.mkOption {
145 description = ''
146 Function used to instantiate the home configuration.
147
148 Depending on class, defaults to:
149 `homeManager`: inputs.home-manager.lib.homeManagerConfiguration
150
151 Set explicitly if you need:
152
153 - a custom input name, eg, home-manager-unstable.
154 - adding extraSpecialArgs when absolutely required.
155 '';
156 example = lib.literalExpression "inputs.home-manager.lib.homeManagerConfiguration";
157 type = lib.types.unspecified;
158 default =
159 {
160 homeManager = inputs.home-manager.lib.homeManagerConfiguration;
161 }
162 .${config.class};
163 };
164 intoAttr = lib.mkOption {
165 description = ''
166 Flake attr where to add the named result of this configuration.
167 flake.<intoAttr>.<name>
168
169 Depending on class, defaults to:
170 `homeManager`: homeConfigurations
171 '';
172 example = lib.literalExpression ''"homeConfigurations"'';
173 type = lib.types.str;
174 default =
175 {
176 homeManager = "homeConfigurations";
177 }
178 .${config.class};
179 };
180 mainModule = lib.mkOption {
181 internal = true;
182 visible = false;
183 readOnly = true;
184 type = lib.types.deferredModule;
185 default = mainModule config "HM" "home";
186 };
187 };
188 }
189 );
190
191 mainModule =
192 from: intent: name:
193 let
194 asp = den.aspects.${from.aspect};
195 ctx = {
196 ${intent} = asp;
197 ${name} = from;
198 };
199 mod = (asp ctx).resolve { inherit (from) class; };
200 in
201 mod;
202in
203{
204 inherit hostsOption homesOption;
205}