a flake module to ease creating and managing multiple hosts in your nix flake.
1{
2 lib,
3 inputs,
4 config,
5 withSystem,
6 ...
7}:
8let
9 inherit (builtins) concatLists attrNames;
10 inherit (lib.options) mkOption mkEnableOption literalExpression;
11 inherit (lib) types;
12
13 inherit (import ./lib.nix { inherit lib inputs withSystem; })
14 constructSystem
15 mkHosts
16 buildHosts
17 ;
18
19 cfg = config.easy-hosts;
20
21 mkBasicParams = name: {
22 modules = mkOption {
23 # we really expect a list of paths but i want to accept lists of lists of lists and so on
24 # since they will be flattened in the final function that applies the settings
25 type = types.listOf types.deferredModule;
26 default = [ ];
27 description = "${name} modules to be included in the system";
28 example = literalExpression ''
29 [ ./hardware-configuration.nix ./networking.nix ]
30 '';
31 };
32
33 specialArgs = mkOption {
34 type = types.lazyAttrsOf types.raw;
35 default = { };
36 description = "${name} special arguments to be passed to the system";
37 example = literalExpression ''
38 { foo = "bar"; }
39 '';
40 };
41 };
42in
43{
44 # module for flake-parts
45 _class = "flake";
46
47 options = {
48 easy-hosts = {
49 autoConstruct = lib.mkEnableOption "Automatically construct hosts";
50
51 path = mkOption {
52 type = types.nullOr types.path;
53 default = null;
54 example = literalExpression "./hosts";
55 description = "Path to the directory containing the host files";
56 };
57
58 onlySystem = mkOption {
59 type = types.nullOr types.str;
60 default = null;
61 example = literalExpression "aarch64-darwin";
62 description = "Only construct the hosts with for this platform";
63 };
64
65 shared = mkBasicParams "Shared";
66
67 perClass = mkOption {
68 default = _: {
69 modules = [ ];
70 specialArgs = { };
71 };
72 defaultText = ''
73 class: {
74 modules = [ ];
75 specialArgs = { };
76 };
77 '';
78
79 type = types.functionTo (
80 types.submodule {
81 options = mkBasicParams "Per class";
82 }
83 );
84
85 example = literalExpression ''
86 class: {
87 modules = [
88 { system.nixos.label = class; }
89 ];
90
91 specialArgs = { };
92 }
93 '';
94
95 description = "Per class settings";
96 };
97
98 perArch = mkOption {
99 default = _: {
100 modules = [ ];
101 specialArgs = { };
102 };
103 defaultText = ''
104 arch: {
105 modules = [ ];
106 specialArgs = { };
107 };
108 '';
109
110 type = types.functionTo (
111 types.submodule {
112 options = mkBasicParams "Per arch";
113 }
114 );
115
116 example = literalExpression ''
117 arch: {
118 modules = [
119 { system.nixos.label = arch; }
120 ];
121
122 specialArgs = { };
123 }
124 '';
125
126 description = "Per arch settings";
127 };
128
129 perTag = mkOption {
130 default = _: {
131 modules = [ ];
132 specialArgs = { };
133 };
134 defaultText = ''
135 tag: {
136 modules = [ ];
137 specialArgs = { };
138 };
139 '';
140
141 type = types.functionTo (
142 types.submodule {
143 options = mkBasicParams "Per tag";
144 }
145 );
146
147 example = literalExpression ''
148 let
149 tagModule = {
150 laptop = ./modules/laptop;
151 gaming = ./modules/gaming;
152 };
153 in
154 tag: {
155 modules = [ tagModule.''${tag} ];
156
157 specialArgs = { };
158 }
159 '';
160
161 description = "Per tag settings";
162 };
163
164 additionalClasses = mkOption {
165 default = { };
166 type = types.attrsOf types.str;
167 description = "Additional classes and their respective mappings to already existing classes";
168 example = lib.literalExpression ''
169 {
170 wsl = "nixos";
171 rpi = "nixos";
172 macos = "darwin";
173 }
174 '';
175 };
176
177 hosts = mkOption {
178 description = "Hosts to be defined by the flake";
179
180 default = { };
181
182 type = types.attrsOf (
183 types.submodule (
184 { name, config, ... }:
185 {
186 options = {
187 nixpkgs = mkOption {
188 type = types.anything;
189 default = inputs.nixpkgs or (throw "cannot find nixpkgs input");
190 defaultText = literalExpression "inputs.nixpkgs";
191 example = literalExpression "inputs.nixpkgs-unstable";
192 description = "The nixpkgs flake to be used for the host";
193 };
194
195 nix-darwin = mkOption {
196 type = types.anything;
197 default = inputs.darwin or inputs.nix-darwin or null;
198 defaultText = literalExpression "inputs.darwin or inputs.nix-darwin";
199 example = literalExpression "inputs.my-nix-darwin";
200 description = "The nix-darwin flake to be used for the host";
201 };
202
203 # keep this up to date with
204 # https://github.com/NixOS/nixpkgs/blob/75a43236cfd40adbc6138029557583eb77920afd/lib/systems/flake-systems.nix#L1
205 arch = mkOption {
206 type = types.enum [
207 "x86_64"
208 "aarch64"
209 "armv6l"
210 "armv7l"
211 "i686"
212 "powerpc64le"
213 "riscv64"
214 ];
215 default = "x86_64";
216 example = "aarch64";
217 description = "The architecture of the host";
218 };
219
220 class = mkOption {
221 type = types.enum (concatLists [
222 [
223 "nixos"
224 "darwin"
225 "iso"
226 ]
227
228 (attrNames cfg.additionalClasses)
229 ]);
230 default = "nixos";
231 example = "darwin";
232 description = "The class of the host";
233 };
234
235 tags = mkOption {
236 type = types.listOf types.str;
237 default = [ ];
238 example = [ "laptop" ];
239 description = "Extra tags for the host";
240 };
241
242 system = mkOption {
243 type = types.str;
244 default = constructSystem cfg.additionalClasses config.arch config.class;
245 example = "aarch64-darwin";
246 description = "The system to be used for the host";
247 internal = true; # this should ideally be set by easy-hosts
248 };
249
250 path = mkOption {
251 type = types.nullOr types.path;
252 default = null;
253 example = literalExpression "./hosts/myhost";
254 description = "Path to the directory containing the host files";
255 };
256
257 deployable = mkEnableOption "Is this host deployable" // {
258 default = false;
259 };
260 }
261 // (mkBasicParams name);
262 }
263 )
264 );
265 };
266 };
267 };
268
269 config = {
270 # if the user has made it such that they want the hosts to be constructed automatically
271 # i.e. from the file paths then we will do that
272 easy-hosts.hosts = lib.mkIf cfg.autoConstruct (buildHosts cfg);
273
274 flake = mkHosts cfg;
275 };
276}