confctl 2.2.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. checksums.yaml +4 -4
  2. data/.git-hooks/pre_commit/nixfmt.rb +13 -0
  3. data/.github/workflows/rspec.yml +64 -0
  4. data/.github/workflows/rubocop.yml +27 -0
  5. data/.github/workflows/tests.yml +139 -0
  6. data/.gitignore +12 -7
  7. data/.overcommit.yml +2 -0
  8. data/.rspec +1 -0
  9. data/.rubocop.yml +8 -1
  10. data/AGENTS.md +42 -0
  11. data/CHANGELOG.md +57 -0
  12. data/Gemfile +3 -2
  13. data/Gemfile.lock +198 -0
  14. data/README.md +166 -58
  15. data/Rakefile +5 -0
  16. data/confctl.gemspec +20 -2
  17. data/docs/carrier.md +3 -3
  18. data/docs/flake-inputs.md +159 -0
  19. data/docs/swpins-to-flakes.md +325 -0
  20. data/example/cluster/module-list.nix +2 -1
  21. data/example/cluster/nixos-machine/config.nix +12 -2
  22. data/example/cluster/nixos-machine/hardware.nix +6 -1
  23. data/example/cluster/vpsadminos-container/config.nix +6 -1
  24. data/example/cluster/vpsadminos-container/module.nix +4 -1
  25. data/example/cluster/vpsadminos-machine/config.nix +6 -1
  26. data/example/cluster/vpsadminos-machine/hardware.nix +6 -1
  27. data/example/cluster/vpsadminos-machine/module.nix +5 -2
  28. data/example/cluster/vpsfreecz-vps/config.nix +6 -1
  29. data/example/cluster/vpsfreecz-vps/module.nix +4 -1
  30. data/example/configs/swpins.nix +8 -3
  31. data/example/environments/base.nix +6 -1
  32. data/example/swpins/core.json +35 -0
  33. data/example-flake/.gitignore +2 -0
  34. data/example-flake/README.md +38 -0
  35. data/example-flake/cluster/cluster.nix +5 -0
  36. data/example-flake/cluster/module-list.nix +4 -0
  37. data/example-flake/cluster/nested/nixos-machine/config.nix +25 -0
  38. data/example-flake/cluster/nested/nixos-machine/hardware.nix +9 -0
  39. data/example-flake/cluster/nested/nixos-machine/module.nix +8 -0
  40. data/example-flake/cluster/nixos-machine/config.nix +25 -0
  41. data/example-flake/cluster/nixos-machine/hardware.nix +9 -0
  42. data/example-flake/cluster/nixos-machine/module.nix +8 -0
  43. data/example-flake/cluster/vpsadminos-container/config.nix +28 -0
  44. data/example-flake/cluster/vpsadminos-container/module.nix +8 -0
  45. data/example-flake/cluster/vpsadminos-machine/config.nix +27 -0
  46. data/example-flake/cluster/vpsadminos-machine/hardware.nix +9 -0
  47. data/example-flake/cluster/vpsadminos-machine/module.nix +8 -0
  48. data/example-flake/cluster/vpsfreecz-vps/config.nix +31 -0
  49. data/example-flake/cluster/vpsfreecz-vps/module.nix +8 -0
  50. data/example-flake/configs/confctl.nix +10 -0
  51. data/example-flake/data/default.nix +5 -0
  52. data/example-flake/data/ssh-keys.nix +7 -0
  53. data/example-flake/environments/base.nix +18 -0
  54. data/example-flake/flake.lock +75 -0
  55. data/example-flake/flake.nix +36 -0
  56. data/example-flake/modules/module-list.nix +13 -0
  57. data/example-flake/shell.nix +11 -0
  58. data/flake.lock +159 -0
  59. data/flake.nix +189 -0
  60. data/gemset.nix +1022 -0
  61. data/lib/confctl/cli/app.rb +145 -0
  62. data/lib/confctl/cli/attr_filters.rb +1 -1
  63. data/lib/confctl/cli/cluster.rb +681 -108
  64. data/lib/confctl/cli/command.rb +24 -2
  65. data/lib/confctl/cli/configuration.rb +171 -106
  66. data/lib/confctl/cli/generation.rb +65 -1
  67. data/lib/confctl/cli/inputs/channels.rb +190 -0
  68. data/lib/confctl/cli/inputs/machines.rb +83 -0
  69. data/lib/confctl/cli/inputs/root.rb +101 -0
  70. data/lib/confctl/cli/inputs.rb +5 -0
  71. data/lib/confctl/cli/log_view.rb +21 -10
  72. data/lib/confctl/cli/migrate/swpins_to_flakes.rb +866 -0
  73. data/lib/confctl/cli/migrate.rb +5 -0
  74. data/lib/confctl/cli/output_formatter.rb +5 -7
  75. data/lib/confctl/cli/swpins/base.rb +9 -0
  76. data/lib/confctl/cli/swpins/channel.rb +2 -5
  77. data/lib/confctl/cli/swpins/cluster.rb +2 -5
  78. data/lib/confctl/cli/swpins/core.rb +2 -5
  79. data/lib/confctl/config_type.rb +7 -0
  80. data/lib/confctl/flake_lock.rb +78 -0
  81. data/lib/confctl/flake_lock_diff.rb +36 -0
  82. data/lib/confctl/generation/build.rb +131 -24
  83. data/lib/confctl/generation/build_list.rb +4 -3
  84. data/lib/confctl/generation/unified.rb +14 -1
  85. data/lib/confctl/git_repo_mirror.rb +2 -2
  86. data/lib/confctl/health_checks/run_command.rb +3 -2
  87. data/lib/confctl/health_checks/systemd/properties.rb +1 -1
  88. data/lib/confctl/health_checks/systemd/property_list.rb +2 -2
  89. data/lib/confctl/inputs/commit_message.rb +125 -0
  90. data/lib/confctl/inputs/git_commit.rb +17 -0
  91. data/lib/confctl/inputs/nix_output_guard.rb +37 -0
  92. data/lib/confctl/inputs/setter.rb +179 -0
  93. data/lib/confctl/inputs/updater.rb +76 -0
  94. data/lib/confctl/inputs.rb +5 -0
  95. data/lib/confctl/inputs_info.rb +50 -0
  96. data/lib/confctl/line_buffer.rb +1 -1
  97. data/lib/confctl/machine.rb +18 -3
  98. data/lib/confctl/machine_control.rb +8 -2
  99. data/lib/confctl/machine_list.rb +2 -2
  100. data/lib/confctl/machine_status.rb +63 -20
  101. data/lib/confctl/nix/args.rb +48 -0
  102. data/lib/confctl/nix.rb +30 -437
  103. data/lib/confctl/nix_build_flake.rb +95 -0
  104. data/lib/confctl/nix_copy.rb +14 -2
  105. data/lib/confctl/nix_flake.rb +449 -0
  106. data/lib/confctl/nix_format.rb +3 -3
  107. data/lib/confctl/nix_legacy.rb +467 -0
  108. data/lib/confctl/swpins/change_set.rb +29 -3
  109. data/lib/confctl/swpins/specs/base.rb +2 -2
  110. data/lib/confctl/ui.rb +19 -0
  111. data/lib/confctl/version.rb +1 -1
  112. data/man/index.html +11 -0
  113. data/man/man8/confctl-options.nix.8.html +113 -0
  114. data/man/man8/confctl.8 +152 -21
  115. data/man/man8/confctl.8.html +355 -0
  116. data/man/man8/confctl.8.md +148 -17
  117. data/man/style.css +301 -0
  118. data/nix/evaluator.nix +94 -69
  119. data/nix/flake/mk-confctl-devshell.nix +85 -0
  120. data/nix/flake/mk-confctl-outputs.nix +522 -0
  121. data/nix/flake/mk-config-devshell.nix +168 -0
  122. data/nix/lib/default.nix +118 -65
  123. data/nix/lib/machine/default.nix +65 -45
  124. data/nix/lib/machine/info.nix +16 -5
  125. data/nix/lib/swpins/eval.nix +42 -29
  126. data/nix/lib/swpins/options.nix +6 -2
  127. data/nix/machines.nix +23 -15
  128. data/nix/modules/cluster/default.nix +104 -41
  129. data/nix/modules/confctl/carrier/base.nix +11 -4
  130. data/nix/modules/confctl/carrier/carrier-env.rb +2 -2
  131. data/nix/modules/confctl/carrier/netboot/build-netboot-server.rb +50 -16
  132. data/nix/modules/confctl/carrier/netboot/nixos.nix +56 -24
  133. data/nix/modules/confctl/configuration-info.nix +17 -0
  134. data/nix/modules/confctl/generations.nix +2 -2
  135. data/nix/modules/confctl/host.nix +13 -0
  136. data/nix/modules/confctl/inputs-info.nix +21 -0
  137. data/nix/modules/confctl/kexec-netboot/default.nix +13 -6
  138. data/nix/modules/confctl/kexec-netboot/kexec-netboot.8.adoc +3 -0
  139. data/nix/modules/confctl/kexec-netboot/kexec-netboot.rb +25 -16
  140. data/nix/modules/confctl/nix.nix +31 -1
  141. data/nix/modules/confctl/swpins.nix +13 -6
  142. data/nix/modules/module-list.nix +4 -2
  143. data/nix/modules/system-list.nix +4 -1
  144. data/nix/package.nix +37 -0
  145. data/shell.nix +39 -8
  146. data/skills/confctl-configuration-update/SKILL.md +228 -0
  147. data/skills/confctl-configuration-update/agents/openai.yaml +4 -0
  148. data/skills/confctl-release/SKILL.md +102 -0
  149. data/skills/confctl-release/agents/openai.yaml +4 -0
  150. data/spec/confctl/cli/cluster_health_check_spec.rb +43 -0
  151. data/spec/confctl/cli/cluster_skip_current_deploy_spec.rb +157 -0
  152. data/spec/confctl/cli/cluster_status_flake_spec.rb +83 -0
  153. data/spec/confctl/cli/inputs_set_output_spec.rb +70 -0
  154. data/spec/confctl/configuration_spec.rb +45 -0
  155. data/spec/confctl/inputs_spec.rb +104 -0
  156. data/spec/confctl/machine_control_spec.rb +112 -0
  157. data/spec/confctl/machine_list_spec.rb +47 -0
  158. data/spec/confctl/nix_flake_spec.rb +41 -0
  159. data/spec/confctl/swpins_spec.rb +106 -0
  160. data/spec/generation/build_modes_spec.rb +132 -0
  161. data/spec/inputs/commit_message_spec.rb +205 -0
  162. data/spec/inputs/nix_cached_fallback_spec.rb +50 -0
  163. data/spec/inputs/nix_output_guard_spec.rb +45 -0
  164. data/spec/spec_helper.rb +12 -0
  165. data/spec/support/cli_helper.rb +57 -0
  166. data/test-runner.sh +7 -0
  167. data/tests/all-tests.nix +30 -0
  168. data/tests/make-test.nix +15 -0
  169. data/tests/runner/extensions/confctl_helpers.rb +238 -0
  170. data/tests/runner/extensions/hostfwd_ports.rb +41 -0
  171. data/tests/suite/auto_rollback.nix +344 -0
  172. data/tests/suite/carrier/deploy.nix +751 -0
  173. data/tests/suite/carrier/netboot.nix +849 -0
  174. data/tests/suite/deploy/base.nix +806 -0
  175. data/tests/suite/deploy/flakes.nix +1 -0
  176. data/tests/suite/deploy/swpins.nix +1 -0
  177. metadata +104 -4
  178. data/nix/modules/confctl/overlays.nix +0 -15
