bolt 2.15.0 → 2.20.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bolt might be problematic. Click here for more details.

Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -0
  3. data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +1 -0
  4. data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +20 -9
  5. data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +1 -0
  6. data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +123 -0
  7. data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +1 -0
  8. data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +1 -0
  9. data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +1 -0
  10. data/bolt-modules/boltlib/lib/puppet/functions/get_target.rb +1 -0
  11. data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +1 -0
  12. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb +1 -0
  13. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +1 -0
  14. data/bolt-modules/boltlib/lib/puppet/functions/remove_from_group.rb +1 -0
  15. data/bolt-modules/boltlib/lib/puppet/functions/resolve_references.rb +1 -0
  16. data/bolt-modules/boltlib/lib/puppet/functions/resource.rb +1 -0
  17. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +3 -0
  18. data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +2 -1
  19. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +7 -4
  20. data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +2 -1
  21. data/bolt-modules/boltlib/lib/puppet/functions/set_config.rb +1 -0
  22. data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +1 -0
  23. data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +1 -0
  24. data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +1 -0
  25. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -0
  26. data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +1 -0
  27. data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +1 -0
  28. data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +1 -0
  29. data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +1 -0
  30. data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +2 -0
  31. data/bolt-modules/ctrl/lib/puppet/functions/ctrl/sleep.rb +2 -0
  32. data/bolt-modules/file/lib/puppet/functions/file/exists.rb +1 -0
  33. data/bolt-modules/file/lib/puppet/functions/file/join.rb +2 -0
  34. data/bolt-modules/file/lib/puppet/functions/file/read.rb +2 -0
  35. data/bolt-modules/file/lib/puppet/functions/file/readable.rb +2 -0
  36. data/bolt-modules/file/lib/puppet/functions/file/write.rb +2 -0
  37. data/bolt-modules/out/lib/puppet/functions/out/message.rb +2 -0
  38. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +1 -0
  39. data/bolt-modules/system/lib/puppet/functions/system/env.rb +2 -0
  40. data/lib/bolt/applicator.rb +21 -15
  41. data/lib/bolt/apply_result.rb +1 -1
  42. data/lib/bolt/bolt_option_parser.rb +55 -20
  43. data/lib/bolt/catalog.rb +3 -2
  44. data/lib/bolt/cli.rb +116 -47
  45. data/lib/bolt/config.rb +48 -148
  46. data/lib/bolt/config/options.rb +488 -0
  47. data/lib/bolt/config/transport/base.rb +16 -16
  48. data/lib/bolt/config/transport/docker.rb +9 -23
  49. data/lib/bolt/config/transport/local.rb +6 -44
  50. data/lib/bolt/config/transport/options.rb +460 -0
  51. data/lib/bolt/config/transport/orch.rb +9 -18
  52. data/lib/bolt/config/transport/remote.rb +3 -6
  53. data/lib/bolt/config/transport/ssh.rb +74 -154
  54. data/lib/bolt/config/transport/winrm.rb +18 -47
  55. data/lib/bolt/executor.rb +15 -0
  56. data/lib/bolt/inventory/group.rb +4 -3
  57. data/lib/bolt/inventory/inventory.rb +4 -17
  58. data/lib/bolt/inventory/target.rb +18 -5
  59. data/lib/bolt/logger.rb +24 -1
  60. data/lib/bolt/outputter.rb +1 -1
  61. data/lib/bolt/outputter/rainbow.rb +14 -3
  62. data/lib/bolt/pal.rb +31 -11
  63. data/lib/bolt/pal/yaml_plan/evaluator.rb +19 -2
  64. data/lib/bolt/pal/yaml_plan/step.rb +11 -2
  65. data/lib/bolt/pal/yaml_plan/step/download.rb +38 -0
  66. data/lib/bolt/pal/yaml_plan/step/upload.rb +3 -3
  67. data/lib/bolt/plugin/module.rb +2 -4
  68. data/lib/bolt/plugin/puppetdb.rb +3 -2
  69. data/lib/bolt/project.rb +41 -44
  70. data/lib/bolt/puppetdb/client.rb +2 -0
  71. data/lib/bolt/puppetdb/config.rb +16 -0
  72. data/lib/bolt/result.rb +7 -0
  73. data/lib/bolt/shell/bash.rb +53 -45
  74. data/lib/bolt/shell/powershell.rb +23 -12
  75. data/lib/bolt/shell/powershell/snippets.rb +15 -6
  76. data/lib/bolt/transport/base.rb +24 -0
  77. data/lib/bolt/transport/docker.rb +17 -5
  78. data/lib/bolt/transport/docker/connection.rb +20 -2
  79. data/lib/bolt/transport/local/connection.rb +14 -1
  80. data/lib/bolt/transport/orch.rb +20 -0
  81. data/lib/bolt/transport/simple.rb +6 -0
  82. data/lib/bolt/transport/ssh.rb +7 -1
  83. data/lib/bolt/transport/ssh/connection.rb +9 -1
  84. data/lib/bolt/transport/ssh/exec_connection.rb +23 -2
  85. data/lib/bolt/transport/winrm/connection.rb +109 -8
  86. data/lib/bolt/util.rb +26 -11
  87. data/lib/bolt/version.rb +1 -1
  88. data/lib/bolt_server/transport_app.rb +3 -2
  89. data/lib/bolt_spec/bolt_context.rb +7 -2
  90. data/lib/bolt_spec/plans.rb +15 -2
  91. data/lib/bolt_spec/plans/action_stubs.rb +2 -1
  92. data/lib/bolt_spec/plans/action_stubs/download_stub.rb +66 -0
  93. data/lib/bolt_spec/plans/mock_executor.rb +14 -1
  94. data/lib/bolt_spec/run.rb +22 -0
  95. data/libexec/bolt_catalog +3 -2
  96. metadata +20 -29
