bolt 2.42.0 → 3.3.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 +21 -19
- data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -1
- 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/run_plan.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +27 -5
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +7 -3
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +3 -2
- data/lib/bolt/analytics.rb +3 -2
- data/lib/bolt/applicator.rb +11 -1
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/bolt_option_parser.rb +9 -116
- data/lib/bolt/catalog.rb +10 -29
- data/lib/bolt/cli.rb +90 -154
- data/lib/bolt/config.rb +66 -239
- data/lib/bolt/config/options.rb +79 -102
- data/lib/bolt/config/transport/local.rb +1 -0
- data/lib/bolt/config/transport/lxd.rb +21 -0
- data/lib/bolt/config/transport/options.rb +9 -2
- data/lib/bolt/config/transport/orch.rb +1 -0
- data/lib/bolt/executor.rb +23 -6
- data/lib/bolt/inventory.rb +1 -1
- data/lib/bolt/inventory/group.rb +7 -4
- data/lib/bolt/logger.rb +123 -11
- data/lib/bolt/module_installer.rb +6 -4
- data/lib/bolt/module_installer/puppetfile.rb +2 -2
- data/lib/bolt/module_installer/resolver.rb +59 -14
- data/lib/bolt/module_installer/specs/forge_spec.rb +10 -4
- data/lib/bolt/module_installer/specs/git_spec.rb +19 -4
- data/lib/bolt/outputter/human.rb +56 -17
- data/lib/bolt/outputter/json.rb +16 -16
- data/lib/bolt/outputter/rainbow.rb +3 -3
- data/lib/bolt/pal.rb +95 -15
- data/lib/bolt/pal/yaml_plan.rb +9 -4
- data/lib/bolt/pal/yaml_plan/evaluator.rb +5 -153
- data/lib/bolt/pal/yaml_plan/step.rb +91 -52
- data/lib/bolt/pal/yaml_plan/step/command.rb +16 -16
- data/lib/bolt/pal/yaml_plan/step/download.rb +15 -16
- data/lib/bolt/pal/yaml_plan/step/eval.rb +11 -11
- data/lib/bolt/pal/yaml_plan/step/message.rb +13 -4
- data/lib/bolt/pal/yaml_plan/step/plan.rb +19 -15
- data/lib/bolt/pal/yaml_plan/step/resources.rb +82 -21
- data/lib/bolt/pal/yaml_plan/step/script.rb +32 -17
- data/lib/bolt/pal/yaml_plan/step/task.rb +19 -16
- data/lib/bolt/pal/yaml_plan/step/upload.rb +16 -17
- data/lib/bolt/pal/yaml_plan/transpiler.rb +2 -1
- data/lib/bolt/plan_creator.rb +1 -1
- data/lib/bolt/plugin.rb +2 -2
- data/lib/bolt/plugin/cache.rb +7 -7
- 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 +5 -4
- data/lib/bolt/project_manager/module_migrator.rb +7 -6
- data/lib/bolt/rerun.rb +1 -1
- data/lib/bolt/result.rb +6 -1
- data/lib/bolt/shell.rb +16 -0
- data/lib/bolt/shell/bash.rb +57 -25
- data/lib/bolt/shell/bash/tmpdir.rb +6 -3
- data/lib/bolt/shell/powershell.rb +33 -10
- data/lib/bolt/shell/powershell/snippets.rb +37 -150
- data/lib/bolt/task.rb +2 -2
- data/lib/bolt/transport/base.rb +0 -9
- data/lib/bolt/transport/docker.rb +1 -125
- data/lib/bolt/transport/docker/connection.rb +86 -161
- data/lib/bolt/transport/local.rb +1 -9
- data/lib/bolt/transport/lxd.rb +26 -0
- data/lib/bolt/transport/lxd/connection.rb +99 -0
- data/lib/bolt/transport/orch/connection.rb +1 -1
- data/lib/bolt/transport/ssh.rb +1 -2
- data/lib/bolt/transport/ssh/connection.rb +2 -2
- data/lib/bolt/transport/winrm/connection.rb +1 -1
- data/lib/bolt/validator.rb +2 -2
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/config.rb +1 -1
- data/lib/bolt_server/transport_app.rb +61 -32
- data/lib/bolt_spec/bolt_context.rb +9 -4
- data/lib/bolt_spec/plans.rb +1 -109
- data/lib/bolt_spec/plans/action_stubs.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +4 -0
- 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 +67 -0
- data/modules/puppetdb_fact/plans/init.pp +10 -0
- metadata +13 -9
- data/modules/aggregate/plans/nodes.pp +0 -36
data/lib/bolt/inventory.rb
CHANGED
@@ -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|
|
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)
|
data/lib/bolt/inventory/group.rb
CHANGED
@@ -27,7 +27,10 @@ module Bolt
|
|
27
27
|
|
28
28
|
if all_group
|
29
29
|
if input.key?('name') && input['name'] != 'all'
|
30
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
374
|
+
Bolt::Logger.warn("unknown_config_keys", msg)
|
372
375
|
end
|
373
376
|
end
|
374
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,24 @@ 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?
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.stream
|
95
|
+
@stream
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.stream=(stream)
|
99
|
+
@stream = stream
|
76
100
|
end
|
77
101
|
|
78
102
|
# A helper to ensure the Logging library is always initialized with our
|
@@ -123,18 +147,106 @@ module Bolt
|
|
123
147
|
Logging.reset
|
124
148
|
end
|
125
149
|
|
126
|
-
|
150
|
+
# The following methods are used in place of the Logging.logger
|
151
|
+
# methods of the same name when logging warning messages or logging
|
152
|
+
# any messages prior to the logger being configured. If the logger
|
153
|
+
# is not configured when any of these methods are called, the message
|
154
|
+
# will be added to a queue, otherwise they are logged immediately.
|
155
|
+
# The message queue is flushed by calling #flush_queue, which is
|
156
|
+
# called from Bolt::CLI after configuring the logger.
|
157
|
+
#
|
158
|
+
def self.warn(id, msg)
|
159
|
+
log(type: :warn, msg: "#{msg} [ID: #{id}]", id: id)
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.warn_once(id, msg)
|
163
|
+
log(type: :warn_once, msg: "#{msg} [ID: #{id}]", id: id)
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.deprecate(id, msg)
|
167
|
+
log(type: :deprecate, msg: "#{msg} [ID: #{id}]", id: id)
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.deprecate_once(id, msg)
|
171
|
+
log(type: :deprecate_once, msg: "#{msg} [ID: #{id}]", id: id)
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.debug(msg)
|
175
|
+
log(type: :debug, msg: msg)
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.info(msg)
|
179
|
+
log(type: :info, msg: msg)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Logs a message. If the logger has not been configured, this will queue
|
183
|
+
# the message to be logged later. Once the logger is configured, the
|
184
|
+
# queue will be flushed of all messages and new messages will be logged
|
185
|
+
# immediately.
|
186
|
+
#
|
187
|
+
# Logging with this method is controlled by a mutex, as the Bolt::Logger
|
188
|
+
# module is treated as a global singleton to allow multiple classes
|
189
|
+
# access to its methods.
|
190
|
+
#
|
191
|
+
private_class_method def self.log(type:, msg:, id: nil)
|
127
192
|
@mutex.synchronize do
|
128
|
-
|
129
|
-
|
130
|
-
|
193
|
+
if configured?
|
194
|
+
log_message(type: type, msg: msg, id: id)
|
195
|
+
else
|
196
|
+
@message_queue << { type: type, msg: msg, id: id }
|
131
197
|
end
|
132
198
|
end
|
133
199
|
end
|
134
200
|
|
135
|
-
|
136
|
-
|
137
|
-
|
201
|
+
# Logs all messages in the message queue and then flushes the queue.
|
202
|
+
#
|
203
|
+
def self.flush_queue
|
204
|
+
@mutex.synchronize do
|
205
|
+
@message_queue.each do |message|
|
206
|
+
log_message(message)
|
207
|
+
end
|
208
|
+
|
209
|
+
@message_queue.clear
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Handles the actual logging of a message.
|
214
|
+
#
|
215
|
+
private_class_method def self.log_message(type:, msg:, id: nil)
|
216
|
+
case type
|
217
|
+
when :warn
|
218
|
+
do_warn(msg, id)
|
219
|
+
when :warn_once
|
220
|
+
do_warn_once(msg, id)
|
221
|
+
when :deprecate
|
222
|
+
do_deprecate(msg, id)
|
223
|
+
when :deprecate_once
|
224
|
+
do_deprecate_once(msg, id)
|
225
|
+
else
|
226
|
+
logger(self).send(type, msg)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# The following methods do the actual warning.
|
231
|
+
#
|
232
|
+
private_class_method def self.do_warn(msg, id)
|
233
|
+
return if @disable_warnings.include?(id)
|
234
|
+
logger(self).warn(msg)
|
235
|
+
end
|
236
|
+
|
237
|
+
private_class_method def self.do_warn_once(msg, id)
|
238
|
+
return unless @warnings.add?(id)
|
239
|
+
do_warn(msg, id)
|
240
|
+
end
|
241
|
+
|
242
|
+
private_class_method def self.do_deprecate(msg, id)
|
243
|
+
@analytics&.event('Warn', 'deprecation', label: id)
|
244
|
+
do_warn(msg, id)
|
245
|
+
end
|
246
|
+
|
247
|
+
private_class_method def self.do_deprecate_once(msg, id)
|
248
|
+
@analytics&.event('Warn', 'deprecation', label: id)
|
249
|
+
do_warn_once(msg, id)
|
138
250
|
end
|
139
251
|
end
|
140
252
|
end
|
@@ -45,7 +45,7 @@ module Bolt
|
|
45
45
|
# specss. If that fails, fall back to resolving from project specs.
|
46
46
|
# This prevents Bolt from modifying installed modules unless there is
|
47
47
|
# a version conflict.
|
48
|
-
@outputter.print_action_step("Resolving module dependencies, this
|
48
|
+
@outputter.print_action_step("Resolving module dependencies, this might take a moment")
|
49
49
|
|
50
50
|
@outputter.start_spin
|
51
51
|
begin
|
@@ -156,7 +156,7 @@ module Bolt
|
|
156
156
|
# If forcibly installing or if there is no Puppetfile, resolve
|
157
157
|
# and write a Puppetfile.
|
158
158
|
if force || !path.exist?
|
159
|
-
@outputter.print_action_step("Resolving module dependencies, this
|
159
|
+
@outputter.print_action_step("Resolving module dependencies, this might take a moment")
|
160
160
|
|
161
161
|
# This doesn't use the block as it's more testable to just mock *_spin
|
162
162
|
@outputter.start_spin
|
@@ -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
|
|
@@ -36,7 +36,7 @@ module Bolt
|
|
36
36
|
raise Bolt::ValidationError, <<~MSG
|
37
37
|
Unable to parse Puppetfile #{path}:
|
38
38
|
#{parsed.validation_errors.join("\n\n")}.
|
39
|
-
This
|
39
|
+
This Puppetfile might not be managed by Bolt.
|
40
40
|
MSG
|
41
41
|
end
|
42
42
|
|
@@ -106,7 +106,7 @@ module Bolt
|
|
106
106
|
|
107
107
|
#{unsatisfied_specs.map(&:to_hash).to_yaml.lines.drop(1).join.chomp}
|
108
108
|
|
109
|
-
This
|
109
|
+
This Puppetfile might not be managed by Bolt. To forcibly overwrite the
|
110
110
|
Puppetfile, run '#{command}'.
|
111
111
|
MESSAGE
|
112
112
|
|
@@ -13,10 +13,15 @@ module Bolt
|
|
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.
|
@@ -47,20 +52,40 @@ module Bolt
|
|
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,16 +93,36 @@ module Bolt
|
|
68
93
|
end
|
69
94
|
end
|
70
95
|
|
71
|
-
#
|
72
|
-
|
73
|
-
|
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
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
@@ -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)
|
@@ -33,8 +39,8 @@ module Bolt
|
|
33
39
|
unless (match = name.match(NAME_REGEX))
|
34
40
|
raise Bolt::ValidationError,
|
35
41
|
"Invalid name for Forge module specification: #{name}. Name must match "\
|
36
|
-
"'owner/name'. Owner segment
|
37
|
-
"segment must start with a lowercase letter and
|
42
|
+
"'owner/name'. Owner segment can only include letters or digits. Name "\
|
43
|
+
"segment must start with a lowercase letter and can only include lowercase "\
|
38
44
|
"letters, digits, and underscores."
|
39
45
|
end
|
40
46
|
|
@@ -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
|
@@ -36,8 +49,8 @@ module Bolt
|
|
36
49
|
unless (match = name.match(NAME_REGEX))
|
37
50
|
raise Bolt::ValidationError,
|
38
51
|
"Invalid name for Git module specification: #{name}. Name must match "\
|
39
|
-
"'name' or 'owner/name'. Owner segment
|
40
|
-
"Name segment must start with a lowercase letter and
|
52
|
+
"'name' or 'owner/name'. Owner segment can only include letters or digits. "\
|
53
|
+
"Name segment must start with a lowercase letter and can only include "\
|
41
54
|
"lowercase letters, digits, and underscores."
|
42
55
|
end
|
43
56
|
|
@@ -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')
|