@@ -0,0 +1,522 @@
1
+ {
2
+ confDir,
3
+ inputs,
4
+ channels ? { },
5
+ system ? null,
6
+ }:
7
+ let
8
+ resolvedSystem =
9
+ if system != null then
10
+ system
11
+ else if builtins ? currentSystem then
12
+ builtins.currentSystem
13
+ else
14
+ "x86_64-linux";
15
+
16
+ confctlSrc = inputs.confctl;
17
+
18
+ coreNixpkgs = inputs.nixpkgs;
19
+ corePkgs = import coreNixpkgs { system = resolvedSystem; };
20
+ coreLib = corePkgs.lib;
21
+
22
+ flakeInputs = coreLib.filterAttrs (n: _: n != "self") inputs;
23
+
24
+ configurationSource = inputs.self or null;
25
+ configurationRevision =
26
+ if configurationSource == null then
27
+ null
28
+ else if configurationSource ? rev then
29
+ configurationSource.rev
30
+ else if configurationSource ? dirtyRev then
31
+ coreLib.removeSuffix "-dirty" configurationSource.dirtyRev
32
+ else
33
+ null;
34
+ configurationInfo =
35
+ if configurationRevision != null && builtins.match "[0-9a-f]{40}" configurationRevision != null then
36
+ {
37
+ schemaVersion = 1;
38
+ revision = configurationRevision;
39
+ revisionDirty = !(configurationSource ? rev) && configurationSource ? dirtyRev;
40
+ }
41
+ else
42
+ null;
43
+
44
+ confLib = import (confctlSrc + "/nix/lib") {
45
+ inherit confDir;
46
+ coreLib = coreLib;
47
+ corePkgs = corePkgs;
48
+ };
49
+
50
+ userClusterModules = confDir + "/modules/cluster/default.nix";
51
+
52
+ clusterModules = [
53
+ (
54
+ { ... }:
55
+ {
56
+ _module.args = {
57
+ pkgs = corePkgs;
58
+ inherit confLib flakeInputs;
59
+ confMachine = null;
60
+ };
61
+ }
62
+ )
63
+ (confctlSrc + "/nix/modules/cluster")
64
+ ]
65
+ ++ (import (confDir + "/cluster/module-list.nix"))
66
+ ++ (coreLib.optional (builtins.pathExists userClusterModules) userClusterModules);
67
+
68
+ evalCluster = coreLib.evalModules {
69
+ modules = clusterModules;
70
+ };
71
+
72
+ cluster = evalCluster.config.cluster;
73
+
74
+ clusterModulesForSystem = [
75
+ (confctlSrc + "/nix/modules/cluster")
76
+ ]
77
+ ++ (import (confDir + "/cluster/module-list.nix"));
78
+
79
+ makeMachine =
80
+ {
81
+ name,
82
+ metaConfig,
83
+ carrier ? null,
84
+ alias ? null,
85
+ clusterName ? null,
86
+ extraModules ? [ ],
87
+ }:
88
+ let
89
+ ensuredClusterName = if clusterName == null then name else clusterName;
90
+ in
91
+ {
92
+ inherit
93
+ name
94
+ alias
95
+ metaConfig
96
+ carrier
97
+ extraModules
98
+ ;
99
+ clusterName = ensuredClusterName;
100
+ };
101
+
102
+ generationUpdates =
103
+ cm:
104
+ coreLib.flatten (
105
+ map
106
+ (
107
+ generations:
108
+ map
109
+ (attr: {
110
+ path = [
111
+ generations
112
+ attr
113
+ ];
114
+ update =
115
+ old:
116
+ let
117
+ v = cm.${generations}.${attr};
118
+ in
119
+ if v == null then old else v;
120
+ })
121
+ [
122
+ "min"
123
+ "max"
124
+ "maxAge"
125
+ ]
126
+ )
127
+ [
128
+ "buildGenerations"
129
+ "hostGenerations"
130
+ ]
131
+ );
132
+
133
+ expandCarrier =
134
+ machineAttrs: carrierMachine:
135
+ map (
136
+ cm:
137
+ makeMachine {
138
+ name = "${carrierMachine.name}#${if cm.alias == null then cm.machine else cm.alias}";
139
+ alias = cm.alias;
140
+ clusterName = cm.machine;
141
+ carrier = carrierMachine.name;
142
+ extraModules = cm.extraModules;
143
+ metaConfig = coreLib.updateManyAttrsByPath (
144
+ [
145
+ {
146
+ path = [ "labels" ];
147
+ update = old: old // cm.labels;
148
+ }
149
+ {
150
+ path = [ "tags" ];
151
+ update = old: old ++ cm.tags;
152
+ }
153
+ {
154
+ path = [ "buildAttribute" ];
155
+ update = _: cm.buildAttribute;
156
+ }
157
+ ]
158
+ ++ (generationUpdates cm)
159
+ ) machineAttrs.${cm.machine}.metaConfig;
160
+ }
161
+ ) carrierMachine.metaConfig.carrier.machines;
162
+
163
+ expandCarriers =
164
+ machineAttrs:
165
+ coreLib.flatten (
166
+ coreLib.mapAttrsToList (
167
+ name: m: if m.metaConfig.carrier.enable then [ m ] ++ (expandCarrier machineAttrs m) else m
168
+ ) machineAttrs
169
+ );
170
+
171
+ getClusterMachines =
172
+ cluster:
173
+ let
174
+ machineAttrs = coreLib.mapAttrs (
175
+ name: metaConfig: makeMachine { inherit name metaConfig; }
176
+ ) cluster;
177
+ in
178
+ expandCarriers machineAttrs;
179
+
180
+ mkMachineKey =
181
+ name:
182
+ let
183
+ sanitized =
184
+ let
185
+ chars =
186
+ if builtins ? stringToCharacters then
187
+ builtins.stringToCharacters name
188
+ else
189
+ coreLib.stringToCharacters name;
190
+ safeChar =
191
+ c:
192
+ if (c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || (c >= "0" && c <= "9") || (c == "_") then
193
+ c
194
+ else
195
+ "_";
196
+ s = builtins.concatStringsSep "" (map safeChar chars);
197
+ in
198
+ if builtins.match "^[0-9].*" s != null then "_" + s else s;
199
+
200
+ hash = builtins.substring 0 8 (builtins.hashString "sha256" name);
201
+ in
202
+ "m_" + sanitized + "_" + hash;
203
+
204
+ machines = getClusterMachines cluster;
205
+
206
+ machinesWithKey = map (m: m // { key = mkMachineKey m.name; }) machines;
207
+
208
+ machineNames = map (m: m.name) machinesWithKey;
209
+
210
+ machineKeys = builtins.listToAttrs (
211
+ map (m: {
212
+ name = m.name;
213
+ value = m.key;
214
+ }) machinesWithKey
215
+ );
216
+
217
+ machinesAttrs = builtins.listToAttrs (
218
+ map (m: {
219
+ name = m.name;
220
+ value = m // {
221
+ key = m.key;
222
+ flakeKey = m.key;
223
+ };
224
+ }) machinesWithKey
225
+ );
226
+
227
+ machinesByKey = builtins.listToAttrs (
228
+ map (m: {
229
+ name = m.key;
230
+ value = m;
231
+ }) machinesWithKey
232
+ );
233
+
234
+ lock = builtins.fromJSON (builtins.readFile (confDir + "/flake.lock"));
235
+
236
+ derivedUrlForNode =
237
+ node:
238
+ let
239
+ original = node.original or { };
240
+ in
241
+ if (original.type or null) == "github" then
242
+ "https://github.com/${original.owner}/${original.repo}"
243
+ else if original ? url then
244
+ let
245
+ raw = original.url;
246
+ in
247
+ if coreLib.hasPrefix "git+" raw then coreLib.removePrefix "git+" raw else raw
248
+ else
249
+ null;
250
+
251
+ roleInputsForChannels =
252
+ channelNames: builtins.foldl' (acc: chan: acc // (channels.${chan} or { })) { } channelNames;
253
+
254
+ inputPathsFor = roleInputs: coreLib.mapAttrs (_: inputName: inputs.${inputName}.outPath) roleInputs;
255
+
256
+ mkRoleInfo =
257
+ inputName:
258
+ let
259
+ node = lock.nodes.${inputName} or { };
260
+ src = node.locked or { };
261
+ rev = src.rev or null;
262
+ in
263
+ {
264
+ input = inputName;
265
+ url = derivedUrlForNode node;
266
+ inherit rev;
267
+ shortRev = if rev == null then null else builtins.substring 0 8 rev;
268
+ lastModified = src.lastModified or null;
269
+ };
270
+
271
+ inputsInfoFor = roleInputs: coreLib.mapAttrs (_: inputName: mkRoleInfo inputName) roleInputs;
272
+ buildPlan = builtins.listToAttrs (
273
+ map (
274
+ m:
275
+ let
276
+ channelNames = (m.metaConfig.inputs.channels or [ ]);
277
+ inputsOverrides = (m.metaConfig.inputs or { }).overrides or { };
278
+
279
+ roleInputs = (roleInputsForChannels channelNames) // inputsOverrides;
280
+ inputPaths = inputPathsFor roleInputs;
281
+ inputsInfo = inputsInfoFor roleInputs;
282
+ in
283
+ {
284
+ name = m.name;
285
+ value = {
286
+ key = m.key;
287
+ flakeKey = m.key;
288
+ inputs = inputPaths;
289
+ inputsInfo = inputsInfo;
290
+ };
291
+ }
292
+ ) machinesWithKey
293
+ );
294
+
295
+ inputsOutput = builtins.listToAttrs (
296
+ map (m: {
297
+ name = m.key;
298
+ value = buildPlan.${m.name}.inputs;
299
+ }) machinesWithKey
300
+ );
301
+
302
+ inputsInfoOutput = builtins.listToAttrs (
303
+ map (m: {
304
+ name = m.key;
305
+ value = buildPlan.${m.name}.inputsInfo;
306
+ }) machinesWithKey
307
+ );
308
+
309
+ confctlModules = [
310
+ (confctlSrc + "/nix/modules/confctl/generations.nix")
311
+ (confctlSrc + "/nix/modules/confctl/cli.nix")
312
+ (confctlSrc + "/nix/modules/confctl/nix.nix")
313
+ (confctlSrc + "/nix/modules/confctl/inputs-info.nix")
314
+ (confctlSrc + "/nix/modules/confctl/configuration-info.nix")
315
+ ];
316
+
317
+ confctlConfig = confDir + "/configs/confctl.nix";
318
+ userModuleListPath = confDir + "/modules/module-list.nix";
319
+ userModuleList =
320
+ if builtins.pathExists userModuleListPath then
321
+ import userModuleListPath
322
+ else
323
+ {
324
+ nixos = [ ];
325
+ vpsadminos = [ ];
326
+ };
327
+
328
+ evalConfctl = import (coreNixpkgs + "/nixos/lib/eval-config.nix") {
329
+ modules = confctlModules ++ (coreLib.optional (builtins.pathExists confctlConfig) confctlConfig);
330
+ pkgs = corePkgs;
331
+ lib = coreLib;
332
+ system = resolvedSystem;
333
+ };
334
+
335
+ settings = evalConfctl.config.confctl;
336
+ confData = import (confDir + "/data/default.nix") { lib = coreLib; };
337
+
338
+ baseSystemModule =
339
+ { ... }:
340
+ {
341
+ _module.args = {
342
+ inherit
343
+ confDir
344
+ confLib
345
+ configurationInfo
346
+ flakeInputs
347
+ ;
348
+ inherit confData;
349
+ };
350
+ };
351
+
352
+ confMachineFor =
353
+ m:
354
+ (confLib.findMetaConfig {
355
+ inherit cluster;
356
+ name = m.clusterName;
357
+ })
358
+ // {
359
+ name = m.clusterName;
360
+ };
361
+
362
+ machineArgsModule =
363
+ m:
364
+ { ... }:
365
+ let
366
+ plan = buildPlan.${m.name};
367
+ in
368
+ {
369
+ _module.args = {
370
+ inputs = plan.inputs;
371
+ inputsInfo = plan.inputsInfo;
372
+ confMachine = confMachineFor m;
373
+ };
374
+ };
375
+
376
+ specialArgsFor =
377
+ m:
378
+ let
379
+ plan = buildPlan.${m.name};
380
+ in
381
+ {
382
+ inherit
383
+ confDir
384
+ confLib
385
+ configurationInfo
386
+ flakeInputs
387
+ confData
388
+ ;
389
+ confMachine = confMachineFor m;
390
+ inputs = plan.inputs;
391
+ inputsInfo = plan.inputsInfo;
392
+ };
393
+
394
+ systemModulesFor =
395
+ m:
396
+ let
397
+ userModules = userModuleList.${m.metaConfig.spin} or [ ];
398
+ systemModules = (import (confctlSrc + "/nix/modules/system-list.nix")).${m.metaConfig.spin} or [ ];
399
+ moduleArgsModules =
400
+ if m.metaConfig.spin == "vpsadminos" then
401
+ [ ]
402
+ else
403
+ [
404
+ baseSystemModule
405
+ (machineArgsModule m)
406
+ ];
407
+ in
408
+ moduleArgsModules
409
+ ++ clusterModulesForSystem
410
+ ++ [
411
+ (confDir + "/cluster/${m.clusterName}/config.nix")
412
+ ]
413
+ ++ confctlModules
414
+ ++ systemModules
415
+ ++ userModules
416
+ ++ m.extraModules;
417
+
418
+ autoRollbackFor =
419
+ pkgs:
420
+ let
421
+ args = {
422
+ src = confctlSrc + "/libexec/auto-rollback.rb";
423
+ isExecutable = true;
424
+ };
425
+
426
+ replacements = {
427
+ ruby = pkgs.ruby;
428
+ };
429
+ in
430
+ if pkgs ? replaceVarsWith then
431
+ pkgs.replaceVarsWith {
432
+ inherit (args) src isExecutable;
433
+ inherit replacements;
434
+ }
435
+ else
436
+ pkgs.substituteAll ({ inherit (args) src isExecutable; } // replacements);
437
+
438
+ evalConfigFor =
439
+ m:
440
+ let
441
+ plan = buildPlan.${m.name};
442
+ nixpkgsInput = inputs.${plan.inputsInfo.nixpkgs.input};
443
+ modules = systemModulesFor m;
444
+ specialArgs = specialArgsFor m;
445
+ in
446
+ if m.metaConfig.spin == "nixos" then
447
+ let
448
+ evalConfig = nixpkgsInput.lib.nixosSystem {
449
+ inherit modules specialArgs;
450
+ system = resolvedSystem;
451
+ };
452
+ in
453
+ {
454
+ inherit evalConfig;
455
+ pkgs = evalConfig.pkgs;
456
+ }
457
+ else if m.metaConfig.spin == "vpsadminos" then
458
+ let
459
+ vpsadminosInput = inputs.${plan.inputsInfo.vpsadminos.input};
460
+ evalResult = vpsadminosInput.lib.vpsadminosSystem {
461
+ pkgs = nixpkgsInput.legacyPackages.${resolvedSystem};
462
+ inherit modules specialArgs;
463
+ system = resolvedSystem;
464
+ };
465
+ evalConfig = evalResult.eval;
466
+ in
467
+ {
468
+ evalConfig = evalConfig;
469
+ pkgs = evalResult.pkgs;
470
+ }
471
+ else
472
+ abort "Unsupported spin ${m.metaConfig.spin}";
473
+
474
+ buildOutputs = builtins.listToAttrs (
475
+ map (
476
+ m:
477
+ let
478
+ plan = buildPlan.${m.name};
479
+ evalResult = evalConfigFor m;
480
+ evalConfig = evalResult.evalConfig;
481
+ pkgs = evalResult.pkgs;
482
+ buildAttrPath =
483
+ m.metaConfig.buildAttribute or [
484
+ "system"
485
+ "build"
486
+ "toplevel"
487
+ ];
488
+ buildAttr = coreLib.attrByPath buildAttrPath null evalConfig.config;
489
+ buildValue =
490
+ if buildAttr == null then
491
+ abort "Attribute 'config.${coreLib.concatStringsSep "." buildAttrPath}' not found on machine ${m.name}"
492
+ else
493
+ buildAttr;
494
+ in
495
+ {
496
+ name = plan.key;
497
+ value = {
498
+ toplevel = buildValue;
499
+ autoRollback = autoRollbackFor pkgs;
500
+ };
501
+ }
502
+ ) machinesWithKey
503
+ );
504
+
505
+ toplevelOutput = coreLib.mapAttrs (_: v: v.toplevel) buildOutputs;
506
+ in
507
+ {
508
+ settings = settings;
509
+ channels = channels;
510
+ machineNames = machineNames;
511
+ machineKeys = machineKeys;
512
+ machines = machinesAttrs;
513
+ machinesJson = corePkgs.writeText "machine-list.json" (builtins.toJSON machinesAttrs);
514
+ buildPlan = buildPlan;
515
+ inputs = inputsOutput;
516
+ inputsInfo = inputsInfoOutput;
517
+ build = buildOutputs;
518
+ toplevel = toplevelOutput;
519
+ lib = {
520
+ mkMachineKey = mkMachineKey;
521
+ };
522
+ }
@@ -0,0 +1,168 @@
1
+ { self, nixpkgs }:
2
+
3
+ # mkConfigDevShell builds a dev shell intended for cluster configuration repos
4
+ # that use confctl as a flake input.
5
+ #
6
+ # Modes:
7
+ # - minimal: confctl comes from the flake package, no Bundler bootstrap
8
+ # - tools: repo-local Bundler environment for repo tooling, confctl stays packaged
9
+ # - bundled-confctl: repo-local Bundler environment and confctl runs via bundle exec
10
+
11
+ {
12
+ system,
13
+ pkgs ? nixpkgs.legacyPackages.${system},
14
+ mode ? "minimal",
15
+ extraPackages ? [ ],
16
+ legacyCompat ? false,
17
+ }:
18
+ let
19
+ lib = pkgs.lib;
20
+ confctlPackage = self.packages.${system}.confctl;
21
+ bundlerMode = builtins.elem mode [
22
+ "tools"
23
+ "bundled-confctl"
24
+ ];
25
+
26
+ fail = message: ''
27
+ echo ${lib.escapeShellArg message} >&2
28
+ return 1 2>/dev/null || exit 1
29
+ '';
30
+
31
+ bundlerBootstrap = ''
32
+ export GEM_ROOT="$PWD/.gems"
33
+ export GEM_HOME="$GEM_ROOT/ruby/$(ruby -e 'require "rbconfig"; print RbConfig::CONFIG["ruby_version"]')"
34
+ export GEM_PATH="$GEM_HOME"
35
+ export BUNDLE_PATH="$GEM_ROOT"
36
+ export BUNDLE_APP_CONFIG="''${BUNDLE_APP_CONFIG:-$PWD/.bundle}"
37
+ export RUBOCOP_CACHE_ROOT="$PWD/.rubocop_cache"
38
+
39
+ mkdir -p "$GEM_ROOT" "$GEM_HOME" "$PWD/.bin"
40
+ rm -f "$PWD/.bin/bundle" "$PWD/.bin/bundler"
41
+
42
+ bundler_version=""
43
+ if [ -f "$PWD/Gemfile.lock" ]; then
44
+ bundler_version="$(awk '/^BUNDLED WITH$/{getline; sub(/^[[:space:]]+/, "", $0); print; exit}' "$PWD/Gemfile.lock")"
45
+ fi
46
+
47
+ if [ -n "$bundler_version" ]; then
48
+ export BUNDLER_VERSION="$bundler_version"
49
+ if [ ! -x "$GEM_HOME/bin/bundle" ] \
50
+ || ! gem list -i bundler -v "$bundler_version" >/dev/null 2>&1; then
51
+ gem install --no-document bundler -v "$bundler_version" >/dev/null
52
+ fi
53
+ elif [ ! -x "$GEM_HOME/bin/bundle" ]; then
54
+ gem install --no-document bundler >/dev/null
55
+ fi
56
+ bundle_bin="$GEM_HOME/bin/bundle"
57
+
58
+ "$bundle_bin" config set --local path "$BUNDLE_PATH" >/dev/null
59
+ "$bundle_bin" config set --local bin "$PWD/.bin" >/dev/null
60
+ '';
61
+
62
+ modeShellHook =
63
+ if mode == "minimal" then
64
+ ''
65
+ export PATH="${confctlPackage}/bin:$PATH"
66
+ ''
67
+ else if mode == "tools" then
68
+ ''
69
+ if [ ! -f "$PWD/Gemfile" ]; then
70
+ ${fail "mkConfigDevShell mode 'tools' requires ./Gemfile"}
71
+ fi
72
+
73
+ ${bundlerBootstrap}
74
+
75
+ export PATH="${confctlPackage}/bin:$PWD/.bin:$GEM_HOME/bin:$PATH"
76
+
77
+ NIX_ENFORCE_PURITY=0 "$bundle_bin" install >/dev/null
78
+
79
+ if "$bundle_bin" info rubocop >/dev/null 2>&1; then
80
+ "$bundle_bin" binstubs rubocop --force >/dev/null
81
+ fi
82
+ ''
83
+ else
84
+ ''
85
+ if [ ! -f "$PWD/Gemfile" ]; then
86
+ ${
87
+ if legacyCompat then
88
+ ''
89
+ mkdir -p "$PWD/.confctl-shell"
90
+
91
+ cat > "$PWD/.confctl-shell/Gemfile" <<EOF
92
+ source "https://rubygems.org"
93
+ gem "confctl", path: "$CONFCTL_SRC"
94
+ EOF
95
+
96
+ export BUNDLE_GEMFILE="$PWD/.confctl-shell/Gemfile"
97
+ export BUNDLE_APP_CONFIG="$PWD/.confctl-shell/.bundle"
98
+ mkdir -p "$BUNDLE_APP_CONFIG"
99
+ ''
100
+ else
101
+ fail "mkConfigDevShell mode 'bundled-confctl' requires ./Gemfile containing `gem \"confctl\"`"
102
+ }
103
+ fi
104
+
105
+ ${bundlerBootstrap}
106
+
107
+ export PATH="$PWD/.bin:$GEM_HOME/bin:${confctlPackage}/bin:$PATH"
108
+
109
+ "$bundle_bin" config set --local "local.confctl" "$CONFCTL_SRC" >/dev/null
110
+
111
+ NIX_ENFORCE_PURITY=0 "$bundle_bin" install >/dev/null
112
+
113
+ if ! "$bundle_bin" info confctl >/dev/null 2>&1; then
114
+ ${fail "mkConfigDevShell mode 'bundled-confctl' requires ./Gemfile to include `gem \"confctl\"`"}
115
+ fi
116
+
117
+ if "$bundle_bin" info rubocop >/dev/null 2>&1; then
118
+ "$bundle_bin" binstubs rubocop --force >/dev/null
119
+ fi
120
+
121
+ cat > "$PWD/.bin/confctl" <<'EOF'
122
+ #!/usr/bin/env bash
123
+ bundle_bin="$GEM_HOME/bin/bundle"
124
+ exec "$bundle_bin" exec confctl "$@"
125
+ EOF
126
+
127
+ chmod +x "$PWD/.bin/confctl"
128
+ '';
129
+ in
130
+ assert builtins.elem mode [
131
+ "minimal"
132
+ "tools"
133
+ "bundled-confctl"
134
+ ];
135
+ pkgs.mkShell {
136
+ packages =
137
+ (with pkgs; [
138
+ confctlPackage
139
+ git
140
+ ncurses
141
+ man-db
142
+ groff
143
+ less
144
+ nix-prefetch-git
145
+ nixfmt
146
+ nixfmt-tree
147
+ ])
148
+ ++ lib.optionals bundlerMode (
149
+ with pkgs;
150
+ [
151
+ openssl
152
+ ruby
153
+ ]
154
+ )
155
+ ++ extraPackages;
156
+
157
+ shellHook = ''
158
+ export CONFCTL_SRC="${self.outPath}"
159
+
160
+ ${modeShellHook}
161
+
162
+ export MANPATH="$CONFCTL_SRC/man:$(man --path)"
163
+ export PS1="(confctl) $PS1"
164
+
165
+ echo
166
+ echo "confctl config dev shell ready (${mode})"
167
+ '';
168
+ }