@@ -27,7 +27,7 @@ module Bolt
27
27
  end
28
28
  end
29
29
 
30
- def copy_file(source, dest)
30
+ def upload_file(source, dest)
31
31
  @logger.debug { "Uploading #{source}, to #{dest}" }
32
32
  if source.is_a?(StringIO)
33
33
  Tempfile.create(File.basename(dest)) do |f|
@@ -45,6 +45,19 @@ module Bolt
45
45
  raise Bolt::Node::FileError.new(message, 'COPY_ERROR')
46
46
  end
47
47
 
48
+ def download_file(source, dest, _download)
49
+ @logger.debug { "Downloading #{source} to #{dest}" }
50
+ # Create the destination directory for the target, or the
51
+ # copied file will have the target's name
52
+ FileUtils.mkdir_p(dest)
53
+ # Mimic the behavior of `cp --remove-destination`
54
+ # since the flag isn't supported on MacOS
55
+ FileUtils.cp_r(source, dest, remove_destination: true)
56
+ rescue StandardError => e
57
+ message = "Could not download file to #{dest}: #{e}"
58
+ raise Bolt::Node::FileError.new(message, 'DOWNLOAD_ERROR')
59
+ end
60
+
48
61
  def execute(command)
49
62
  if Bolt::Util.windows?
50
63
  # If it's already a powershell command then invoke it normally.
@@ -82,6 +82,10 @@ module Bolt
82
82
  end
83
83
 
84
84
  def batch_command(targets, command, options = {}, &callback)
85
+ if options[:env_vars] && !options[:env_vars].empty?
86
+ raise NotImplementedError, "pcp transport does not support setting environment variables"
87
+ end
88
+
85
89
  params = {
86
90
  'command' => command
87
91
  }
@@ -98,6 +102,10 @@ module Bolt
98
102
  end
99
103
 
100
104
  def batch_script(targets, script, arguments, options = {}, &callback)
105
+ if options[:env_vars] && !options[:env_vars].empty?
106
+ raise NotImplementedError, "pcp transport does not support setting environment variables"
107
+ end
108
+
101
109
  content = File.open(script, &:read)
