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,83 @@
1
+ require 'confctl/cli/command'
2
+ require 'confctl/config_type'
3
+ require 'confctl/nix'
4
+ require 'confctl/inputs/setter'
5
+ require 'confctl/inputs/updater'
6
+
7
+ module ConfCtl::Cli
8
+ class Inputs::Machines < Command
9
+ def update
10
+ ensure_flake_config!
11
+ require_args!('machine', 'role')
12
+
13
+ machine_name = args[0]
14
+ role = args[1]
15
+
16
+ inputs_info = ConfCtl::Nix.new.eval_inputs_info(machine_name)
17
+ unless inputs_info.is_a?(Hash)
18
+ raise ConfCtl::Error, "inputs info unavailable for machine '#{machine_name}'"
19
+ end
20
+
21
+ role_info = inputs_info[role] || inputs_info[role.to_s] || inputs_info[role.to_sym]
22
+ input = role_info.is_a?(Hash) ? (role_info['input'] || role_info[:input]) : nil
23
+
24
+ raise ConfCtl::Error, "machine '#{machine_name}' has no role '#{role}'" unless input
25
+
26
+ ConfCtl::Inputs::Updater.run!(
27
+ conf_dir: ConfCtl::ConfDir.path,
28
+ inputs: [input],
29
+ commit: opts[:commit],
30
+ changelog: opts[:changelog],
31
+ downgrade: opts[:downgrade],
32
+ editor: opts[:editor]
33
+ )
34
+
35
+ lock = ConfCtl::FlakeLock.load(File.join(ConfCtl::ConfDir.path, 'flake.lock'))
36
+ info = lock.input_info(input)
37
+ rev = info[:short_rev] || info[:rev] || '-'
38
+ puts "Updating #{role} in #{machine_name} -> #{rev}"
39
+ end
40
+
41
+ def set
42
+ ensure_flake_config!
43
+ require_args!('machine', 'role', 'rev')
44
+
45
+ machine_name = args[0]
46
+ role = args[1]
47
+ rev = args[2]
48
+
49
+ inputs_info = ConfCtl::Nix.new.eval_inputs_info(machine_name)
50
+ unless inputs_info.is_a?(Hash)
51
+ raise ConfCtl::Error, "inputs info unavailable for machine '#{machine_name}'"
52
+ end
53
+
54
+ role_info = inputs_info[role] || inputs_info[role.to_s] || inputs_info[role.to_sym]
55
+ input = role_info.is_a?(Hash) ? (role_info['input'] || role_info[:input]) : nil
56
+
57
+ raise ConfCtl::Error, "machine '#{machine_name}' has no role '#{role}'" unless input
58
+
59
+ ConfCtl::Inputs::Setter.run!(
60
+ conf_dir: ConfCtl::ConfDir.path,
61
+ inputs: [input],
62
+ rev: rev,
63
+ commit: opts[:commit],
64
+ changelog: opts[:changelog],
65
+ downgrade: opts[:downgrade],
66
+ editor: opts[:editor]
67
+ )
68
+
69
+ lock = ConfCtl::FlakeLock.load(File.join(ConfCtl::ConfDir.path, 'flake.lock'))
70
+ info = lock.input_info(input)
71
+ resolved_rev = info[:short_rev] || info[:rev] || '-'
72
+ puts "Configuring #{role} in #{machine_name} -> #{resolved_rev}"
73
+ end
74
+
75
+ protected
76
+
77
+ def ensure_flake_config!
78
+ return if ConfCtl::ConfigType.flake?(ConfCtl::ConfDir.path)
79
+
80
+ raise ConfCtl::Error, 'confctl inputs machine is available only in flake configs'
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,101 @@
1
+ require 'confctl/cli/command'
2
+ require 'confctl/config_type'
3
+ require 'confctl/flake_lock'
4
+ require 'confctl/pattern'
5
+ require 'confctl/inputs/setter'
6
+ require 'confctl/inputs/updater'
7
+
8
+ module ConfCtl::Cli
9
+ class Inputs::Root < Command
10
+ def list
11
+ ensure_flake_config!
12
+
13
+ lock = ConfCtl::FlakeLock.load(File.join(ConfCtl::ConfDir.path, 'flake.lock'))
14
+ rows = lock.root_inputs.map do |input_name|
15
+ info = lock.input_info(input_name)
16
+ {
17
+ input: input_name,
18
+ type: info[:type],
19
+ ref: info[:ref],
20
+ rev: info[:short_rev],
21
+ url: info[:url]
22
+ }
23
+ end
24
+
25
+ if args[0]
26
+ rows.select! { |r| ConfCtl::Pattern.match?(args[0], r[:input]) }
27
+ end
28
+
29
+ OutputFormatter.print(rows, %i[input type ref rev url], layout: :columns)
30
+ end
31
+
32
+ def update
33
+ ensure_flake_config!
34
+
35
+ conf_dir = ConfCtl::ConfDir.path
36
+ inputs = if opts[:all]
37
+ lock = ConfCtl::FlakeLock.load_optional(File.join(conf_dir, 'flake.lock'))
38
+ raise ConfCtl::Error, 'flake.lock missing; specify inputs explicitly' if lock.nil?
39
+
40
+ lock.root_inputs
41
+ else
42
+ raise GLI::BadCommandLine, 'missing input name (or pass --all)' if args.empty?
43
+
44
+ args
45
+ end
46
+
47
+ raise ConfCtl::Error, 'no inputs selected' if inputs.empty?
48
+
49
+ res = ConfCtl::Inputs::Updater.run!(
50
+ conf_dir: conf_dir,
51
+ inputs: inputs,
52
+ commit: opts[:commit],
53
+ changelog: opts[:changelog],
54
+ downgrade: opts[:downgrade],
55
+ editor: opts[:editor]
56
+ )
57
+
58
+ print_update_summary(res[:changes])
59
+ end
60
+
61
+ def set
62
+ ensure_flake_config!
63
+ require_args!('input-name', 'rev')
64
+
65
+ input = args[0]
66
+ rev = args[1]
67
+
68
+ res = ConfCtl::Inputs::Setter.run!(
69
+ conf_dir: ConfCtl::ConfDir.path,
70
+ inputs: [input],
71
+ rev: rev,
72
+ commit: opts[:commit],
73
+ changelog: opts[:changelog],
74
+ downgrade: opts[:downgrade],
75
+ editor: opts[:editor]
76
+ )
77
+
78
+ print_update_summary(res[:changes])
79
+ end
80
+
81
+ protected
82
+
83
+ def ensure_flake_config!
84
+ return if ConfCtl::ConfigType.flake?(ConfCtl::ConfDir.path)
85
+
86
+ raise ConfCtl::Error, 'inputs is for flake configs only; this config has no flake.nix; use swpins.'
87
+ end
88
+
89
+ def print_update_summary(changes)
90
+ if changes.empty?
91
+ puts 'No changes.'
92
+ return
93
+ end
94
+
95
+ puts "Updated inputs (#{changes.length}):"
96
+ changes.each do |ch|
97
+ puts " #{ch.name}: #{ch.old_short_rev || '-'} -> #{ch.new_short_rev || '-'}"
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,5 @@
1
+ module ConfCtl::Cli
2
+ module Inputs; end
3
+ end
4
+
5
+ require_rel 'inputs'
@@ -14,8 +14,8 @@ module ConfCtl::Cli
14
14
 
