confctl 2.2.3 → 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 +54 -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 -5
  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 +24 -2
  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 -13
  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
data/lib/confctl/nix.rb CHANGED
@@ -1,450 +1,43 @@
1
- require 'confctl/utils/file'
2
- require 'digest'
3
- require 'etc'
4
- require 'json'
5
- require 'securerandom'
6
- require 'tempfile'
1
+ require 'forwardable'
2
+ require_relative 'config_type'
3
+ require_relative 'nix_legacy'
4
+ require_relative 'nix_flake'
7
5
 
8
6
  module ConfCtl
9
7
  class Nix
8
+ extend Forwardable
9
+
10
10
  # Create a new instance without access to {ConfCtl::Settings}, i.e. when
11
11
  # called outside of cluster configuration directory.
12
12
  # @return [Nix]
13
13
  def self.stateless(show_trace: false, max_jobs: 'auto')
14
- new(show_trace:, max_jobs:)
14
+ new(show_trace: show_trace, max_jobs: max_jobs)
15
15
  end
16
16
 
17
- include Utils::File
18
-
19
17
  def initialize(conf_dir: nil, show_trace: false, max_jobs: nil, cores: nil)
20
18
  @conf_dir = conf_dir || ConfDir.path
21
- @show_trace = show_trace
22
- @max_jobs = max_jobs || Settings.instance.max_jobs
23
- @cores = cores
24
- @cmd = SystemCommand.new
25
- end
26
-
27
- def confctl_settings
28
- out_link = File.join(
29
- cache_dir,
30
- 'build',
31
- 'settings.json'
32
- )
33
-
34
- with_cache(out_link) do
35
- with_argument({
36
- confDir: conf_dir,
37
- build: :confctl
38
- }) do |arg|
39
- cmd_args = [
40
- 'nix-build',
41
- '--arg', 'jsonArg', arg,
42
- '--out-link', out_link,
43
- (show_trace ? '--show-trace' : nil),
44
- (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
45
- (cores ? ['--cores', cores.to_s] : nil),
46
- ConfCtl.nix_asset('evaluator.nix')
47
- ].flatten.compact
48
-
49
- cmd.run(*cmd_args)
50
- end
51
- end
52
-
53
- demodulify(JSON.parse(File.read(out_link))['confctl'])
54
- end
55
-
56
- # Returns an array with options from all confctl modules
57
- # @return [Array]
58
- def module_options
59
- options = nix_instantiate({
60
- confDir: conf_dir,
61
- build: :moduleOptions
62
- })
63
- end
64
-
65
- # Returns an array with machine fqdns
66
- # @return [Array<String>]
67
- def list_machine_fqdns
68
- nix_instantiate({
69
- confDir: conf_dir,
70
- build: :list
71
- })['machines']
72
- end
73
-
74
- # Return machines and their config in a hash
75
- # @return [Hash]
76
- def list_machines
77
- out_link = File.join(
78
- cache_dir,
79
- 'build',
80
- 'machine-list.json'
81
- )
82
-
83
- with_cache(out_link) do
84
- with_argument({
85
- confDir: conf_dir,
86
- build: :info
87
- }, core_swpins: true) do |arg|
88
- cmd_args = [
89
- 'nix-build',
90
- '--arg', 'jsonArg', arg,
91
- '--out-link', out_link,
92
- (show_trace ? '--show-trace' : nil),
93
- (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
94
- (cores ? ['--cores', cores.to_s] : nil),
95
- ConfCtl.nix_asset('evaluator.nix')
96
- ].flatten.compact
97
-
98
- cmd.run(*cmd_args)
99
- end
100
- end
101
-
102
- demodulify(JSON.parse(File.read(out_link)))
103
- end
104
-
105
- def list_swpins_channels
106
- nix_instantiate({
107
- confDir: conf_dir,
108
- build: :listSwpinsChannels
109
- })
110
- end
111
-
112
- # Evaluate swpins for host
113
- # @return [Hash]
114
- def eval_core_swpins
115
- with_argument({
116
- confDir: conf_dir,
117
- build: :evalCoreSwpins
118
- }) do |arg|
119
- out_link = File.join(
120
- cache_dir,
121
- 'build',
122
- 'core.swpins'
123
- )
124
-
125
- cmd_args = [
126
- 'nix-build',
127
- '--arg', 'jsonArg', arg,
128
- '--out-link', out_link,
129
- (show_trace ? '--show-trace' : nil),
130
- (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
131
- (cores ? ['--cores', cores.to_s] : nil),
132
- ConfCtl.nix_asset('evaluator.nix')
133
- ].flatten.compact
134
-
135
- out, = cmd.run(*cmd_args).stdout
136
-
137
- JSON.parse(File.read(out.strip))
138
- end
139
- end
140
-
141
- # Evaluate swpins for hosts
142
- # @param hosts [Array<String>]
143
- # @return [Hash] host => swpins
144
- def eval_host_swpins(hosts)
145
- with_argument({
146
- confDir: conf_dir,
147
- build: :evalHostSwpins,
148
- machines: hosts
149
- }, core_swpins: true) do |arg|
150
- out_link = File.join(
151
- cache_dir,
152
- 'build',
153
- "#{Digest::SHA256.hexdigest(hosts.join(','))[0..11]}.swpins"
154
- )
155
-
156
- cmd_args = [
157
- 'nix-build',
158
- '--arg', 'jsonArg', arg,
159
- '--out-link', out_link,
160
- (show_trace ? '--show-trace' : nil),
161
- (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
162
- (cores ? ['--cores', cores.to_s] : nil),
163
- ConfCtl.nix_asset('evaluator.nix')
164
- ].flatten.compact
165
-
166
- out, = cmd.run(*cmd_args)
167
-
168
- JSON.parse(File.read(out.strip))
169
- end
170
- end
171
-
172
- # Build config.system.build.toplevel for selected hosts
173
- #
174
- # @param hosts [Array<Machine>]
175
- # @param swpin_paths [Hash]
176
- # @param host_swpin_specs [Hash]
177
- # @param time [Time]
178
- #
179
- # @yieldparam type [:build, :fetch]
180
- # @yieldparam progress [Integer]
181
- # @yieldparam total [Integer]
182
- # @yieldparam path [String]
183
- #
184
- # @return [Hash<String, Generation::Build>]
185
- def build_attributes(hosts: [], swpin_paths: {}, host_swpin_specs: {}, time: nil, &block)
186
- with_argument({
187
- confDir: conf_dir,
188
- build: :toplevel,
189
- machines: hosts
190
- }) do |arg|
191
- time ||= Time.now
192
- ret_generations = {}
193
- out_link = File.join(cache_dir, 'build', "#{SecureRandom.hex(4)}.build")
194
-
195
- cmd_args = [
196
- '--arg', 'jsonArg', arg,
197
- '--out-link', out_link,
198
- (show_trace ? '--show-trace' : nil),
199
- (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
200
- (cores ? ['--cores', cores.to_s] : nil),
201
- ConfCtl.nix_asset('evaluator.nix')
202
- ].flatten.compact
203
-
204
- nb = NixBuild.new(cmd_args, swpin_paths)
205
- nb.run(&block)
206
-
207
- begin
208
- host_results = JSON.parse(File.read(out_link))
209
- host_results.each do |host, result|
210
- host_generations = Generation::BuildList.new(host)
211
- generation = host_generations.find(result['attribute'], swpin_paths)
212
-
213
- if generation.nil?
214
- generation = Generation::Build.new(host)
215
- generation.create(
216
- result['attribute'],
217
- result['autoRollback'],
218
- swpin_paths,
219
- host_swpin_specs[host],
220
- date: time
221
- )
222
- generation.save
223
- end
224
-
225
- host_generations.current = generation
226
- ret_generations[host] = generation
227
- end
228
- ensure
229
- unlink_if_exists(out_link)
230
- end
231
-
232
- ret_generations
233
- end
234
- end
235
-
236
- # @param machine [Machine]
237
- # @param paths [Array<String>]
238
- #
239
- # @yieldparam progress [Integer]
240
- # @yieldparam total [Integer]
241
- # @yieldparam path [String]
242
- #
243
- # @return [Boolean]
244
- def copy(machine, paths, &)
245
- if machine.localhost?
246
- true
247
- elsif machine.carried?
248
- cp = NixCopy.new(machine.carrier_machine.target_host, paths)
249
- cp.run!(&).success?
250
- else
251
- cp = NixCopy.new(machine.target_host, paths)
252
- cp.run!(&).success?
253
- end
254
- end
255
-
256
- # @param machine [Machine]
257
- # @param generation [Generation::Build]
258
- # @param action [String]
259
- # @return [Boolean]
260
- def activate(machine, generation, action)
261
- args = [File.join(generation.toplevel, 'bin/switch-to-configuration'), action]
262
-
263
- MachineControl.new(machine).execute!(*args).success?
264
- end
265
-
266
- # @param machine [Machine]
267
- # @param generation [Generation::Build]
268
- # @param action [String]
269
- # @return [Boolean]
270
- def activate_with_rollback(machine, generation, action)
271
- check_file = File.join('/run', "confctl-confirm-#{SecureRandom.hex(3)}")
272
- timeout = machine['autoRollback']['timeout']
273
- logger = NullLogger.new
274
-
275
- args = [
276
- generation.auto_rollback,
277
- '-t', timeout,
278
- generation.toplevel,
279
- action,
280
- check_file
281
- ]
282
-
283
- activation_success = nil
284
-
285
- activation_thread = Thread.new do
286
- activation_success = MachineControl.new(machine).execute!(*args).success?
287
- end
288
-
289
- # Wait for the configuration to be switched
290
- t = Time.now
291
-
292
- loop do
293
- out, = MachineControl.new(machine, logger:).execute!('cat', check_file, '2>/dev/null')
294
- stripped = out.strip
295
- break if stripped == 'switched' || ((t + timeout + 10) < Time.now && stripped != 'switching')
296
-
297
- sleep(1)
298
- end
299
-
300
- # Confirm it
301
- 10.times do
302
- break if MachineControl.new(machine, logger:).execute!('sh', '-c', "'echo confirmed > #{check_file}'").success?
303
- end
304
-
305
- activation_thread.join
306
- activation_success
307
- end
308
-
309
- # @param machine [Machine]
310
- # @param toplevel [String]
311
- # @return [Boolean]
312
- def set_profile(machine, toplevel)
313
- args = [
314
- 'nix-env',
315
- '-p', machine.profile,
316
- '--set', toplevel
317
- ]
318
-
319
- MachineControl.new(machine).execute!(*args).success?
320
- end
321
-
322
- # @param machine [Machine]
323
- # @param toplevel [String]
324
- # @return [Boolean]
325
- def set_carried_profile(machine, toplevel)
326
- args = [
327
- 'carrier-env',
328
- '-p', machine.profile,
329
- '--set', toplevel
330
- ]
331
-
332
- MachineControl.new(machine.carrier_machine).execute!(*args).success?
333
- end
334
-
335
- # @param packages [Array<String>]
336
- # @param command [String]
337
- # @return [Boolean]
338
- def run_command_in_shell(packages: [], command: nil)
339
- args = ['nix-shell']
340
-
341
- if packages.any?
342
- args << '-p'
343
- args.concat(packages)
344
- end
345
-
346
- args << '--command'
347
- args << command
348
-
349
- cmd.run!(*args, env: { 'shellHook' => nil }).success?
350
- end
351
-
352
- # @param machine [Machine]
353
- # @yieldparam progress [NixCollectGarbage::Progress]
354
- # @return [Boolean]
355
- def collect_garbage(machine, &)
356
- gc = NixCollectGarbage.new(machine)
357
- gc.run!(&).success?
358
- end
359
-
360
- protected
361
-
362
- attr_reader :conf_dir, :show_trace, :max_jobs, :cores, :cmd
363
-
364
- # Execute block only if `out_link` does not exist or conf dir has changed
365
- # @param out_link [String] out link path
366
- def with_cache(out_link)
367
- unchanged = false
368
-
369
- if File.exist?(out_link)
370
- unchanged = ConfDir.unchanged?
371
-
372
- if unchanged
373
- cache_mtime = ConfDir.state_mtime
374
-
375
- if cache_mtime
376
- begin
377
- st = File.lstat(out_link)
378
- rescue Errno::ENOENT
379
- # pass
380
- else
381
- if st.mtime > cache_mtime
382
- Logger.instance << "Using #{out_link}\n"
383
- return
384
- end
385
- end
386
- end
387
- end
388
- end
389
-
390
- Logger.instance << "Building #{out_link}\n"
391
-
392
- unless unchanged
393
- Logger.instance << "Updating configuration cache\n"
394
- ConfDir.update_state
395
- end
396
- yield
397
- end
398
-
399
- # @param hash [Hash]
400
- # @param core_swpins [Boolean]
401
- # @yieldparam file [String]
402
- def with_argument(hash, core_swpins: false)
403
- if core_swpins
404
- paths = Swpins::Core.get.pre_evaluated_store_paths
405
- hash[:coreSwpins] = paths if paths
406
- end
407
-
408
- f = Tempfile.new('confctl')
409
- f.puts(hash.to_json)
410
- f.close
411
- yield(f.path)
412
- ensure
413
- f.unlink
414
- end
415
-
416
- def nix_instantiate(hash, **opts)
417
- with_argument(hash, **opts) do |arg|
418
- cmd_args = [
419
- 'nix-instantiate',
420
- '--eval',
421
- '--json',
422
- '--strict',
423
- '--read-write-mode',
424
- '--arg', 'jsonArg', arg,
425
- (show_trace ? '--show-trace' : nil),
426
- (max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
427
- (cores ? ['--cores', cores.to_s] : nil),
428
- ConfCtl.nix_asset('evaluator.nix')
429
- ].flatten.compact
430
-
431
- out, = cmd.run(*cmd_args).stdout
432
-
433
- demodulify(JSON.parse(out))
434
- end
435
- end
436
-
437
- def demodulify(value)
438
- if value.is_a?(Array)
439
- value.each { |item| demodulify(item) }
440
- elsif value.is_a?(Hash)
441
- value.delete('_module')
442
- value.each_value { |v| demodulify(v) }
443
- end
444
- end
445
-
446
- def cache_dir
447
- ConfDir.cache_dir
448
- end
19
+ impl_klass = ConfigType.flake?(@conf_dir) ? NixFlake : NixLegacy
20
+ @impl = impl_klass.new(conf_dir: @conf_dir, show_trace: show_trace, max_jobs: max_jobs, cores: cores)
21
+ end
22
+
23
+ def_delegators :@impl,
24
+ :confctl_settings,
25
+ :module_options,
26
+ :list_machine_fqdns,
27
+ :list_machines,
28
+ :build_attributes,
29
+ :copy,
30
+ :activate,
31
+ :activate_with_rollback,
32
+ :set_profile,
33
+ :set_carried_profile,
34
+ :run_command_in_shell,
35
+ :collect_garbage,
36
+ :list_swpins_channels,
37
+ :eval_core_swpins,
38
+ :eval_host_swpins,
39
+ :eval_inputs_info,
40
+ :eval_inputs,
41
+ :eval_json
449
42
  end
450
43
  end
@@ -0,0 +1,95 @@
1
+ module ConfCtl
2
+ class NixBuildFlake
3
+ # @param args [Array<String>]
4
+ # @param chdir [String]
5
+ def initialize(args, chdir:)
6
+ @args = args
7
+ @chdir = chdir
8
+ @build_progress = 0
9
+ @build_total = 0
10
+ @fetch_progress = 0
11
+ @fetch_total = 0
12
+ end
13
+
14
+ # @yieldparam type [:build, :fetch]
15
+ # @yieldparam progress [Integer]
16
+ # @yieldparam total [Integer]
17
+ # @yieldparam path [String]
18
+ def run(&block)
19
+ cmd = SystemCommand.new
20
+
21
+ line_buf = StdLineBuffer.new do |_out, err|
22
+ parse_line(err, &block) if err && block
23
+ end
24
+
25
+ ret = cmd.run('nix', 'build', *args, chdir: chdir, &line_buf.feed_block)
26
+
27
+ line_buf.flush
28
+ ret
29
+ end
30
+
31
+ protected
32
+
33
+ attr_reader :args, :chdir
34
+
35
+ def parse_line(line)
36
+ # Beware that nix can fetch/build stuff even before those two
37
+ # summary lines are printed. Therefore we report progress with total=0
38
+ # (indeterminate) until the total becomes known.
39
+
40
+ # Nix >= around 2.11
41
+ case line
42
+ when /^this derivation will be built:/
43
+ @build_total = 1
44
+ return
45
+ when /^these (\d+) derivations will be built:/
46
+ @build_total = ::Regexp.last_match(1).to_i
47
+ return
48
+ when /^this path will be fetched /
49
+ @fetch_total = 1
50
+ return
51
+ when /^these (\d+) paths will be fetched /
52
+ @fetch_total = ::Regexp.last_match(1).to_i
53
+ return
54
+ end
55
+
56
+ # Nix < around 2.11, we can drop this in the future
57
+ if /^these derivations will be built:/ =~ line
58
+ @in_derivation_list = true
59
+ @in_fetch_list = false
60
+ return
61
+ elsif /^these paths will be fetched / =~ line
62
+ @in_derivation_list = false
63
+ @in_fetch_list = true
64
+ return
65
+ end
66
+
67
+ if @in_derivation_list
68
+ if %r{^\s+/nix/store/} =~ line
69
+ @build_total += 1
70
+ return
71
+ else
72
+ @in_derivation_list = false
73
+ @build_total += @build_progress
74
+ end
75
+
76
+ elsif @in_fetch_list
77
+ if %r{^\s+/nix/store/} =~ line
78
+ @fetch_total += 1
79
+ return
80
+ else
81
+ @in_fetch_list = false
82
+ @fetch_total += @fetch_progress
83
+ end
84
+ end
85
+
86
+ if /^building '([^']+)/ =~ line
87
+ @build_progress += 1
88
+ yield(:build, @build_progress, @build_total, ::Regexp.last_match(1))
89
+ elsif /^copying path '([^']+)/ =~ line
90
+ @fetch_progress += 1
91
+ yield(:fetch, @fetch_progress, @fetch_total, ::Regexp.last_match(1))
92
+ end
93
+ end
94
+ end
95
+ end
@@ -1,10 +1,12 @@
1
1
  module ConfCtl
2
2
  class NixCopy
3
3
  # @param target [String]
4
+ # @param port [Integer]
4
5
  # @param store_paths [Array<String>]
5
- def initialize(target, store_paths)
6
+ def initialize(target, store_paths, port: 22)
6
7
  @target = target
7
8
  @store_paths = store_paths
9
+ @port = port.to_i
8
10
  @total = nil
9
11
  @progress = 0
10
12
  end
@@ -23,6 +25,7 @@ module ConfCtl
23
25
  'nix-copy-closure',
24
26
  '--to', "root@#{target}",
25
27
  *store_paths,
28
+ env: nix_ssh_env,
26
29
  &line_buf.feed_block
27
30
  )
28
31
 
@@ -32,7 +35,16 @@ module ConfCtl
32
35
 
33
36
  protected
34
37
 
35
- attr_reader :target, :store_paths
38
+ attr_reader :target, :store_paths, :port
39
+
40
+ def nix_ssh_env
41
+ return {} if port == 22
42
+
43
+ ssh_opts = [ENV.fetch('NIX_SSHOPTS', nil), "-p #{port}"].compact.join(' ').strip
44
+ {
45
+ 'NIX_SSHOPTS' => ssh_opts
46
+ }
47
+ end
36
48
 
37
49
  def parse_line(line)
38
50
  if @total.nil? && /^copying (\d+) paths/ =~ line