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.
- checksums.yaml +4 -4
- data/.git-hooks/pre_commit/nixfmt.rb +13 -0
- data/.github/workflows/rspec.yml +64 -0
- data/.github/workflows/rubocop.yml +27 -0
- data/.github/workflows/tests.yml +139 -0
- data/.gitignore +12 -7
- data/.overcommit.yml +2 -0
- data/.rspec +1 -0
- data/.rubocop.yml +8 -1
- data/AGENTS.md +42 -0
- data/CHANGELOG.md +54 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +198 -0
- data/README.md +166 -58
- data/Rakefile +5 -0
- data/confctl.gemspec +20 -2
- data/docs/carrier.md +3 -3
- data/docs/flake-inputs.md +159 -0
- data/docs/swpins-to-flakes.md +325 -0
- data/example/cluster/module-list.nix +2 -1
- data/example/cluster/nixos-machine/config.nix +12 -2
- data/example/cluster/nixos-machine/hardware.nix +6 -1
- data/example/cluster/vpsadminos-container/config.nix +6 -1
- data/example/cluster/vpsadminos-container/module.nix +4 -1
- data/example/cluster/vpsadminos-machine/config.nix +6 -1
- data/example/cluster/vpsadminos-machine/hardware.nix +6 -1
- data/example/cluster/vpsadminos-machine/module.nix +5 -2
- data/example/cluster/vpsfreecz-vps/config.nix +6 -1
- data/example/cluster/vpsfreecz-vps/module.nix +4 -1
- data/example/configs/swpins.nix +8 -3
- data/example/environments/base.nix +6 -1
- data/example/swpins/core.json +35 -0
- data/example-flake/.gitignore +2 -0
- data/example-flake/README.md +38 -0
- data/example-flake/cluster/cluster.nix +5 -0
- data/example-flake/cluster/module-list.nix +4 -0
- data/example-flake/cluster/nested/nixos-machine/config.nix +25 -0
- data/example-flake/cluster/nested/nixos-machine/hardware.nix +9 -0
- data/example-flake/cluster/nested/nixos-machine/module.nix +8 -0
- data/example-flake/cluster/nixos-machine/config.nix +25 -0
- data/example-flake/cluster/nixos-machine/hardware.nix +9 -0
- data/example-flake/cluster/nixos-machine/module.nix +8 -0
- data/example-flake/cluster/vpsadminos-container/config.nix +28 -0
- data/example-flake/cluster/vpsadminos-container/module.nix +8 -0
- data/example-flake/cluster/vpsadminos-machine/config.nix +27 -0
- data/example-flake/cluster/vpsadminos-machine/hardware.nix +9 -0
- data/example-flake/cluster/vpsadminos-machine/module.nix +8 -0
- data/example-flake/cluster/vpsfreecz-vps/config.nix +31 -0
- data/example-flake/cluster/vpsfreecz-vps/module.nix +8 -0
- data/example-flake/configs/confctl.nix +10 -0
- data/example-flake/data/default.nix +5 -0
- data/example-flake/data/ssh-keys.nix +7 -0
- data/example-flake/environments/base.nix +18 -0
- data/example-flake/flake.lock +75 -0
- data/example-flake/flake.nix +36 -0
- data/example-flake/modules/module-list.nix +13 -0
- data/example-flake/shell.nix +11 -0
- data/flake.lock +159 -0
- data/flake.nix +189 -0
- data/gemset.nix +1022 -0
- data/lib/confctl/cli/app.rb +145 -0
- data/lib/confctl/cli/attr_filters.rb +1 -1
- data/lib/confctl/cli/cluster.rb +681 -108
- data/lib/confctl/cli/command.rb +24 -2
- data/lib/confctl/cli/configuration.rb +171 -106
- data/lib/confctl/cli/generation.rb +65 -1
- data/lib/confctl/cli/inputs/channels.rb +190 -0
- data/lib/confctl/cli/inputs/machines.rb +83 -0
- data/lib/confctl/cli/inputs/root.rb +101 -0
- data/lib/confctl/cli/inputs.rb +5 -0
- data/lib/confctl/cli/log_view.rb +21 -10
- data/lib/confctl/cli/migrate/swpins_to_flakes.rb +866 -0
- data/lib/confctl/cli/migrate.rb +5 -0
- data/lib/confctl/cli/output_formatter.rb +5 -5
- data/lib/confctl/cli/swpins/base.rb +9 -0
- data/lib/confctl/cli/swpins/channel.rb +2 -5
- data/lib/confctl/cli/swpins/cluster.rb +2 -5
- data/lib/confctl/cli/swpins/core.rb +2 -5
- data/lib/confctl/config_type.rb +7 -0
- data/lib/confctl/flake_lock.rb +78 -0
- data/lib/confctl/flake_lock_diff.rb +36 -0
- data/lib/confctl/generation/build.rb +131 -24
- data/lib/confctl/generation/build_list.rb +4 -3
- data/lib/confctl/generation/unified.rb +14 -1
- data/lib/confctl/git_repo_mirror.rb +2 -2
- data/lib/confctl/health_checks/run_command.rb +3 -2
- data/lib/confctl/health_checks/systemd/properties.rb +1 -1
- data/lib/confctl/health_checks/systemd/property_list.rb +2 -2
- data/lib/confctl/inputs/commit_message.rb +125 -0
- data/lib/confctl/inputs/git_commit.rb +17 -0
- data/lib/confctl/inputs/nix_output_guard.rb +37 -0
- data/lib/confctl/inputs/setter.rb +179 -0
- data/lib/confctl/inputs/updater.rb +76 -0
- data/lib/confctl/inputs.rb +5 -0
- data/lib/confctl/inputs_info.rb +50 -0
- data/lib/confctl/line_buffer.rb +1 -1
- data/lib/confctl/machine.rb +18 -3
- data/lib/confctl/machine_control.rb +8 -2
- data/lib/confctl/machine_list.rb +2 -2
- data/lib/confctl/machine_status.rb +63 -20
- data/lib/confctl/nix/args.rb +48 -0
- data/lib/confctl/nix.rb +30 -437
- data/lib/confctl/nix_build_flake.rb +95 -0
- data/lib/confctl/nix_copy.rb +14 -2
- data/lib/confctl/nix_flake.rb +449 -0
- data/lib/confctl/nix_format.rb +3 -3
- data/lib/confctl/nix_legacy.rb +467 -0
- data/lib/confctl/swpins/change_set.rb +24 -2
- data/lib/confctl/swpins/specs/base.rb +2 -2
- data/lib/confctl/ui.rb +19 -0
- data/lib/confctl/version.rb +1 -1
- data/man/index.html +11 -0
- data/man/man8/confctl-options.nix.8.html +113 -0
- data/man/man8/confctl.8 +152 -21
- data/man/man8/confctl.8.html +355 -0
- data/man/man8/confctl.8.md +148 -17
- data/man/style.css +301 -0
- data/nix/evaluator.nix +94 -69
- data/nix/flake/mk-confctl-devshell.nix +85 -0
- data/nix/flake/mk-confctl-outputs.nix +522 -0
- data/nix/flake/mk-config-devshell.nix +168 -0
- data/nix/lib/default.nix +118 -65
- data/nix/lib/machine/default.nix +65 -45
- data/nix/lib/machine/info.nix +16 -5
- data/nix/lib/swpins/eval.nix +42 -29
- data/nix/lib/swpins/options.nix +6 -2
- data/nix/machines.nix +23 -15
- data/nix/modules/cluster/default.nix +104 -41
- data/nix/modules/confctl/carrier/base.nix +11 -4
- data/nix/modules/confctl/carrier/carrier-env.rb +2 -2
- data/nix/modules/confctl/carrier/netboot/build-netboot-server.rb +50 -13
- data/nix/modules/confctl/carrier/netboot/nixos.nix +56 -24
- data/nix/modules/confctl/configuration-info.nix +17 -0
- data/nix/modules/confctl/generations.nix +2 -2
- data/nix/modules/confctl/host.nix +13 -0
- data/nix/modules/confctl/inputs-info.nix +21 -0
- data/nix/modules/confctl/kexec-netboot/default.nix +13 -6
- data/nix/modules/confctl/kexec-netboot/kexec-netboot.8.adoc +3 -0
- data/nix/modules/confctl/kexec-netboot/kexec-netboot.rb +25 -16
- data/nix/modules/confctl/nix.nix +31 -1
- data/nix/modules/confctl/swpins.nix +13 -6
- data/nix/modules/module-list.nix +4 -2
- data/nix/modules/system-list.nix +4 -1
- data/nix/package.nix +37 -0
- data/shell.nix +39 -8
- data/skills/confctl-configuration-update/SKILL.md +228 -0
- data/skills/confctl-configuration-update/agents/openai.yaml +4 -0
- data/skills/confctl-release/SKILL.md +102 -0
- data/skills/confctl-release/agents/openai.yaml +4 -0
- data/spec/confctl/cli/cluster_health_check_spec.rb +43 -0
- data/spec/confctl/cli/cluster_skip_current_deploy_spec.rb +157 -0
- data/spec/confctl/cli/cluster_status_flake_spec.rb +83 -0
- data/spec/confctl/cli/inputs_set_output_spec.rb +70 -0
- data/spec/confctl/configuration_spec.rb +45 -0
- data/spec/confctl/inputs_spec.rb +104 -0
- data/spec/confctl/machine_control_spec.rb +112 -0
- data/spec/confctl/machine_list_spec.rb +47 -0
- data/spec/confctl/nix_flake_spec.rb +41 -0
- data/spec/confctl/swpins_spec.rb +106 -0
- data/spec/generation/build_modes_spec.rb +132 -0
- data/spec/inputs/commit_message_spec.rb +205 -0
- data/spec/inputs/nix_cached_fallback_spec.rb +50 -0
- data/spec/inputs/nix_output_guard_spec.rb +45 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/cli_helper.rb +57 -0
- data/test-runner.sh +7 -0
- data/tests/all-tests.nix +30 -0
- data/tests/make-test.nix +15 -0
- data/tests/runner/extensions/confctl_helpers.rb +238 -0
- data/tests/runner/extensions/hostfwd_ports.rb +41 -0
- data/tests/suite/auto_rollback.nix +344 -0
- data/tests/suite/carrier/deploy.nix +751 -0
- data/tests/suite/carrier/netboot.nix +849 -0
- data/tests/suite/deploy/base.nix +806 -0
- data/tests/suite/deploy/flakes.nix +1 -0
- data/tests/suite/deploy/swpins.nix +1 -0
- metadata +104 -4
- data/nix/modules/confctl/overlays.nix +0 -15
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
require 'confctl/utils/file'
|
|
2
|
+
require 'digest'
|
|
3
|
+
require 'etc'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'securerandom'
|
|
6
|
+
require 'shellwords'
|
|
7
|
+
require 'tempfile'
|
|
8
|
+
|
|
9
|
+
module ConfCtl
|
|
10
|
+
class NixLegacy
|
|
11
|
+
# Create a new instance without access to {ConfCtl::Settings}, i.e. when
|
|
12
|
+
# called outside of cluster configuration directory.
|
|
13
|
+
# @return [Nix]
|
|
14
|
+
def self.stateless(show_trace: false, max_jobs: 'auto')
|
|
15
|
+
new(show_trace:, max_jobs:)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
include Utils::File
|
|
19
|
+
|
|
20
|
+
def initialize(conf_dir: nil, show_trace: false, max_jobs: nil, cores: nil)
|
|
21
|
+
@conf_dir = conf_dir || ConfDir.path
|
|
22
|
+
@show_trace = show_trace
|
|
23
|
+
@max_jobs = max_jobs || Settings.instance.max_jobs
|
|
24
|
+
@cores = cores
|
|
25
|
+
@cmd = SystemCommand.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def confctl_settings
|
|
29
|
+
out_link = File.join(
|
|
30
|
+
cache_dir,
|
|
31
|
+
'build',
|
|
32
|
+
'settings.json'
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
with_cache(out_link) do
|
|
36
|
+
with_argument({
|
|
37
|
+
confDir: conf_dir,
|
|
38
|
+
build: :confctl
|
|
39
|
+
}) do |arg|
|
|
40
|
+
cmd_args = [
|
|
41
|
+
'nix-build',
|
|
42
|
+
'--arg', 'jsonArg', arg,
|
|
43
|
+
'--out-link', out_link,
|
|
44
|
+
(show_trace ? '--show-trace' : nil),
|
|
45
|
+
(max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
|
|
46
|
+
(cores ? ['--cores', cores.to_s] : nil),
|
|
47
|
+
ConfCtl.nix_asset('evaluator.nix')
|
|
48
|
+
].flatten.compact
|
|
49
|
+
|
|
50
|
+
cmd.run(*cmd_args)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
demodulify(JSON.parse(File.read(out_link))['confctl'])
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Returns an array with options from all confctl modules
|
|
58
|
+
# @return [Array]
|
|
59
|
+
def module_options
|
|
60
|
+
options = nix_instantiate({
|
|
61
|
+
confDir: conf_dir,
|
|
62
|
+
build: :moduleOptions
|
|
63
|
+
})
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Returns an array with machine fqdns
|
|
67
|
+
# @return [Array<String>]
|
|
68
|
+
def list_machine_fqdns
|
|
69
|
+
nix_instantiate({
|
|
70
|
+
confDir: conf_dir,
|
|
71
|
+
build: :list
|
|
72
|
+
})['machines']
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Return machines and their config in a hash
|
|
76
|
+
# @return [Hash]
|
|
77
|
+
def list_machines
|
|
78
|
+
out_link = File.join(
|
|
79
|
+
cache_dir,
|
|
80
|
+
'build',
|
|
81
|
+
'machine-list.json'
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
with_cache(out_link) do
|
|
85
|
+
with_argument({
|
|
86
|
+
confDir: conf_dir,
|
|
87
|
+
build: :info
|
|
88
|
+
}, core_swpins: true) do |arg|
|
|
89
|
+
cmd_args = [
|
|
90
|
+
'nix-build',
|
|
91
|
+
'--arg', 'jsonArg', arg,
|
|
92
|
+
'--out-link', out_link,
|
|
93
|
+
(show_trace ? '--show-trace' : nil),
|
|
94
|
+
(max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
|
|
95
|
+
(cores ? ['--cores', cores.to_s] : nil),
|
|
96
|
+
ConfCtl.nix_asset('evaluator.nix')
|
|
97
|
+
].flatten.compact
|
|
98
|
+
|
|
99
|
+
cmd.run(*cmd_args)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
demodulify(JSON.parse(File.read(out_link)))
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def list_swpins_channels
|
|
107
|
+
nix_instantiate({
|
|
108
|
+
confDir: conf_dir,
|
|
109
|
+
build: :listSwpinsChannels
|
|
110
|
+
})
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Evaluate swpins for host
|
|
114
|
+
# @return [Hash]
|
|
115
|
+
def eval_core_swpins
|
|
116
|
+
with_argument({
|
|
117
|
+
confDir: conf_dir,
|
|
118
|
+
build: :evalCoreSwpins
|
|
119
|
+
}) do |arg|
|
|
120
|
+
out_link = File.join(
|
|
121
|
+
cache_dir,
|
|
122
|
+
'build',
|
|
123
|
+
'core.swpins'
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
cmd_args = [
|
|
127
|
+
'nix-build',
|
|
128
|
+
'--arg', 'jsonArg', arg,
|
|
129
|
+
'--out-link', out_link,
|
|
130
|
+
(show_trace ? '--show-trace' : nil),
|
|
131
|
+
(max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
|
|
132
|
+
(cores ? ['--cores', cores.to_s] : nil),
|
|
133
|
+
ConfCtl.nix_asset('evaluator.nix')
|
|
134
|
+
].flatten.compact
|
|
135
|
+
|
|
136
|
+
out, = cmd.run(*cmd_args).stdout
|
|
137
|
+
|
|
138
|
+
JSON.parse(File.read(out.strip))
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Evaluate swpins for hosts
|
|
143
|
+
# @param hosts [Array<String>]
|
|
144
|
+
# @return [Hash] host => swpins
|
|
145
|
+
def eval_host_swpins(hosts)
|
|
146
|
+
with_argument({
|
|
147
|
+
confDir: conf_dir,
|
|
148
|
+
build: :evalHostSwpins,
|
|
149
|
+
machines: hosts
|
|
150
|
+
}, core_swpins: true) do |arg|
|
|
151
|
+
out_link = File.join(
|
|
152
|
+
cache_dir,
|
|
153
|
+
'build',
|
|
154
|
+
"#{Digest::SHA256.hexdigest(hosts.join(','))[0..11]}.swpins"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
cmd_args = [
|
|
158
|
+
'nix-build',
|
|
159
|
+
'--arg', 'jsonArg', arg,
|
|
160
|
+
'--out-link', out_link,
|
|
161
|
+
(show_trace ? '--show-trace' : nil),
|
|
162
|
+
(max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
|
|
163
|
+
(cores ? ['--cores', cores.to_s] : nil),
|
|
164
|
+
ConfCtl.nix_asset('evaluator.nix')
|
|
165
|
+
].flatten.compact
|
|
166
|
+
|
|
167
|
+
out, = cmd.run(*cmd_args)
|
|
168
|
+
|
|
169
|
+
JSON.parse(File.read(out.strip))
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def eval_inputs_info(_host)
|
|
174
|
+
raise Error, 'inputs info is available only in flake configs'
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def eval_inputs(_host)
|
|
178
|
+
raise Error, 'inputs are available only in flake configs'
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def eval_json(_installable)
|
|
182
|
+
raise Error, 'nix eval is available only in flake configs'
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Build config.system.build.toplevel for selected hosts
|
|
186
|
+
#
|
|
187
|
+
# @param hosts [Array<Machine>]
|
|
188
|
+
# @param swpin_paths [Hash]
|
|
189
|
+
# @param host_swpin_specs [Hash]
|
|
190
|
+
# @param time [Time]
|
|
191
|
+
#
|
|
192
|
+
# @yieldparam type [:build, :fetch]
|
|
193
|
+
# @yieldparam progress [Integer]
|
|
194
|
+
# @yieldparam total [Integer]
|
|
195
|
+
# @yieldparam path [String]
|
|
196
|
+
#
|
|
197
|
+
# @return [Hash<String, Generation::Build>]
|
|
198
|
+
def build_attributes(hosts: [], swpin_paths: {}, host_swpin_specs: {}, time: nil, &block)
|
|
199
|
+
with_argument({
|
|
200
|
+
confDir: conf_dir,
|
|
201
|
+
build: :toplevel,
|
|
202
|
+
machines: hosts
|
|
203
|
+
}) do |arg|
|
|
204
|
+
time ||= Time.now
|
|
205
|
+
ret_generations = {}
|
|
206
|
+
out_link = File.join(cache_dir, 'build', "#{SecureRandom.hex(4)}.build")
|
|
207
|
+
|
|
208
|
+
cmd_args = [
|
|
209
|
+
'--arg', 'jsonArg', arg,
|
|
210
|
+
'--out-link', out_link,
|
|
211
|
+
(show_trace ? '--show-trace' : nil),
|
|
212
|
+
(max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
|
|
213
|
+
(cores ? ['--cores', cores.to_s] : nil),
|
|
214
|
+
ConfCtl.nix_asset('evaluator.nix')
|
|
215
|
+
].flatten.compact
|
|
216
|
+
|
|
217
|
+
nb = NixBuild.new(cmd_args, swpin_paths)
|
|
218
|
+
nb.run(&block)
|
|
219
|
+
|
|
220
|
+
begin
|
|
221
|
+
host_results = JSON.parse(File.read(out_link))
|
|
222
|
+
host_results.each do |host, result|
|
|
223
|
+
host_generations = Generation::BuildList.new(host)
|
|
224
|
+
generation = host_generations.find(result['attribute'], swpin_paths)
|
|
225
|
+
|
|
226
|
+
if generation.nil?
|
|
227
|
+
generation = Generation::Build.new(host)
|
|
228
|
+
generation.create(
|
|
229
|
+
result['attribute'],
|
|
230
|
+
result['autoRollback'],
|
|
231
|
+
swpin_paths,
|
|
232
|
+
host_swpin_specs[host],
|
|
233
|
+
date: time
|
|
234
|
+
)
|
|
235
|
+
generation.save
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
host_generations.current = generation
|
|
239
|
+
ret_generations[host] = generation
|
|
240
|
+
end
|
|
241
|
+
ensure
|
|
242
|
+
unlink_if_exists(out_link)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
ret_generations
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# @param machine [Machine]
|
|
250
|
+
# @param paths [Array<String>]
|
|
251
|
+
#
|
|
252
|
+
# @yieldparam progress [Integer]
|
|
253
|
+
# @yieldparam total [Integer]
|
|
254
|
+
# @yieldparam path [String]
|
|
255
|
+
#
|
|
256
|
+
# @return [Boolean]
|
|
257
|
+
def copy(machine, paths, &)
|
|
258
|
+
if machine.localhost?
|
|
259
|
+
true
|
|
260
|
+
elsif machine.carried?
|
|
261
|
+
carrier = machine.carrier_machine
|
|
262
|
+
cp = NixCopy.new(carrier.target_host, paths, port: carrier.target_port)
|
|
263
|
+
cp.run!(&).success?
|
|
264
|
+
else
|
|
265
|
+
cp = NixCopy.new(machine.target_host, paths, port: machine.target_port)
|
|
266
|
+
cp.run!(&).success?
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# @param machine [Machine]
|
|
271
|
+
# @param generation [Generation::Build]
|
|
272
|
+
# @param action [String]
|
|
273
|
+
# @return [Boolean]
|
|
274
|
+
def activate(machine, generation, action)
|
|
275
|
+
args = [File.join(generation.toplevel, 'bin/switch-to-configuration'), action]
|
|
276
|
+
|
|
277
|
+
MachineControl.new(machine).execute!(*args).success?
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# @param machine [Machine]
|
|
281
|
+
# @param generation [Generation::Build]
|
|
282
|
+
# @param action [String]
|
|
283
|
+
# @return [Boolean]
|
|
284
|
+
def activate_with_rollback(machine, generation, action)
|
|
285
|
+
check_file = File.join('/run', "confctl-confirm-#{SecureRandom.hex(3)}")
|
|
286
|
+
timeout = machine['autoRollback']['timeout']
|
|
287
|
+
logger = NullLogger.new
|
|
288
|
+
|
|
289
|
+
args = [
|
|
290
|
+
generation.auto_rollback,
|
|
291
|
+
'-t', timeout,
|
|
292
|
+
generation.toplevel,
|
|
293
|
+
action,
|
|
294
|
+
check_file
|
|
295
|
+
]
|
|
296
|
+
|
|
297
|
+
activation_success = nil
|
|
298
|
+
|
|
299
|
+
activation_thread = Thread.new do
|
|
300
|
+
activation_success = MachineControl.new(machine).execute!(*args).success?
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Wait for the configuration to be switched
|
|
304
|
+
t = Time.now
|
|
305
|
+
mc = MachineControl.new(machine, logger:)
|
|
306
|
+
|
|
307
|
+
loop do
|
|
308
|
+
out, = mc.execute!('sh', '-c', "cat #{Shellwords.escape(check_file)} 2>/dev/null")
|
|
309
|
+
stripped = out.strip
|
|
310
|
+
break if stripped == 'switched' || ((t + timeout + 10) < Time.now && stripped != 'switching')
|
|
311
|
+
|
|
312
|
+
sleep(1)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Confirm it
|
|
316
|
+
confirm_cmd = ['sh', '-c', "echo confirmed > #{Shellwords.escape(check_file)}"]
|
|
317
|
+
|
|
318
|
+
10.times do
|
|
319
|
+
break if mc.execute!(*confirm_cmd).success?
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
activation_thread.join
|
|
323
|
+
activation_success
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# @param machine [Machine]
|
|
327
|
+
# @param toplevel [String]
|
|
328
|
+
# @return [Boolean]
|
|
329
|
+
def set_profile(machine, toplevel)
|
|
330
|
+
args = [
|
|
331
|
+
'nix-env',
|
|
332
|
+
'-p', machine.profile,
|
|
333
|
+
'--set', toplevel
|
|
334
|
+
]
|
|
335
|
+
|
|
336
|
+
MachineControl.new(machine).execute!(*args).success?
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# @param machine [Machine]
|
|
340
|
+
# @param toplevel [String]
|
|
341
|
+
# @return [Boolean]
|
|
342
|
+
def set_carried_profile(machine, toplevel)
|
|
343
|
+
args = [
|
|
344
|
+
'carrier-env',
|
|
345
|
+
'-p', machine.profile,
|
|
346
|
+
'--set', toplevel
|
|
347
|
+
]
|
|
348
|
+
|
|
349
|
+
MachineControl.new(machine.carrier_machine).execute!(*args).success?
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# @param packages [Array<String>]
|
|
353
|
+
# @param command [String]
|
|
354
|
+
# @return [Boolean]
|
|
355
|
+
def run_command_in_shell(packages: [], command: nil)
|
|
356
|
+
args = ['nix-shell']
|
|
357
|
+
|
|
358
|
+
if packages.any?
|
|
359
|
+
args << '-p'
|
|
360
|
+
args.concat(packages)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
args << '--command'
|
|
364
|
+
args << command
|
|
365
|
+
|
|
366
|
+
cmd.run!(*args, env: { 'shellHook' => nil }).success?
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# @param machine [Machine]
|
|
370
|
+
# @yieldparam progress [NixCollectGarbage::Progress]
|
|
371
|
+
# @return [Boolean]
|
|
372
|
+
def collect_garbage(machine, &)
|
|
373
|
+
gc = NixCollectGarbage.new(machine)
|
|
374
|
+
gc.run!(&).success?
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
protected
|
|
378
|
+
|
|
379
|
+
attr_reader :conf_dir, :show_trace, :max_jobs, :cores, :cmd
|
|
380
|
+
|
|
381
|
+
# Execute block only if `out_link` does not exist or conf dir has changed
|
|
382
|
+
# @param out_link [String] out link path
|
|
383
|
+
def with_cache(out_link)
|
|
384
|
+
unchanged = false
|
|
385
|
+
|
|
386
|
+
if File.exist?(out_link)
|
|
387
|
+
unchanged = ConfDir.unchanged?
|
|
388
|
+
|
|
389
|
+
if unchanged
|
|
390
|
+
cache_mtime = ConfDir.state_mtime
|
|
391
|
+
|
|
392
|
+
if cache_mtime
|
|
393
|
+
begin
|
|
394
|
+
st = File.lstat(out_link)
|
|
395
|
+
rescue Errno::ENOENT
|
|
396
|
+
# pass
|
|
397
|
+
else
|
|
398
|
+
if st.mtime > cache_mtime
|
|
399
|
+
Logger.instance << "Using #{out_link}\n"
|
|
400
|
+
return
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
Logger.instance << "Building #{out_link}\n"
|
|
408
|
+
|
|
409
|
+
unless unchanged
|
|
410
|
+
Logger.instance << "Updating configuration cache\n"
|
|
411
|
+
ConfDir.update_state
|
|
412
|
+
end
|
|
413
|
+
yield
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# @param hash [Hash]
|
|
417
|
+
# @param core_swpins [Boolean]
|
|
418
|
+
# @yieldparam file [String]
|
|
419
|
+
def with_argument(hash, core_swpins: false)
|
|
420
|
+
if core_swpins
|
|
421
|
+
paths = Swpins::Core.get.pre_evaluated_store_paths
|
|
422
|
+
hash[:coreSwpins] = paths if paths
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
f = Tempfile.new('confctl')
|
|
426
|
+
f.puts(hash.to_json)
|
|
427
|
+
f.close
|
|
428
|
+
yield(f.path)
|
|
429
|
+
ensure
|
|
430
|
+
f.unlink
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
def nix_instantiate(hash, **opts)
|
|
434
|
+
with_argument(hash, **opts) do |arg|
|
|
435
|
+
cmd_args = [
|
|
436
|
+
'nix-instantiate',
|
|
437
|
+
'--eval',
|
|
438
|
+
'--json',
|
|
439
|
+
'--strict',
|
|
440
|
+
'--read-write-mode',
|
|
441
|
+
'--arg', 'jsonArg', arg,
|
|
442
|
+
(show_trace ? '--show-trace' : nil),
|
|
443
|
+
(max_jobs ? ['--max-jobs', max_jobs.to_s] : nil),
|
|
444
|
+
(cores ? ['--cores', cores.to_s] : nil),
|
|
445
|
+
ConfCtl.nix_asset('evaluator.nix')
|
|
446
|
+
].flatten.compact
|
|
447
|
+
|
|
448
|
+
out, = cmd.run(*cmd_args).stdout
|
|
449
|
+
|
|
450
|
+
demodulify(JSON.parse(out))
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
def demodulify(value)
|
|
455
|
+
if value.is_a?(Array)
|
|
456
|
+
value.each { |item| demodulify(item) }
|
|
457
|
+
elsif value.is_a?(Hash)
|
|
458
|
+
value.delete('_module')
|
|
459
|
+
value.each_value { |v| demodulify(v) }
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def cache_dir
|
|
464
|
+
ConfDir.cache_dir
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
end
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
|
|
1
3
|
module ConfCtl
|
|
2
4
|
class Swpins::ChangeSet
|
|
3
5
|
class SpecOwner
|
|
4
6
|
attr_reader :name, :path
|
|
5
7
|
end
|
|
6
8
|
|
|
7
|
-
SpecSet = Struct.new(:spec, :original_version, :original_info
|
|
9
|
+
SpecSet = Struct.new(:spec, :original_version, :original_info) do
|
|
8
10
|
def name
|
|
9
11
|
spec.name
|
|
10
12
|
end
|
|
@@ -52,6 +54,16 @@ module ConfCtl
|
|
|
52
54
|
def commit(type: :upgrade, changelog: false, editor: true)
|
|
53
55
|
return if @owners.empty?
|
|
54
56
|
|
|
57
|
+
git_add = [
|
|
58
|
+
'git',
|
|
59
|
+
'add',
|
|
60
|
+
*changed_files
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
unless Kernel.system(*git_add)
|
|
64
|
+
raise 'git add exited with non-zero status code'
|
|
65
|
+
end
|
|
66
|
+
|
|
55
67
|
git_commit = [
|
|
56
68
|
'git',
|
|
57
69
|
'commit',
|
|
@@ -135,7 +147,17 @@ module ConfCtl
|
|
|
135
147
|
end
|
|
136
148
|
|
|
137
149
|
def changed_files
|
|
138
|
-
|
|
150
|
+
root_path = Pathname.new(ConfCtl::ConfDir.path)
|
|
151
|
+
|
|
152
|
+
@owners.each_key.map do |owner|
|
|
153
|
+
owner_path = Pathname.new(owner.path)
|
|
154
|
+
|
|
155
|
+
if owner_path.absolute?
|
|
156
|
+
owner_path.relative_path_from(root_path).to_s
|
|
157
|
+
else
|
|
158
|
+
owner.path
|
|
159
|
+
end
|
|
160
|
+
end
|
|
139
161
|
end
|
|
140
162
|
|
|
141
163
|
def same_changes?
|
data/lib/confctl/ui.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module ConfCtl
|
|
2
|
+
module Ui
|
|
3
|
+
module_function
|
|
4
|
+
|
|
5
|
+
# Return true when interactive TTY UI features should be enabled.
|
|
6
|
+
#
|
|
7
|
+
# Set CONFCTL_TTY=0 to force non-interactive output.
|
|
8
|
+
def tty?
|
|
9
|
+
return false if ENV['CONFCTL_TTY'] == '0'
|
|
10
|
+
|
|
11
|
+
$stdout.tty? && $stderr.tty?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def no_color?
|
|
15
|
+
v = ENV.fetch('NO_COLOR', nil)
|
|
16
|
+
!v.nil? && !v.strip.empty?
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/confctl/version.rb
CHANGED
data/man/index.html
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="generator" content="md2man 5.1.2 https://github.com/sunaku/md2man" />
|
|
6
|
+
<title>man/index — release-source</title>
|
|
7
|
+
<link rel="stylesheet" href="style.css"/>
|
|
8
|
+
<!--[if lt IE 9]><script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
|
|
9
|
+
</head>
|
|
10
|
+
<body><div class="container-fluid"><h2 id="man8">man8</h2><dl class="dl-horizontal"><dt><a href="man8/confctl.8.html">confctl(8)</a></dt><dd>Nix deployment management tool</dd></dl><dl class="dl-horizontal"><dt><a href="man8/confctl-options.nix.8.html">confctl-options(nix.8)</a></dt><dd>confctl configuration documentation</dd></dl></div></body>
|
|
11
|
+
</html>
|