bolt 2.42.0 → 2.44.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +12 -12
  3. data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +6 -8
  4. data/lib/bolt/analytics.rb +3 -2
  5. data/lib/bolt/applicator.rb +11 -1
  6. data/lib/bolt/bolt_option_parser.rb +20 -13
  7. data/lib/bolt/catalog.rb +10 -29
  8. data/lib/bolt/cli.rb +22 -32
  9. data/lib/bolt/config.rb +84 -82
  10. data/lib/bolt/config/options.rb +68 -0
  11. data/lib/bolt/config/transport/options.rb +7 -0
  12. data/lib/bolt/config/transport/orch.rb +1 -0
  13. data/lib/bolt/executor.rb +15 -5
  14. data/lib/bolt/inventory.rb +1 -1
  15. data/lib/bolt/inventory/group.rb +7 -4
  16. data/lib/bolt/logger.rb +114 -10
  17. data/lib/bolt/module_installer.rb +4 -2
  18. data/lib/bolt/module_installer/resolver.rb +59 -14
  19. data/lib/bolt/module_installer/specs/forge_spec.rb +8 -2
  20. data/lib/bolt/module_installer/specs/git_spec.rb +17 -2
  21. data/lib/bolt/outputter/human.rb +8 -4
  22. data/lib/bolt/outputter/rainbow.rb +3 -3
  23. data/lib/bolt/pal.rb +93 -14
  24. data/lib/bolt/pal/yaml_plan.rb +8 -2
  25. data/lib/bolt/pal/yaml_plan/evaluator.rb +2 -2
  26. data/lib/bolt/pal/yaml_plan/transpiler.rb +1 -0
  27. data/lib/bolt/plugin.rb +2 -2
  28. data/lib/bolt/plugin/cache.rb +7 -7
  29. data/lib/bolt/plugin/module.rb +1 -1
  30. data/lib/bolt/plugin/puppet_connect_data.rb +35 -0
  31. data/lib/bolt/plugin/puppetdb.rb +1 -1
  32. data/lib/bolt/project.rb +56 -43
  33. data/lib/bolt/rerun.rb +1 -1
  34. data/lib/bolt/shell/bash.rb +1 -1
  35. data/lib/bolt/shell/bash/tmpdir.rb +4 -1
  36. data/lib/bolt/shell/powershell.rb +2 -2
  37. data/lib/bolt/task.rb +1 -1
  38. data/lib/bolt/transport/docker/connection.rb +2 -2
  39. data/lib/bolt/transport/local.rb +1 -1
  40. data/lib/bolt/transport/orch/connection.rb +1 -1
  41. data/lib/bolt/transport/ssh.rb +1 -2
  42. data/lib/bolt/transport/ssh/connection.rb +1 -1
  43. data/lib/bolt/validator.rb +2 -2
  44. data/lib/bolt/version.rb +1 -1
  45. data/lib/bolt_server/config.rb +1 -1
  46. data/lib/bolt_server/transport_app.rb +2 -1
  47. data/libexec/bolt_catalog +1 -1
  48. metadata +9 -8
@@ -58,11 +58,32 @@ module Bolt
58
58
  "plan function or the `bolt apply` command.",
59
59
  type: Hash,
60
60
  properties: {
61
+ "evaltrace" => {
62
+ description: "Whether each resource should log when it is being evaluated.",
63
+ type: [TrueClass, FalseClass],
64
+ _example: true,
65
+ _default: false
66
+ },
67
+ "log_level" => {
68
+ description: "The log level for logs in apply reports from Puppet. These can be seen "\
69
+ "in ApplyResults.",
70
+ type: String,
71
+ enum: %w[debug info notice warning err alert emerg crit],
72
+ _example: "debug",
73
+ _default: "notice"
74
+ },
61
75
  "show_diff" => {
62
76
  description: "Whether to log and report a contextual diff.",
63
77
  type: [TrueClass, FalseClass],
64
78
  _example: true,
65
79
  _default: false
80
+ },
81
+ "trace" => {
82
+ description: "Whether to print stack traces on some errors. Will print internal Ruby "\
83
+ "stack trace interleaved with Puppet function frames.",
84
+ type: [TrueClass, FalseClass],
85
+ _example: true,
86
+ _default: false
66
87
  }
67
88
  },
68
89
  _plugin: false,