102
110
  content = Base64.encode64(content)
103
111
  params = {
@@ -176,6 +184,18 @@ module Bolt
176
184
  end
177
185
  end
178
186
 
187
+ def batch_download(targets, *_args)
188
+ error = {
189
+ 'kind' => 'bolt/not-supported-error',
190
+ 'msg' => 'pcp transport does not support downloading files',
191
+ 'details' => {}
192
+ }
193
+
194
+ targets.map do |target|
195
+ Bolt::Result.new(target, error: error, action: 'download')
196
+ end
197
+ end
198
+
179
199
  def batches(targets)
180
200
  targets.group_by { |target| Connection.get_key(target.options) }.values
181
201
  end
@@ -32,6 +32,12 @@ module Bolt
32
32
  end
33
33
  end
34
34
 
35
+ def download(target, source, destination, options = {})
36
+ with_connection(target) do |conn|
37
+ conn.shell.download(source, destination, options)
38
+ end
39
+ end
40
+
35
41
  def run_script(target, script, arguments, options = {})
36
42
  with_connection(target) do |conn|
37
43
  conn.shell.run_script(script, arguments, options)
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'bolt/logger'
3
4
  require 'bolt/node/errors'
4
5
  require 'bolt/transport/simple'
5
6
 
@@ -21,7 +22,12 @@ module Bolt
21
22
  end
22
23
 
23
24
  def with_connection(target)
24
- conn = if target.transport_config['ssh-command']
25
+ if target.transport_config['ssh-command'] && !target.transport_config['native-ssh']
26
+ Bolt::Logger.warn_once("ssh-command and native-ssh conflict",
27
+ "native-ssh must be true to use ssh-command")
28
+ end
29
+
30
+ conn = if target.transport_config['native-ssh']
25
31
  ExecConnection.new(target)
26
32
  else
27
33
  Connection.new(target, @transport_logger)
@@ -235,7 +235,7 @@ module Bolt
235
235
  raise Bolt::Error.new(msg, 'bolt/too-many-files')
236
236
  end
237
237
 
238
- def copy_file(source, destination)
238
+ def upload_file(source, destination)
239
239
  # Do not log wrapper script content
240
240
  @logger.debug { "Uploading #{source}, to #{destination}" } unless source.is_a?(StringIO)
241
241
  @session.scp.upload!(source, destination, recursive: true)
@@ -243,6 +243,14 @@ module Bolt
243
243
  raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
244
244
  end
245
245
 
246
+ def download_file(source, destination, _download)
247
+ # Do not log wrapper script content
248
+ @logger.debug { "Downloading #{source} to #{destination}" }
249
+ @session.scp.download!(source, destination, recursive: true)
250
+ rescue StandardError => e
251
+ raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
252
+ end
253
+
246
254
  # This handles renaming Net::SSH verifiers between version 4.x and 5.x
247
255
  # of the gem
248
256
  def net_ssh_verifier(verifier)
@@ -58,14 +58,14 @@ module Bolt
58
58
  end
59
59
 
60
60
  def build_ssh_command(command)
61
- ssh_conf = @target.transport_config['ssh-command']
61
+ ssh_conf = @target.transport_config['ssh-command'] || 'ssh'
62
62
  ssh_cmd = Array(ssh_conf)
63
63
  ssh_cmd += ssh_opts
64
64
  ssh_cmd << userhost
65
65
  ssh_cmd << command
66
66
  end
67
67
 
68
- def copy_file(source, dest)
68
+ def upload_file(source, dest)
69
69
  @logger.debug { "Uploading #{source}, to #{userhost}:#{dest}" } unless source.is_a?(StringIO)
70
70
 
71
71
  cp_conf = @target.transport_config['copy-command'] || ["scp", "-r"]
@@ -94,6 +94,27 @@ module Bolt
94
94
  end
95
95
  end
96
96
 
97
+ def download_file(source, dest, _download)
98
+ @logger.debug { "Downloading #{userhost}:#{source} to #{dest}" }
99
+
100
+ FileUtils.mkdir_p(dest)
101
+
102
+ cp_conf = @target.transport_config['copy-command'] || ["scp", "-r"]
103
+ cp_cmd = Array(cp_conf)
104
+ cp_cmd += ssh_opts
105
+ cp_cmd << "#{userhost}:#{Shellwords.escape(source)}"
106
+ cp_cmd << dest
107
+
108
+ _, err, stat = Open3.capture3(*cp_cmd)
109
+
110
+ if stat.success?
111
+ @logger.debug "Successfully downloaded #{userhost}:#{source} to #{dest}"
112
+ else
113
+ message = "Could not copy file to #{dest}: #{err}"
114
+ raise Bolt::Node::FileError.new(message, 'COPY_ERROR')
115
+ end
116
+ end
117
+
97
118
  def execute(command)
98
119
  cmd_array = build_ssh_command(command)
99
120
  Open3.popen3(*cmd_array)
@@ -129,23 +129,23 @@ module Bolt
129
129
  raise
130
130
  end
131
131
 
132
- def copy_file(source, destination)
132
+ def upload_file(source, destination)
133
133
  @logger.debug { "Uploading #{source}, to #{destination}" }
134
134
  if target.options['file-protocol'] == 'smb'
135
- copy_file_smb(source, destination)
135
+ upload_file_smb(source, destination)
136
136
  else
137
- copy_file_winrm(source, destination)
137
+ upload_file_winrm(source, destination)
138
138
  end
139
139
  end
140
140
 
141
- def copy_file_winrm(source, destination)
141
+ def upload_file_winrm(source, destination)
142
142
  fs = ::WinRM::FS::FileManager.new(@connection)
143
143
  fs.upload(source, destination)
144
144
  rescue StandardError => e
145
145
  raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
146
146
  end
147
147
 
148
- def copy_file_smb(source, destination)
148
+ def upload_file_smb(source, destination)
149
149
  # lazy-load expensive gem code
150
150
  require 'ruby_smb'
151
151
 
@@ -165,7 +165,7 @@ module Bolt
165
165
  client = smb_client_login
166
166
  tree = client.tree_connect(path)
167
167
  begin
168
- copy_file_smb_recursive(tree, source, dest)
168
+ upload_file_smb_recursive(tree, source, dest)
169
169
  ensure
170
170
  tree.disconnect!
171
171
  end
@@ -175,6 +175,61 @@ module Bolt
175
175
  raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
176
176
  end
177
177
 
178
+ def download_file(source, destination, download)
179
+ @logger.debug { "Downloading #{source} to #{destination}" }
180
+ if target.options['file-protocol'] == 'smb'
181
+ download_file_smb(source, destination)
182
+ else
183
+ download_file_winrm(source, destination, download)
184
+ end
185
+ end
186
+
187
+ def download_file_winrm(source, destination, download)
188
+ # The winrm gem doesn't create the destination directory if it's missing,
189
+ # so create it here
190
+ FileUtils.mkdir_p(destination)
191
+ fs = ::WinRM::FS::FileManager.new(@connection)
192
+ # params: source, destination, chunksize, first
193
+ # first needs to be set to false, otherwise if the source is a directory it
194
+ # will be nested inside a directory with the same name
195
+ fs.download(source, download, 1024 * 1024, false)
196
+ rescue StandardError => e
197
+ raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
198
+ end
199
+
200
+ def download_file_smb(source, destination)
201
+ # lazy-load expensive gem code
202
+ require 'ruby_smb'
203
+
204
+ win_source = source.tr('/', '\\')
205
+ if (md = win_source.match(/^([a-z]):\\(.*)/i))
206
+ # if drive, use admin share for that drive, so path is '\\host\C$'
207
+ path = "\\\\#{@target.host}\\#{md[1]}$"
208
+ src = md[2]
209
+ elsif (md = win_source.match(/^(\\\\[^\\]+\\[^\\]+)\\(.*)/))
210
+ # if unc, path is '\\host\share'
211
+ path = md[1]
212
+ src = md[2]
213
+ else
214
+ raise ArgumentError, "Unknown source '#{source}'"
215
+ end
216
+
217
+ client = smb_client_login
218
+ tree = client.tree_connect(path)
219
+
220
+ begin
221
+ # Make sure the root download directory for the target exists
222
+ FileUtils.mkdir_p(destination)
223
+ download_file_smb_recursive(tree, src, destination)
224
+ ensure
225
+ tree.disconnect!
226
+ end
227
+ rescue ::RubySMB::Error::UnexpectedStatusCode => e
228
+ raise Bolt::Node::FileError.new("SMB Error: #{e.message}", 'DOWNLOAD_ERROR')
229
+ rescue StandardError => e
230
+ raise Bolt::Node::FileError.new(e.message, 'DOWNLOAD_ERROR')
231
+ end
232
+
178
233
  def shell
179
234
  @shell ||= Bolt::Shell::Powershell.new(target, self)
180
235
  end
@@ -230,13 +285,13 @@ module Bolt
230
285
  )
