confctl 2.0.0 → 2.2.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/CHANGELOG.md +19 -1
- data/Gemfile +6 -0
- data/README.md +2 -1
- data/docs/carrier.md +12 -0
- data/lib/confctl/cli/app.rb +19 -0
- data/lib/confctl/cli/cluster.rb +183 -47
- data/lib/confctl/cli/generation.rb +44 -15
- data/lib/confctl/cli/swpins/channel.rb +6 -4
- data/lib/confctl/cli/swpins/cluster.rb +6 -4
- data/lib/confctl/cli/swpins/core.rb +6 -4
- data/lib/confctl/generation/build.rb +42 -1
- data/lib/confctl/generation/build_list.rb +10 -0
- data/lib/confctl/generation/host.rb +9 -5
- data/lib/confctl/generation/host_list.rb +20 -5
- data/lib/confctl/generation/unified.rb +5 -0
- data/lib/confctl/generation/unified_list.rb +10 -0
- data/lib/confctl/machine.rb +4 -0
- data/lib/confctl/machine_control.rb +3 -2
- data/lib/confctl/machine_list.rb +4 -0
- data/lib/confctl/machine_status.rb +1 -1
- data/lib/confctl/nix.rb +63 -18
- data/lib/confctl/nix_copy.rb +5 -5
- data/lib/confctl/null_logger.rb +7 -0
- data/lib/confctl/swpins/change_set.rb +11 -4
- data/lib/confctl/swpins/specs/git.rb +23 -16
- data/lib/confctl/swpins/specs/git_rev.rb +1 -1
- data/lib/confctl/system_command.rb +3 -2
- data/lib/confctl/version.rb +1 -1
- data/libexec/auto-rollback.rb +106 -0
- data/man/man8/confctl.8 +109 -72
- data/man/man8/confctl.8.md +91 -54
- data/nix/evaluator.nix +26 -1
- data/nix/modules/cluster/default.nix +20 -0
- data/nix/modules/confctl/carrier/base.nix +8 -6
- data/nix/modules/confctl/carrier/netboot/build-netboot-server.rb +209 -50
- data/nix/modules/confctl/carrier/netboot/nixos.nix +5 -3
- data/nix/modules/confctl/kexec-netboot/default.nix +38 -0
- data/nix/modules/confctl/kexec-netboot/kexec-netboot.8.adoc +62 -0
- data/nix/modules/confctl/kexec-netboot/kexec-netboot.rb +455 -0
- data/nix/modules/confctl/overlays.nix +15 -0
- data/nix/modules/module-list.nix +1 -0
- data/nix/modules/system-list.nix +3 -1
- data/shell.nix +9 -2
- metadata +8 -6
@@ -46,11 +46,12 @@ module ConfCtl::Cli
|
|
46
46
|
core.save
|
47
47
|
core.pre_evaluate
|
48
48
|
|
49
|
-
return
|
49
|
+
return if !opts[:commit] || !change_set.any_changes?
|
50
50
|
|
51
51
|
change_set.commit(
|
52
52
|
type: opts[:downgrade] ? :downgrade : :upgrade,
|
53
|
-
changelog: opts[:changelog]
|
53
|
+
changelog: opts[:changelog],
|
54
|
+
editor: opts[:editor]
|
54
55
|
)
|
55
56
|
end
|
56
57
|
|
@@ -75,11 +76,12 @@ module ConfCtl::Cli
|
|
75
76
|
core.save
|
76
77
|
core.pre_evaluate
|
77
78
|
|
78
|
-
return
|
79
|
+
return if !opts[:commit] || !change_set.any_changes?
|
79
80
|
|
80
81
|
change_set.commit(
|
81
82
|
type: opts[:downgrade] ? :downgrade : :upgrade,
|
82
|
-
changelog: opts[:changelog]
|
83
|
+
changelog: opts[:changelog],
|
84
|
+
editor: opts[:editor]
|
83
85
|
)
|
84
86
|
end
|
85
87
|
end
|
@@ -16,6 +16,9 @@ module ConfCtl
|
|
16
16
|
# @return [String]
|
17
17
|
attr_reader :toplevel
|
18
18
|
|
19
|
+
# @return [String]
|
20
|
+
attr_reader :auto_rollback
|
21
|
+
|
19
22
|
# @return [Array<String>]
|
20
23
|
attr_reader :swpin_names
|
21
24
|
|
@@ -29,22 +32,28 @@ module ConfCtl
|
|
29
32
|
# @return [Boolean]
|
30
33
|
attr_accessor :current
|
31
34
|
|
35
|
+
# @return [String, nil]
|
36
|
+
attr_reader :kernel_version
|
37
|
+
|
32
38
|
# @param host [String]
|
33
39
|
def initialize(host)
|
34
40
|
@host = host
|
35
41
|
end
|
36
42
|
|
37
43
|
# @param toplevel [String]
|
44
|
+
# @param auto_rollback [String]
|
38
45
|
# @param swpin_paths [Hash]
|
39
46
|
# @param swpin_specs [Hash]
|
40
47
|
# @param date [Time]
|
41
|
-
def create(toplevel, swpin_paths, swpin_specs, date: nil)
|
48
|
+
def create(toplevel, auto_rollback, swpin_paths, swpin_specs, date: nil)
|
42
49
|
@toplevel = toplevel
|
50
|
+
@auto_rollback = auto_rollback
|
43
51
|
@swpin_names = swpin_paths.keys
|
44
52
|
@swpin_paths = swpin_paths
|
45
53
|
@swpin_specs = swpin_specs
|
46
54
|
@date = date || Time.now
|
47
55
|
@name = date.strftime('%Y-%m-%d--%H-%M-%S')
|
56
|
+
@kernel_version = extract_kernel_version
|
48
57
|
end
|
49
58
|
|
50
59
|
# @param name [String]
|
@@ -53,6 +62,7 @@ module ConfCtl
|
|
53
62
|
|
54
63
|
cfg = JSON.parse(File.read(config_path))
|
55
64
|
@toplevel = cfg['toplevel']
|
65
|
+
@auto_rollback = cfg['auto_rollback']
|
56
66
|
|
57
67
|
@swpin_names = []
|
58
68
|
@swpin_paths = {}
|
@@ -69,6 +79,7 @@ module ConfCtl
|
|
69
79
|
end
|
70
80
|
|
71
81
|
@date = Time.iso8601(cfg['date'])
|
82
|
+
@kernel_version = extract_kernel_version
|
72
83
|
rescue StandardError => e
|
73
84
|
raise Error, "invalid generation '#{name}': #{e.message}"
|
74
85
|
end
|
@@ -76,6 +87,7 @@ module ConfCtl
|
|
76
87
|
def save
|
77
88
|
FileUtils.mkdir_p(dir)
|
78
89
|
File.symlink(toplevel, toplevel_path)
|
90
|
+
File.symlink(auto_rollback, auto_rollback_path)
|
79
91
|
|
80
92
|
swpin_paths.each do |name, path|
|
81
93
|
File.symlink(path, swpin_path(name))
|
@@ -85,6 +97,7 @@ module ConfCtl
|
|
85
97
|
f.puts(JSON.pretty_generate({
|
86
98
|
date: date.iso8601,
|
87
99
|
toplevel:,
|
100
|
+
auto_rollback:,
|
88
101
|
swpins: swpin_paths.to_h do |name, path|
|
89
102
|
[name, { path:, spec: swpin_specs[name].as_json }]
|
90
103
|
end
|
@@ -97,6 +110,13 @@ module ConfCtl
|
|
97
110
|
def destroy
|
98
111
|
remove_gcroot
|
99
112
|
File.unlink(toplevel_path)
|
113
|
+
|
114
|
+
begin
|
115
|
+
File.unlink(auto_rollback_path)
|
116
|
+
rescue Errno::ENOENT
|
117
|
+
# Older generations might not have auto_rollback
|
118
|
+
end
|
119
|
+
|
100
120
|
swpin_paths.each_key { |name| File.unlink(swpin_path(name)) }
|
101
121
|
File.unlink(config_path)
|
102
122
|
Dir.rmdir(dir)
|
@@ -104,6 +124,7 @@ module ConfCtl
|
|
104
124
|
|
105
125
|
def add_gcroot
|
106
126
|
GCRoot.add(gcroot_name('toplevel'), toplevel_path)
|
127
|
+
GCRoot.add(gcroot_name('auto_rollback'), auto_rollback_path)
|
107
128
|
swpin_paths.each_key do |name|
|
108
129
|
GCRoot.add(gcroot_name("swpin.#{name}"), toplevel_path)
|
109
130
|
end
|
@@ -111,6 +132,7 @@ module ConfCtl
|
|
111
132
|
|
112
133
|
def remove_gcroot
|
113
134
|
GCRoot.remove(gcroot_name('toplevel'))
|
135
|
+
GCRoot.remove(gcroot_name('auto_rollback'))
|
114
136
|
swpin_paths.each_key do |name|
|
115
137
|
GCRoot.remove(gcroot_name("swpin.#{name}"))
|
116
138
|
end
|
@@ -130,6 +152,10 @@ module ConfCtl
|
|
130
152
|
@toplevel_path ||= File.join(dir, 'toplevel')
|
131
153
|
end
|
132
154
|
|
155
|
+
def auto_rollback_path
|
156
|
+
@auto_rollback_path ||= File.join(dir, 'auto_rollback')
|
157
|
+
end
|
158
|
+
|
133
159
|
def swpin_path(name)
|
134
160
|
File.join(dir, "#{name}.swpin")
|
135
161
|
end
|
@@ -141,5 +167,20 @@ module ConfCtl
|
|
141
167
|
def gcroot_name(file)
|
142
168
|
"#{escaped_host}-generation-#{name}-#{file}"
|
143
169
|
end
|
170
|
+
|
171
|
+
def extract_kernel_version
|
172
|
+
# `kernel` is for NixOS/vpsAdminOS and also carried NixOS machines (netboot)
|
173
|
+
# `bzImage` is for carried vpsAdminOS machines (netboot)
|
174
|
+
%w[kernel bzImage].each do |v|
|
175
|
+
link = File.readlink(File.join(toplevel, v))
|
176
|
+
next unless %r{\A/nix/store/[^-]+-linux-([^/]+)} =~ link
|
177
|
+
|
178
|
+
return ::Regexp.last_match(1)
|
179
|
+
rescue Errno::ENOENT, Errno::EINVAL
|
180
|
+
next
|
181
|
+
end
|
182
|
+
|
183
|
+
nil
|
184
|
+
end
|
144
185
|
end
|
145
186
|
end
|
@@ -55,6 +55,16 @@ module ConfCtl
|
|
55
55
|
index[name]
|
56
56
|
end
|
57
57
|
|
58
|
+
# @param offset [Integer] 0 = current/last, 1 = first (oldest), -1 = before last
|
59
|
+
# @return [Generation::Build]
|
60
|
+
def at_offset(offset)
|
61
|
+
if offset == 0
|
62
|
+
generations.last
|
63
|
+
else
|
64
|
+
generations[offset - 1]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
58
68
|
def each(&)
|
59
69
|
generations.each(&)
|
60
70
|
end
|
@@ -1,19 +1,22 @@
|
|
1
1
|
module ConfCtl
|
2
2
|
class Generation::Host
|
3
|
-
attr_reader :host, :profile, :id, :toplevel, :date, :current
|
3
|
+
attr_reader :host, :profile, :id, :toplevel, :date, :kernel_version, :current
|
4
4
|
|
5
|
-
# @param
|
5
|
+
# @param machine [Machine]
|
6
6
|
# @param profile [String]
|
7
7
|
# @param id [Integer]
|
8
8
|
# @param toplevel [String]
|
9
9
|
# @param date [Time]
|
10
|
+
# @param kernel_version [String, nil]
|
10
11
|
# @param mc [MachineControl]
|
11
|
-
def initialize(
|
12
|
-
@host =
|
12
|
+
def initialize(machine, profile, id, toplevel, date, kernel_version, current: false, mc: nil)
|
13
|
+
@host = machine.name
|
14
|
+
@machine = machine
|
13
15
|
@profile = profile
|
14
16
|
@id = id
|
15
17
|
@toplevel = toplevel
|
16
18
|
@date = date
|
19
|
+
@kernel_version = kernel_version
|
17
20
|
@current = current
|
18
21
|
@mc = mc
|
19
22
|
end
|
@@ -25,7 +28,8 @@ module ConfCtl
|
|
25
28
|
def destroy
|
26
29
|
raise 'machine control not available' if mc.nil?
|
27
30
|
|
28
|
-
|
31
|
+
env_cmd = @machine.carried? ? 'carrier-env' : 'nix-env'
|
32
|
+
mc.execute(env_cmd, '-p', profile, '--delete-generations', id.to_s)
|
29
33
|
end
|
30
34
|
|
31
35
|
protected
|
@@ -1,24 +1,33 @@
|
|
1
1
|
module ConfCtl
|
2
2
|
class Generation::HostList
|
3
|
+
# @parma machine [Machine]
|
3
4
|
# @param mc [MachineControl]
|
4
5
|
# @param profile [String]
|
5
6
|
# @return [Generation::HostList]
|
6
|
-
def self.fetch(mc, profile:)
|
7
|
+
def self.fetch(machine, mc, profile:)
|
7
8
|
out, = mc.bash_script(<<~END)
|
8
9
|
realpath #{profile}
|
9
10
|
|
10
11
|
for generation in `ls -d -1 #{profile}-*-link` ; do
|
11
|
-
echo "$generation
|
12
|
+
echo -n "$generation;"
|
13
|
+
echo -n "$(readlink $generation);"
|
14
|
+
echo -n "$(stat --format=%Y $generation);"
|
15
|
+
|
16
|
+
for kernel_file in kernel bzImage ; do
|
17
|
+
[ -h "$generation/$kernel_file" ] && echo -n $(readlink "$generation/$kernel_file")
|
18
|
+
done
|
19
|
+
|
20
|
+
echo
|
12
21
|
done
|
13
22
|
END
|
14
23
|
|
15
|
-
list = new(
|
24
|
+
list = new(machine.name)
|
16
25
|
lines = out.strip.split("\n")
|
17
26
|
current_path = lines.shift
|
18
27
|
id_rx = /^#{Regexp.escape(profile)}-(\d+)-link$/
|
19
28
|
|
20
29
|
lines.each do |line|
|
21
|
-
link, path, created_at = line.split(';')
|
30
|
+
link, path, created_at, kernel = line.split(';')
|
22
31
|
|
23
32
|
if id_rx =~ link
|
24
33
|
id = ::Regexp.last_match(1).to_i
|
@@ -27,12 +36,18 @@ module ConfCtl
|
|
27
36
|
next
|
28
37
|
end
|
29
38
|
|
39
|
+
kernel_version =
|
40
|
+
if kernel && %r{\A/nix/store/[^-]+-linux-([^/]+)} =~ kernel
|
41
|
+
::Regexp.last_match(1)
|
42
|
+
end
|
43
|
+
|
30
44
|
list << Generation::Host.new(
|
31
|
-
|
45
|
+
machine,
|
32
46
|
profile,
|
33
47
|
id,
|
34
48
|
path,
|
35
49
|
Time.at(created_at.to_i),
|
50
|
+
kernel_version,
|
36
51
|
current: path == current_path,
|
37
52
|
mc:
|
38
53
|
)
|
@@ -15,6 +15,9 @@ module ConfCtl
|
|
15
15
|
# @return [Time]
|
16
16
|
attr_reader :date
|
17
17
|
|
18
|
+
# @return [String, nil]
|
19
|
+
attr_reader :kernel_version
|
20
|
+
|
18
21
|
# @return [Boolean]
|
19
22
|
attr_reader :current
|
20
23
|
|
@@ -37,11 +40,13 @@ module ConfCtl
|
|
37
40
|
@name = build_generation.name
|
38
41
|
@toplevel = build_generation.toplevel
|
39
42
|
@date = build_generation.date
|
43
|
+
@kernel_version = build_generation.kernel_version
|
40
44
|
@current ||= build_generation.current
|
41
45
|
elsif host_generation
|
42
46
|
@name = host_generation.approx_name
|
43
47
|
@toplevel = host_generation.toplevel
|
44
48
|
@date = host_generation.date
|
49
|
+
@kernel_version = host_generation.kernel_version
|
45
50
|
@current ||= host_generation.current
|
46
51
|
else
|
47
52
|
raise ArgumentError, 'set build or host'
|
@@ -46,6 +46,16 @@ module ConfCtl
|
|
46
46
|
generations.each(&)
|
47
47
|
end
|
48
48
|
|
49
|
+
# @param offset [Integer] 0 = current/last, 1 = first (oldest), -1 = before last
|
50
|
+
# @return [Generation::Unified]
|
51
|
+
def at_offset(offset)
|
52
|
+
if offset == 0
|
53
|
+
generations.last
|
54
|
+
else
|
55
|
+
generations[offset - 1]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
49
59
|
def delete_if(&)
|
50
60
|
generations.delete_if(&)
|
51
61
|
end
|
data/lib/confctl/machine.rb
CHANGED
@@ -6,10 +6,11 @@ module ConfCtl
|
|
6
6
|
attr_reader :machine
|
7
7
|
|
8
8
|
# @param machine [Machine]
|
9
|
-
|
9
|
+
# @param logger [#<<]
|
10
|
+
def initialize(machine, logger: nil)
|
10
11
|
@machine = machine
|
11
12
|
@extra_ssh_opts = []
|
12
|
-
@cmd = SystemCommand.new
|
13
|
+
@cmd = SystemCommand.new(logger:)
|
13
14
|
end
|
14
15
|
|
15
16
|
# Try to open SSH connection
|
data/lib/confctl/machine_list.rb
CHANGED
@@ -103,7 +103,7 @@ module ConfCtl
|
|
103
103
|
|
104
104
|
if generations
|
105
105
|
begin
|
106
|
-
@generations = Generation::HostList.fetch(mc, profile: machine.profile)
|
106
|
+
@generations = Generation::HostList.fetch(machine, mc, profile: machine.profile)
|
107
107
|
rescue TTY::Command::ExitError
|
108
108
|
return
|
109
109
|
end
|
data/lib/confctl/nix.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'confctl/utils/file'
|
2
|
+
require 'digest'
|
2
3
|
require 'etc'
|
3
4
|
require 'json'
|
4
5
|
require 'securerandom'
|
@@ -137,19 +138,19 @@ module ConfCtl
|
|
137
138
|
end
|
138
139
|
end
|
139
140
|
|
140
|
-
# Evaluate swpins for
|
141
|
-
# @param
|
142
|
-
# @return [Hash]
|
143
|
-
def eval_host_swpins(
|
141
|
+
# Evaluate swpins for hosts
|
142
|
+
# @param hosts [Array<String>]
|
143
|
+
# @return [Hash] host => swpins
|
144
|
+
def eval_host_swpins(hosts)
|
144
145
|
with_argument({
|
145
146
|
confDir: conf_dir,
|
146
147
|
build: :evalHostSwpins,
|
147
|
-
machines:
|
148
|
+
machines: hosts
|
148
149
|
}, core_swpins: true) do |arg|
|
149
150
|
out_link = File.join(
|
150
151
|
cache_dir,
|
151
152
|
'build',
|
152
|
-
"#{
|
153
|
+
"#{Digest::SHA256.hexdigest(hosts.join(','))[0..11]}.swpins"
|
153
154
|
)
|
154
155
|
|
155
156
|
cmd_args = [
|
@@ -164,7 +165,7 @@ module ConfCtl
|
|
164
165
|
|
165
166
|
out, = cmd.run(*cmd_args)
|
166
167
|
|
167
|
-
JSON.parse(File.read(out.strip))
|
168
|
+
JSON.parse(File.read(out.strip))
|
168
169
|
end
|
169
170
|
end
|
170
171
|
|
@@ -204,15 +205,16 @@ module ConfCtl
|
|
204
205
|
nb.run(&block)
|
205
206
|
|
206
207
|
begin
|
207
|
-
|
208
|
-
|
208
|
+
host_results = JSON.parse(File.read(out_link))
|
209
|
+
host_results.each do |host, result|
|
209
210
|
host_generations = Generation::BuildList.new(host)
|
210
|
-
generation = host_generations.find(
|
211
|
+
generation = host_generations.find(result['attribute'], swpin_paths)
|
211
212
|
|
212
213
|
if generation.nil?
|
213
214
|
generation = Generation::Build.new(host)
|
214
215
|
generation.create(
|
215
|
-
|
216
|
+
result['attribute'],
|
217
|
+
result['autoRollback'],
|
216
218
|
swpin_paths,
|
217
219
|
host_swpin_specs[host],
|
218
220
|
date: time
|
@@ -232,35 +234,78 @@ module ConfCtl
|
|
232
234
|
end
|
233
235
|
|
234
236
|
# @param machine [Machine]
|
235
|
-
# @param
|
237
|
+
# @param paths [Array<String>]
|
236
238
|
#
|
237
239
|
# @yieldparam progress [Integer]
|
238
240
|
# @yieldparam total [Integer]
|
239
241
|
# @yieldparam path [String]
|
240
242
|
#
|
241
243
|
# @return [Boolean]
|
242
|
-
def copy(machine,
|
244
|
+
def copy(machine, paths, &)
|
243
245
|
if machine.localhost?
|
244
246
|
true
|
245
247
|
elsif machine.carried?
|
246
|
-
cp = NixCopy.new(machine.carrier_machine.target_host,
|
248
|
+
cp = NixCopy.new(machine.carrier_machine.target_host, paths)
|
247
249
|
cp.run!(&).success?
|
248
250
|
else
|
249
|
-
cp = NixCopy.new(machine.target_host,
|
251
|
+
cp = NixCopy.new(machine.target_host, paths)
|
250
252
|
cp.run!(&).success?
|
251
253
|
end
|
252
254
|
end
|
253
255
|
|
254
256
|
# @param machine [Machine]
|
255
|
-
# @param
|
257
|
+
# @param generation [Generation::Build]
|
256
258
|
# @param action [String]
|
257
259
|
# @return [Boolean]
|
258
|
-
def activate(machine,
|
259
|
-
args = [File.join(toplevel, 'bin/switch-to-configuration'), action]
|
260
|
+
def activate(machine, generation, action)
|
261
|
+
args = [File.join(generation.toplevel, 'bin/switch-to-configuration'), action]
|
260
262
|
|
261
263
|
MachineControl.new(machine).execute!(*args).success?
|
262
264
|
end
|
263
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
|
+
|
264
309
|
# @param machine [Machine]
|
265
310
|
# @param toplevel [String]
|
266
311
|
# @return [Boolean]
|
data/lib/confctl/nix_copy.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module ConfCtl
|
2
2
|
class NixCopy
|
3
3
|
# @param target [String]
|
4
|
-
# @param
|
5
|
-
def initialize(target,
|
4
|
+
# @param store_paths [Array<String>]
|
5
|
+
def initialize(target, store_paths)
|
6
6
|
@target = target
|
7
|
-
@
|
7
|
+
@store_paths = store_paths
|
8
8
|
@total = nil
|
9
9
|
@progress = 0
|
10
10
|
end
|
@@ -22,7 +22,7 @@ module ConfCtl
|
|
22
22
|
ret = cmd.run!(
|
23
23
|
'nix-copy-closure',
|
24
24
|
'--to', "root@#{target}",
|
25
|
-
|
25
|
+
*store_paths,
|
26
26
|
&line_buf.feed_block
|
27
27
|
)
|
28
28
|
|
@@ -32,7 +32,7 @@ module ConfCtl
|
|
32
32
|
|
33
33
|
protected
|
34
34
|
|
35
|
-
attr_reader :target, :
|
35
|
+
attr_reader :target, :store_paths
|
36
36
|
|
37
37
|
def parse_line(line)
|
38
38
|
if @total.nil? && /^copying (\d+) paths/ =~ line
|
@@ -48,22 +48,29 @@ module ConfCtl
|
|
48
48
|
|
49
49
|
# @param type [:upgrade, :downgrade]
|
50
50
|
# @param changelog [Boolean]
|
51
|
-
|
51
|
+
# @param editor [Boolean]
|
52
|
+
def commit(type: :upgrade, changelog: false, editor: true)
|
52
53
|
return if @owners.empty?
|
53
54
|
|
54
|
-
|
55
|
+
git_commit = [
|
55
56
|
'git',
|
56
57
|
'commit',
|
57
|
-
'-e',
|
58
|
+
editor ? '-e' : nil,
|
58
59
|
'-m', build_message(type, changelog),
|
59
60
|
*changed_files
|
60
|
-
|
61
|
+
].compact
|
62
|
+
|
63
|
+
ret = Kernel.system(*git_commit)
|
61
64
|
|
62
65
|
return if ret
|
63
66
|
|
64
67
|
raise 'git commit exited with non-zero status code'
|
65
68
|
end
|
66
69
|
|
70
|
+
def any_changes?
|
71
|
+
@owners.any? { |_, spec_sets| spec_sets.any?(&:changed?) }
|
72
|
+
end
|
73
|
+
|
67
74
|
protected
|
68
75
|
|
69
76
|
def build_message(type, changelog)
|
@@ -90,14 +90,18 @@ module ConfCtl
|
|
90
90
|
raise "nix-prefetch-git failed with status #{$?.exitstatus}" if $?.exitstatus != 0
|
91
91
|
|
92
92
|
ret = JSON.parse(json.strip)
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
93
|
+
|
94
|
+
if state.nil? || state['rev'] != ret['rev']
|
95
|
+
self.state = {
|
96
|
+
'rev' => ret['rev'],
|
97
|
+
'date' => Time.now.iso8601
|
98
|
+
}
|
99
|
+
self.info = {
|
100
|
+
'rev' => ret['rev'],
|
101
|
+
'sha256' => ret['sha256']
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
101
105
|
ret
|
102
106
|
end
|
103
107
|
|
@@ -111,14 +115,17 @@ module ConfCtl
|
|
111
115
|
|
112
116
|
raise "nix-prefetch-url failed with status #{$?.exitstatus}" if $?.exitstatus != 0
|
113
117
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
118
|
+
if state.nil? || state['rev'] != rev
|
119
|
+
self.state = {
|
120
|
+
'rev' => rev,
|
121
|
+
'date' => Time.now.iso8601
|
122
|
+
}
|
123
|
+
self.info = {
|
124
|
+
'rev' => rev,
|
125
|
+
'sha256' => hash
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
122
129
|
{ 'url' => url, 'sha256' => hash }
|
123
130
|
end
|
124
131
|
|
@@ -2,9 +2,10 @@ require 'tty-command'
|
|
2
2
|
|
3
3
|
module ConfCtl
|
4
4
|
module SystemCommand
|
5
|
+
# @param logger [#<<]
|
5
6
|
# @return [TTY::Command]
|
6
|
-
def self.new
|
7
|
-
TTY::Command.new(output: Logger.instance, color: false)
|
7
|
+
def self.new(logger: nil)
|
8
|
+
TTY::Command.new(output: logger || Logger.instance, color: false)
|
8
9
|
end
|
9
10
|
end
|
10
11
|
end
|
data/lib/confctl/version.rb
CHANGED