bolt 2.37.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.
- checksums.yaml +4 -4
- data/Puppetfile +17 -17
- data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +6 -8
- data/lib/bolt/analytics.rb +3 -2
- data/lib/bolt/applicator.rb +11 -1
- data/lib/bolt/bolt_option_parser.rb +20 -13
- data/lib/bolt/catalog.rb +10 -29
- data/lib/bolt/cli.rb +58 -40
- data/lib/bolt/config.rb +134 -119
- data/lib/bolt/config/options.rb +142 -77
- 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 +18 -68
- 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 +26 -0
- data/lib/bolt/inventory/group.rb +35 -12
- 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/logger.rb +114 -10
- data/lib/bolt/module.rb +10 -2
- data/lib/bolt/module_installer.rb +25 -15
- 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.rb +19 -5
- data/lib/bolt/outputter/human.rb +24 -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.rb +93 -14
- data/lib/bolt/pal/yaml_plan.rb +8 -2
- data/lib/bolt/pal/yaml_plan/evaluator.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 +1 -1
- data/lib/bolt/plugin/puppet_connect_data.rb +35 -0
- data/lib/bolt/plugin/puppetdb.rb +2 -2
- data/lib/bolt/project.rb +76 -50
- 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 -1
- data/lib/bolt/shell/bash.rb +1 -1
- data/lib/bolt/shell/bash/tmpdir.rb +4 -1
- data/lib/bolt/shell/powershell.rb +7 -5
- data/lib/bolt/target.rb +4 -0
- data/lib/bolt/task.rb +1 -1
- data/lib/bolt/transport/docker/connection.rb +2 -2
- data/lib/bolt/transport/local.rb +13 -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 +1 -1
- data/lib/bolt/validator.rb +227 -0
- 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 +28 -27
- data/libexec/bolt_catalog +1 -1
- metadata +27 -11
- data/lib/bolt/config/validator.rb +0 -231
data/lib/bolt/pal.rb
CHANGED
@@ -153,8 +153,10 @@ module Bolt
|
|
153
153
|
Dir.children(path).select { |name| Puppet::Module.is_module_directory?(name, path) }
|
154
154
|
end
|
155
155
|
if modules.include?(project.name)
|
156
|
-
Bolt::Logger.warn_once(
|
157
|
-
|
156
|
+
Bolt::Logger.warn_once(
|
157
|
+
"project_shadows_module",
|
158
|
+
"The project '#{project.name}' shadows an existing module of the same name"
|
159
|
+
)
|
158
160
|
end
|
159
161
|
end
|
160
162
|
|
@@ -357,19 +359,52 @@ module Bolt
|
|
357
359
|
Bolt::Task.from_task_signature(task)
|
358
360
|
end
|
359
361
|
|
362
|
+
def list_plans_with_cache(filter_content: false)
|
363
|
+
# Don't filter content yet, so that if users update their plan filters
|
364
|
+
# we don't need to refresh the cache
|
365
|
+
plan_names = list_plans(filter_content: false).map(&:first)
|
366
|
+
plan_cache = if @project
|
367
|
+
Bolt::Util.read_optional_json_file(@project.plan_cache_file, 'Plan cache file')
|
368
|
+
else
|
369
|
+
{}
|
370
|
+
end
|
371
|
+
updated = false
|
372
|
+
|
373
|
+
plan_list = plan_names.each_with_object([]) do |plan_name, list|
|
374
|
+
info = plan_cache[plan_name] || get_plan_info(plan_name, with_mtime: true)
|
375
|
+
|
376
|
+
# If the plan is a 'local' plan (in the project itself, or the
|
377
|
+
# modules/ directory) then verify it hasn't been updated since we
|
378
|
+
# cached it. If it has been updated, refresh the cache and use the
|
379
|
+
# new data.
|
380
|
+
if info['file'] &&
|
381
|
+
(File.mtime(info.dig('file', 'path')) <=> info.dig('file', 'mtime')) != 0
|
382
|
+
info = get_plan_info(plan_name, with_mtime: true)
|
383
|
+
updated = true
|
384
|
+
plan_cache[plan_name] = info
|
385
|
+
end
|
386
|
+
|
387
|
+
list << [plan_name] unless info['private']
|
388
|
+
end
|
389
|
+
|
390
|
+
File.write(@project.plan_cache_file, plan_cache.to_json) if updated
|
391
|
+
|
392
|
+
filter_content ? filter_content(plan_list, @project&.plans) : plan_list
|
393
|
+
end
|
394
|
+
|
360
395
|
def list_plans(filter_content: false)
|
361
396
|
in_bolt_compiler do |compiler|
|
362
397
|
errors = []
|
363
398
|
plans = compiler.list_plans(nil, errors).map { |plan| [plan.name] }.sort
|
364
399
|
errors.each do |error|
|
365
|
-
|
400
|
+
Bolt::Logger.warn("plan_load_error", error.details['original_error'])
|
366
401
|
end
|
367
402
|
|
368
403
|
filter_content ? filter_content(plans, @project&.plans) : plans
|
369
404
|
end
|
370
405
|
end
|
371
406
|
|
372
|
-
def get_plan_info(plan_name)
|
407
|
+
def get_plan_info(plan_name, with_mtime: false)
|
373
408
|
plan_sig = in_bolt_compiler do |compiler|
|
374
409
|
compiler.plan_signature(plan_name)
|
375
410
|
end
|
@@ -412,16 +447,28 @@ module Bolt
|
|
412
447
|
params[name]['default_value'] = defaults[name] if defaults.key?(name)
|
413
448
|
params[name]['description'] = param.text unless param.text.empty?
|
414
449
|
else
|
415
|
-
|
450
|
+
Bolt::Logger.warn(
|
451
|
+
"missing_plan_parameter",
|
452
|
+
"The documented parameter '#{name}' does not exist in plan signature"
|
453
|
+
)
|
416
454
|
end
|
417
455
|
end
|
418
456
|
|
419
|
-
|
420
|
-
|
457
|
+
privie = plan.tag(:private)&.text
|
458
|
+
unless privie.nil? || %w[true false].include?(privie.downcase)
|
459
|
+
msg = "Plan #{plan_name} key 'private' must be a boolean, received: #{privie}"
|
460
|
+
raise Bolt::Error.new(msg, 'bolt/invalid-plan')
|
461
|
+
end
|
462
|
+
|
463
|
+
pp_info = {
|
464
|
+
'name' => plan_name,
|
421
465
|
'description' => description,
|
422
|
-
'parameters'
|
423
|
-
'module'
|
466
|
+
'parameters' => parameters,
|
467
|
+
'module' => mod
|
424
468
|
}
|
469
|
+
pp_info.merge!({ 'private' => privie&.downcase == 'true' }) unless privie.nil?
|
470
|
+
pp_info.merge!(get_plan_mtime(plan.file)) if with_mtime
|
471
|
+
pp_info
|
425
472
|
|
426
473
|
# If it's a YAML plan, fall back to limited data
|
427
474
|
else
|
@@ -444,12 +491,32 @@ module Bolt
|
|
444
491
|
params[name]['default_value'] = param.value unless param.value.nil?
|
445
492
|
params[name]['description'] = param.description if param.description
|
446
493
|
end
|
447
|
-
|
448
|
-
|
494
|
+
|
495
|
+
yaml_info = {
|
496
|
+
'name' => plan_name,
|
449
497
|
'description' => plan.description,
|
450
|
-
'parameters'
|
451
|
-
'module'
|
498
|
+
'parameters' => parameters,
|
499
|
+
'module' => mod
|
452
500
|
}
|
501
|
+
yaml_info.merge!({ 'private' => plan.private }) unless plan.private.nil?
|
502
|
+
yaml_info.merge!(get_plan_mtime(yaml_path)) if with_mtime
|
503
|
+
yaml_info
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
def get_plan_mtime(path)
|
508
|
+
# If the plan is from the project modules/ directory, or is in the
|
509
|
+
# project itself, include the last mtime of the file so we can compare
|
510
|
+
# if the plan has been updated since it was cached.
|
511
|
+
if @project &&
|
512
|
+
File.exist?(path) &&
|
513
|
+
(path.include?(File.join(@project.path, 'modules')) ||
|
514
|
+
path.include?(@project.plans_path.to_s))
|
515
|
+
|
516
|
+
{ 'file' => { 'mtime' => File.mtime(path),
|
517
|
+
'path' => path } }
|
518
|
+
else
|
519
|
+
{}
|
453
520
|
end
|
454
521
|
end
|
455
522
|
|
@@ -490,16 +557,28 @@ module Bolt
|
|
490
557
|
end
|
491
558
|
end
|
492
559
|
|
493
|
-
def generate_types
|
560
|
+
def generate_types(cache: false)
|
494
561
|
require 'puppet/face/generate'
|
495
562
|
in_bolt_compiler do
|
496
563
|
generator = Puppet::Generate::Type
|
497
564
|
inputs = generator.find_inputs(:pcore)
|
498
565
|
FileUtils.mkdir_p(@resource_types)
|
566
|
+
cache_plan_info if @project && cache
|
499
567
|
generator.generate(inputs, @resource_types, true)
|
500
568
|
end
|
501
569
|
end
|
502
570
|
|
571
|
+
def cache_plan_info
|
572
|
+
# plan_name is an array here
|
573
|
+
plans_info = list_plans(filter_content: false).map do |plan_name,|
|
574
|
+
data = get_plan_info(plan_name, with_mtime: true)
|
575
|
+
{ plan_name => data }
|
576
|
+
end.reduce({}, :merge)
|
577
|
+
|
578
|
+
FileUtils.touch(@project.plan_cache_file)
|
579
|
+
File.write(@project.plan_cache_file, plans_info.to_json)
|
580
|
+
end
|
581
|
+
|
503
582
|
def run_task(task_name, targets, params, executor, inventory, description = nil)
|
504
583
|
in_task_compiler(executor, inventory) do |compiler|
|
505
584
|
params = params.merge('_bolt_api_call' => true, '_catch_errors' => true)
|
data/lib/bolt/pal/yaml_plan.rb
CHANGED
@@ -6,10 +6,10 @@ require 'bolt/pal/yaml_plan/step'
|
|
6
6
|
module Bolt
|
7
7
|
class PAL
|
8
8
|
class YamlPlan
|
9
|
-
PLAN_KEYS = Set['parameters', 'steps', 'return', 'version', 'description']
|
9
|
+
PLAN_KEYS = Set['parameters', 'private', 'steps', 'return', 'version', 'description']
|
10
10
|
VAR_NAME_PATTERN = /\A[a-z_][a-z0-9_]*\z/.freeze
|
11
11
|
|
12
|
-
attr_reader :name, :parameters, :steps, :return, :description
|
12
|
+
attr_reader :name, :parameters, :private, :steps, :return, :description
|
13
13
|
|
14
14
|
def initialize(name, plan)
|
15
15
|
# Top-level plan keys aren't allowed to be Puppet code, so force them
|
@@ -30,6 +30,12 @@ module Bolt
|
|
30
30
|
Parameter.new(param, definition)
|
31
31
|
end.freeze
|
32
32
|
|
33
|
+
@private = plan['private']
|
34
|
+
unless @private.nil? || @private.is_a?(TrueClass) || @private.is_a?(FalseClass)
|
35
|
+
msg = "Plan #{@name} key 'private' must be a boolean, received: #{@private.inspect}"
|
36
|
+
raise Bolt::Error.new(msg, "bolt/invalid-plan")
|
37
|
+
end
|
38
|
+
|
33
39
|
# Validate top level plan keys
|
34
40
|
top_level_keys = plan.keys.to_set
|
35
41
|
unless PLAN_KEYS.superset?(top_level_keys)
|
@@ -157,13 +157,13 @@ module Bolt
|
|
157
157
|
if plan.steps.any? { |step| step.body.key?('target') }
|
158
158
|
msg = "The 'target' parameter for YAML plan steps is deprecated and will be removed "\
|
159
159
|
"in a future version of Bolt. Use the 'targets' parameter instead."
|
160
|
-
Bolt::Logger.
|
160
|
+
Bolt::Logger.deprecate("yaml_plan_target", msg)
|
161
161
|
end
|
162
162
|
|
163
163
|
if plan.steps.any? { |step| step.body.key?('source') }
|
164
164
|
msg = "The 'source' parameter for YAML plan upload steps is deprecated and will be removed "\
|
165
165
|
"in a future version of Bolt. Use the 'upload' parameter instead."
|
166
|
-
Bolt::Logger.
|
166
|
+
Bolt::Logger.deprecate("yaml_plan_source", msg)
|
167
167
|
end
|
168
168
|
|
169
169
|
plan_result = closure_scope.with_local_scope(args_hash) do |scope|
|
@@ -30,6 +30,7 @@ module Bolt
|
|
30
30
|
plan_string = String.new('')
|
31
31
|
plan_string << "# #{plan_object.description}\n" if plan_object.description
|
32
32
|
plan_string << "# WARNING: This is an autogenerated plan. It may not behave as expected.\n"
|
33
|
+
plan_string << "# @private #{plan_object.private}\n" unless plan_object.private.nil?
|
33
34
|
plan_string << "#{param_descriptions}\n" unless param_descriptions.empty?
|
34
35
|
|
35
36
|
plan_string << "plan #{plan_object.name}("
|
@@ -64,7 +65,11 @@ module Bolt
|
|
64
65
|
raise Bolt::FileError.new(msg, @plan_path)
|
65
66
|
end
|
66
67
|
|
67
|
-
|
68
|
+
begin
|
69
|
+
Bolt::PAL::YamlPlan::Loader.from_string(@modulename, file_contents, @plan_path)
|
70
|
+
rescue Puppet::PreformattedError, StandardError => e
|
71
|
+
raise PALError.from_preformatted_error(e)
|
72
|
+
end
|
68
73
|
end
|
69
74
|
|
70
75
|
def validate_path
|
data/lib/bolt/plugin.rb
CHANGED
@@ -139,7 +139,7 @@ module Bolt
|
|
139
139
|
plugins
|
140
140
|
end
|
141
141
|
|
142
|
-
RUBY_PLUGINS = %w[task prompt env_var puppetdb].freeze
|
142
|
+
RUBY_PLUGINS = %w[task prompt env_var puppetdb puppet_connect_data].freeze
|
143
143
|
BUILTIN_PLUGINS = %w[task terraform pkcs7 prompt vault aws_inventory puppetdb azure_inventory
|
144
144
|
yaml env_var gcloud_inventory].freeze
|
145
145
|
DEFAULT_PLUGIN_HOOKS = { 'puppet_library' => { 'plugin' => 'puppet_agent', 'stop_service' => true } }.freeze
|
@@ -170,7 +170,7 @@ module Bolt
|
|
170
170
|
end
|
171
171
|
|
172
172
|
def modules
|
173
|
-
@modules ||= Bolt::Module.discover(@pal.full_modulepath)
|
173
|
+
@modules ||= Bolt::Module.discover(@pal.full_modulepath, @config.project)
|
174
174
|
end
|
175
175
|
|
176
176
|
def add_plugin(plugin)
|
@@ -297,7 +297,7 @@ module Bolt
|
|
297
297
|
def resolve_single_reference(reference)
|
298
298
|
plugin_cache = if cache?(reference)
|
299
299
|
cache = Bolt::Plugin::Cache.new(reference,
|
300
|
-
@config.project.
|
300
|
+
@config.project.plugin_cache_file,
|
301
301
|
@config.plugin_cache)
|
302
302
|
entry = cache.read_and_clean_cache
|
303
303
|
return entry unless entry.nil?
|
data/lib/bolt/plugin/cache.rb
CHANGED
@@ -7,11 +7,11 @@ require 'bolt/util'
|
|
7
7
|
module Bolt
|
8
8
|
class Plugin
|
9
9
|
class Cache
|
10
|
-
attr_reader :reference, :
|
10
|
+
attr_reader :reference, :plugin_cache_file, :default_config, :id
|
11
11
|
|
12
|
-
def initialize(reference,
|
12
|
+
def initialize(reference, plugin_cache_file, default_config)
|
13
13
|
@reference = reference
|
14
|
-
@
|
14
|
+
@plugin_cache_file = plugin_cache_file
|
15
15
|
@default_config = default_config
|
16
16
|
end
|
17
17
|
|
@@ -21,7 +21,7 @@ module Bolt
|
|
21
21
|
|
22
22
|
# Luckily we don't need to use a serious hash algorithm
|
23
23
|
require 'digest/bubblebabble'
|
24
|
-
r = reference.
|
24
|
+
r = reference.reject { |k, _| k == '_cache' }.sort.to_s
|
25
25
|
@id = Digest::SHA2.bubblebabble(r)[0..20]
|
26
26
|
|
27
27
|
unmodified = true
|
@@ -32,21 +32,21 @@ module Bolt
|
|
32
32
|
unmodified = false if expired
|
33
33
|
expired
|
34
34
|
end
|
35
|
-
File.write(
|
35
|
+
File.write(plugin_cache_file, cache.to_json) unless cache.empty? || unmodified
|
36
36
|
|
37
37
|
cache.dig(id, 'result')
|
38
38
|
end
|
39
39
|
|
40
40
|
private def cache
|
41
|
-
@cache ||= Bolt::Util.read_optional_json_file(@
|
41
|
+
@cache ||= Bolt::Util.read_optional_json_file(@plugin_cache_file, 'cache')
|
42
42
|
end
|
43
43
|
|
44
44
|
def write_cache(result)
|
45
45
|
cache.merge!({ id => { 'result' => result,
|
46
46
|
'mtime' => Time.now,
|
47
47
|
'ttl' => ttl } })
|
48
|
-
FileUtils.touch(
|
49
|
-
File.write(
|
48
|
+
FileUtils.touch(plugin_cache_file)
|
49
|
+
File.write(plugin_cache_file, cache.to_json)
|
50
50
|
end
|
51
51
|
|
52
52
|
def validate
|
data/lib/bolt/plugin/module.rb
CHANGED
@@ -187,7 +187,7 @@ module Bolt
|
|
187
187
|
if params.key?('private-key') || params.key?('public-key')
|
188
188
|
message = "pkcs7 keys 'private-key' and 'public-key' have been deprecated and will be "\
|
189
189
|
"removed in a future version of Bolt; use 'private_key' and 'public_key' instead."
|
190
|
-
Bolt::Logger.
|
190
|
+
Bolt::Logger.deprecate("pkcs7_params", message)
|
191
191
|
end
|
192
192
|
|
193
193
|
params['private_key'] = params.delete('private-key') if params.key?('private-key')
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bolt
|
4
|
+
class Plugin
|
5
|
+
class PuppetConnectData
|
6
|
+
def initialize(context:, **_opts)
|
7
|
+
puppet_connect_data_yaml_path = File.join(context.boltdir, 'puppet_connect_data.yaml')
|
8
|
+
@data = Bolt::Util.read_optional_yaml_hash(
|
9
|
+
puppet_connect_data_yaml_path,
|
10
|
+
'puppet_connect_data.yaml'
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
'puppet_connect_data'
|
16
|
+
end
|
17
|
+
|
18
|
+
def hooks
|
19
|
+
%i[resolve_reference validate_resolve_reference]
|
20
|
+
end
|
21
|
+
|
22
|
+
def resolve_reference(opts)
|
23
|
+
key = opts['key']
|
24
|
+
@data[key]
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate_resolve_reference(opts)
|
28
|
+
unless opts['key']
|
29
|
+
raise Bolt::ValidationError,
|
30
|
+
"puppet_connect_data plugin requires that 'key' be specified"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/bolt/plugin/puppetdb.rb
CHANGED
@@ -12,7 +12,7 @@ module Bolt
|
|
12
12
|
end
|
13
13
|
|
14
14
|
TEMPLATE_OPTS = %w[alias config facts features name uri vars].freeze
|
15
|
-
PLUGIN_OPTS = %w[_plugin query target_mapping].freeze
|
15
|
+
PLUGIN_OPTS = %w[_plugin _cache query target_mapping].freeze
|
16
16
|
|
17
17
|
attr_reader :puppetdb_client
|
18
18
|
|
@@ -31,7 +31,7 @@ module Bolt
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def warn_missing_fact(certname, fact)
|
34
|
-
|
34
|
+
Bolt::Logger.warn("puppetdb_missing_fact", "Could not find fact #{fact} for node #{certname}")
|
35
35
|
end
|
36
36
|
|
37
37
|
def fact_path(raw_fact)
|
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
|
|
@@ -12,46 +12,49 @@ module Bolt
|
|
12
12
|
CONFIG_NAME = 'bolt-project.yaml'
|
13
13
|
|
14
14
|
attr_reader :path, :data, :config_file, :inventory_file, :hiera_config,
|
15
|
-
:puppetfile, :rerunfile, :type, :resource_types, :
|
16
|
-
:
|
17
|
-
:backup_dir, :
|
15
|
+
:puppetfile, :rerunfile, :type, :resource_types, :project_file,
|
16
|
+
:downloads, :plans_path, :modulepath, :managed_moduledir,
|
17
|
+
:backup_dir, :plugin_cache_file, :plan_cache_file
|
18
18
|
|
19
|
-
def self.default_project
|
20
|
-
create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user'
|
19
|
+
def self.default_project
|
20
|
+
create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user')
|
21
21
|
# If homedir isn't defined use the system config path
|
22
22
|
rescue ArgumentError
|
23
|
-
create_project(Bolt::Config.system_path, 'system'
|
23
|
+
create_project(Bolt::Config.system_path, 'system')
|
24
24
|
end
|
25
25
|
|
26
26
|
# Search recursively up the directory hierarchy for the Project. Look for a
|
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
|
30
|
+
def self.find_boltdir(dir)
|
31
31
|
dir = Pathname.new(dir)
|
32
32
|
|
33
33
|
if (dir + BOLTDIR_NAME).directory?
|
34
|
-
create_project(dir + BOLTDIR_NAME, 'embedded'
|
34
|
+
create_project(dir + BOLTDIR_NAME, 'embedded')
|
35
35
|
elsif (dir + 'bolt.yaml').file?
|
36
36
|
command = Bolt::Util.powershell? ? 'Update-BoltProject' : 'bolt project migrate'
|
37
|
-
|
37
|
+
Bolt::Logger.deprecate(
|
38
|
+
"bolt_yaml",
|
39
|
+
"Configuration file #{dir + 'bolt.yaml'} is deprecated and will be "\
|
38
40
|
"removed in Bolt 3.0.\nUpdate your Bolt project to the latest Bolt practices "\
|
39
|
-
"using #{command}"
|
40
|
-
|
41
|
-
|
42
|
-
create_project(dir, 'local', logs, deprecations)
|
41
|
+
"using #{command}."
|
42
|
+
)
|
43
|
+
create_project(dir, 'local')
|
43
44
|
elsif (dir + CONFIG_NAME).file?
|
44
|
-
create_project(dir, 'local'
|
45
|
+
create_project(dir, 'local')
|
45
46
|
elsif dir.root?
|
46
|
-
default_project
|
47
|
+
default_project
|
47
48
|
else
|
48
|
-
|
49
|
-
|
50
|
-
|
49
|
+
Bolt::Logger.debug(
|
50
|
+
"Did not detect Boltdir, bolt.yaml, or bolt-project.yaml at '#{dir}'. "\
|
51
|
+
"This directory won't be loaded as a project."
|
52
|
+
)
|
53
|
+
find_boltdir(dir.parent)
|
51
54
|
end
|
52
55
|
end
|
53
56
|
|
54
|
-
def self.create_project(path, type = 'option'
|
57
|
+
def self.create_project(path, type = 'option')
|
55
58
|
fullpath = Pathname.new(path).expand_path
|
56
59
|
|
57
60
|
if type == 'user'
|
@@ -59,8 +62,11 @@ module Bolt
|
|
59
62
|
# This is already expanded if the type is user
|
60
63
|
FileUtils.mkdir_p(path)
|
61
64
|
rescue StandardError
|
62
|
-
|
63
|
-
|
65
|
+
Bolt::Logger.warn(
|
66
|
+
"non_writeable_project",
|
67
|
+
"Could not create default project at #{path}. Continuing without a writeable project. "\
|
68
|
+
"Log and rerun files will not be written."
|
69
|
+
)
|
64
70
|
end
|
65
71
|
end
|
66
72
|
|
@@ -81,36 +87,40 @@ module Bolt
|
|
81
87
|
default = type =~ /user|system/ ? 'default ' : ''
|
82
88
|
exist = File.exist?(File.expand_path(project_file))
|
83
89
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
# with all validation errors.
|
88
|
-
schema = Bolt::Config::OPTIONS.slice(*Bolt::Config::BOLT_PROJECT_OPTIONS)
|
90
|
+
if exist
|
91
|
+
Bolt::Logger.info("Loaded #{default}project from '#{fullpath}'")
|
92
|
+
end
|
89
93
|
|
90
|
-
Bolt::
|
94
|
+
Bolt::Validator.new.tap do |validator|
|
91
95
|
validator.validate(data, schema, project_file)
|
92
|
-
|
93
|
-
validator.
|
94
|
-
|
95
|
-
validator.deprecations.each do |dep|
|
96
|
-
deprecations << { type: "#{CONFIG_NAME} #{dep[:option]}", msg: dep[:message] }
|
97
|
-
end
|
96
|
+
validator.warnings.each { |warning| Bolt::Logger.warn(warning[:id], warning[:msg]) }
|
97
|
+
validator.deprecations.each { |dep| Bolt::Logger.deprecate(dep[:id], dep[:msg]) }
|
98
98
|
end
|
99
99
|
|
100
|
-
new(data, path, type
|
100
|
+
new(data, path, type)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Builds the schema for bolt-project.yaml used by the validator.
|
104
|
+
#
|
105
|
+
def self.schema
|
106
|
+
{
|
107
|
+
type: Hash,
|
108
|
+
properties: Bolt::Config::BOLT_PROJECT_OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
|
109
|
+
definitions: Bolt::Config::OPTIONS
|
110
|
+
}
|
101
111
|
end
|
102
112
|
|
103
|
-
def initialize(raw_data, path, type = 'option'
|
113
|
+
def initialize(raw_data, path, type = 'option')
|
104
114
|
@path = Pathname.new(path).expand_path
|
105
115
|
@project_file = @path + CONFIG_NAME
|
106
|
-
@logs = logs
|
107
|
-
@deprecations = deprecations
|
108
116
|
|
109
117
|
if (@path + 'bolt.yaml').file? && project_file?
|
110
|
-
|
118
|
+
Bolt::Logger.deprecate(
|
119
|
+
"bolt_yaml",
|
120
|
+
"Project-level configuration in bolt.yaml is deprecated if using bolt-project.yaml. "\
|
111
121
|
"Transport config should be set in inventory.yaml, all other config should be set in "\
|
112
122
|
"bolt-project.yaml."
|
113
|
-
|
123
|
+
)
|
114
124
|
end
|
115
125
|
|
116
126
|
@inventory_file = @path + 'inventory.yaml'
|
@@ -123,12 +133,14 @@ module Bolt
|
|
123
133
|
@plans_path = @path + 'plans'
|
124
134
|
@managed_moduledir = @path + '.modules'
|
125
135
|
@backup_dir = @path + '.bolt-bak'
|
126
|
-
@
|
136
|
+
@plugin_cache_file = @path + '.plugin_cache.json'
|
137
|
+
@plan_cache_file = @path + '.plan_cache.json'
|
127
138
|
|
128
|
-
tc = Bolt::Config::INVENTORY_OPTIONS.keys & raw_data.keys
|
129
|
-
|
130
|
-
|
131
|
-
|
139
|
+
if (tc = Bolt::Config::INVENTORY_OPTIONS.keys & raw_data.keys).any?
|
140
|
+
Bolt::Logger.warn(
|
141
|
+
"project_transport_config",
|
142
|
+
"Transport configuration isn't supported in bolt-project.yaml. Ignoring keys #{tc}."
|
143
|
+
)
|
132
144
|
end
|
133
145
|
|
134
146
|
@data = raw_data.reject { |k, _| Bolt::Config::INVENTORY_OPTIONS.include?(k) }
|
@@ -145,8 +157,10 @@ module Bolt
|
|
145
157
|
# and replaced with .project_file in lib/bolt/config.rb
|
146
158
|
@config_file = if (Bolt::Config::BOLT_OPTIONS & @data.keys).any?
|
147
159
|
if (@path + 'bolt.yaml').file?
|
148
|
-
|
149
|
-
|
160
|
+
Bolt::Logger.warn(
|
161
|
+
"project_config_conflict",
|
162
|
+
"bolt-project.yaml contains valid config keys, bolt.yaml will be ignored"
|
163
|
+
)
|
150
164
|
end
|
151
165
|
@project_file
|
152
166
|
else
|
@@ -196,6 +210,14 @@ module Bolt
|
|
196
210
|
@data['plugin-cache']
|
197
211
|
end
|
198
212
|
|
213
|
+
def module_install
|
214
|
+
@data['module-install']
|
215
|
+
end
|
216
|
+
|
217
|
+
def disable_warnings
|
218
|
+
@data['disable-warnings'] || []
|
219
|
+
end
|
220
|
+
|
199
221
|
def modules
|
200
222
|
@modules ||= @data['modules']&.map do |mod|
|
201
223
|
if mod.is_a?(String)
|
@@ -217,16 +239,20 @@ module Bolt
|
|
217
239
|
raise Bolt::ValidationError, "The project '#{name}' will not be loaded. The project name conflicts "\
|
218
240
|
"with a built-in Bolt module of the same name."
|
219
241
|
end
|
220
|
-
|
242
|
+
elsif name.nil? &&
|
243
|
+
(File.directory?(plans_path) ||
|
244
|
+
File.directory?(@path + 'tasks') ||
|
245
|
+
File.directory?(@path + 'files'))
|
221
246
|
message = "No project name is specified in bolt-project.yaml. Project-level content will not be available."
|
222
|
-
|
247
|
+
|
248
|
+
Bolt::Logger.warn("missing_project_name", message)
|
223
249
|
end
|
224
250
|
end
|
225
251
|
|
226
252
|
def check_deprecated_file
|
227
253
|
if (@path + 'project.yaml').file?
|
228
254
|
msg = "Project configuration file 'project.yaml' is deprecated; use 'bolt-project.yaml' instead."
|
229
|
-
Bolt::Logger.
|
255
|
+
Bolt::Logger.warn("project_yaml", msg)
|
230
256
|
end
|
231
257
|
end
|
232
258
|
end
|