231
286
  end
232
287
 
233
- def copy_file_smb_recursive(tree, source, dest)
288
+ def upload_file_smb_recursive(tree, source, dest)
234
289
  if Dir.exist?(source)
235
290
  tree.open_directory(directory: dest, write: true, disposition: ::RubySMB::Dispositions::FILE_OPEN_IF)
236
291
 
237
292
  Dir.children(source).each do |child|
238
293
  child_dest = dest + '\\' + child
239
- copy_file_smb_recursive(tree, File.join(source, child), child_dest)
294
+ upload_file_smb_recursive(tree, File.join(source, child), child_dest)
240
295
  end
241
296
  return
242
297
  end
@@ -255,6 +310,52 @@ module Bolt
255
310
  file.close
256
311
  end
257
312
  end
313
+
314
+ def download_file_smb_recursive(tree, source, destination)
315
+ dest = File.expand_path(Bolt::Util.windows_basename(source), destination)
316
+
317
+ # Check if the source is a directory by attempting to list its children.
318
+ # If the source is a directory, create the directory on the host and then
319
+ # recurse through the children.
320
+ if (children = list_directory_children_smb(tree, source))
321
+ FileUtils.mkdir_p(dest)
322
+
323
+ children.each do |child|
324
+ # File names are encoded UTF_16LE.
325
+ filename = child.file_name.encode(Encoding::UTF_8)
326
+
327
+ next if %w[. ..].include?(filename)
328
+
329
+ src = source + '\\' + filename
330
+ download_file_smb_recursive(tree, src, dest)
331
+ end
332
+ # If the source wasn't a directory and just returns 'STATUS_NOT_A_DIRECTORY, then
333
+ # it is a file. Write it to the host.
334
+ else
335
+ begin
336
+ file = tree.open_file(filename: source)
337
+ data = file.read
338
+
339
+ # Files may be encoded UTF_16LE
340
+ data = data.encode(Encoding::UTF_8) if data.encoding == Encoding::UTF_16LE
341
+
342
+ File.write(dest, data)
343
+ ensure
344
+ file.close
345
+ end
346
+ end
347
+ end
348
+
349
+ # Lists the children of a directory using rb_smb
350
+ # Returns an array of RubySMB::Fscc::FileInformation::FileIdFullDirectoryInformation objects
351
+ # if the source is a directory, or raises RubySMB::Error::UnexpectedStatusCode otherwise.
352
+ def list_directory_children_smb(tree, source)
353
+ tree.list(directory: source)
354
+ rescue RubySMB::Error::UnexpectedStatusCode => e
355
+ unless e.message == 'STATUS_NOT_A_DIRECTORY'
356
+ raise e
357
+ end
358
+ end
258
359
  end
