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