bolt 2.28.0 → 2.33.1

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.

Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +15 -14
  3. data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +1 -1
  4. data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +6 -0
  5. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +2 -2
  6. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +1 -1
  7. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +1 -1
  8. data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +1 -1
  9. data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +1 -1
  10. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
  11. data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +2 -2
  12. data/bolt-modules/out/lib/puppet/functions/out/message.rb +44 -1
  13. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +3 -0
  14. data/guides/logging.txt +18 -0
  15. data/guides/module.txt +19 -0
  16. data/guides/modulepath.txt +25 -0
  17. data/lib/bolt/bolt_option_parser.rb +50 -28
  18. data/lib/bolt/catalog.rb +1 -1
  19. data/lib/bolt/cli.rb +159 -112
  20. data/lib/bolt/config.rb +13 -1
  21. data/lib/bolt/config/modulepath.rb +30 -0
  22. data/lib/bolt/config/options.rb +38 -9
  23. data/lib/bolt/config/transport/options.rb +2 -2
  24. data/lib/bolt/error.rb +4 -0
  25. data/lib/bolt/executor.rb +13 -13
  26. data/lib/bolt/inventory.rb +10 -9
  27. data/lib/bolt/logger.rb +26 -19
  28. data/lib/bolt/module_installer.rb +198 -0
  29. data/lib/bolt/{puppetfile → module_installer}/installer.rb +3 -2
  30. data/lib/bolt/module_installer/puppetfile.rb +117 -0
  31. data/lib/bolt/module_installer/puppetfile/forge_module.rb +54 -0
  32. data/lib/bolt/module_installer/puppetfile/git_module.rb +37 -0
  33. data/lib/bolt/module_installer/puppetfile/module.rb +26 -0
  34. data/lib/bolt/module_installer/resolver.rb +76 -0
  35. data/lib/bolt/module_installer/specs.rb +93 -0
  36. data/lib/bolt/module_installer/specs/forge_spec.rb +84 -0
  37. data/lib/bolt/module_installer/specs/git_spec.rb +178 -0
  38. data/lib/bolt/outputter.rb +2 -45
  39. data/lib/bolt/outputter/human.rb +78 -18
  40. data/lib/bolt/outputter/json.rb +22 -7
  41. data/lib/bolt/outputter/logger.rb +2 -2
  42. data/lib/bolt/pal.rb +55 -45
  43. data/lib/bolt/pal/yaml_plan.rb +4 -2
  44. data/lib/bolt/pal/yaml_plan/evaluator.rb +23 -1
  45. data/lib/bolt/pal/yaml_plan/loader.rb +14 -9
  46. data/lib/bolt/plugin.rb +1 -1
  47. data/lib/bolt/plugin/module.rb +1 -1
  48. data/lib/bolt/project.rb +32 -22
  49. data/lib/bolt/project_migrator.rb +80 -0
  50. data/lib/bolt/project_migrator/base.rb +39 -0
  51. data/lib/bolt/project_migrator/config.rb +67 -0
  52. data/lib/bolt/project_migrator/inventory.rb +67 -0
  53. data/lib/bolt/project_migrator/modules.rb +200 -0
  54. data/lib/bolt/result.rb +23 -11
  55. data/lib/bolt/shell/bash.rb +15 -9
  56. data/lib/bolt/shell/powershell.rb +11 -6
  57. data/lib/bolt/transport/base.rb +18 -18
  58. data/lib/bolt/transport/docker.rb +23 -6
  59. data/lib/bolt/transport/orch.rb +23 -14
  60. data/lib/bolt/transport/remote.rb +2 -2
  61. data/lib/bolt/transport/simple.rb +6 -6
  62. data/lib/bolt/transport/ssh/connection.rb +1 -1
  63. data/lib/bolt/util.rb +41 -0
  64. data/lib/bolt/version.rb +1 -1
  65. data/lib/bolt_server/acl.rb +2 -2
  66. data/lib/bolt_server/base_config.rb +3 -3
  67. data/lib/bolt_server/schemas/partials/task.json +17 -2
  68. data/lib/bolt_server/transport_app.rb +93 -13
  69. data/lib/bolt_spec/bolt_context.rb +4 -2
  70. data/lib/bolt_spec/plans.rb +1 -1
  71. data/lib/bolt_spec/plans/action_stubs/command_stub.rb +1 -1
  72. data/lib/bolt_spec/plans/action_stubs/script_stub.rb +1 -1
  73. data/lib/bolt_spec/plans/mock_executor.rb +6 -6
  74. data/lib/bolt_spec/run.rb +1 -1
  75. metadata +31 -12
  76. data/lib/bolt/project_migrate.rb +0 -138
  77. data/lib/bolt/puppetfile.rb +0 -160
  78. data/lib/bolt/puppetfile/module.rb +0 -66
  79. data/lib/bolt_server/pe/pal.rb +0 -67
  80. data/modules/secure_env_vars/plans/init.pp +0 -20
