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,5 @@
1
+ module ConfCtl::Cli
2
+ module Migrate; end
3
+ end
4
+
5
+ require_rel 'migrate'
@@ -2,13 +2,13 @@ require 'rainbow'
2
2
 
3
3
  module ConfCtl::Cli
4
4
  class OutputFormatter
5
- def self.to_s(*args)
6
- f = new(*args)
5
+ def self.to_s(*)
6
+ f = new(*)
7
7
  f.to_s
8
8
  end
9
9
 
10
- def self.print(*args, **kwargs)
11
- f = new(*args, **kwargs)
10
+ def self.print(*, **)
11
+ f = new(*, **)
12
12
  f.print
13
13
  end
14
14
 
@@ -33,7 +33,7 @@ module ConfCtl::Cli
33
33
  @cols = parse_cols(cols)
34
34
 
35
35
  elsif @objects.is_a?(::Array)
36
- if @objects.count == 0
36
+ if @objects.none?
37
37
  @cols = []
38
38
  else
39
39
  @cols ||= parse_cols(@objects.first.keys)
@@ -185,9 +185,7 @@ module ConfCtl::Cli
185
185
  v = o[i]
186
186
  end
187
187
 
188
- # rubocop:disable Lint/FormatParameterMismatch
189
188
  line format("%#{w}s: %s", c[:label], v)
190
- # rubocop:enable Lint/FormatParameterMismatch
191
189
  end
192
190
 
193
191
  line
@@ -5,6 +5,15 @@ module ConfCtl::Cli
5
5
  class Swpins::Base < Command
6
6
  include Swpins::Utils
7
7
 
8
+ def initialize(...)
9
+ super
10
+
11
+ return unless ConfCtl::ConfigType.flake?(ConfCtl::ConfDir.path)
12
+
13
+ raise ConfCtl::Error,
14
+ 'swpins are not supported in flake configs; use `confctl inputs ls` / `confctl inputs update`'
15
+ end
16
+
8
17
  def update
9
18
  run_command(Swpins::Core, :update)
10
19
  run_command(Swpins::Channel, :update)
@@ -1,10 +1,7 @@
1
- require 'confctl/cli/command'
2
- require 'confctl/cli/swpins/utils'
1
+ require 'confctl/cli/swpins/base'
3
2
 
4
3
  module ConfCtl::Cli
5
- class Swpins::Channel < Command
6
- include Swpins::Utils
7
-
4
+ class Swpins::Channel < Swpins::Base
8
5
  def list
9
6
  rows = []
10
7
 
@@ -1,10 +1,7 @@
1
- require 'confctl/cli/command'
2
- require 'confctl/cli/swpins/utils'
1
+ require 'confctl/cli/swpins/base'
3
2
 
4
3
  module ConfCtl::Cli
5
- class Swpins::Cluster < Command
6
- include Swpins::Utils
7
-
4
+ class Swpins::Cluster < Swpins::Base
8
5
  def list
9
6
  rows = []
10
7
 
@@ -1,10 +1,7 @@
1
- require 'confctl/cli/command'
2
- require 'confctl/cli/swpins/utils'
1
+ require 'confctl/cli/swpins/base'
3
2
 
4
3
  module ConfCtl::Cli
5
- class Swpins::Core < Command
6
- include Swpins::Utils
7
-
4
+ class Swpins::Core < Swpins::Base
8
5
  def list
9
6
  core = ConfCtl::Swpins::Core.get
10
7
 
