bolt 2.29.0 → 2.30.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 +4 -3
- data/lib/bolt/bolt_option_parser.rb +48 -9
- data/lib/bolt/catalog.rb +1 -1
- data/lib/bolt/cli.rb +131 -110
- data/lib/bolt/config.rb +13 -1
- data/lib/bolt/config/options.rb +2 -1
- data/lib/bolt/executor.rb +1 -1
- data/lib/bolt/module_installer.rb +172 -0
- data/lib/bolt/outputter.rb +4 -0
- data/lib/bolt/outputter/human.rb +53 -11
- data/lib/bolt/outputter/json.rb +7 -1
- data/lib/bolt/outputter/logger.rb +2 -2
- data/lib/bolt/pal.rb +8 -10
- data/lib/bolt/plugin/module.rb +1 -1
- data/lib/bolt/project.rb +31 -15
- data/lib/bolt/project_migrator.rb +80 -0
- data/lib/bolt/project_migrator/base.rb +39 -0
- data/lib/bolt/project_migrator/config.rb +67 -0
- data/lib/bolt/project_migrator/inventory.rb +67 -0
- data/lib/bolt/project_migrator/modules.rb +198 -0
- data/lib/bolt/puppetfile.rb +26 -44
- data/lib/bolt/puppetfile/module.rb +2 -1
- data/lib/bolt/shell/bash.rb +1 -1
- data/lib/bolt/util.rb +22 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/acl.rb +2 -2
- data/lib/bolt_server/base_config.rb +3 -3
- data/lib/bolt_spec/plans/mock_executor.rb +1 -1
- metadata +8 -3
- data/lib/bolt/project_migrate.rb +0 -138
data/lib/bolt/config.rb
CHANGED
@@ -391,6 +391,12 @@ module Bolt
|
|
391
391
|
@logs << { warn: msg }
|
392
392
|
end
|
393
393
|
|
394
|
+
if @project.modules && @data['modulepath']&.include?(@project.managed_moduledir.to_s)
|
395
|
+
raise Bolt::ValidationError,
|
396
|
+
"Found invalid path in modulepath: #{@project.managed_moduledir}. This path "\
|
397
|
+
"is automatically appended to the modulepath and cannot be configured."
|
398
|
+
end
|
399
|
+
|
394
400
|
keys = OPTIONS.keys - %w[plugins plugin_hooks puppetdb]
|
395
401
|
keys.each do |key|
|
396
402
|
next unless Bolt::Util.references?(@data[key])
|
@@ -445,7 +451,13 @@ module Bolt
|
|
445
451
|
end
|
446
452
|
|
447
453
|
def modulepath
|
448
|
-
@data['modulepath'] || @project.modulepath
|
454
|
+
path = @data['modulepath'] || @project.modulepath
|
455
|
+
|
456
|
+
if @project.modules
|
457
|
+
path + [@project.managed_moduledir.to_s]
|
458
|
+
else
|
459
|
+
path
|
460
|
+
end
|
449
461
|
end
|
450
462
|
|
451
463
|
def modulepath=(value)
|
data/lib/bolt/config/options.rb
CHANGED
@@ -233,7 +233,7 @@ module Bolt
|
|
233
233
|
"install` command.",
|
234
234
|
type: Array,
|
235
235
|
items: {
|
236
|
-
type: Hash,
|
236
|
+
type: [Hash, String],
|
237
237
|
required: ["name"],
|
238
238
|
properties: {
|
239
239
|
"name" => {
|
@@ -250,6 +250,7 @@ module Bolt
|
|
250
250
|
_plugin: false,
|
251
251
|
_example: [
|
252
252
|
{ "name" => "puppetlabs-mysql" },
|
253
|
+
"puppetlabs-facts",
|
253
254
|
{ "name" => "puppetlabs-apache", "version_requirement" => "5.5.0" },
|
254
255
|
{ "name" => "puppetlabs-puppetdb", "version_requirement" => "7.x" },
|
255
256
|
{ "name" => "puppetlabs-firewall", "version_requirement" => ">= 1.0.0 < 3.0.0" }
|
data/lib/bolt/executor.rb
CHANGED
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/error'
|
4
|
+
require 'bolt/logger'
|
5
|
+
|
6
|
+
module Bolt
|
7
|
+
class ModuleInstaller
|
8
|
+
def initialize(outputter, pal)
|
9
|
+
@outputter = outputter
|
10
|
+
@pal = pal
|
11
|
+
@logger = Bolt::Logger.logger(self)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Adds a single module to the project.
|
15
|
+
#
|
16
|
+
def add(name, modules, puppetfile_path, moduledir, config_path)
|
17
|
+
require 'bolt/puppetfile'
|
18
|
+
|
19
|
+
# If the project configuration file already includes this module,
|
20
|
+
# exit early.
|
21
|
+
puppetfile = Bolt::Puppetfile.new(modules)
|
22
|
+
new_module = Bolt::Puppetfile::Module.from_hash('name' => name)
|
23
|
+
|
24
|
+
if puppetfile.modules.include?(new_module)
|
25
|
+
@outputter.print_message "Project configuration file #{config_path} already "\
|
26
|
+
"includes module #{new_module}. Nothing to do."
|
27
|
+
return true
|
28
|
+
end
|
29
|
+
|
30
|
+
# If the Puppetfile exists, make sure it's managed by Bolt.
|
31
|
+
if puppetfile_path.exist?
|
32
|
+
assert_managed_puppetfile(puppetfile, puppetfile_path)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Create a Puppetfile object that includes the new module and its
|
36
|
+
# dependencies. We error early here so we don't add the new module to the
|
37
|
+
# project config or modify the Puppetfile.
|
38
|
+
puppetfile = add_new_module_to_puppetfile(new_module, modules, puppetfile_path)
|
39
|
+
|
40
|
+
# Add the module to the project configuration.
|
41
|
+
@outputter.print_message "Updating project configuration file at #{config_path}"
|
42
|
+
|
43
|
+
data = Bolt::Util.read_yaml_hash(config_path, 'project')
|
44
|
+
data['modules'] ||= []
|
45
|
+
data['modules'] << { 'name' => new_module.title }
|
46
|
+
|
47
|
+
begin
|
48
|
+
File.write(config_path, data.to_yaml)
|
49
|
+
rescue SystemCallError => e
|
50
|
+
raise Bolt::FileError.new(
|
51
|
+
"Unable to update project configuration file: #{e.message}",
|
52
|
+
config
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Write the Puppetfile.
|
57
|
+
@outputter.print_message "Writing Puppetfile at #{puppetfile_path}"
|
58
|
+
puppetfile.write(puppetfile_path, moduledir)
|
59
|
+
|
60
|
+
# Install the modules.
|
61
|
+
install_puppetfile(puppetfile_path, moduledir)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Creates a new Puppetfile that includes the new module and its dependencies.
|
65
|
+
#
|
66
|
+
private def add_new_module_to_puppetfile(new_module, modules, path)
|
67
|
+
@outputter.print_message "Resolving module dependencies, this may take a moment"
|
68
|
+
|
69
|
+
# If there is an existing Puppetfile, add the new module and attempt
|
70
|
+
# to resolve. This will not update the versions of any installed modules.
|
71
|
+
if path.exist?
|
72
|
+
puppetfile = Bolt::Puppetfile.parse(path)
|
73
|
+
puppetfile.add_modules(new_module)
|
74
|
+
|
75
|
+
begin
|
76
|
+
puppetfile.resolve
|
77
|
+
return puppetfile
|
78
|
+
rescue Bolt::Error
|
79
|
+
@logger.debug "Unable to find a version of #{new_module} compatible "\
|
80
|
+
"with installed modules. Attempting to re-resolve modules "\
|
81
|
+
"from project configuration; some versions of installed "\
|
82
|
+
"modules may change."
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# If there is not an existing Puppetfile, or resolving with pinned
|
87
|
+
# modules fails, resolve all of the module declarations with the new
|
88
|
+
# module.
|
89
|
+
puppetfile = Bolt::Puppetfile.new(modules)
|
90
|
+
puppetfile.add_modules(new_module)
|
91
|
+
puppetfile.resolve
|
92
|
+
puppetfile
|
93
|
+
end
|
94
|
+
|
95
|
+
# Installs a project's module dependencies.
|
96
|
+
#
|
97
|
+
def install(modules, path, moduledir, force: false, resolve: true)
|
98
|
+
require 'bolt/puppetfile'
|
99
|
+
|
100
|
+
puppetfile = Bolt::Puppetfile.new(modules)
|
101
|
+
|
102
|
+
# If the Puppetfile exists, check if it includes specs for each declared
|
103
|
+
# module, erroring if there are any missing. Otherwise, resolve the
|
104
|
+
# module dependencies and write a new Puppetfile. Users can forcibly
|
105
|
+
# overwrite an existing Puppetfile with the '--force' option, or opt to
|
106
|
+
# install the Puppetfile as-is with --no-resolve.
|
107
|
+
#
|
108
|
+
# This is just if resolve is not false (nil should default to true)
|
109
|
+
if resolve != false
|
110
|
+
if path.exist? && !force
|
111
|
+
assert_managed_puppetfile(puppetfile, path)
|
112
|
+
else
|
113
|
+
@outputter.print_message "Resolving module dependencies, this may take a moment"
|
114
|
+
puppetfile.resolve
|
115
|
+
|
116
|
+
@outputter.print_message "Writing Puppetfile at #{path}"
|
117
|
+
# We get here either through 'bolt module install' which uses the
|
118
|
+
# managed modulepath (which isn't configurable) or through bolt
|
119
|
+
# project init --modules, which uses the default modulepath. This
|
120
|
+
# should be safe to assume that if `.modules/` is the moduledir the
|
121
|
+
# user is using the new workflow
|
122
|
+
if moduledir.basename == '.modules'
|
123
|
+
puppetfile.write(path, moduledir)
|
124
|
+
else
|
125
|
+
puppetfile.write(path)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Install the modules.
|
131
|
+
install_puppetfile(path, moduledir)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Installs the Puppetfile and generates types.
|
135
|
+
#
|
136
|
+
def install_puppetfile(path, moduledir, config = {})
|
137
|
+
require 'bolt/puppetfile/installer'
|
138
|
+
|
139
|
+
@outputter.print_message "Syncing modules from #{path} to #{moduledir}"
|
140
|
+
ok = Bolt::Puppetfile::Installer.new(config).install(path, moduledir)
|
141
|
+
|
142
|
+
# Automatically generate types after installing modules
|
143
|
+
@pal.generate_types
|
144
|
+
|
145
|
+
@outputter.print_puppetfile_result(ok, path, moduledir)
|
146
|
+
|
147
|
+
ok
|
148
|
+
end
|
149
|
+
|
150
|
+
# Asserts that an existing Puppetfile is managed by Bolt.
|
151
|
+
#
|
152
|
+
private def assert_managed_puppetfile(puppetfile, path)
|
153
|
+
existing_puppetfile = Bolt::Puppetfile.parse(path)
|
154
|
+
|
155
|
+
unless existing_puppetfile.modules.superset? puppetfile.modules
|
156
|
+
missing_modules = puppetfile.modules - existing_puppetfile.modules
|
157
|
+
|
158
|
+
message = <<~MESSAGE.chomp
|
159
|
+
Puppetfile #{path} is missing specifications for the following
|
160
|
+
module declarations:
|
161
|
+
|
162
|
+
#{missing_modules.map(&:to_hash).to_yaml.lines.drop(1).join.chomp}
|
163
|
+
|
164
|
+
This may not be a Puppetfile managed by Bolt. To forcibly overwrite the
|
165
|
+
Puppetfile, run 'bolt module install --force'.
|
166
|
+
MESSAGE
|
167
|
+
|
168
|
+
raise Bolt::Error.new(message, 'bolt/missing-module-specs')
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
data/lib/bolt/outputter.rb
CHANGED
@@ -35,6 +35,10 @@ module Bolt
|
|
35
35
|
raise NotImplementedError, "print_message() must be implemented by the outputter class"
|
36
36
|
end
|
37
37
|
|
38
|
+
def print_error
|
39
|
+
raise NotImplementedError, "print_error() must be implemented by the outputter class"
|
40
|
+
end
|
41
|
+
|
38
42
|
def stringify(message)
|
39
43
|
formatted = format_message(message)
|
40
44
|
if formatted.is_a?(Hash) || formatted.is_a?(Array)
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -5,9 +5,12 @@ require 'bolt/pal'
|
|
5
5
|
module Bolt
|
6
6
|
class Outputter
|
7
7
|
class Human < Bolt::Outputter
|
8
|
-
COLORS = {
|
9
|
-
|
10
|
-
|
8
|
+
COLORS = {
|
9
|
+
red: "31",
|
10
|
+
green: "32",
|
11
|
+
yellow: "33",
|
12
|
+
cyan: "36"
|
13
|
+
}.freeze
|
11
14
|
|
12
15
|
def print_head; end
|
13
16
|
|
@@ -31,6 +34,10 @@ module Bolt
|
|
31
34
|
string.sub(/\s\z/, '')
|
32
35
|
end
|
33
36
|
|
37
|
+
def wrap(string, width = 80)
|
38
|
+
string.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n")
|
39
|
+
end
|
40
|
+
|
34
41
|
def handle_event(event)
|
35
42
|
case event[:type]
|
36
43
|
when :enable_default_output
|
@@ -48,9 +55,9 @@ module Bolt
|
|
48
55
|
when :node_result
|
49
56
|
print_result(event[:result]) if @verbose
|
50
57
|
when :step_start
|
51
|
-
print_step_start(event) if plan_logging?
|
58
|
+
print_step_start(**event) if plan_logging?
|
52
59
|
when :step_finish
|
53
|
-
print_step_finish(event) if plan_logging?
|
60
|
+
print_step_finish(**event) if plan_logging?
|
54
61
|
when :plan_start
|
55
62
|
print_plan_start(event)
|
56
63
|
when :plan_finish
|
@@ -188,7 +195,7 @@ module Bolt
|
|
188
195
|
@stream.puts total_msg
|
189
196
|
end
|
190
197
|
|
191
|
-
def print_table(results)
|
198
|
+
def print_table(results, padding_left = 0, padding_right = 3)
|
192
199
|
# lazy-load expensive gem code
|
193
200
|
require 'terminal-table'
|
194
201
|
|
@@ -198,8 +205,8 @@ module Bolt
|
|
198
205
|
border_x: '',
|
199
206
|
border_y: '',
|
200
207
|
border_i: '',
|
201
|
-
padding_left:
|
202
|
-
padding_right:
|
208
|
+
padding_left: padding_left,
|
209
|
+
padding_right: padding_right,
|
203
210
|
border_top: false,
|
204
211
|
border_bottom: false
|
205
212
|
}
|
@@ -299,9 +306,9 @@ module Bolt
|
|
299
306
|
def print_module_list(module_list)
|
300
307
|
module_list.each do |path, modules|
|
301
308
|
if (mod = modules.find { |m| m[:internal_module_group] })
|
302
|
-
@stream.puts(mod[:internal_module_group])
|
309
|
+
@stream.puts(colorize(:cyan, mod[:internal_module_group]))
|
303
310
|
else
|
304
|
-
@stream.puts(path)
|
311
|
+
@stream.puts(colorize(:cyan, path))
|
305
312
|
end
|
306
313
|
|
307
314
|
if modules.empty?
|
@@ -317,7 +324,7 @@ module Bolt
|
|
317
324
|
[m[:name], version]
|
318
325
|
end
|
319
326
|
|
320
|
-
print_table(module_info)
|
327
|
+
print_table(module_info, 2, 1)
|
321
328
|
end
|
322
329
|
|
323
330
|
@stream.write("\n")
|
@@ -394,6 +401,41 @@ module Bolt
|
|
394
401
|
@stream.puts(message)
|
395
402
|
end
|
396
403
|
|
404
|
+
def print_error(message)
|
405
|
+
@stream.puts(colorize(:red, message))
|
406
|
+
end
|
407
|
+
|
408
|
+
def print_prompt(prompt)
|
409
|
+
@stream.print(colorize(:cyan, indent(4, prompt)))
|
410
|
+
end
|
411
|
+
|
412
|
+
def print_prompt_error(message)
|
413
|
+
@stream.puts(colorize(:red, indent(4, message)))
|
414
|
+
end
|
415
|
+
|
416
|
+
def print_migrate_step(step)
|
417
|
+
first, *remaining = wrap(step, 76).lines
|
418
|
+
|
419
|
+
first = indent(2, "→ #{first}")
|
420
|
+
remaining = remaining.map { |line| indent(4, line) }
|
421
|
+
step = [first, *remaining, "\n"].join
|
422
|
+
|
423
|
+
@stream.puts(step)
|
424
|
+
end
|
425
|
+
|
426
|
+
def print_migrate_error(error)
|
427
|
+
# Running everything through 'wrap' messes with newlines. Separating
|
428
|
+
# into lines and wrapping each individually ensures separate errors are
|
429
|
+
# distinguishable.
|
430
|
+
first, *remaining = error.lines
|
431
|
+
first = colorize(:red, indent(2, "→ #{wrap(first, 76)}"))
|
432
|
+
wrapped = remaining.map { |l| wrap(l) }
|
433
|
+
to_print = wrapped.map { |line| colorize(:red, indent(4, line)) }
|
434
|
+
step = [first, *to_print, "\n"].join
|
435
|
+
|
436
|
+
@stream.puts(step)
|
437
|
+
end
|
438
|
+
|
397
439
|
def duration_to_string(duration)
|
398
440
|
hrs = (duration / 3600).floor
|
399
441
|
mins = ((duration % 3600) / 60).floor
|
data/lib/bolt/outputter/json.rb
CHANGED
@@ -97,7 +97,7 @@ module Bolt
|
|
97
97
|
def print_puppetfile_result(success, puppetfile, moduledir)
|
98
98
|
@stream.puts({ "success": success,
|
99
99
|
"puppetfile": puppetfile,
|
100
|
-
"moduledir": moduledir }.to_json)
|
100
|
+
"moduledir": moduledir.to_s }.to_json)
|
101
101
|
end
|
102
102
|
|
103
103
|
def print_targets(targets)
|
@@ -135,6 +135,12 @@ module Bolt
|
|
135
135
|
def print_message(message)
|
136
136
|
$stderr.puts(message)
|
137
137
|
end
|
138
|
+
alias print_error print_message
|
139
|
+
|
140
|
+
def print_migrate_step(step)
|
141
|
+
$stderr.puts(step)
|
142
|
+
end
|
143
|
+
alias print_migrate_error print_migrate_step
|
138
144
|
end
|
139
145
|
end
|
140
146
|
end
|
@@ -13,9 +13,9 @@ module Bolt
|
|
13
13
|
def handle_event(event)
|
14
14
|
case event[:type]
|
15
15
|
when :step_start
|
16
|
-
log_step_start(event)
|
16
|
+
log_step_start(**event)
|
17
17
|
when :step_finish
|
18
|
-
log_step_finish(event)
|
18
|
+
log_step_finish(**event)
|
19
19
|
when :plan_start
|
20
20
|
log_plan_start(event)
|
21
21
|
when :plan_finish
|
data/lib/bolt/pal.rb
CHANGED
@@ -29,15 +29,12 @@ module Bolt
|
|
29
29
|
message = err.cause ? err.cause.message : err.message
|
30
30
|
|
31
31
|
# Provide the location of an error if it came from a plan
|
32
|
-
details =
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
else
|
37
|
-
{}
|
38
|
-
end
|
32
|
+
details = {}
|
33
|
+
details[:file] = err.file if defined?(err.file)
|
34
|
+
details[:line] = err.line if defined?(err.line)
|
35
|
+
details[:column] = err.pos if defined?(err.pos)
|
39
36
|
|
40
|
-
e = new(message, details)
|
37
|
+
e = new(message, details.compact)
|
41
38
|
|
42
39
|
e.set_backtrace(err.backtrace)
|
43
40
|
e
|
@@ -441,13 +438,14 @@ module Bolt
|
|
441
438
|
# Returns a mapping of all modules available to the Bolt compiler
|
442
439
|
#
|
443
440
|
# @return [Hash{String => Array<Hash{Symbol => String,nil}>}]
|
444
|
-
# A hash that associates each directory on the
|
441
|
+
# A hash that associates each directory on the modulepath with an array
|
445
442
|
# containing a hash of information for each module in that directory.
|
446
443
|
# The information hash provides the name, version, and a string
|
447
444
|
# indicating whether the module belongs to an internal module group.
|
448
445
|
def list_modules
|
449
446
|
internal_module_groups = { BOLTLIB_PATH => 'Plan Language Modules',
|
450
|
-
MODULES_PATH => 'Packaged Modules'
|
447
|
+
MODULES_PATH => 'Packaged Modules',
|
448
|
+
@project.managed_moduledir.to_s => 'Project Dependencies' }
|
451
449
|
|
452
450
|
in_bolt_compiler do
|
453
451
|
# NOTE: Can replace map+to_h with transform_values when Ruby 2.4
|
data/lib/bolt/plugin/module.rb
CHANGED
data/lib/bolt/project.rb
CHANGED
@@ -16,9 +16,10 @@ module Bolt
|
|
16
16
|
"These tasks are included in `bolt task show` output"
|
17
17
|
}.freeze
|
18
18
|
|
19
|
-
attr_reader :path, :data, :config_file, :inventory_file, :
|
19
|
+
attr_reader :path, :data, :config_file, :inventory_file, :hiera_config,
|
20
20
|
:puppetfile, :rerunfile, :type, :resource_types, :logs, :project_file,
|
21
|
-
:deprecations, :downloads, :plans_path
|
21
|
+
:deprecations, :downloads, :plans_path, :modulepath, :managed_moduledir,
|
22
|
+
:backup_dir
|
22
23
|
|
23
24
|
def self.default_project(logs = [])
|
24
25
|
create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user', logs)
|
@@ -94,15 +95,16 @@ module Bolt
|
|
94
95
|
@deprecations << { type: 'Using bolt.yaml for project configuration', msg: msg }
|
95
96
|
end
|
96
97
|
|
97
|
-
@inventory_file
|
98
|
-
@
|
99
|
-
@
|
100
|
-
@
|
101
|
-
@
|
102
|
-
@
|
103
|
-
@
|
104
|
-
@
|
105
|
-
@
|
98
|
+
@inventory_file = @path + 'inventory.yaml'
|
99
|
+
@hiera_config = @path + 'hiera.yaml'
|
100
|
+
@puppetfile = @path + 'Puppetfile'
|
101
|
+
@rerunfile = @path + '.rerun.json'
|
102
|
+
@resource_types = @path + '.resource_types'
|
103
|
+
@type = type
|
104
|
+
@downloads = @path + 'downloads'
|
105
|
+
@plans_path = @path + 'plans'
|
106
|
+
@managed_moduledir = @path + '.modules'
|
107
|
+
@backup_dir = @path + '.bolt-bak'
|
106
108
|
|
107
109
|
tc = Bolt::Config::INVENTORY_OPTIONS.keys & raw_data.keys
|
108
110
|
if tc.any?
|
@@ -112,6 +114,14 @@ module Bolt
|
|
112
114
|
|
113
115
|
@data = raw_data.reject { |k, _| Bolt::Config::INVENTORY_OPTIONS.include?(k) }
|
114
116
|
|
117
|
+
# If the 'modules' key is present in the project configuration file,
|
118
|
+
# use the new, shorter modulepath.
|
119
|
+
@modulepath = if @data.key?('modules')
|
120
|
+
[(@path + 'modules').to_s]
|
121
|
+
else
|
122
|
+
[(@path + 'modules').to_s, (@path + 'site-modules').to_s, (@path + 'site').to_s]
|
123
|
+
end
|
124
|
+
|
115
125
|
# Once bolt.yaml deprecation is removed, this attribute should be removed
|
116
126
|
# and replaced with .project_file in lib/bolt/config.rb
|
117
127
|
@config_file = if (Bolt::Config::BOLT_OPTIONS & @data.keys).any?
|
@@ -164,7 +174,13 @@ module Bolt
|
|
164
174
|
end
|
165
175
|
|
166
176
|
def modules
|
167
|
-
@data['modules']
|
177
|
+
@modules ||= @data['modules']&.map do |mod|
|
178
|
+
if mod.is_a?(String)
|
179
|
+
{ 'name' => mod }
|
180
|
+
else
|
181
|
+
mod
|
182
|
+
end
|
183
|
+
end
|
168
184
|
end
|
169
185
|
|
170
186
|
def validate
|
@@ -195,11 +211,11 @@ module Bolt
|
|
195
211
|
end
|
196
212
|
|
197
213
|
@data['modules'].each do |mod|
|
198
|
-
next if mod.is_a?(Hash)
|
199
|
-
raise Bolt::ValidationError, "Module declaration #{mod.inspect} must be a hash"
|
214
|
+
next if (mod.is_a?(Hash) && mod.key?('name')) || mod.is_a?(String)
|
215
|
+
raise Bolt::ValidationError, "Module declaration #{mod.inspect} must be a hash with a name key"
|
200
216
|
end
|
201
217
|
|
202
|
-
unknown_keys =
|
218
|
+
unknown_keys = modules.flat_map(&:keys).uniq - %w[name version_requirement]
|
203
219
|
if unknown_keys.any?
|
204
220
|
@logs << { warn: "Ignoring unknown keys in module declarations: #{unknown_keys.join(', ')}." }
|
205
221
|
end
|