@@ -97,7 +97,7 @@ module Bolt
97
97
  }
98
98
 
99
99
  with_puppet_settings(puppet_settings) do
100
- Puppet::Pal.in_tmp_environment('bolt_catalog', env_conf) do |pal|
100
+ Puppet::Pal.in_tmp_environment('bolt_catalog', **env_conf) do |pal|
101
101
  Puppet.override(puppet_overrides) do
102
102
  Puppet.lookup(:pal_current_node).trusted_data = target['trusted']
103
103
  pal.with_catalog_compiler do |compiler|
@@ -20,28 +20,31 @@ require 'bolt/logger'
20
20
  require 'bolt/outputter'
21
21
  require 'bolt/puppetdb'
22
22
  require 'bolt/plugin'
23
- require 'bolt/project_migrate'
23
+ require 'bolt/project_migrator'
24
24
  require 'bolt/pal'
25
25
  require 'bolt/target'
26
26
  require 'bolt/version'
27
27
  require 'bolt/secret'
28
+ require 'bolt/module_installer'
28
29
 
29
30
  module Bolt
30
31
  class CLIExit < StandardError; end
31
32
  class CLI
32
- COMMANDS = { 'command' => %w[run],
33
- 'script' => %w[run],
34
- 'task' => %w[show run],
35
- 'plan' => %w[show run convert new],
36
- 'file' => %w[download upload],
37
- 'puppetfile' => %w[install show-modules generate-types],
38
- 'secret' => %w[encrypt decrypt createkeys],
39
- 'inventory' => %w[show],
40
- 'group' => %w[show],
41
- 'project' => %w[init migrate],
42
- 'apply' => %w[],
43
- 'guide' => %w[],
44
- 'module' => %w[install show generate-types] }.freeze
33
+ COMMANDS = {
34
+ 'command' => %w[run],
35
+ 'script' => %w[run],
36
+ 'task' => %w[show run],
37
+ 'plan' => %w[show run convert new],
38
+ 'file' => %w[download upload],
39
+ 'puppetfile' => %w[install show-modules generate-types],
40
+ 'secret' => %w[encrypt decrypt createkeys],
41
+ 'inventory' => %w[show],
42
+ 'group' => %w[show],
43
+ 'project' => %w[init migrate],
44
+ 'module' => %w[add generate-types install show],
45
+ 'apply' => %w[],
46
+ 'guide' => %w[]
47
+ }.freeze
45
48
 
46
49
  attr_reader :config, :options
47
50
 
@@ -99,6 +102,10 @@ module Bolt
99
102
  # This part aims to handle both `bolt <mode> --help` and `bolt help <mode>`.
100
103
  remaining = handle_parser_errors { parser.permute(@argv) } unless @argv.empty?
101
104
  if @argv.empty? || help?(remaining)
105
+ # If the subcommand is not enabled, display the default
106
+ # help text
107
+ options[:subcommand] = nil unless COMMANDS.include?(options[:subcommand])
108
+
102
109
  # Update the parser for the subcommand (or lack thereof)
103
110
  parser.update
104
111
  puts parser.help
@@ -107,6 +114,11 @@ module Bolt
107
114
 
108
115
  options[:object] = remaining.shift
109
116
 