259
360
  end
260
361
  end
@@ -107,12 +107,13 @@ module Bolt
107
107
  # Accepts a Data object and returns a copy with all hash keys
108
108
  # modified by block. use &:to_s to stringify keys or &:to_sym to symbolize them
109
109
  def walk_keys(data, &block)
110
- if data.is_a? Hash
110
+ case data
111
+ when Hash
111
112
  data.each_with_object({}) do |(k, v), acc|
112
113
  v = walk_keys(v, &block)
113
114
  acc[yield(k)] = v
114
115
  end
115
- elsif data.is_a? Array
116
+ when Array
116
117
  data.map { |v| walk_keys(v, &block) }
117
118
  else
118
119
  data
@@ -124,9 +125,10 @@ module Bolt
124
125
  # their descendants are.
125
126
  def walk_vals(data, skip_top = false, &block)
126
127
  data = yield(data) unless skip_top
127
- if data.is_a? Hash
128
+ case data
129
+ when Hash
128
130
  data.transform_values { |v| walk_vals(v, &block) }
129
- elsif data.is_a? Array
131
+ when Array
130
132
  data.map { |v| walk_vals(v, &block) }
131
133
  else
132
134
  data
@@ -137,9 +139,10 @@ module Bolt
137
139
  # modified by the given block. Descendants are modified before their