@@ -0,0 +1,7 @@
1
+ module ConfCtl
2
+ module ConfigType
3
+ def self.flake?(conf_dir)
4
+ File.exist?(File.join(conf_dir, 'flake.nix'))
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,78 @@
1
+ require 'json'
2
+
3
+ module ConfCtl
4
+ class FlakeLock
5
+ def self.load(path)
6
+ new(path, JSON.parse(File.read(path)))
7
+ end
8
+
9
+ def self.load_optional(path)
10
+ return nil unless File.exist?(path)
11
+
12
+ load(path)
13
+ end
14
+
15
+ attr_reader :path, :data
16
+
17
+ def initialize(path, data)
18
+ @path = path
19
+ @data = data
20
+ end
21
+
22
+ def root_inputs
23
+ inputs_map = data.dig('nodes', 'root', 'inputs') || {}
24
+ inputs_map.keys.sort
25
+ end
26
+
27
+ def node_for_input(input_name)
28
+ node_id = data.dig('nodes', 'root', 'inputs', input_name)
29
+
30
+ if node_id.is_a?(String)
31
+ return data.dig('nodes', node_id)
32
+ end
33
+
34
+ if node_id.is_a?(Array) && node_id[0].is_a?(String)
35
+ return data.dig('nodes', node_id[0])
36
+ end
37
+
38
+ nil
39
+ end
40
+
41
+ def input_info(input_name)
42
+ node = node_for_input(input_name) || {}
43
+ locked = node['locked'] || {}
44
+ original = node['original'] || {}
45
+
46
+ type = locked['type'] || original['type'] || '-'
47
+ rev = locked['rev']
48
+ short_rev = rev ? rev[0, 8] : nil
49
+ ref = original['ref'] || locked['ref']
50
+ url = derive_url(original, locked)
51
+
52
+ {
53
+ type: type,
54
+ rev: rev,
55
+ short_rev: short_rev,
56
+ ref: ref,
57
+ url: url,
58
+ locked: locked,
59
+ original: original
60
+ }
61
+ end
62
+
63
+ def derive_url(original, locked)
64
+ url = (locked && locked['url']) || (original && original['url'])
65
+ url = url.sub(/^git\+/, '') if url.is_a?(String)
66
+
67
+ if (locked && locked['type'] == 'github') || (original && original['type'] == 'github')
68
+ owner = (locked && locked['owner']) || (original && original['owner'])
69
+ repo = (locked && locked['repo']) || (original && original['repo'])
70
+ if owner && repo
71
+ return "https://github.com/#{owner}/#{repo}"
72
+ end
73
+ end
74
+
75
+ url
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,36 @@
1
+ module ConfCtl
2
+ class FlakeLockDiff
3
+ Change = Struct.new(
4
+ :name,
5
+ :old_info,
6
+ :new_info
7
+ ) do
8
+ def old_rev = old_info && old_info[:rev]
9
+ def new_rev = new_info && new_info[:rev]
10
+ def old_short_rev = old_info && old_info[:short_rev]
11
+ def new_short_rev = new_info && new_info[:short_rev]
12
+ def url = (new_info && new_info[:url]) || (old_info && old_info[:url])
13
+
14
+ def changed?
15
+ old_rev != new_rev
16
+ end
17
+ end
18
+
19
+ def self.diff(old_lock, new_lock, inputs: nil)
20
+ return [] if new_lock.nil?
21
+
22
+ names =
23
+ if inputs.nil?
24
+ new_lock.root_inputs
25
+ else
26
+ inputs
27
+ end
28
+
29
+ names.map do |name|
30
+ old_info = old_lock&.input_info(name)
31
+ new_info = new_lock.input_info(name)
32
+ Change.new(name: name, old_info: old_info, new_info: new_info)
33
+ end.select(&:changed?)
34
+ end
35
+ end
36
+ end
@@ -7,6 +7,9 @@ module ConfCtl
7
7
  # @return [String]
8
8
  attr_reader :host
9
9
 
10
+ # @return [String]
11
+ attr_reader :mode
12
+
10
13
  # @return [String]
11
14
  attr_reader :name
12
15
 
@@ -28,6 +31,12 @@ module ConfCtl
28
31
  # @return [Hash]
29
32
  attr_reader :swpin_specs
30
33
 
34
+ # @return [Hash, nil]
35
+ attr_reader :inputs_info
36
+
37
+ # @return [Hash, nil]
38
+ attr_reader :inputs
39
+
31
40
  # @param current [Boolean]
32
41
  # @return [Boolean]
33
42
  attr_accessor :current
@@ -45,14 +54,38 @@ module ConfCtl
45
54
  # @param swpin_paths [Hash]
46
55
  # @param swpin_specs [Hash]
47
56
  # @param date [Time]