@@ -73,11 +94,33 @@ module Bolt
73
94
  "plan function or the `bolt apply` command.",
74
95
  type: Hash,
75
96
  properties: {
97
+ "evaltrace" => {
98
+ description: "Whether each resource should log when it is being evaluated. This allows "\
99
+ "you to interactively see exactly what is being done.",
100
+ type: [TrueClass, FalseClass],
101
+ _example: true,
102
+ _default: false
103
+ },
104
+ "log_level" => {
105
+ description: "The log level for logs in apply reports from Puppet. These can be seen "\
106
+ "in ApplyResults.",
107
+ type: String,
108
+ enum: %w[debug info notice warning err alert emerg crit],
109
+ _example: "debug",
110
+ _default: "notice"
111
+ },
76
112
  "show_diff" => {
77
113
  description: "Whether to log and report a contextual diff.",
78
114
  type: [TrueClass, FalseClass],
79
115
  _example: true,
80
116
  _default: false
117
+ },
118
+ "trace" => {
119
+ description: "Whether to print stack traces on some errors. Will print internal Ruby "\
120
+ "stack trace interleaved with Puppet function frames.",
121
+ type: [TrueClass, FalseClass],
122
+ _example: true,
123
+ _default: false
81
124
  }
82
125
  },
83
126
  _plugin: false
@@ -105,6 +148,17 @@ module Bolt
105
148
  _example: 50,
106
149
  _default: "100 or 1/7 the ulimit, whichever is lower."
107
150
  },
151
+ "disable-warnings" => {
152
+ description: "An array of IDs of warnings to suppress. Warnings with a matching ID will not be logged "\
153
+ "by Bolt. If you are upgrading Bolt to a new major version, you should re-enable all warnings "\
154
+ "until you have finished upgrading.",
155
+ type: Array,
156
+ items: {
157
+ type: String
158
+ },
159
+ _plugin: false,
160
+ _example: ["powershell_2"]
161
+ },
108
162
  "format" => {
109
163
  description: "The format to use when printing results.",
110
164
  type: String,
@@ -260,6 +314,10 @@ module Bolt
260
314
  description: "The name of the module.",
261
315
  type: String
262
316
  },
317
+ "resolve" => {
318
+ description: "Whether to resolve the module's dependencies when installing modules.",
319
+ type: [TrueClass, FalseClass]
320
+ },
263
321
  "version_requirement" => {
264
322
  description: "The version requirement for the module. Accepts a specific version (1.2.3), version "\
265
323
  "shorthand (1.2.x), or a version range (>= 1.2.0).",
@@ -274,9 +332,17 @@ module Bolt
274
332
  description: "The URL to the public git repository.",
275
333
  type: String
276
334
  },
335
+ "name" => {
336
+ description: "The name of the module. Required when `resolve` is `false`.",
337
+ type: String
338
+ },
277
339
  "ref" => {
278
340
  description: "The git reference to check out. Can be either a branch, tag, or commit SHA.",
279
341
  type: String
342
+ },
343
+ "resolve" => {
344
+ description: "Whether to resolve the module's dependencies when installing modules.",
345
+ type: [TrueClass, FalseClass]
280
346
  }
281
347
  }
282
348
  }
@@ -556,6 +622,7 @@ module Bolt
556
622
  color
557
623
  compile-concurrency
558
624
  concurrency
625
+ disable-warnings
559
626
  format
560
627
  inventory-config
561
628
  log
@@ -577,6 +644,7 @@ module Bolt
577
644
  color
578
645
  compile-concurrency
579
646
  concurrency
647
+ disable-warnings
580
648
  format
581
649
  hiera-config
582
650
  inventoryfile
@@ -253,6 +253,13 @@ module Bolt
253
253
  _plugin: true,
254
254
  _example: "jump.example.com"
255
255
  },