117
+ # Handle reading a command from a file
118
+ if options[:subcommand] == 'command' && options[:object]
119
+ options[:object] = Bolt::Util.get_arg_input(options[:object])
120
+ end
121
+
110
122
  # Only parse task_options for task or plan
111
123
  if %w[task plan].include?(options[:subcommand])
112
124
  task_options, remaining = remaining.partition { |s| s =~ /.+=/ }
@@ -184,6 +196,10 @@ module Bolt
184
196
 
185
197
  warn_inventory_overrides_cli(options)
186
198
 
199
+ # Assert whether the puppetfile/module commands are available depending
200
+ # on whether 'modules' is configured.
201
+ assert_puppetfile_or_module_command(config.project.modules)
202
+
187
203
  options
188
204
  rescue Bolt::Error => e
189
205
  outputter.fatal_error(e)
@@ -211,14 +227,10 @@ module Bolt
211
227
  end
212
228
 
213
229
  def validate(options)
214
- # Disables the 'module' subcommand unless the module feature flag is set.
215
- commands = COMMANDS.dup
216
- commands.delete('module') unless ENV['BOLT_MODULE_FEATURE']
217
-
218
- unless commands.include?(options[:subcommand])
230
+ unless COMMANDS.include?(options[:subcommand])
219
231
  raise Bolt::CLIError,
220
232
  "Expected subcommand '#{options[:subcommand]}' to be one of " \
221
- "#{commands.keys.join(', ')}"
233
+ "#{COMMANDS.keys.join(', ')}"
222
234
  end
223
235
 
224
236
  actions = COMMANDS[options[:subcommand]]
@@ -235,12 +247,6 @@ module Bolt
235
247
  end
236
248
  end
237
249
 
238
- if options[:subcommand] != 'file' && options[:subcommand] != 'script' &&
239
- !options[:leftovers].empty?
240
- raise Bolt::CLIError,
241
- "Unknown argument(s) #{options[:leftovers].join(', ')}"
242
- end
243
-
244
250
  if %w[task plan].include?(options[:subcommand]) && options[:action] == 'run'
245
251
  if options[:object].nil?
246
252
  raise Bolt::CLIError, "Must specify a #{options[:subcommand]} to run"
@@ -252,23 +258,6 @@ module Bolt
252
258
  end
253
259
  end
254
260
 
255
- if options[:boltdir] && options[:configfile]
256
- raise Bolt::CLIError, "Only one of '--boltdir', '--project', or '--configfile' may be specified"
257
- end
258
-
259
- if options[:noop] &&
260
- !(options[:subcommand] == 'task' && options[:action] == 'run') && options[:subcommand] != 'apply'
261
- raise Bolt::CLIError,
262
- "Option '--noop' may only be specified when running a task or applying manifest code"
263
- end
264
-
265
- if options[:env_vars]
266
- unless %w[command script].include?(options[:subcommand]) && options[:action] == 'run'
267
- raise Bolt::CLIError,
268
- "Option '--env-var' may only be specified when running a command or script"
269
- end
270
- end
271
-
272
261
  if options[:subcommand] == 'apply' && (options[:object] && options[:code])
273
262
  raise Bolt::CLIError, "--execute is unsupported when specifying a manifest file"
274
263
  end
@@ -291,6 +280,38 @@ module Bolt
291
280
  raise Bolt::CLIError, "Must specify a plan name."
292
281
  end
293
282
 
