cli-kit 5.3.0 → 5.3.1
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/Gemfile.lock +1 -1
- data/lib/cli/kit/config.rb +30 -44
- data/lib/cli/kit/core_ext.rb +1 -0
- data/lib/cli/kit/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a71589e7905e5dc0152e3f34baada5c16289f98af365aa7f4f795cc2fee3c3d1
|
|
4
|
+
data.tar.gz: 796526fbab7044920aa42f5d42ca86c6f17a28c9285c344fa210dda10f0e8397
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 645d68f82d0147a7176b8a29d5d75f42848456528c06e3db2798823fc9cd732317f3935ddd5d4572e580d13186d6b17267d168f654ef57a19eaec5510074f64d
|
|
7
|
+
data.tar.gz: 5c9250e416b97184027b1742bfb88ee05873baf07d5d8bdfdcb6d5b24102eea0498914ee29ee3056a27dcb557879209c85c36f7c80ea53682b3b580cb887b773
|
data/Gemfile.lock
CHANGED
data/lib/cli/kit/config.rb
CHANGED
|
@@ -14,11 +14,9 @@ module CLI
|
|
|
14
14
|
# pointing into +/nix/store+).
|
|
15
15
|
#
|
|
16
16
|
# Inherits from SystemCallError so existing `rescue SystemCallError`
|
|
17
|
-
# handlers around +Config#set+ continue to match.
|
|
18
|
-
# only
|
|
19
|
-
#
|
|
20
|
-
# excluded so the failure message can never leak secrets through
|
|
21
|
-
# stderr or exception reports.
|
|
17
|
+
# handlers around +Config#set+ continue to match. Diagnostics contain
|
|
18
|
+
# only changed section/key names, never their values. Config contents
|
|
19
|
+
# are not retained on the exception, to keep them out of error reports.
|
|
22
20
|
class ConfigWriteError < SystemCallError
|
|
23
21
|
class << self
|
|
24
22
|
# Ruby's +SystemCallError.===+ uses errno-based matching,
|
|
@@ -41,10 +39,10 @@ module CLI
|
|
|
41
39
|
# checkers and callers see the real signature, and allocate
|
|
42
40
|
# the instance manually to bypass +SystemCallError+'s
|
|
43
41
|
# factory behaviour.
|
|
44
|
-
#: (String config_path, String
|
|
45
|
-
def new(config_path,
|
|
42
|
+
#: (String config_path, Hash[String, Hash[String, String]] old_config, Hash[String, Hash[String, String]] new_config, SystemCallError cause) -> ConfigWriteError
|
|
43
|
+
def new(config_path, old_config, new_config, cause)
|
|
46
44
|
instance = allocate
|
|
47
|
-
instance.__send__(:initialize, config_path,
|
|
45
|
+
instance.__send__(:initialize, config_path, old_config, new_config, cause)
|
|
48
46
|
instance
|
|
49
47
|
end
|
|
50
48
|
end
|
|
@@ -53,10 +51,7 @@ module CLI
|
|
|
53
51
|
attr_reader :config_path
|
|
54
52
|
|
|
55
53
|
#: String
|
|
56
|
-
attr_reader :
|
|
57
|
-
|
|
58
|
-
#: String
|
|
59
|
-
attr_reader :new_content
|
|
54
|
+
attr_reader :diff
|
|
60
55
|
|
|
61
56
|
# rubocop:disable Lint/MissingSuper
|
|
62
57
|
# +SystemCallError#initialize+ has a factory-style signature that
|
|
@@ -65,16 +60,15 @@ module CLI
|
|
|
65
60
|
# initialize via Exception so we just get a message-only
|
|
66
61
|
# exception that +rescue SystemCallError+ still catches via
|
|
67
62
|
# inheritance. +super+ would not work here.
|
|
68
|
-
#: (String config_path, String
|
|
69
|
-
def initialize(config_path,
|
|
63
|
+
#: (String config_path, Hash[String, Hash[String, String]] old_config, Hash[String, Hash[String, String]] new_config, SystemCallError cause) -> void
|
|
64
|
+
def initialize(config_path, old_config, new_config, cause)
|
|
70
65
|
@config_path = config_path
|
|
71
|
-
@
|
|
72
|
-
@new_content = new_content
|
|
66
|
+
@diff = build_diff(old_config, new_config)
|
|
73
67
|
@wrapped_errno = cause.errno
|
|
74
68
|
message = <<~MSG.rstrip
|
|
75
69
|
Could not write to #{config_path}: #{cause.message}
|
|
76
70
|
|
|
77
|
-
Attempted changes (unchanged keys omitted):
|
|
71
|
+
Attempted changes (values and unchanged keys omitted):
|
|
78
72
|
#{diff}
|
|
79
73
|
MSG
|
|
80
74
|
Exception.instance_method(:initialize).bind(self).call(message)
|
|
@@ -88,19 +82,14 @@ module CLI
|
|
|
88
82
|
@wrapped_errno
|
|
89
83
|
end
|
|
90
84
|
|
|
91
|
-
|
|
92
|
-
# between +old_content+ and +new_content+. Unchanged keys are
|
|
93
|
-
# omitted so that sensitive values stored elsewhere in the config
|
|
94
|
-
# are never included in the failure message.
|
|
95
|
-
#: -> String
|
|
96
|
-
def diff
|
|
97
|
-
old_ini = CLI::Kit::Ini.new(config: @old_content).tap(&:parse).ini
|
|
98
|
-
new_ini = CLI::Kit::Ini.new(config: @new_content).tap(&:parse).ini
|
|
85
|
+
private
|
|
99
86
|
|
|
87
|
+
#: (Hash[String, Hash[String, String]] old_config, Hash[String, Hash[String, String]] new_config) -> String
|
|
88
|
+
def build_diff(old_config, new_config)
|
|
100
89
|
lines = []
|
|
101
|
-
(
|
|
102
|
-
old_section =
|
|
103
|
-
new_section =
|
|
90
|
+
(old_config.keys | new_config.keys).each do |section|
|
|
91
|
+
old_section = old_config[section] || {}
|
|
92
|
+
new_section = new_config[section] || {}
|
|
104
93
|
|
|
105
94
|
changes = []
|
|
106
95
|
(old_section.keys | new_section.keys).each do |key|
|
|
@@ -108,8 +97,8 @@ module CLI
|
|
|
108
97
|
new_val = new_section[key]
|
|
109
98
|
next if old_val == new_val
|
|
110
99
|
|
|
111
|
-
changes << "- #{key}
|
|
112
|
-
changes << "+ #{key}
|
|
100
|
+
changes << "- #{key}" if old_val
|
|
101
|
+
changes << "+ #{key}" if new_val
|
|
113
102
|
end
|
|
114
103
|
next if changes.empty?
|
|
115
104
|
|
|
@@ -270,8 +259,8 @@ module CLI
|
|
|
270
259
|
# for nix/home-manager configs that live in +/nix/store+. Wrap
|
|
271
260
|
# it the same way as +EACCES+/+EPERM+ so callers always see
|
|
272
261
|
# the diff and any +rescue ConfigWriteError+ handler matches.
|
|
273
|
-
|
|
274
|
-
raise(ConfigWriteError.new(config_path,
|
|
262
|
+
old_config = read_config_for_diff(config_path)
|
|
263
|
+
raise(ConfigWriteError.new(config_path, old_config, all_configs, e))
|
|
275
264
|
end
|
|
276
265
|
end
|
|
277
266
|
|
|
@@ -285,17 +274,14 @@ module CLI
|
|
|
285
274
|
begin
|
|
286
275
|
tmpfile.write(new_content)
|
|
287
276
|
tmpfile.close
|
|
288
|
-
#
|
|
289
|
-
#
|
|
290
|
-
#
|
|
291
|
-
#
|
|
292
|
-
# otherwise. This avoids silently tightening permissions on
|
|
293
|
-
# an existing config and avoids creating new configs with
|
|
294
|
-
# the more restrictive Tempfile default.
|
|
277
|
+
# Configs may contain credentials. Keep them private to the
|
|
278
|
+
# owner, including when replacing a previously shared file.
|
|
279
|
+
# Preserve stricter owner permissions on existing files and
|
|
280
|
+
# respect the umask when creating new ones.
|
|
295
281
|
mode = if File.exist?(config_path)
|
|
296
|
-
File.stat(config_path).mode
|
|
282
|
+
File.stat(config_path).mode & 0o600
|
|
297
283
|
else
|
|
298
|
-
|
|
284
|
+
0o600 & ~File.umask
|
|
299
285
|
end
|
|
300
286
|
File.chmod(mode, tmpfile_path)
|
|
301
287
|
File.rename(tmpfile_path, config_path)
|
|
@@ -306,11 +292,11 @@ module CLI
|
|
|
306
292
|
end
|
|
307
293
|
end
|
|
308
294
|
|
|
309
|
-
#: (String config_path) -> String
|
|
295
|
+
#: (String config_path) -> Hash[String, Hash[String, String]]
|
|
310
296
|
def read_config_for_diff(config_path)
|
|
311
|
-
File.read(config_path)
|
|
297
|
+
CLI::Kit::Ini.new(config: File.read(config_path)).tap(&:parse).ini
|
|
312
298
|
rescue SystemCallError
|
|
313
|
-
|
|
299
|
+
{}
|
|
314
300
|
end
|
|
315
301
|
|
|
316
302
|
# Resolve +config_path+ through any symlinks so the atomic rename
|
data/lib/cli/kit/core_ext.rb
CHANGED
data/lib/cli/kit/version.rb
CHANGED