256
+ "read-timeout" => {
257
+ type: Integer,
258
+ description: "How long to wait in seconds when making requests to the Orchestrator.",
259
+ minimum: 1,
260
+ _plugin: true,
261
+ _example: 15
262
+ },
256
263
  "realm" => {
257
264
  type: String,
258
265
  description: "The Kerberos realm (Active Directory domain) to authenticate against.",
@@ -12,6 +12,7 @@ module Bolt
12
12
  host
13
13
  job-poll-interval
14
14
  job-poll-timeout
15
+ read-timeout
15
16
  service-url
16
17
  task-environment
17
18
  token-file
@@ -100,6 +100,8 @@ module Bolt
100
100
  # that type of event, publish the event
101
101
  next unless types.nil? || types.include?(event[:type])
102
102
  @publisher.post(subscriber) do |sub|
103
+ # Wait for user to input to prompt before printing anything
104
+ sleep(0.1) while @prompting
103
105
  sub.handle_event(event)
104
106
  end
105
107
  end
@@ -119,11 +121,12 @@ module Bolt
119
121
  def queue_execute(targets)
120
122
  if @warn_concurrency && targets.length > @concurrency
121
123
  @warn_concurrency = false
122
- @logger.warn("The ulimit is low, which may cause file limit issues. Default concurrency has been set to "\
123
- "'#{@concurrency}' to mitigate those issues, which may cause Bolt to run slow. "\
124
- "Disable this warning by configuring ulimit using 'ulimit -n <limit>' in your shell "\
125
- "configuration, or by configuring Bolt's concurrency. "\
126
- "See https://puppet.com/docs/bolt/latest/bolt_known_issues.html for details.")
124
+ msg = "The ulimit is low, which may cause file limit issues. Default concurrency has been set to "\
125
+ "'#{@concurrency}' to mitigate those issues, which may cause Bolt to run slow. "\
126
+ "Disable this warning by configuring ulimit using 'ulimit -n <limit>' in your shell "\
127
+ "configuration, or by configuring Bolt's concurrency. "\
128
+ "See https://puppet.com/docs/bolt/latest/bolt_known_issues.html for details."
129
+ Bolt::Logger.warn("low_ulimit", msg)
127
130
  end
128
131
 
129
132
  targets.group_by(&:transport).flat_map do |protocol, protocol_targets|
@@ -258,7 +261,9 @@ module Bolt
258
261
 
259
262
  def with_node_logging(description, batch, log_level = :info)
260
263
  @logger.send(log_level, "#{description} on #{batch.map(&:safe_name)}")
264
+ publish_event(type: :start_spin)
261
265
  result = yield
266
+ publish_event(type: :stop_spin)
262
267
  @logger.send(log_level, result.to_json)
263
268
  result
264
269
  end
@@ -410,6 +415,7 @@ module Bolt
410
415
  subscribe(self, [:node_result])
411
416
  results = Array.new(skein.length)
412
417
  @in_parallel = true
418
+ publish_event(type: :stop_spin)
413
419
 
414
420
  until skein.empty?
415
421
  @thread_completed = false
@@ -417,6 +423,7 @@ module Bolt
417
423
 
418
424
  skein.each do |yarn|
419
425
  if yarn.alive?
426
+ publish_event(type: :stop_spin)
420
427
  r = yarn.resume
421
428
  else
422
429
  results[yarn.index] = yarn.value
@@ -428,6 +435,7 @@ module Bolt
428
435
  sleep(0.1) until @thread_completed || skein.empty?
429
436
  end
430
437
 
438
+ publish_event(type: :stop_spin)
431
439
  @in_parallel = false
432
440
  unsubscribe(self, [:node_result])
433
441
  results
@@ -469,6 +477,7 @@ module Bolt
469
477
  end
470
478
 
471
479
  def prompt(prompt, options)
480
+ @prompting = true
472
481
  unless $stdin.tty?
473
482
  raise Bolt::Error.new('STDIN is not a tty, unable to prompt', 'bolt/no-tty-error')
474
483
  end
@@ -480,6 +489,7 @@ module Bolt
480
489
  else
481
490
  $stdin.gets.to_s.chomp
482
491
  end
492
+ @prompting = false
483
493
 
484
494
  $stderr.puts if options[:sensitive]
485
495
 
@@ -97,7 +97,7 @@ module Bolt
97
97
 
98
98
  Bolt::Validator.new.tap do |validator|
99
99
  validator.validate(data, schema, source)
100
- validator.warnings.each { |warning| logger.warn(warning) }
100
+ validator.warnings.each { |warning| Bolt::Logger.warn(warning[:id], warning[:msg]) }
101
101
  end
102
102
 
103
103
  inventory = create_version(data, config.transport, config.transports, plugins)
@@ -27,7 +27,10 @@ module Bolt
27
27
 
28
28
  if all_group
29
29
  if input.key?('name') && input['name'] != 'all'
30
- @logger.warn("Top-level group '#{input['name']}' cannot specify a name, using 'all' instead.")
30
+ Bolt::Logger.warn(
31
+ "top_level_group_name",
32
+ "Top-level group '#{input['name']}' cannot specify a name, using 'all' instead."
33
+ )
31
34
  end
32
35
 
33
36
  input = input.merge('name' => 'all')
@@ -134,7 +137,7 @@ module Bolt
134
137
 
135
138
  unless (unexpected_keys = target.keys - TARGET_KEYS).empty?
136
139
  msg = "Found unexpected key(s) #{unexpected_keys.join(', ')} in target #{t_name}"
137
- @logger.warn(msg)
140
+ Bolt::Logger.warn("unknown_target_keys", msg)
138
141
  end
139
142
 
140
143
  validate_data_keys(target, t_name)
@@ -261,7 +264,7 @@ module Bolt
261
264
 
262
265
  unless (unexpected_keys = input.keys - GROUP_KEYS).empty?
263
266
  msg = "Found unexpected key(s) #{unexpected_keys.join(', ')} in group #{@name}"
264
- @logger.warn(msg)
267
+ Bolt::Logger.warn("unknown_group_keys", msg)
265
268
  end
266
269
  end
267
270
 
@@ -368,7 +371,7 @@ module Bolt
368
371
  msg = +"Found unexpected key(s) #{unexpected_keys.join(', ')} in config for"
369
372
  msg << " target #{target} in" if target
370
373
  msg << " group #{@name}"
371
- @logger.warn(msg)
374
+ Bolt::Logger.warn("unknown_config_keys", msg)
372
375
  end
373
376
  end
374
377
  end
@@ -5,8 +5,14 @@ require 'logging'
5
5
  module Bolt
6
6
  module Logger
7
7
  LEVELS = %w[trace debug info notice warn error fatal].freeze
8
- @mutex = Mutex.new
9
- @warnings = Set.new
8
+
9
+ # This module is treated as a global singleton so that multiple classes
10
+ # in Bolt can log warnings with IDs. Access to the following variables
11
+ # are controlled by a mutex.
12
+ @mutex = Mutex.new
13
+ @warnings = Set.new
14
+ @disable_warnings = Set.new
15
+ @message_queue = []
10
16
 
11
17
  # This method provides a single point-of-entry to setup logging for both
12
18
  # the CLI and for tests. This is necessary because we define custom log
@@ -36,7 +42,7 @@ module Bolt
36
42
  end
37
43
  end
38
44
 
39
- def self.configure(destinations, color)
45
+ def self.configure(destinations, color, disable_warnings = nil)
40
46
  root_logger = Bolt::Logger.logger(:root)
41
47
 
42
48
  root_logger.add_appenders Logging.appenders.stderr(
@@ -73,6 +79,16 @@ module Bolt
73
79
 
74
80
  appender.level = params[:level] if params[:level]
75
81
  end
82
+
83
+ # Set the list of disabled warnings and mark the logger as configured.
84
+ # Log all messages in the message queue and flush the queue.
85
+ if disable_warnings
86
+ @mutex.synchronize { @disable_warnings = disable_warnings }
87
+ end
88
+ end
89
+
90
+ def self.configured?
91
+ Logging.logger[:root].appenders.any?
76
92
  end
77
93
 
78
94
  # A helper to ensure the Logging library is always initialized with our
@@ -123,18 +139,106 @@ module Bolt
123
139
  Logging.reset
124
140
  end
125
141
 
126
- def self.warn_once(type, msg)
142
+ # The following methods are used in place of the Logging.logger
143
+ # methods of the same name when logging warning messages or logging
144
+ # any messages prior to the logger being configured. If the logger
145
+ # is not configured when any of these methods are called, the message
146
+ # will be added to a queue, otherwise they are logged immediately.
147
+ # The message queue is flushed by calling #flush_queue, which is
148
+ # called from Bolt::CLI after configuring the logger.
149
+ #
150
+ def self.warn(id, msg)
151
+ log(type: :warn, msg: "#{msg} [ID: #{id}]", id: id)
152
+ end
153
+
154
+ def self.warn_once(id, msg)
155
+ log(type: :warn_once, msg: "#{msg} [ID: #{id}]", id: id)
156
+ end
157
+
158
+ def self.deprecate(id, msg)
159
+ log(type: :deprecate, msg: "#{msg} [ID: #{id}]", id: id)
160
+ end
161
+
162
+ def self.deprecate_once(id, msg)
163
+ log(type: :deprecate_once, msg: "#{msg} [ID: #{id}]", id: id)
164
+ end
165
+
166
+ def self.debug(msg)
167
+ log(type: :debug, msg: msg)
168
+ end
169
+
170
+ def self.info(msg)
171
+ log(type: :info, msg: msg)
172
+ end
173
+
174
+ # Logs a message. If the logger has not been configured, this will queue
175
+ # the message to be logged later. Once the logger is configured, the
176
+ # queue will be flushed of all messages and new messages will be logged
177
+ # immediately.
178
+ #
179
+ # Logging with this method is controlled by a mutex, as the Bolt::Logger
180
+ # module is treated as a global singleton to allow multiple classes
181
+ # access to its methods.
182
+ #
183
+ private_class_method def self.log(type:, msg:, id: nil)
184
+ @mutex.synchronize do
185
+ if configured?
186
+ log_message(type: type, msg: msg, id: id)
187
+ else
188
+ @message_queue << { type: type, msg: msg, id: id }
189
+ end
190
+ end
191
+ end
192
+
193
+ # Logs all messages in the message queue and then flushes the queue.
194
+ #
195
+ def self.flush_queue
127
196
  @mutex.synchronize do
128
- @logger ||= Bolt::Logger.logger(self)
129
- if @warnings.add?(type)
130
- @logger.warn(msg)
197
+ @message_queue.each do |message|
198
+ log_message(message)
131
199
  end
200
+
201
+ @message_queue.clear
202
+ end
203
+ end
204
+
205
+ # Handles the actual logging of a message.
206
+ #
207
+ private_class_method def self.log_message(type:, msg:, id: nil)
208
+ case type
209
+ when :warn
210
+ do_warn(msg, id)
211
+ when :warn_once
212
+ do_warn_once(msg, id)
213
+ when :deprecate
214
+ do_deprecate(msg, id)
215
+ when :deprecate_once
216
+ do_deprecate_once(msg, id)
217
+ else
218
+ logger(self).send(type, msg)
132
219
  end
133
220
  end
134
221
 
135
- def self.deprecation_warning(type, msg)
136
- @analytics&.event('Warn', 'deprecation', label: type)
137
- warn_once(type, msg)
222
+ # The following methods do the actual warning.
223
+ #
224
+ private_class_method def self.do_warn(msg, id)
225
+ return if @disable_warnings.include?(id)
226
+ logger(self).warn(msg)
227
+ end
228
+
229
+ private_class_method def self.do_warn_once(msg, id)
230
+ return unless @warnings.add?(id)
231
+ do_warn(msg, id)
232
+ end
233
+
234
+ private_class_method def self.do_deprecate(msg, id)
235
+ @analytics&.event('Warn', 'deprecation', label: id)
236
+ do_warn(msg, id)
237
+ end
238
+
239
+ private_class_method def self.do_deprecate_once(msg, id)
240
+ @analytics&.event('Warn', 'deprecation', label: id)
241
+ do_warn_once(msg, id)
138
242
  end
139
243
  end
140
244
  end
@@ -195,8 +195,10 @@ module Bolt
195
195
  @outputter.stop_spin
196
196
 
197
197
  # Automatically generate types after installing modules
198
- @outputter.print_action_step("Generating type references")
199
- @pal.generate_types
198
+ if ok
199
+ @outputter.print_action_step("Generating type references")
200
+ @pal.generate_types(cache: true)
201
+ end
200
202
 
201
203
  @outputter.print_puppetfile_result(ok, path, moduledir)
202
204
 
@@ -13,10 +13,15 @@ module Bolt
13
13
  require 'puppetfile-resolver'
14
14
 
15
15
  # Build the document model from the specs.
16
- document = PuppetfileResolver::Puppetfile::Document.new('')
16
+ document = PuppetfileResolver::Puppetfile::Document.new('')
17
+ unresolved = []
17
18
 
18
19
  specs.specs.each do |spec|
19
- document.add_module(spec.to_resolver_module)
20
+ if spec.resolve
21
+ document.add_module(spec.to_resolver_module)
22
+ else
23
+ unresolved << spec
24
+ end
20
25
  end
21
26
 
22
27
  # Make sure the document model is valid.
@@ -47,20 +52,40 @@ module Bolt
47
52
  raise Bolt::Error.new(e.message, 'bolt/module-resolver-error')
48
53
  end
49
54
 
50
- # Convert the specs returned from the resolver into Bolt module objects.
51
- modules = result.specifications.values.each_with_object([]) do |mod, acc|
55
+ # Create the Puppetfile object.
56
+ generate_puppetfile(specs, result.specifications.values, unresolved)
57
+ end
58
+
59
+ # Creates a puppetfile-resolver config object.
60
+ #
61
+ private def spec_searcher_config(config)
62
+ PuppetfileResolver::SpecSearchers::Configuration.new.tap do |obj|
63
+ obj.forge.proxy = config.dig('forge', 'proxy') || config.dig('proxy')
64
+ obj.git.proxy = config.dig('proxy')
65
+ obj.forge.forge_api = config.dig('forge', 'baseurl')
66
+ end
67
+ end
68
+
69
+ # Creates a Puppetfile object with Module objects created from resolved and
70
+ # unresolved specs.
71
+ #
72
+ private def generate_puppetfile(specs, resolved, unresolved)
73
+ modules = []
74
+
75
+ # Convert the resolved specs into Bolt module objects.
76
+ resolved.each do |mod|
52
77
  # Skip over anything that isn't a module spec, such as a Puppet spec.
53
78
  next unless mod.is_a? PuppetfileResolver::Models::ModuleSpecification
54
79
 
55
80
  case mod.origin
56
81
  when :forge
57
- acc << Bolt::ModuleInstaller::Puppetfile::ForgeModule.new(
82
+ modules << Bolt::ModuleInstaller::Puppetfile::ForgeModule.new(
58
83
  "#{mod.owner}/#{mod.name}",
59
84
  mod.version.to_s
60
85
  )
61
86
  when :git
62
87
  spec = specs.specs.find { |s| s.name == mod.name }
63
- acc << Bolt::ModuleInstaller::Puppetfile::GitModule.new(
88
+ modules << Bolt::ModuleInstaller::Puppetfile::GitModule.new(
64
89
  spec.name,
65
90
  spec.git,
66
91
  spec.sha
@@ -68,16 +93,36 @@ module Bolt
68
93
  end
69
94
  end
70
95
 
71
- # Create the Puppetfile object.
72
- Bolt::ModuleInstaller::Puppetfile.new(modules)
73
- end
96
+ # Error if there are any name conflicts between unresolved specs and
97
+ # resolved modules. r10k will error if a Puppetfile includes duplicate
98
+ # names, but we error early here to provide a more helpful message.
99
+ if (name_conflicts = modules.map(&:name) & unresolved.map(&:name)).any?
100
+ raise Bolt::Error.new(
101
+ "Detected unresolved module specifications with the same name as a resolved module "\
102
+ "dependency: #{name_conflicts.join(', ')}. Either remove the unresolved module specification "\
103
+ "or set the module with the conflicting dependency to not resolve.",
104
+ "bolt/module-name-conflict-error"
105
+ )
106
+ end
74
107
 
75
- private def spec_searcher_config(config)
76
- PuppetfileResolver::SpecSearchers::Configuration.new.tap do |obj|
77
- obj.forge.proxy = config.dig('forge', 'proxy') || config.dig('proxy')
78
- obj.git.proxy = config.dig('proxy')
79
- obj.forge.forge_api = config.dig('forge', 'baseurl')
108
+ # Convert the unresolved specs into Bolt module objects.
109
+ unresolved.each do |spec|
110
+ case spec.type
111
+ when :forge
112
+ modules << Bolt::ModuleInstaller::Puppetfile::ForgeModule.new(
113
+ spec.full_name,
114
+ spec.version_requirement
115
+ )
116
+ when :git
117
+ modules << Bolt::ModuleInstaller::Puppetfile::GitModule.new(
118
+ spec.name,
119
+ spec.git,
120
+ spec.ref
121
+ )
122
+ end
80
123
  end
124
+
125
+ Bolt::ModuleInstaller::Puppetfile.new(modules)
81
126
  end
82
127
  end
83
128
  end