283
+ if options[:subcommand] == 'module' && options[:action] == 'add' && !options[:object]
284
+ raise Bolt::CLIError, "Must specify a module name."
285
+ end
286
+
287
+ if options[:subcommand] == 'module' && options[:action] == 'install' && options[:object]
288
+ raise Bolt::CLIError, "Invalid argument '#{options[:object]}'. To add a new module to "\
289
+ "the project, run 'bolt module add #{options[:object]}'."
290
+ end
291
+
292
+ if options[:subcommand] != 'file' && options[:subcommand] != 'script' &&
293
+ !options[:leftovers].empty?
294
+ raise Bolt::CLIError,
295
+ "Unknown argument(s) #{options[:leftovers].join(', ')}"
296
+ end
297
+
298
+ if options[:boltdir] && options[:configfile]
299
+ raise Bolt::CLIError, "Only one of '--boltdir', '--project', or '--configfile' may be specified"
300
+ end
301
+
302
+ if options[:noop] &&
303
+ !(options[:subcommand] == 'task' && options[:action] == 'run') && options[:subcommand] != 'apply'
304
+ raise Bolt::CLIError,
305
+ "Option '--noop' may only be specified when running a task or applying manifest code"
306
+ end
307
+
308
+ if options[:env_vars]
309
+ unless %w[command script].include?(options[:subcommand]) && options[:action] == 'run'
310
+ raise Bolt::CLIError,
311
+ "Option '--env-var' may only be specified when running a command or script"
312
+ end
313
+ end
314
+
294
315
  if options.key?(:debug) && options.key?(:log)
295
316
  raise Bolt::CLIError, "Only one of '--debug' or '--log-level' may be specified"
296
317
  end
@@ -384,7 +405,7 @@ module Bolt
384
405
  inventory_version: inventory.version)
385
406
  end
386
407
 
387
- analytics.screen_view(screen, screen_view_fields)
408
+ analytics.screen_view(screen, **screen_view_fields)
388
409
 
389
410
  case options[:action]
390
411
  when 'show'
@@ -439,9 +460,7 @@ module Bolt
439
460
  when 'init'
440
461
  code = initialize_project
441
462
  when 'migrate'
442
- inv = config.inventoryfile
443
- path = config.project.path
444
- code = Bolt::ProjectMigrate.new(path, outputter, inv).migrate_project
463
+ code = Bolt::ProjectMigrator.new(config, outputter).migrate
445
464
  end
446
465
  when 'plan'
447
466
  case options[:action]
@@ -452,8 +471,10 @@ module Bolt
452
471
  end
453
472
  when 'module'
454
473
  case options[:action]
474
+ when 'add'
475
+ code = add_project_module(options[:object], config.project)
455
476
  when 'install'
456
- code = install_project_modules
477
+ code = install_project_modules(config.project, options[:force], options[:resolve])
457
478
  when 'generate-types'
458
479
  code = generate_types
459
480
  end
@@ -462,7 +483,11 @@ module Bolt
462
483
  when 'generate-types'
463
484
  code = generate_types
464
485
  when 'install'
465
- code = install_puppetfile(config.puppetfile_config, config.puppetfile, config.modulepath.first)
486
+ code = install_puppetfile(
487
+ config.puppetfile_config,
488
+ config.puppetfile,
489
+ config.modulepath.first
490
+ )
466
491
  end
467
492
  when 'secret'
468
493
  code = Bolt::Secret.execute(plugins, outputter, options)
@@ -562,8 +587,24 @@ module Bolt
562
587
  end
563
588
 
564
589
  def list_targets
590
+ inventoryfile = config.inventoryfile || config.default_inventoryfile
591
+
592
+ # Retrieve the known group and target names. This needs to be done before
593
+ # updating targets, as that will add adhoc targets to the inventory.
594
+ known_names = inventory.target_names
595
+
565
596
  update_targets(options)
566
- outputter.print_targets(options[:targets])
597
+
598
+ inventory_targets, adhoc_targets = options[:targets].partition do |target|
599
+ known_names.include?(target.name)
600
+ end
601
+
602
+ target_list = {
603
+ inventory: inventory_targets,
604
+ adhoc: adhoc_targets
605
+ }
606
+
607
+ outputter.print_targets(target_list, inventoryfile)
567
608
  end
568
609
 
569
610
  def show_targets
@@ -592,10 +633,10 @@ module Bolt
592
633
  message = <<~MESSAGE.chomp
593
634
  Invalid plan name '#{plan_name}'. Plan names are composed of one or more name segments
594
635
  separated by double colons '::'.
595
-
636
+
596
637
  Each name segment must begin with a lowercase letter, and may only include lowercase
597
638
  letters, digits, and underscores.
598
-
639
+
599
640
  Examples of valid plan names:
600
641
  - #{config.project.name}
601
642
  - #{config.project.name}::my_plan