138
140
  # parents.
139
141
  def postwalk_vals(data, skip_top = false, &block)
140
- new_data = if data.is_a? Hash
142
+ new_data = case data
143
+ when Hash
141
144
  data.transform_values { |v| postwalk_vals(v, &block) }
142
- elsif data.is_a? Array
145
+ when Array
143
146
  data.map { |v| postwalk_vals(v, &block) }
144
147
  else
145
148
  data
@@ -193,11 +196,12 @@ module Bolt
193
196
  cloned[obj.object_id] = cl
194
197
  cloned[cl.object_id] = cl
195
198
 
196
- if cl.is_a? Hash
199
+ case cl
200
+ when Hash
197
201
  obj.each { |k, v| cl[k] = deep_clone(v, cloned) }
198
- elsif cl.is_a? Array
202
+ when Array
199
203
  cl.collect! { |v| deep_clone(v, cloned) }
200
- elsif cl.is_a? Struct
204
+ when Struct
201
205
  obj.each_pair { |k, v| cl[k] = deep_clone(v, cloned) }
202
206
  end
203
207
 
@@ -257,14 +261,25 @@ module Bolt
257
261
 
258
262
  # Recursively searches a data structure for plugin references
259
263
  def references?(input)
260
- if input.is_a?(Hash)
264
+ case input
265
+ when Hash
261
266
  input.key?('_plugin') || input.values.any? { |v| references?(v) }
262
- elsif input.is_a?(Array)
267
+ when Array
263
268
  input.any? { |v| references?(v) }
264
269
  else
265
270
  false
266
271
  end
267
272
  end
273
+
274
+ def unix_basename(path)
275
+ raise Bolt::ValidationError, "path must be a String, received #{path.class} #{path}" unless path.is_a?(String)
276
+ path.split('/').last
277
+ end
278
+
279
+ def windows_basename(path)
280
+ raise Bolt::ValidationError, "path must be a String, received #{path.class} #{path}" unless path.is_a?(String)
281
+ path.split(%r{[/\\]}).last
282
+ end
268
283
  end
269
284
  end
270
285
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '2.15.0'
4
+ VERSION = '2.20.0'
5
5
  end
@@ -151,13 +151,14 @@ module BoltServer
151
151
  sha256 = file['sha256']
152
152
  kind = file['kind']
153
153
  path = File.join(cache_dir, relative_path)
154
- if kind == 'file'
154
+ case kind
155
+ when 'file'
155
156
  # The parent should already be created by `directory` entries,
156
157
  # but this is to be on the safe side.
157
158
  parent = File.dirname(path)
158
159
  FileUtils.mkdir_p(parent)
159
160
  @file_cache.serial_execute { @file_cache.download_file(path, sha256, uri) }
160
- elsif kind == 'directory'
161
+ when 'directory'
161
162
  # Create directory in cache so we can move files in.
162
163
  FileUtils.mkdir_p(path)
163
164
  else