bolt 2.38.0 → 3.0.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.
- checksums.yaml +4 -4
- data/Puppetfile +17 -17
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +25 -0
- data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +6 -8
- data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +7 -3
- data/lib/bolt/analytics.rb +3 -2
- data/lib/bolt/applicator.rb +11 -1
- data/lib/bolt/bolt_option_parser.rb +3 -113
- data/lib/bolt/catalog.rb +10 -29
- data/lib/bolt/cli.rb +54 -155
- data/lib/bolt/config.rb +63 -269
- data/lib/bolt/config/options.rb +59 -97
- data/lib/bolt/config/transport/local.rb +1 -0
- data/lib/bolt/config/transport/options.rb +10 -2
- data/lib/bolt/config/transport/orch.rb +1 -0
- data/lib/bolt/config/transport/ssh.rb +0 -5
- data/lib/bolt/executor.rb +15 -5
- data/lib/bolt/inventory.rb +3 -2
- data/lib/bolt/inventory/group.rb +35 -12
- data/lib/bolt/inventory/inventory.rb +1 -1
- data/lib/bolt/logger.rb +115 -11
- data/lib/bolt/module.rb +10 -2
- data/lib/bolt/module_installer.rb +4 -2
- data/lib/bolt/module_installer/resolver.rb +65 -12
- data/lib/bolt/module_installer/specs/forge_spec.rb +8 -2
- data/lib/bolt/module_installer/specs/git_spec.rb +17 -2
- data/lib/bolt/outputter/human.rb +9 -5
- data/lib/bolt/outputter/json.rb +16 -16
- data/lib/bolt/outputter/rainbow.rb +3 -3
- data/lib/bolt/pal.rb +93 -14
- data/lib/bolt/pal/yaml_plan.rb +8 -2
- data/lib/bolt/pal/yaml_plan/evaluator.rb +7 -19
- data/lib/bolt/pal/yaml_plan/step.rb +3 -24
- data/lib/bolt/pal/yaml_plan/step/upload.rb +2 -2
- data/lib/bolt/pal/yaml_plan/transpiler.rb +6 -1
- data/lib/bolt/plugin.rb +3 -3
- data/lib/bolt/plugin/cache.rb +8 -8
- data/lib/bolt/plugin/module.rb +0 -23
- data/lib/bolt/plugin/puppet_connect_data.rb +77 -0
- data/lib/bolt/plugin/puppetdb.rb +1 -1
- data/lib/bolt/project.rb +54 -81
- data/lib/bolt/project_manager.rb +4 -3
- data/lib/bolt/project_manager/module_migrator.rb +6 -5
- data/lib/bolt/rerun.rb +1 -1
- data/lib/bolt/shell/bash.rb +1 -1
- data/lib/bolt/shell/bash/tmpdir.rb +4 -1
- data/lib/bolt/shell/powershell.rb +3 -4
- data/lib/bolt/shell/powershell/snippets.rb +9 -149
- data/lib/bolt/task.rb +1 -1
- data/lib/bolt/transport/docker/connection.rb +2 -2
- data/lib/bolt/transport/local.rb +1 -9
- data/lib/bolt/transport/orch/connection.rb +1 -1
- data/lib/bolt/transport/ssh.rb +1 -2
- data/lib/bolt/transport/ssh/connection.rb +1 -1
- data/lib/bolt/validator.rb +16 -15
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/config.rb +1 -1
- data/lib/bolt_server/schemas/partials/task.json +1 -1
- data/lib/bolt_server/transport_app.rb +3 -2
- data/libexec/bolt_catalog +1 -1
- data/modules/aggregate/plans/count.pp +21 -0
- data/modules/aggregate/plans/targets.pp +21 -0
- data/modules/puppet_connect/plans/test_input_data.pp +31 -0
- data/modules/puppetdb_fact/plans/init.pp +10 -0
- metadata +26 -17
- data/modules/aggregate/plans/nodes.pp +0 -36
data/lib/bolt/inventory/group.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'bolt/config/options'
|
3
4
|
require 'bolt/inventory/group'
|
4
5
|
require 'bolt/inventory/inventory'
|
5
6
|
require 'bolt/inventory/target'
|
@@ -18,12 +19,23 @@ module Bolt
|
|
18
19
|
GROUP_KEYS = DATA_KEYS + %w[name groups targets]
|
19
20
|
CONFIG_KEYS = Bolt::Config::INVENTORY_OPTIONS.keys
|
20
21
|
|
21
|
-
def initialize(input, plugins)
|
22
|
+
def initialize(input, plugins, all_group: false)
|
22
23
|
@logger = Bolt::Logger.logger(self)
|
23
24
|
@plugins = plugins
|
24
25
|
|
25
26
|
input = @plugins.resolve_top_level_references(input) if @plugins.reference?(input)
|
26
27
|
|
28
|
+
if all_group
|
29
|
+
if input.key?('name') && input['name'] != 'all'
|
30
|
+
Bolt::Logger.warn(
|
31
|
+
"top_level_group_name",
|
32
|
+
"Top-level group '#{input['name']}' cannot specify a name, using 'all' instead."
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
input = input.merge('name' => 'all')
|
37
|
+
end
|
38
|
+
|
27
39
|
raise ValidationError.new("Group does not have a name", nil) unless input.key?('name')
|
28
40
|
|
29
41
|
@name = @plugins.resolve_references(input['name'])
|
@@ -125,7 +137,7 @@ module Bolt
|
|
125
137
|
|
126
138
|
unless (unexpected_keys = target.keys - TARGET_KEYS).empty?
|
127
139
|
msg = "Found unexpected key(s) #{unexpected_keys.join(', ')} in target #{t_name}"
|
128
|
-
|
140
|
+
Bolt::Logger.warn("unknown_target_keys", msg)
|
129
141
|
end
|
130
142
|
|
131
143
|
validate_data_keys(target, t_name)
|
@@ -252,15 +264,7 @@ module Bolt
|
|
252
264
|
|
253
265
|
unless (unexpected_keys = input.keys - GROUP_KEYS).empty?
|
254
266
|
msg = "Found unexpected key(s) #{unexpected_keys.join(', ')} in group #{@name}"
|
255
|
-
|
256
|
-
end
|
257
|
-
|
258
|
-
Bolt::Util.walk_keys(input) do |key|
|
259
|
-
if @plugins.reference?(key)
|
260
|
-
raise ValidationError.new("Group keys cannot be specified as _plugin references", @name)
|
261
|
-
else
|
262
|
-
key
|
263
|
-
end
|
267
|
+
Bolt::Logger.warn("unknown_group_keys", msg)
|
264
268
|
end
|
265
269
|
end
|
266
270
|
|
@@ -323,7 +327,26 @@ module Bolt
|
|
323
327
|
'features' => @plugins.resolve_references(data.fetch('features', [])),
|
324
328
|
'plugin_hooks' => @plugins.resolve_references(data.fetch('plugin_hooks', {}))
|
325
329
|
}
|
330
|
+
|
326
331
|
validate_data_keys(result, target)
|
332
|
+
|
333
|
+
Bolt::Config::Options::TRANSPORT_CONFIG.each_key do |transport|
|
334
|
+
next unless result['config'].key?(transport)
|
335
|
+
transport_config = result['config'][transport]
|
336
|
+
next unless transport_config.is_a?(Hash)
|
337
|
+
transport_config = Bolt::Util.postwalk_vals(transport_config) do |val|
|
338
|
+
if val.is_a?(Hash)
|
339
|
+
val = val.compact
|
340
|
+
val = nil if val.empty?
|
341
|
+
end
|
342
|
+
val
|
343
|
+
end
|
344
|
+
# the transport config is user-specified data so we
|
345
|
+
# still want to preserve it even if it exclusively
|
346
|
+
# contains nil-resolved keys
|
347
|
+
result['config'][transport] = transport_config || {}
|
348
|
+
end
|
349
|
+
|
327
350
|
result['features'] = Set.new(result['features'].flatten)
|
328
351
|
result
|
329
352
|
end
|
@@ -348,7 +371,7 @@ module Bolt
|
|
348
371
|
msg = +"Found unexpected key(s) #{unexpected_keys.join(', ')} in config for"
|
349
372
|
msg << " target #{target} in" if target
|
350
373
|
msg << " group #{@name}"
|
351
|
-
|
374
|
+
Bolt::Logger.warn("unknown_config_keys", msg)
|
352
375
|
end
|
353
376
|
end
|
354
377
|
end
|
data/lib/bolt/logger.rb
CHANGED
@@ -4,9 +4,15 @@ require 'logging'
|
|
4
4
|
|
5
5
|
module Bolt
|
6
6
|
module Logger
|
7
|
-
LEVELS = %w[trace debug info
|
8
|
-
|
9
|
-
|
7
|
+
LEVELS = %w[trace debug info warn error fatal].freeze
|
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
|
-
|
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
|
-
@
|
129
|
-
|
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
|
-
|
136
|
-
|
137
|
-
|
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
|
data/lib/bolt/module.rb
CHANGED
@@ -6,8 +6,14 @@ module Bolt
|
|
6
6
|
CONTENT_NAME_REGEX = /\A[a-z][a-z0-9_]*(::[a-z][a-z0-9_]*)*\z/.freeze
|
7
7
|
MODULE_NAME_REGEX = /\A[a-z][a-z0-9_]*\z/.freeze
|
8
8
|
|
9
|
-
def self.discover(modulepath)
|
10
|
-
|
9
|
+
def self.discover(modulepath, project)
|
10
|
+
mods = {}
|
11
|
+
|
12
|
+
if project.load_as_module?
|
13
|
+
mods[project.name] = Bolt::Module.new(project.name, project.path.to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
modulepath.each do |path|
|
11
17
|
next unless File.exist?(path) && File.directory?(path)
|
12
18
|
Dir.children(path)
|
13
19
|
.map { |dir| File.join(path, dir) }
|
@@ -20,6 +26,8 @@ module Bolt
|
|
20
26
|
end
|
21
27
|
end
|
22
28
|
end
|
29
|
+
|
30
|
+
mods
|
23
31
|
end
|
24
32
|
|
25
33
|
attr_reader :name, :path
|
@@ -195,8 +195,10 @@ module Bolt
|
|
195
195
|
@outputter.stop_spin
|
196
196
|
|
197
197
|
# Automatically generate types after installing modules
|
198
|
-
|
199
|
-
|
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
|
|
@@ -9,14 +9,19 @@ module Bolt
|
|
9
9
|
class Resolver
|
10
10
|
# Resolves module specs and returns a Puppetfile object.
|
11
11
|
#
|
12
|
-
def resolve(specs,
|
12
|
+
def resolve(specs, config = {})
|
13
13
|
require 'puppetfile-resolver'
|
14
14
|
|
15
15
|
# Build the document model from the specs.
|
16
|
-
document
|
16
|
+
document = PuppetfileResolver::Puppetfile::Document.new('')
|
17
|
+
unresolved = []
|
17
18
|
|
18
19
|
specs.specs.each do |spec|
|
19
|
-
|
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.
|
@@ -38,29 +43,49 @@ module Bolt
|
|
38
43
|
# raised by puppetfile-resolver and re-raising them as Bolt errors.
|
39
44
|
begin
|
40
45
|
result = resolver.resolve(
|
41
|
-
cache:
|
42
|
-
ui:
|
43
|
-
|
44
|
-
|
46
|
+
cache: nil,
|
47
|
+
ui: nil,
|
48
|
+
allow_missing_modules: false,
|
49
|
+
spec_searcher_configuration: spec_searcher_config(config)
|
45
50
|
)
|
46
51
|
rescue StandardError => e
|
47
52
|
raise Bolt::Error.new(e.message, 'bolt/module-resolver-error')
|
48
53
|
end
|
49
54
|
|
50
|
-
#
|
51
|
-
|
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
|
-
|
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
|
-
|
88
|
+
modules << Bolt::ModuleInstaller::Puppetfile::GitModule.new(
|
64
89
|
spec.name,
|
65
90
|
spec.git,
|
66
91
|
spec.sha
|
@@ -68,7 +93,35 @@ module Bolt
|
|
68
93
|
end
|
69
94
|
end
|
70
95
|
|
71
|
-
#
|
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
|
107
|
+
|
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
|
123
|
+
end
|
124
|
+
|
72
125
|
Bolt::ModuleInstaller::Puppetfile.new(modules)
|
73
126
|
end
|
74
127
|
end
|
@@ -13,14 +13,20 @@ module Bolt
|
|
13
13
|
class ForgeSpec
|
14
14
|
NAME_REGEX = %r{\A[a-zA-Z0-9]+[-/](?<name>[a-z][a-z0-9_]*)\z}.freeze
|
15
15
|
REQUIRED_KEYS = Set.new(%w[name]).freeze
|
16
|
-
KNOWN_KEYS = Set.new(%w[name version_requirement]).freeze
|
16
|
+
KNOWN_KEYS = Set.new(%w[name resolve version_requirement]).freeze
|
17
17
|
|
18
|
-
attr_reader :full_name, :name, :semantic_version, :type
|
18
|
+
attr_reader :full_name, :name, :resolve, :semantic_version, :type, :version_requirement
|
19
19
|
|
20
20
|
def initialize(init_hash)
|
21
|
+
@resolve = init_hash.key?('resolve') ? init_hash['resolve'] : true
|
21
22
|
@full_name, @name = parse_name(init_hash['name'])
|
22
23
|
@version_requirement, @semantic_version = parse_version_requirement(init_hash['version_requirement'])
|
23
24
|
@type = :forge
|
25
|
+
|
26
|
+
unless @resolve == true || @resolve == false
|
27
|
+
raise Bolt::ValidationError,
|
28
|
+
"Option 'resolve' for module spec #{@full_name} must be a Boolean"
|
29
|
+
end
|
24
30
|
end
|
25
31
|
|
26
32
|
def self.implements?(hash)
|
@@ -13,18 +13,31 @@ module Bolt
|
|
13
13
|
class GitSpec
|
14
14
|
NAME_REGEX = %r{\A(?:[a-zA-Z0-9]+[-/])?(?<name>[a-z][a-z0-9_]*)\z}.freeze
|
15
15
|
REQUIRED_KEYS = Set.new(%w[git ref]).freeze
|
16
|
+
KNOWN_KEYS = Set.new(%w[git name ref resolve]).freeze
|
16
17
|
|
17
|
-
attr_reader :git, :ref, :type
|
18
|
+
attr_reader :git, :ref, :resolve, :type
|
18
19
|
|
19
20
|
def initialize(init_hash)
|
21
|
+
@resolve = init_hash.key?('resolve') ? init_hash['resolve'] : true
|
20
22
|
@name = parse_name(init_hash['name'])
|
21
23
|
@git, @repo = parse_git(init_hash['git'])
|
22
24
|
@ref = init_hash['ref']
|
23
25
|
@type = :git
|
26
|
+
|
27
|
+
if @name.nil? && @resolve == false
|
28
|
+
raise Bolt::ValidationError,
|
29
|
+
"Missing name for Git module specification: #{@git}. Git module specifications "\
|
30
|
+
"must include a 'name' key when 'resolve' is false."
|
31
|
+
end
|
32
|
+
|
33
|
+
unless @resolve == true || @resolve == false
|
34
|
+
raise Bolt::ValidationError,
|
35
|
+
"Option 'resolve' for module spec #{@git} must be a Boolean"
|
36
|
+
end
|
24
37
|
end
|
25
38
|
|
26
39
|
def self.implements?(hash)
|
27
|
-
|
40
|
+
KNOWN_KEYS.superset?(hash.keys.to_set) && REQUIRED_KEYS.subset?(hash.keys.to_set)
|
28
41
|
end
|
29
42
|
|
30
43
|
# Parses the name into owner and name segments, and formats the full
|
@@ -47,6 +60,8 @@ module Bolt
|
|
47
60
|
# Gets the repo from the git URL.
|
48
61
|
#
|
49
62
|
private def parse_git(git)
|
63
|
+
return [git, nil] unless @resolve
|
64
|
+
|
50
65
|
repo = if git.start_with?('git@github.com:')
|
51
66
|
git.split('git@github.com:').last.split('.git').first
|
52
67
|
elsif git.start_with?('https://github.com')
|