@@ -837,7 +878,8 @@ module Bolt
837
878
  "project with modules."
838
879
  end
839
880
 
840
- install_modules(puppetfile, {}, moduledir, options[:modules])
881
+ installer = Bolt::ModuleInstaller.new(outputter, pal)
882
+ installer.install(options[:modules], puppetfile, moduledir)
841
883
  end
842
884
 
843
885
  # If either bolt.yaml or bolt-project.yaml exist, the user has already
@@ -858,81 +900,86 @@ module Bolt
858
900
 
859
901
  # Installs modules declared in the project configuration file.
860
902
  #
861
- def install_project_modules
862
- if config.project.modules.nil?
863
- outputter.print_message "Project configuration file '#{config.project.project_file}' "\
864
- "does not specify any module dependencies. Nothing to do."
903
+ def install_project_modules(project, force, resolve)
904
+ assert_project_file(project)
905
+
906
+ unless project.modules
907
+ outputter.print_message "Project configuration file #{project.project_file} does not "\
908
+ "specify any module dependencies. Nothing to do."
865
909
  return 0
866
910
  end
867
911
 
868
- install_modules(
869
- config.puppetfile,
870
- config.puppetfile_config,
871
- config.project.path + '.modules',
872
- config.project.modules
873
- )
912
+ installer = Bolt::ModuleInstaller.new(outputter, pal)
913
+
914
+ ok = installer.install(project.modules,
915
+ project.puppetfile,
916
+ project.managed_moduledir,
917
+ force: force,
918
+ resolve: resolve)
919
+ ok ? 0 : 1
874
920
  end
875
921
 
876
- # Installs modules declared in the project configuration file.
922
+ # Adds a single module to the project.
877
923
  #
878
- def install_modules(puppetfile_path, config, moduledir, modules)
879
- require 'bolt/puppetfile'
880
- require 'bolt/puppetfile/installer'
881
-
882
- puppetfile = Bolt::Puppetfile.new(modules)
883
-
884
- # If the Puppetfile exists, check if it includes specs for each declared
885
- # module, erroring if there are any missing. Otherwise, resolve the
886
- # module dependencies and write a new Puppetfile. Users can forcibly
887
- # overwrite an existing Puppetfile with the '--force' option.
888
- if puppetfile_path.exist? && !options[:force]
889
- outputter.print_message "Parsing existing Puppetfile at #{puppetfile_path}"
890
- existing = Bolt::Puppetfile.parse(puppetfile_path)
891
-
892
- unless existing.modules.superset? puppetfile.modules
893
- missing_modules = puppetfile.modules - existing.modules
894
-
895
- raise Bolt::Error.new(
896
- "Puppetfile #{puppetfile_path} is missing specifications for modules: "\
897
- "#{missing_modules.map(&:title).join(', ')}. This may not be a Puppetfile "\
898
- "managed by Bolt. To forcibly overwrite the Puppetfile, run with the "\
899
- "'--force' option.",
900
- 'bolt/missing-module-specs'
901
- )
902
- end
903
- else
904
- outputter.print_message "Resolving module dependencies, this may take a moment"
905
- puppetfile.resolve
906
- outputter.print_message "Writing Puppetfile at #{puppetfile_path}"
907
- puppetfile.write(puppetfile_path, force: true)
908
- end
909
-
910
- outputter.print_message "Syncing modules from #{puppetfile_path} to #{moduledir}"
911
- ok = Bolt::Puppetfile::Installer.new(config).install(puppetfile_path, moduledir)
924
+ def add_project_module(name, project)
925
+ assert_project_file(project)
912
926
 
913
- # Automatically generate types after installing modules.
914
- pal.generate_types
927
+ modules = project.modules || []
928
+ installer = Bolt::ModuleInstaller.new(outputter, pal)
915
929
 
916
- outputter.print_puppetfile_result(ok, puppetfile_path, moduledir)
930
+ ok = installer.add(name,
931
+ modules,
932
+ project.puppetfile,
933
+ project.managed_moduledir,
934
+ project.project_file)
917
935
  ok ? 0 : 1
918
936
  end
919
937
 