48
- def create(toplevel, auto_rollback, swpin_paths, swpin_specs, date: nil)
57
+ # @param inputs_info [Hash, nil]
58
+ # @param inputs [Hash, nil]
59
+ def create(toplevel, auto_rollback, swpin_paths, swpin_specs, date: nil, inputs_info: nil, inputs: nil)
60
+ @mode = 'swpins'
49
61
  @toplevel = toplevel
50
62
  @auto_rollback = auto_rollback
51
63
  @swpin_names = swpin_paths.keys
52
64
  @swpin_paths = swpin_paths
53
65
  @swpin_specs = swpin_specs
66
+ @inputs_info = inputs_info
67
+ @inputs = inputs
68
+ @date = date || Time.now
69
+ @name = @date.strftime('%Y-%m-%d--%H-%M-%S')
70
+ @kernel_version = extract_kernel_version
71
+ end
72
+
73
+ # @param toplevel [String]
74
+ # @param auto_rollback [String]
75
+ # @param inputs [Hash]
76
+ # @param inputs_info [Hash]
77
+ # @param date [Time]
78
+ def create_flake(toplevel, auto_rollback, inputs:, inputs_info:, date: nil)
79
+ @mode = 'flakes'
80
+ @toplevel = toplevel
81
+ @auto_rollback = auto_rollback
82
+ @inputs = inputs
83
+ @inputs_info = inputs_info
84
+ @swpin_names = []
85
+ @swpin_paths = {}
86
+ @swpin_specs = {}
54
87
  @date = date || Time.now
55
- @name = date.strftime('%Y-%m-%d--%H-%M-%S')
88
+ @name = @date.strftime('%Y-%m-%d--%H-%M-%S')
56
89
  @kernel_version = extract_kernel_version
57
90
  end
58
91
 
@@ -61,6 +94,7 @@ module ConfCtl
61
94
  @name = name
62
95
 
63
96
  cfg = JSON.parse(File.read(config_path))
97
+ @mode = cfg['mode'] || 'swpins'
64
98
  @toplevel = cfg['toplevel']
65
99
  @auto_rollback = cfg['auto_rollback']
66
100
 
@@ -68,14 +102,22 @@ module ConfCtl
68
102
  @swpin_paths = {}
69
103
  @swpin_specs = {}
70
104
 
71
- cfg['swpins'].each do |swpin_name, swpin|
72
- @swpin_names << swpin_name
73
- @swpin_paths[swpin_name] = swpin['path']
74
- @swpin_specs[swpin_name] = Swpins::Spec.for(swpin['spec']['type'].to_sym).new(
75
- swpin_name,
76
- swpin['spec']['nix_options'],
77
- swpin['spec']
78
- )
105
+ if flakes_mode?
106
+ @inputs = cfg['inputs'] || {}
107
+ @inputs_info = cfg['inputs_info'] || cfg['inputsInfo'] || {}
108
+ else
109
+ cfg['swpins'].each do |swpin_name, swpin|
110
+ @swpin_names << swpin_name
111
+ @swpin_paths[swpin_name] = swpin['path']
112
+ @swpin_specs[swpin_name] = Swpins::Spec.for(swpin['spec']['type'].to_sym).new(
113
+ swpin_name,
114
+ swpin['spec']['nix_options'],
115
+ swpin['spec']
116
+ )
117
+ end
118
+
119
+ @inputs_info = cfg['inputs_info'] || cfg['inputsInfo']
120
+ @inputs = cfg['inputs']
79
121
  end
80
122
 
81
123
  @date = Time.iso8601(cfg['date'])
@@ -89,19 +131,45 @@ module ConfCtl
89
131
  File.symlink(toplevel, toplevel_path)
90
132
  File.symlink(auto_rollback, auto_rollback_path)
91
133
 
92
- swpin_paths.each do |name, path|
93
- File.symlink(path, swpin_path(name))
134
+ if flakes_mode?
135
+ (inputs || {}).each do |role, path|
136
+ File.symlink(path, input_path(role))
137
+ end
138
+ else
139
+ swpin_paths.each do |name, path|
140
+ File.symlink(path, swpin_path(name))
141
+ end
94
142
  end
95
143
 
96
144
  File.open(config_path, 'w') do |f|
