bolt 2.36.0 → 2.42.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 +8 -8
- data/lib/bolt/bolt_option_parser.rb +7 -3
- data/lib/bolt/cli.rb +67 -23
- data/lib/bolt/config.rb +70 -45
- data/lib/bolt/config/options.rb +104 -79
- data/lib/bolt/config/transport/base.rb +2 -2
- data/lib/bolt/config/transport/local.rb +1 -0
- data/lib/bolt/config/transport/options.rb +11 -68
- data/lib/bolt/config/transport/ssh.rb +0 -5
- data/lib/bolt/inventory.rb +26 -0
- data/lib/bolt/inventory/group.rb +29 -9
- data/lib/bolt/inventory/inventory.rb +1 -1
- data/lib/bolt/inventory/options.rb +130 -0
- data/lib/bolt/inventory/target.rb +10 -11
- data/lib/bolt/module.rb +10 -2
- data/lib/bolt/module_installer.rb +21 -13
- data/lib/bolt/module_installer/resolver.rb +13 -5
- data/lib/bolt/outputter.rb +19 -5
- data/lib/bolt/outputter/human.rb +20 -1
- data/lib/bolt/outputter/json.rb +1 -1
- data/lib/bolt/outputter/logger.rb +1 -1
- data/lib/bolt/outputter/rainbow.rb +12 -1
- data/lib/bolt/pal/yaml_plan/transpiler.rb +5 -1
- data/lib/bolt/plugin.rb +42 -6
- data/lib/bolt/plugin/cache.rb +76 -0
- data/lib/bolt/plugin/module.rb +4 -4
- data/lib/bolt/plugin/puppetdb.rb +1 -1
- data/lib/bolt/project.rb +38 -13
- data/lib/bolt/project_manager.rb +2 -0
- data/lib/bolt/project_manager/config_migrator.rb +9 -1
- data/lib/bolt/project_manager/module_migrator.rb +2 -0
- data/lib/bolt/puppetdb/client.rb +8 -0
- data/lib/bolt/rerun.rb +1 -5
- data/lib/bolt/shell/bash.rb +7 -1
- data/lib/bolt/shell/powershell.rb +21 -3
- data/lib/bolt/target.rb +4 -0
- data/lib/bolt/transport/local.rb +13 -0
- data/lib/bolt/util.rb +22 -0
- data/lib/bolt/validator.rb +227 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/plugin.rb +13 -0
- data/lib/bolt_server/plugin/puppet_connect_data.rb +37 -0
- data/lib/bolt_server/schemas/connect-data.json +22 -0
- data/lib/bolt_server/schemas/partials/task.json +1 -1
- data/lib/bolt_server/transport_app.rb +64 -36
- metadata +24 -5
- data/lib/bolt/config/validator.rb +0 -231
@@ -9,7 +9,7 @@ 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.
|
@@ -38,10 +38,10 @@ module Bolt
|
|
38
38
|
# raised by puppetfile-resolver and re-raising them as Bolt errors.
|
39
39
|
begin
|
40
40
|
result = resolver.resolve(
|
41
|
-
cache:
|
42
|
-
ui:
|
43
|
-
|
44
|
-
|
41
|
+
cache: nil,
|
42
|
+
ui: nil,
|
43
|
+
allow_missing_modules: false,
|
44
|
+
spec_searcher_configuration: spec_searcher_config(config)
|
45
45
|
)
|
46
46
|
rescue StandardError => e
|
47
47
|
raise Bolt::Error.new(e.message, 'bolt/module-resolver-error')
|
@@ -71,6 +71,14 @@ module Bolt
|
|
71
71
|
# Create the Puppetfile object.
|
72
72
|
Bolt::ModuleInstaller::Puppetfile.new(modules)
|
73
73
|
end
|
74
|
+
|
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')
|
80
|
+
end
|
81
|
+
end
|
74
82
|
end
|
75
83
|
end
|
76
84
|
end
|
data/lib/bolt/outputter.rb
CHANGED
@@ -2,24 +2,25 @@
|
|
2
2
|
|
3
3
|
module Bolt
|
4
4
|
class Outputter
|
5
|
-
def self.for_format(format, color, verbose, trace)
|
5
|
+
def self.for_format(format, color, verbose, trace, spin)
|
6
6
|
case format
|
7
7
|
when 'human'
|
8
|
-
Bolt::Outputter::Human.new(color, verbose, trace)
|
8
|
+
Bolt::Outputter::Human.new(color, verbose, trace, spin)
|
9
9
|
when 'json'
|
10
|
-
Bolt::Outputter::JSON.new(color, verbose, trace)
|
10
|
+
Bolt::Outputter::JSON.new(color, verbose, trace, false)
|
11
11
|
when 'rainbow'
|
12
|
-
Bolt::Outputter::Rainbow.new(color, verbose, trace)
|
12
|
+
Bolt::Outputter::Rainbow.new(color, verbose, trace, spin)
|
13
13
|
when nil
|
14
14
|
raise "Cannot use outputter before parsing."
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def initialize(color, verbose, trace, stream = $stdout)
|
18
|
+
def initialize(color, verbose, trace, spin, stream = $stdout)
|
19
19
|
@color = color
|
20
20
|
@verbose = verbose
|
21
21
|
@trace = trace
|
22
22
|
@stream = stream
|
23
|
+
@spin = spin
|
23
24
|
end
|
24
25
|
|
25
26
|
def indent(indent, string)
|
@@ -34,6 +35,19 @@ module Bolt
|
|
34
35
|
def print_error
|
35
36
|
raise NotImplementedError, "print_error() must be implemented by the outputter class"
|
36
37
|
end
|
38
|
+
|
39
|
+
def start_spin; end
|
40
|
+
|
41
|
+
def stop_spin; end
|
42
|
+
|
43
|
+
def spin
|
44
|
+
start_spin
|
45
|
+
begin
|
46
|
+
yield
|
47
|
+
ensure
|
48
|
+
stop_spin
|
49
|
+
end
|
50
|
+
end
|
37
51
|
end
|
38
52
|
end
|
39
53
|
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -14,12 +14,13 @@ module Bolt
|
|
14
14
|
|
15
15
|
def print_head; end
|
16
16
|
|
17
|
-
def initialize(color, verbose, trace, stream = $stdout)
|
17
|
+
def initialize(color, verbose, trace, spin, stream = $stdout)
|
18
18
|
super
|
19
19
|
# Plans and without_default_logging() calls can both be nested, so we
|
20
20
|
# track each of them with a "stack" consisting of an integer.
|
21
21
|
@plan_depth = 0
|
22
22
|
@disable_depth = 0
|
23
|
+
@pinwheel = %w[- \\ | /]
|
23
24
|
end
|
24
25
|
|
25
26
|
def colorize(color, string)
|
@@ -30,6 +31,24 @@ module Bolt
|
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
34
|
+
def start_spin
|
35
|
+
return unless @spin && @stream.isatty
|
36
|
+
@spin = true
|
37
|
+
@spin_thread = Thread.new do
|
38
|
+
loop do
|
39
|
+
sleep(0.1)
|
40
|
+
@stream.print(colorize(:cyan, @pinwheel.rotate!.first + "\b"))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def stop_spin
|
46
|
+
return unless @spin && @stream.isatty
|
47
|
+
@spin_thread.terminate
|
48
|
+
@spin = false
|
49
|
+
@stream.print("\b")
|
50
|
+
end
|
51
|
+
|
33
52
|
def remove_trail(string)
|
34
53
|
string.sub(/\s\z/, '')
|
35
54
|
end
|
data/lib/bolt/outputter/json.rb
CHANGED
@@ -5,7 +5,7 @@ require 'bolt/pal'
|
|
5
5
|
module Bolt
|
6
6
|
class Outputter
|
7
7
|
class Rainbow < Bolt::Outputter::Human
|
8
|
-
def initialize(color, verbose, trace, stream = $stdout)
|
8
|
+
def initialize(color, verbose, trace, spin, stream = $stdout)
|
9
9
|
begin
|
10
10
|
require 'paint'
|
11
11
|
if Bolt::Util.windows?
|
@@ -62,6 +62,17 @@ module Bolt
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
+
def start_spin
|
66
|
+
return unless @spin && @stream.isatty
|
67
|
+
@spin = true
|
68
|
+
@spin_thread = Thread.new do
|
69
|
+
loop do
|
70
|
+
@stream.print(colorize(:rainbow, @pinwheel.rotate!.first + "\b"))
|
71
|
+
sleep(0.1)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
65
76
|
def print_summary(results, elapsed_time = nil)
|
66
77
|
ok_set = results.ok_set
|
67
78
|
unless ok_set.empty?
|
@@ -64,7 +64,11 @@ module Bolt
|
|
64
64
|
raise Bolt::FileError.new(msg, @plan_path)
|
65
65
|
end
|
66
66
|
|
67
|
-
|
67
|
+
begin
|
68
|
+
Bolt::PAL::YamlPlan::Loader.from_string(@modulename, file_contents, @plan_path)
|
69
|
+
rescue Puppet::PreformattedError, StandardError => e
|
70
|
+
raise PALError.from_preformatted_error(e)
|
71
|
+
end
|
68
72
|
end
|
69
73
|
|
70
74
|
def validate_path
|
data/lib/bolt/plugin.rb
CHANGED
@@ -4,6 +4,7 @@ require 'bolt/inventory'
|
|
4
4
|
require 'bolt/executor'
|
5
5
|
require 'bolt/module'
|
6
6
|
require 'bolt/pal'
|
7
|
+
require 'bolt/plugin/cache'
|
7
8
|
require 'bolt/plugin/puppetdb'
|
8
9
|
|
9
10
|
module Bolt
|
@@ -36,6 +37,13 @@ module Bolt
|
|
36
37
|
super("Plugin #{plugin_name} does not support #{hook}", 'bolt/unsupported-hook')
|
37
38
|
end
|
38
39
|
end
|
40
|
+
|
41
|
+
class LoadingDisabled < PluginError
|
42
|
+
def initialize(plugin_name)
|
43
|
+
msg = "Cannot load plugin #{plugin_name}: plugin loading is disabled"
|
44
|
+
super(msg, 'bolt/plugin-loading-disabled', { 'plugin_name' => plugin_name })
|
45
|
+
end
|
46
|
+
end
|
39
47
|
end
|
40
48
|
|
41
49
|
class PluginContext
|
@@ -119,8 +127,8 @@ module Bolt
|
|
119
127
|
end
|
120
128
|
end
|
121
129
|
|
122
|
-
def self.setup(config, pal, analytics = Bolt::Analytics::NoopClient.new)
|
123
|
-
plugins = new(config, pal, analytics)
|
130
|
+
def self.setup(config, pal, analytics = Bolt::Analytics::NoopClient.new, **opts)
|
131
|
+
plugins = new(config, pal, analytics, **opts)
|
124
132
|
|
125
133
|
config.plugins.each_key do |plugin|
|
126
134
|
plugins.by_name(plugin)
|
@@ -141,12 +149,13 @@ module Bolt
|
|
141
149
|
|
142
150
|
private_class_method :new
|
143
151
|
|
144
|
-
def initialize(config, pal, analytics)
|
152
|
+
def initialize(config, pal, analytics, load_plugins: true)
|
145
153
|
@config = config
|
146
154
|
@analytics = analytics
|
147
155
|
@plugin_context = PluginContext.new(config, pal, self)
|
148
156
|
@plugins = {}
|
149
157
|
@pal = pal
|
158
|
+
@load_plugins = load_plugins
|
150
159
|
@unknown = Set.new
|
151
160
|
@resolution_stack = []
|
152
161
|
@unresolved_plugin_configs = config.plugins.dup
|
@@ -161,7 +170,7 @@ module Bolt
|
|
161
170
|
end
|
162
171
|
|
163
172
|
def modules
|
164
|
-
@modules ||= Bolt::Module.discover(@pal.full_modulepath)
|
173
|
+
@modules ||= Bolt::Module.discover(@pal.full_modulepath, @config.project)
|
165
174
|
end
|
166
175
|
|
167
176
|
def add_plugin(plugin)
|
@@ -169,6 +178,8 @@ module Bolt
|
|
169
178
|
end
|
170
179
|
|
171
180
|
def add_ruby_plugin(plugin_name)
|
181
|
+
raise PluginError::LoadingDisabled, plugin_name unless @load_plugins
|
182
|
+
|
172
183
|
cls_name = Bolt::Util.snake_name_to_class_name(plugin_name)
|
173
184
|
filename = "bolt/plugin/#{plugin_name}"
|
174
185
|
require filename
|
@@ -185,10 +196,17 @@ module Bolt
|
|
185
196
|
def add_module_plugin(plugin_name)
|
186
197
|
opts = {
|
187
198
|
context: @plugin_context,
|
199
|
+
# Make sure that the plugin's config is validated _before_ the unknown-plugin
|
200
|
+
# and loading-disabled checks. This way, we can fail early on invalid plugin
|
201
|
+
# config instead of _after_ loading the modulepath (which can be expensive).
|
188
202
|
config: config_for_plugin(plugin_name)
|
189
203
|
}
|
190
204
|
|
191
|
-
|
205
|
+
mod = modules[plugin_name]
|
206
|
+
raise PluginError::Unknown, plugin_name unless mod&.plugin?
|
207
|
+
raise PluginError::LoadingDisabled, plugin_name unless @load_plugins
|
208
|
+
|
209
|
+
plugin = Bolt::Plugin::Module.load(mod, opts)
|
192
210
|
add_plugin(plugin)
|
193
211
|
end
|
194
212
|
|
@@ -277,6 +295,16 @@ module Bolt
|
|
277
295
|
# Evaluates a single reference. The value returned may be another
|
278
296
|
# reference.
|
279
297
|
def resolve_single_reference(reference)
|
298
|
+
plugin_cache = if cache?(reference)
|
299
|
+
cache = Bolt::Plugin::Cache.new(reference,
|
300
|
+
@config.project.cache_file,
|
301
|
+
@config.plugin_cache)
|
302
|
+
entry = cache.read_and_clean_cache
|
303
|
+
return entry unless entry.nil?
|
304
|
+
|
305
|
+
cache
|
306
|
+
end
|
307
|
+
|
280
308
|
plugin_name = reference['_plugin']
|
281
309
|
hook = get_hook(plugin_name, :resolve_reference)
|
282
310
|
|
@@ -288,16 +316,24 @@ module Bolt
|
|
288
316
|
|
289
317
|
validate_proc.call(reference)
|
290
318
|
|
291
|
-
begin
|
319
|
+
result = begin
|
292
320
|
# Evaluate the plugin and then recursively evaluate any plugin returned by it.
|
293
321
|
hook.call(reference)
|
294
322
|
rescue StandardError => e
|
295
323
|
loc = "resolve_reference in #{plugin_name}"
|
296
324
|
raise PluginError::ExecutionError.new(e.message, plugin_name, loc)
|
297
325
|
end
|
326
|
+
|
327
|
+
plugin_cache.write_cache(result) if cache?(reference)
|
328
|
+
|
329
|
+
result
|
298
330
|
end
|
299
331
|
private :resolve_single_reference
|
300
332
|
|
333
|
+
private def cache?(reference)
|
334
|
+
reference.key?('_cache') || @config.plugin_cache.key?('ttl')
|
335
|
+
end
|
336
|
+
|
301
337
|
# Checks whether a given value is a _plugin reference
|
302
338
|
def reference?(input)
|
303
339
|
input.is_a?(Hash) && input.key?('_plugin')
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'bolt/error'
|
5
|
+
require 'bolt/util'
|
6
|
+
|
7
|
+
module Bolt
|
8
|
+
class Plugin
|
9
|
+
class Cache
|
10
|
+
attr_reader :reference, :cache_file, :default_config, :id
|
11
|
+
|
12
|
+
def initialize(reference, cache_file, default_config)
|
13
|
+
@reference = reference
|
14
|
+
@cache_file = cache_file
|
15
|
+
@default_config = default_config
|
16
|
+
end
|
17
|
+
|
18
|
+
def read_and_clean_cache
|
19
|
+
return if ttl == 0
|
20
|
+
validate
|
21
|
+
|
22
|
+
# Luckily we don't need to use a serious hash algorithm
|
23
|
+
require 'digest/bubblebabble'
|
24
|
+
r = reference.reject { |k, _| k == '_cache' }.sort.to_s
|
25
|
+
@id = Digest::SHA2.bubblebabble(r)[0..20]
|
26
|
+
|
27
|
+
unmodified = true
|
28
|
+
# First remove any cache entries past their ttl
|
29
|
+
# This prevents removing plugins from leaving orphaned cache entries
|
30
|
+
cache.delete_if do |_, entry|
|
31
|
+
expired = Time.now - Time.parse(entry['mtime']) >= entry['ttl']
|
32
|
+
unmodified = false if expired
|
33
|
+
expired
|
34
|
+
end
|
35
|
+
File.write(cache_file, cache.to_json) unless cache.empty? || unmodified
|
36
|
+
|
37
|
+
cache.dig(id, 'result')
|
38
|
+
end
|
39
|
+
|
40
|
+
private def cache
|
41
|
+
@cache ||= Bolt::Util.read_optional_json_file(@cache_file, 'cache')
|
42
|
+
end
|
43
|
+
|
44
|
+
def write_cache(result)
|
45
|
+
cache.merge!({ id => { 'result' => result,
|
46
|
+
'mtime' => Time.now,
|
47
|
+
'ttl' => ttl } })
|
48
|
+
FileUtils.touch(cache_file)
|
49
|
+
File.write(cache_file, cache.to_json)
|
50
|
+
end
|
51
|
+
|
52
|
+
def validate
|
53
|
+
# The default cache `plugin-cache` will be validated by the config
|
54
|
+
# validator
|
55
|
+
return if reference['_cache'].nil?
|
56
|
+
r = reference['_cache']
|
57
|
+
unless r.is_a?(Hash)
|
58
|
+
raise Bolt::ValidationError,
|
59
|
+
"_cache must be a Hash, received #{r.class}: #{r.inspect}"
|
60
|
+
end
|
61
|
+
|
62
|
+
unless r.key?('ttl')
|
63
|
+
raise Bolt::ValidationError, "_cache must set 'ttl' key."
|
64
|
+
end
|
65
|
+
|
66
|
+
unless r['ttl'] >= 0
|
67
|
+
raise Bolt::ValidationError, "'ttl' key under '_cache' must be a minimum of 0."
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private def ttl
|
72
|
+
@ttl ||= reference.dig('_cache', 'ttl') || default_config['ttl']
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/bolt/plugin/module.rb
CHANGED
@@ -12,15 +12,15 @@ module Bolt
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
if mod
|
15
|
+
# mod should not be nil
|
16
|
+
def self.load(mod, opts)
|
17
|
+
if mod.plugin?
|
18
18
|
opts[:mod] = mod
|
19
19
|
plugin = Bolt::Plugin::Module.new(**opts)
|
20
20
|
plugin.setup
|
21
21
|
plugin
|
22
22
|
else
|
23
|
-
raise PluginError::Unknown, name
|
23
|
+
raise PluginError::Unknown, mod.name
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
data/lib/bolt/plugin/puppetdb.rb
CHANGED
data/lib/bolt/project.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'pathname'
|
4
4
|
require 'bolt/config'
|
5
|
-
require 'bolt/
|
5
|
+
require 'bolt/validator'
|
6
6
|
require 'bolt/pal'
|
7
7
|
require 'bolt/module'
|
8
8
|
|
@@ -14,7 +14,7 @@ module Bolt
|
|
14
14
|
attr_reader :path, :data, :config_file, :inventory_file, :hiera_config,
|
15
15
|
:puppetfile, :rerunfile, :type, :resource_types, :logs, :project_file,
|
16
16
|
:deprecations, :downloads, :plans_path, :modulepath, :managed_moduledir,
|
17
|
-
:backup_dir
|
17
|
+
:backup_dir, :cache_file
|
18
18
|
|
19
19
|
def self.default_project(logs = [])
|
20
20
|
create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user', logs)
|
@@ -27,23 +27,31 @@ module Bolt
|
|
27
27
|
# directory called Boltdir or a file called bolt.yaml (for a control repo
|
28
28
|
# type Project). Otherwise, repeat the check on each directory up the
|
29
29
|
# hierarchy, falling back to the default if we reach the root.
|
30
|
-
def self.find_boltdir(dir, logs = [])
|
30
|
+
def self.find_boltdir(dir, logs = [], deprecations = [])
|
31
31
|
dir = Pathname.new(dir)
|
32
32
|
|
33
33
|
if (dir + BOLTDIR_NAME).directory?
|
34
34
|
create_project(dir + BOLTDIR_NAME, 'embedded', logs)
|
35
|
-
elsif (dir + 'bolt.yaml').file?
|
35
|
+
elsif (dir + 'bolt.yaml').file?
|
36
|
+
command = Bolt::Util.powershell? ? 'Update-BoltProject' : 'bolt project migrate'
|
37
|
+
msg = "Configuration file #{dir + 'bolt.yaml'} is deprecated and will be "\
|
38
|
+
"removed in Bolt 3.0.\nUpdate your Bolt project to the latest Bolt practices "\
|
39
|
+
"using #{command}"
|
40
|
+
deprecations << { type: "Project level bolt.yaml",
|
41
|
+
msg: msg }
|
42
|
+
create_project(dir, 'local', logs, deprecations)
|
43
|
+
elsif (dir + CONFIG_NAME).file?
|
36
44
|
create_project(dir, 'local', logs)
|
37
45
|
elsif dir.root?
|
38
46
|
default_project(logs)
|
39
47
|
else
|
40
48
|
logs << { debug: "Did not detect Boltdir, bolt.yaml, or bolt-project.yaml at '#{dir}'. "\
|
41
49
|
"This directory won't be loaded as a project." }
|
42
|
-
find_boltdir(dir.parent, logs)
|
50
|
+
find_boltdir(dir.parent, logs, deprecations)
|
43
51
|
end
|
44
52
|
end
|
45
53
|
|
46
|
-
def self.create_project(path, type = 'option', logs = [])
|
54
|
+
def self.create_project(path, type = 'option', logs = [], deprecations = [])
|
47
55
|
fullpath = Pathname.new(path).expand_path
|
48
56
|
|
49
57
|
if type == 'user'
|
@@ -72,15 +80,10 @@ module Bolt
|
|
72
80
|
data = Bolt::Util.read_optional_yaml_hash(File.expand_path(project_file), 'project')
|
73
81
|
default = type =~ /user|system/ ? 'default ' : ''
|
74
82
|
exist = File.exist?(File.expand_path(project_file))
|
75
|
-
deprecations = []
|
76
83
|
|
77
84
|
logs << { info: "Loaded #{default}project from '#{fullpath}'" } if exist
|
78
85
|
|
79
|
-
|
80
|
-
# with all validation errors.
|
81
|
-
schema = Bolt::Config::OPTIONS.slice(*Bolt::Config::BOLT_PROJECT_OPTIONS)
|
82
|
-
|
83
|
-
Bolt::Config::Validator.new.tap do |validator|
|
86
|
+
Bolt::Validator.new.tap do |validator|
|
84
87
|
validator.validate(data, schema, project_file)
|
85
88
|
|
86
89
|
validator.warnings.each { |warning| logs << { warn: warning } }
|
@@ -93,6 +96,16 @@ module Bolt
|
|
93
96
|
new(data, path, type, logs, deprecations)
|
94
97
|
end
|
95
98
|
|
99
|
+
# Builds the schema for bolt-project.yaml used by the validator.
|
100
|
+
#
|
101
|
+
def self.schema
|
102
|
+
{
|
103
|
+
type: Hash,
|
104
|
+
properties: Bolt::Config::BOLT_PROJECT_OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
|
105
|
+
definitions: Bolt::Config::OPTIONS
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
96
109
|
def initialize(raw_data, path, type = 'option', logs = [], deprecations = [])
|
97
110
|
@path = Pathname.new(path).expand_path
|
98
111
|
@project_file = @path + CONFIG_NAME
|
@@ -116,6 +129,7 @@ module Bolt
|
|
116
129
|
@plans_path = @path + 'plans'
|
117
130
|
@managed_moduledir = @path + '.modules'
|
118
131
|
@backup_dir = @path + '.bolt-bak'
|
132
|
+
@cache_file = @path + '.plugin_cache.json'
|
119
133
|
|
120
134
|
tc = Bolt::Config::INVENTORY_OPTIONS.keys & raw_data.keys
|
121
135
|
if tc.any?
|
@@ -184,6 +198,14 @@ module Bolt
|
|
184
198
|
@data['plans']
|
185
199
|
end
|
186
200
|
|
201
|
+
def plugin_cache
|
202
|
+
@data['plugin-cache']
|
203
|
+
end
|
204
|
+
|
205
|
+
def module_install
|
206
|
+
@data['module-install']
|
207
|
+
end
|
208
|
+
|
187
209
|
def modules
|
188
210
|
@modules ||= @data['modules']&.map do |mod|
|
189
211
|
if mod.is_a?(String)
|
@@ -205,7 +227,10 @@ module Bolt
|
|
205
227
|
raise Bolt::ValidationError, "The project '#{name}' will not be loaded. The project name conflicts "\
|
206
228
|
"with a built-in Bolt module of the same name."
|
207
229
|
end
|
208
|
-
|
230
|
+
elsif name.nil? &&
|
231
|
+
(File.directory?(plans_path) ||
|
232
|
+
File.directory?(@path + 'tasks') ||
|
233
|
+
File.directory?(@path + 'files'))
|
209
234
|
message = "No project name is specified in bolt-project.yaml. Project-level content will not be available."
|
210
235
|
@logs << { warn: message }
|
211
236
|
end
|