920
- # Loads a Puppetfile and installs its modules.
938
+ # Asserts that there is a project configuration file.
921
939
  #
922
- def install_puppetfile(config, puppetfile, moduledir)
923
- require 'bolt/puppetfile/installer'
924
-
925
- ok = Bolt::Puppetfile::Installer.new(config).install(puppetfile, moduledir)
940
+ def assert_project_file(project)
941
+ unless project.project_file?
942
+ msg = if project.config_file.exist?
943
+ "Detected Bolt configuration file #{project.config_file}, unable to install "\
944
+ "modules. To update to a project configuration file, run 'bolt project migrate'."
945
+ else
946
+ "Could not find project configuration file #{project.project_file}, unable "\
947
+ "to install modules. To create a Bolt project, run 'bolt project init'."
948
+ end
926
949
 
927
- # Automatically generate types after installing modules.
928
- pal.generate_types
950
+ raise Bolt::Error.new(msg, 'bolt/missing-project-config-error')
951
+ end
952
+ end
929
953
 
930
- outputter.print_puppetfile_result(ok, puppetfile, moduledir)
954
+ # Loads a Puppetfile and installs its modules.
955
+ #
956
+ def install_puppetfile(config, puppetfile, moduledir)
957
+ outputter.print_message("Installing modules from Puppetfile")
958
+ installer = Bolt::ModuleInstaller.new(outputter, pal)
959
+ ok = installer.install_puppetfile(puppetfile, moduledir, config)
931
960
  ok ? 0 : 1
932
961
  end
933
962
 
963
+ # Raises an error if the 'puppetfile install' command is deprecated due to
964
+ # modules being configured.
965
+ #
966
+ def assert_puppetfile_or_module_command(modules)
967
+ if modules && options[:subcommand] == 'puppetfile'
968
+ raise Bolt::CLIError,
969
+ "Unable to use command 'bolt puppetfile #{options[:action]}' when "\
970
+ "'modules' is configured in bolt-project.yaml. Use the 'module' command "\
971
+ "instead. For a list of available actions for the 'module' command, run "\
972
+ "'bolt module --help'."
973
+ elsif modules.nil? && options[:subcommand] == 'module'
974
+ raise Bolt::CLIError,
975
+ "Unable to use command 'bolt module #{options[:action]}'. To use "\
976
+ "this command, update your project configuration to manage module "\
977
+ "dependencies."
978
+ end
979
+ end
980
+
934
981
  def pal
935
- @pal ||= Bolt::PAL.new(config.modulepath,
982
+ @pal ||= Bolt::PAL.new(Bolt::Config::Modulepath.new(config.modulepath),
936
983
  config.hiera_config,
937
984
  config.project.resource_types,
938
985
  config.compile_concurrency,
@@ -1031,7 +1078,7 @@ module Bolt
1031
1078
  'Task' => [],
1032
1079
  'Plugin' => Bolt::Plugin::BUILTIN_PLUGINS }
1033
1080
  if %w[plan task].include?(options[:subcommand]) && options[:action] == 'run'
1034
- default_content = Bolt::PAL.new([], nil, nil)
1081
+ default_content = Bolt::PAL.new(Bolt::Config::Modulepath.new([]), nil, nil)
1035
1082
  content['Plan'] = default_content.list_plans.each_with_object([]) do |iter, col|
1036
1083
  col << iter&.first
1037
1084
  end
@@ -1046,7 +1093,7 @@ module Bolt
1046
1093
  # Gem installs include the aggregate, canary, and puppetdb_fact modules, while
1047
1094
  # package installs include modules listed in the Bolt repo Puppetfile
1048
1095
  def incomplete_install?
1049
- (Dir.children(Bolt::PAL::MODULES_PATH) - %w[aggregate canary puppetdb_fact secure_env_vars]).empty?
1096
+ (Dir.children(Bolt::Config::Modulepath::MODULES_PATH) - %w[aggregate canary puppetdb_fact secure_env_vars]).empty?
1050
1097
  end
1051
1098
 
1052
1099
  # Mimicks the output from Outputter::Human#fatal_error. This should be used to print
@@ -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)