97
- f.puts(JSON.pretty_generate({
98
- date: date.iso8601,
99
- toplevel:,
100
- auto_rollback:,
101
- swpins: swpin_paths.to_h do |name, path|
102
- [name, { path:, spec: swpin_specs[name].as_json }]
145
+ data =
146
+ if flakes_mode?
147
+ {
148
+ mode: 'flakes',
149
+ date: date.iso8601,
150
+ toplevel:,
151
+ auto_rollback:,
152
+ inputs: inputs || {},
153
+ inputs_info: inputs_info || {}
154
+ }
155
+ else
156
+ {
157
+ mode: 'swpins',
158
+ date: date.iso8601,
159
+ toplevel:,
160
+ auto_rollback:,
161
+ swpins: swpin_paths.to_h do |name, path|
162
+ [name, { path:, spec: swpin_specs[name].as_json }]
163
+ end
164
+ }
103
165
  end
104
- }))
166
+
167
+ if swpins_mode?
168
+ data[:inputs_info] = inputs_info if inputs_info
169
+ data[:inputs] = inputs if inputs
170
+ end
171
+
172
+ f.puts(JSON.pretty_generate(data))
105
173
  end
106
174
 
107
175
  add_gcroot
@@ -117,7 +185,18 @@ module ConfCtl
117
185
  # Older generations might not have auto_rollback
118
186
  end
119
187
 
120
- swpin_paths.each_key { |name| File.unlink(swpin_path(name)) }
188
+ if flakes_mode?
189
+ (inputs || {}).each_key do |role|
190
+ path = input_path(role)
191
+ File.unlink(path) if File.exist?(path) || File.symlink?(path)
192
+ end
193
+ else
194
+ swpin_paths.each_key do |name|
195
+ path = swpin_path(name)
196
+ File.unlink(path) if File.exist?(path) || File.symlink?(path)
197
+ end
198
+ end
199
+
121
200
  File.unlink(config_path)
122
201
  Dir.rmdir(dir)
123
202
  end
@@ -125,16 +204,28 @@ module ConfCtl
125
204
  def add_gcroot
126
205
  GCRoot.add(gcroot_name('toplevel'), toplevel_path)
127
206
  GCRoot.add(gcroot_name('auto_rollback'), auto_rollback_path)
128
- swpin_paths.each_key do |name|
129
- GCRoot.add(gcroot_name("swpin.#{name}"), toplevel_path)
207
+ if flakes_mode?
208
+ (inputs || {}).each_key do |role|
209
+ GCRoot.add(gcroot_name("input.#{role}"), input_path(role))
210
+ end
211
+ else
212
+ swpin_paths.each_key do |name|
213
+ GCRoot.add(gcroot_name("swpin.#{name}"), swpin_path(name))
214
+ end
130
215
  end
131
216
  end
132
217
 
133
218
  def remove_gcroot
134
219
  GCRoot.remove(gcroot_name('toplevel'))
135
220
  GCRoot.remove(gcroot_name('auto_rollback'))
136
- swpin_paths.each_key do |name|
137
- GCRoot.remove(gcroot_name("swpin.#{name}"))
221
+ if flakes_mode?
222
+ (inputs || {}).each_key do |role|
223
+ GCRoot.remove(gcroot_name("input.#{role}"))
224
+ end
225
+ else
226
+ swpin_paths.each_key do |name|
227
+ GCRoot.remove(gcroot_name("swpin.#{name}"))
228
+ end
138
229
  end
139
230
  end
140
231
 
@@ -142,6 +233,18 @@ module ConfCtl
142
233
  @dir ||= File.join(ConfDir.generation_dir, escaped_host, name)
143
234
  end
144
235
 
236
+ def swpins_mode?
237
+ mode != 'flakes'
238
+ end
239
+
240
+ def flakes_mode?
241
+ mode == 'flakes'
242
+ end
243
+
244
+ def pin_paths
245
+ flakes_mode? ? (inputs || {}) : (swpin_paths || {})
246
+ end
247
+
145
248
  protected
146
249
 
147
250
  def config_path
@@ -160,6 +263,10 @@ module ConfCtl
160
263
  File.join(dir, "#{name}.swpin")