15
15
  # Instantiate {LogView}, yield and then cleanup
16
16
  # @yieldparam log_view [LogView]
17
- def self.open(**kwargs)
18
- lw = new(**kwargs)
17
+ def self.open(**)
18
+ lw = new(**)
19
19
  lw.start
20
20
 
21
21
  begin
@@ -28,8 +28,8 @@ module ConfCtl::Cli
28
28
  # Instantiate {LogView} with feed from {ConfCtl::Logger}, yield
29
29
  # and then cleanup
30
30
  # @yieldparam log_view [LogView]
31
- def self.open_with_logger(**kwargs)
32
- lw = new(**kwargs)
31
+ def self.open_with_logger(**)
32
+ lw = new(**)
33
33
  lw.start
34
34
 
35
35
  lb = ConfCtl::LineBuffer.new { |line| lw << line }
@@ -63,7 +63,7 @@ module ConfCtl::Cli
63
63
  @current_size = size if size != :auto
64
64
  @reserved_lines = reserved_lines
65
65
  @output = output
66
- @enabled = output.respond_to?(:tty?) && output.tty?
66
+ @enabled = output.respond_to?(:tty?) && output.tty? && ConfCtl::Ui.tty?
67
67
  @resized = false
68
68
  @stop = false
69
69
  @generation = 0
@@ -209,7 +209,8 @@ module ConfCtl::Cli
209
209
  end
210
210
 
211
211
  output.print(cursor.clear_line)
212
- output.puts("<#{'-' * (cols - 1)}")
212
+ line_len = [cols - 1, 1].max
213
+ output.puts("<#{'-' * line_len}")
213
214
  output.puts
214
215
  end
215
216
  end
@@ -220,21 +221,31 @@ module ConfCtl::Cli
220
221
  ret = ''
221
222
  ret << s
222
223
  ret << ' '
223
- ret << ('-' * (cols - uncolored.length - 2))
224
+ dash_count = cols - uncolored.length - 2
225
+ dash_count = 1 if dash_count < 1
226
+ ret << ('-' * dash_count)
224
227
  ret << '>'
225
228
  ret
226
229
  end
227
230
 
228
231
  def fit_line(line)
229
- if line.length >= (cols - 4)
230
- "#{line[0..(cols - 4)]}..."
232
+ width = [cols - 4, 1].max
233
+ if line.length >= width
234
+ "#{line[0..width]}..."
231
235
  else
232
236
  line
233
237
  end
234
238
  end
235
239
 
236
240
  def fetch_size
237
- @rows, @cols = IO.console.winsize
241
+ console = IO.console
242
+ rows, cols = console ? console.winsize : [24, 80]
243
+ rows = rows.to_i
244
+ cols = cols.to_i
245
+ rows = 24 if rows <= 0
246
+ cols = 80 if cols <= 0
247
+ @rows = rows
248
+ @cols = cols
238
249
 
239
250
  return unless size == :auto
240
251