161
264
  end
162
265
 
266
+ def input_path(role)
267
+ File.join(dir, "#{role}.input")
268
+ end
269
+
163
270
  def escaped_host
164
271
  @escaped_host ||= ConfCtl.safe_host_name(host)
165
272
  end
@@ -87,11 +87,12 @@ module ConfCtl
87
87
  end
88
88
 
89
89
  # @param toplevel [String]
90
- # @param swpin_paths [Hash]
90
+ # @param pin_paths [Hash]
91
+ # @param mode [String]
91
92
  # @return [Generation::Build, nil]
92
- def find(toplevel, swpin_paths)
93
+ def find(toplevel, pin_paths, mode: 'swpins')
93
94
  generations.detect do |gen|
94
- gen.toplevel == toplevel && gen.swpin_paths == swpin_paths
95
+ gen.toplevel == toplevel && gen.mode == mode && gen.pin_paths == pin_paths
95
96
  end
96
97
  end
97
98
 
@@ -76,7 +76,8 @@ module ConfCtl
76
76
 
77
77
  if build_generation && gen.is_a?(Generation::Build)
78
78
  build_generation.name == gen.name \
79
- && build_generation.swpin_paths == gen.swpin_paths
79
+ && build_generation.mode == gen.mode \
80
+ && build_generation.pin_paths == gen.pin_paths
80
81
  else
81
82
  true
82
83
  end
@@ -88,6 +89,18 @@ module ConfCtl
88
89
  end
89
90
  end
90
91
 
92
+ def inputs_info
93
+ build_generation ? build_generation.inputs_info : nil
94
+ end
95
+
96
+ def inputs
97
+ build_generation ? build_generation.inputs : nil
98
+ end
99
+
100
+ def flakes_mode?
101
+ build_generation ? build_generation.flakes_mode? : false
102
+ end
103
+
91
104
  def current_str
92
105
  build = build_generation && build_generation.current
93
106
  host = host_generation && host_generation.current
@@ -55,10 +55,10 @@ module ConfCtl
55
55
 
56
56
  attr_reader :cmd, :quiet
57
57
 
58
- def git_repo(git_cmd, *args, **kwargs)
58
+ def git_repo(git_cmd, *, **kwargs)
59
59
  kwargs[:gopts] ||= []
60
60
  kwargs[:gopts] << '-C' << mirror_path
61
- git(git_cmd, *args, **kwargs)
61
+ git(git_cmd, *, **kwargs)
62
62
  end
63
63
 
64
64
  def git(git_cmd, args: [], opts: [], gopts: [])
@@ -1,4 +1,5 @@
1
1
  require 'confctl/health_checks/base'
2
+ require 'shellwords'
2
3
 
3
4
  module ConfCtl
4
5
  class HealthChecks::RunCommand < HealthChecks::Base
@@ -82,14 +83,14 @@ module ConfCtl
82
83
  end
83
84
 
84
85
  def to_s
85
- @command.join(' ')
86
+ Shellwords.join(@command)
86
87
  end
87
88
 
88
89
  protected
89
90
 
90
91
  def make_command(machine, args)
91
92
  args.map do |arg|
92
- arg.gsub(/\{([^\}]+)\}/) do
93
+ arg.gsub(/\{([^}]+)\}/) do
93
94
  machine[::Regexp.last_match(1)]
94
95
  end
95
96
  end
@@ -34,7 +34,7 @@ module ConfCtl
34
34
  ret << 'systemd: '
35
35
  end
36
36
 
37
- ret << @property_checks.map(&:to_s).join(', ')
37
+ ret << @property_checks.join(', ')
38
38
  ret
39
39
  end
40
40
 
@@ -1,9 +1,9 @@
1
1
  module ConfCtl
2
2
  class HealthChecks::Systemd::PropertyList < Hash
3
- def self.from_enumerator(it)
3
+ def self.from_enumerator(lines)
4
4
  hash = new
5
5
 
6
- it.each do |line|
6
+ lines.each do |line|
7
7
  stripped = line.strip
8
8
  eq = line.index('=')
9
9
  next if eq.nil?