bolt 2.33.2 → 2.38.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.

Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +1 -1
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +1 -0
  4. data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +1 -3
  5. data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +17 -6
  6. data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +56 -0
  7. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +24 -6
  8. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +27 -8
  9. data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +21 -1
  10. data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +18 -1
  11. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +24 -6
  12. data/lib/bolt/analytics.rb +27 -8
  13. data/lib/bolt/apply_result.rb +3 -3
  14. data/lib/bolt/bolt_option_parser.rb +48 -16
  15. data/lib/bolt/cli.rb +154 -249
  16. data/lib/bolt/config.rb +188 -55
  17. data/lib/bolt/config/options.rb +147 -87
  18. data/lib/bolt/config/transport/base.rb +10 -19
  19. data/lib/bolt/config/transport/local.rb +1 -7
  20. data/lib/bolt/config/transport/options.rb +10 -68
  21. data/lib/bolt/config/transport/ssh.rb +8 -14
  22. data/lib/bolt/error.rb +33 -3
  23. data/lib/bolt/executor.rb +92 -6
  24. data/lib/bolt/inventory.rb +25 -0
  25. data/lib/bolt/inventory/group.rb +2 -1
  26. data/lib/bolt/inventory/options.rb +130 -0
  27. data/lib/bolt/inventory/target.rb +10 -11
  28. data/lib/bolt/module_installer.rb +21 -13
  29. data/lib/bolt/module_installer/resolver.rb +1 -1
  30. data/lib/bolt/outputter.rb +19 -5
  31. data/lib/bolt/outputter/human.rb +41 -10
  32. data/lib/bolt/outputter/json.rb +1 -1
  33. data/lib/bolt/outputter/logger.rb +1 -1
  34. data/lib/bolt/outputter/rainbow.rb +13 -2
  35. data/lib/bolt/pal.rb +19 -7
  36. data/lib/bolt/pal/yaml_plan.rb +7 -0
  37. data/lib/bolt/plan_creator.rb +160 -0
  38. data/lib/bolt/plugin.rb +42 -13
  39. data/lib/bolt/plugin/cache.rb +76 -0
  40. data/lib/bolt/plugin/module.rb +4 -4
  41. data/lib/bolt/plugin/puppetdb.rb +1 -1
  42. data/lib/bolt/project.rb +59 -40
  43. data/lib/bolt/project_manager.rb +201 -0
  44. data/lib/bolt/{project_migrator/config.rb → project_manager/config_migrator.rb} +51 -5
  45. data/lib/bolt/{project_migrator/inventory.rb → project_manager/inventory_migrator.rb} +5 -5
  46. data/lib/bolt/{project_migrator/base.rb → project_manager/migrator.rb} +2 -2
  47. data/lib/bolt/{project_migrator/modules.rb → project_manager/module_migrator.rb} +5 -3
  48. data/lib/bolt/puppetdb/client.rb +11 -2
  49. data/lib/bolt/puppetdb/config.rb +9 -8
  50. data/lib/bolt/rerun.rb +1 -5
  51. data/lib/bolt/shell/bash.rb +8 -2
  52. data/lib/bolt/shell/powershell.rb +22 -4
  53. data/lib/bolt/target.rb +4 -0
  54. data/lib/bolt/task/run.rb +1 -1
  55. data/lib/bolt/transport/local.rb +13 -0
  56. data/lib/bolt/transport/orch.rb +0 -5
  57. data/lib/bolt/transport/orch/connection.rb +10 -3
  58. data/lib/bolt/transport/remote.rb +1 -1
  59. data/lib/bolt/transport/ssh/exec_connection.rb +6 -2
  60. data/lib/bolt/util.rb +41 -7
  61. data/lib/bolt/validator.rb +226 -0
  62. data/lib/bolt/version.rb +1 -1
  63. data/lib/bolt/yarn.rb +23 -0
  64. data/lib/bolt_server/base_config.rb +3 -1
  65. data/lib/bolt_server/config.rb +3 -1
  66. data/lib/bolt_server/file_cache.rb +2 -0
  67. data/lib/bolt_server/plugin.rb +13 -0
  68. data/lib/bolt_server/plugin/puppet_connect_data.rb +37 -0
  69. data/lib/bolt_server/schemas/connect-data.json +22 -0
  70. data/lib/bolt_server/schemas/partials/task.json +2 -2
  71. data/lib/bolt_server/transport_app.rb +82 -23
  72. data/lib/bolt_spec/plans/mock_executor.rb +4 -1
  73. data/libexec/apply_catalog.rb +1 -1
  74. data/libexec/custom_facts.rb +1 -1
  75. data/libexec/query_resources.rb +1 -1
  76. metadata +22 -13
  77. data/lib/bolt/project_migrator.rb +0 -80
@@ -15,20 +15,22 @@ require 'bolt/config'
15
15
  require 'bolt/error'
16
16
  require 'bolt/executor'
17
17
  require 'bolt/inventory'
18
- require 'bolt/rerun'
19
18
  require 'bolt/logger'
19
+ require 'bolt/module_installer'
20
20
  require 'bolt/outputter'
21
- require 'bolt/puppetdb'
22
- require 'bolt/plugin'
23
- require 'bolt/project_migrator'
24
21
  require 'bolt/pal'
22
+ require 'bolt/plan_creator'
23
+ require 'bolt/plugin'
24
+ require 'bolt/project_manager'
25
+ require 'bolt/puppetdb'
26
+ require 'bolt/rerun'
27
+ require 'bolt/secret'
25
28
  require 'bolt/target'
26
29
  require 'bolt/version'
27
- require 'bolt/secret'
28
- require 'bolt/module_installer'
29
30
 
30
31
  module Bolt
31
32
  class CLIExit < StandardError; end
33
+
32
34
  class CLI
33
35
  COMMANDS = {
34
36
  'command' => %w[run],
@@ -163,8 +165,9 @@ module Bolt
163
165
  elsif options[:configfile]
164
166
  Bolt::Config.from_file(options[:configfile], options)
165
167
  else
166
- project = if options[:boltdir]
167
- dir = Pathname.new(options[:boltdir])
168
+ cli_flag = options[:project] || options[:boltdir]
169
+ project = if cli_flag
170
+ dir = Pathname.new(cli_flag)
168
171
  if (dir + Bolt::Project::BOLTDIR_NAME).directory?
169
172
  Bolt::Project.create_project(dir + Bolt::Project::BOLTDIR_NAME)
170
173
  else
@@ -194,11 +197,12 @@ module Bolt
194
197
  @parser_deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
195
198
  config.deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
196
199
 
197
- warn_inventory_overrides_cli(options)
200
+ if options[:clear_cache] && File.exist?(config.project.cache_file)
201
+ FileUtils.rm(config.project.cache_file)
202
+ end
198
203
 
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)
204
+ warn_inventory_overrides_cli(options)
205
+ validate_ps_version
202
206
 
203
207
  options
204
208
  rescue Bolt::Error => e
@@ -206,6 +210,17 @@ module Bolt
206
210
  raise e
207
211
  end
208
212
 
213
+ private def validate_ps_version
214
+ if Bolt::Util.powershell?
215
+ target = inventory.get_target('localhost')
216
+ Bolt::Transport::Local.new.with_connection(target) do |conn|
217
+ # This will automatically validate the powershell version on the Bolt
218
+ # controller
219
+ Bolt::Shell::Powershell.new(target, conn)
220
+ end
221
+ end
222
+ end
223
+
209
224
  def update_targets(options)
210
225
  target_opts = options.keys.select { |opt| %i[query rerun targets].include?(opt) }
211
226
  target_string = "'--targets', '--rerun', or '--query'"
@@ -228,9 +243,9 @@ module Bolt
228
243
 
229
244
  def validate(options)
230
245
  unless COMMANDS.include?(options[:subcommand])
246
+ command = Bolt::Util.powershell? ? 'Get-Command -Module PuppetBolt' : 'bolt help'
231
247
  raise Bolt::CLIError,
232
- "Expected subcommand '#{options[:subcommand]}' to be one of " \
233
- "#{COMMANDS.keys.join(', ')}"
248
+ "'#{options[:subcommand]}' is not a Bolt command. See '#{command}'."
234
249
  end
235
250
 
236
251
  actions = COMMANDS[options[:subcommand]]
@@ -285,8 +300,9 @@ module Bolt
285
300
  end
286
301
 
287
302
  if options[:subcommand] == 'module' && options[:action] == 'install' && options[:object]
303
+ command = Bolt::Util.powershell? ? 'Add-BoltModule -Module' : 'bolt module add'
288
304
  raise Bolt::CLIError, "Invalid argument '#{options[:object]}'. To add a new module to "\
289
- "the project, run 'bolt module add #{options[:object]}'."
305
+ "the project, run '#{command} #{options[:object]}'."
290
306
  end
291
307
 
292
308
  if options[:subcommand] != 'file' && options[:subcommand] != 'script' &&
@@ -295,7 +311,7 @@ module Bolt
295
311
  "Unknown argument(s) #{options[:leftovers].join(', ')}"
296
312
  end
297
313
 
298
- if options[:boltdir] && options[:configfile]
314
+ if options.slice(:boltdir, :configfile, :project).length > 1
299
315
  raise Bolt::CLIError, "Only one of '--boltdir', '--project', or '--configfile' may be specified"
300
316
  end
301
317
 
@@ -342,15 +358,10 @@ module Bolt
342
358
  def warn_inventory_overrides_cli(opts)
343
359
  inventory_source = if ENV[Bolt::Inventory::ENVIRONMENT_VAR]
344
360
  Bolt::Inventory::ENVIRONMENT_VAR
345
- elsif config.inventoryfile && Bolt::Util.file_stat(config.inventoryfile)
361
+ elsif config.inventoryfile
346
362
  config.inventoryfile
347
- else
348
- begin
349
- Bolt::Util.file_stat(config.default_inventoryfile)
350
- config.default_inventoryfile
351
- rescue Errno::ENOENT
352
- nil
353
- end
363
+ elsif File.exist?(config.default_inventoryfile)
364
+ config.default_inventoryfile
354
365
  end
355
366
 
356
367
  inventory_cli_opts = %i[authentication escalation transports].each_with_object([]) do |key, acc|
@@ -394,7 +405,7 @@ module Bolt
394
405
  output_format: config.format,
395
406
  # For continuity
396
407
  boltdir_type: config.project.type
397
- }
408
+ }.merge!(analytics.plan_counts(config.project.plans_path))
398
409
 
399
410
  # Only include target and inventory info for commands that take a targets
400
411
  # list. This avoids loading inventory for commands that don't need it.
@@ -458,23 +469,31 @@ module Bolt
458
469
  when 'project'
459
470
  case options[:action]
460
471
  when 'init'
461
- code = initialize_project
472
+ code = Bolt::ProjectManager.new(config, outputter, pal)
473
+ .create(Dir.pwd, options[:object], options[:modules])
462
474
  when 'migrate'
463
- code = Bolt::ProjectMigrator.new(config, outputter).migrate
475
+ code = Bolt::ProjectManager.new(config, outputter, pal).migrate
464
476
  end
465
477
  when 'plan'
466
478
  case options[:action]
467
479
  when 'new'
468
- code = new_plan(options[:object])
480
+ plan_name = options[:object]
481
+
482
+ # If this passes validation, it will return the path to the plan to create
483
+ Bolt::PlanCreator.validate_input(config.project, plan_name)
484
+ code = Bolt::PlanCreator.create_plan(config.project.plans_path,
485
+ plan_name,
486
+ outputter,
487
+ options[:puppet])
469
488
  when 'run'
470
489
  code = run_plan(options[:object], options[:task_options], options[:target_args], options)
471
490
  end
472
491
  when 'module'
473
492
  case options[:action]
474
493
  when 'add'
475
- code = add_project_module(options[:object], config.project)
494
+ code = add_project_module(options[:object], config.project, config.module_install)
476
495
  when 'install'
477
- code = install_project_modules(config.project, options[:force], options[:resolve])
496
+ code = install_project_modules(config.project, config.module_install, options[:force], options[:resolve])
478
497
  when 'generate-types'
479
498
  code = generate_types
480
499
  end
@@ -519,12 +538,15 @@ module Bolt
519
538
  validate_file('script', script)
520
539
  executor.run_script(targets, script, options[:leftovers], executor_opts)
521
540
  when 'task'
522
- pal.run_task(options[:object],
523
- targets,
524
- options[:task_options],
525
- executor,
526
- inventory,
527
- options[:description])
541
+ r = outputter.spin do
542
+ pal.run_task(options[:object],
543
+ targets,
544
+ options[:task_options],
545
+ executor,
546
+ inventory,
547
+ options[:description])
548
+ end
549
+ r
528
550
  when 'file'
529
551
  src = options[:object]
530
552
  dest = options[:leftovers].first
@@ -568,10 +590,15 @@ module Bolt
568
590
  outputter.print_task_info(pal.get_task(task_name))
569
591
  end
570
592
 
593
+ # Filters a list of content by matching substring.
594
+ #
595
+ private def filter_content(content, filter)
596
+ return content unless content && filter
597
+ content.select { |name,| name.include?(filter) }
598
+ end
599
+
571
600
  def list_tasks
572
- tasks = pal.list_tasks
573
- tasks.select! { |task| task.first.include?(options[:filter]) } if options[:filter]
574
- tasks.select! { |task| config.project.tasks.include?(task.first) } unless config.project.tasks.nil?
601
+ tasks = filter_content(pal.list_tasks(filter_content: true), options[:filter])
575
602
  outputter.print_tasks(tasks, pal.user_modulepath)
576
603
  end
577
604
 
@@ -580,9 +607,7 @@ module Bolt
580
607
  end
581
608
 
582
609
  def list_plans
583
- plans = pal.list_plans
584
- plans.select! { |plan| plan.first.include?(options[:filter]) } if options[:filter]
585
- plans.select! { |plan| config.project.plans.include?(plan.first) } unless config.project.plans.nil?
610
+ plans = filter_content(pal.list_plans(filter_content: true), options[:filter])
586
611
  outputter.print_plans(plans, pal.user_modulepath)
587
612
  end
588
613
 
@@ -617,118 +642,6 @@ module Bolt
617
642
  outputter.print_groups(groups)
618
643
  end
619
644
 
620
- def new_plan(plan_name)
621
- @logger.warn("Command 'bolt plan new' is experimental and subject to changes.")
622
-
623
- if config.project.name.nil?
624
- raise Bolt::Error.new(
625
- "Project directory '#{config.project.path}' is not a named project. Unable to create "\
626
- "a project-level plan. To name a project, set the 'name' key in the 'bolt-project.yaml' "\
627
- "configuration file.",
628
- "bolt/unnamed-project-error"
629
- )
630
- end
631
-
632
- if plan_name !~ Bolt::Module::CONTENT_NAME_REGEX
633
- message = <<~MESSAGE.chomp
634
- Invalid plan name '#{plan_name}'. Plan names are composed of one or more name segments
635
- separated by double colons '::'.
636
-
637
- Each name segment must begin with a lowercase letter, and may only include lowercase
638
- letters, digits, and underscores.
639
-
640
- Examples of valid plan names:
641
- - #{config.project.name}
642
- - #{config.project.name}::my_plan
643
- MESSAGE
644
-
645
- raise Bolt::ValidationError, message
646
- end
647
-
648
- prefix, *name_segments, basename = plan_name.split('::')
649
-
650
- # If the plan name is just the project name, then create an 'init' plan.
651
- # Otherwise, use the last name segment for the plan's filename.
652
- basename ||= 'init'
653
-
654
- unless prefix == config.project.name
655
- message = "First segment of plan name '#{plan_name}' must match project name '#{config.project.name}'. "\
656
- "Did you mean '#{config.project.name}::#{plan_name}'?"
657
-
658
- raise Bolt::ValidationError, message
659
- end
660
-
661
- dir_path = config.project.plans_path.join(*name_segments)
662
-
663
- %w[pp yaml].each do |ext|
664
- next unless (path = config.project.plans_path + "#{basename}.#{ext}").exist?
665
- raise Bolt::Error.new(
666
- "A plan with the name '#{plan_name}' already exists at '#{path}', nothing to do.",
667
- 'bolt/existing-plan-error'
668
- )
669
- end
670
-
671
- begin
672
- FileUtils.mkdir_p(dir_path)
673
- rescue Errno::EEXIST => e
674
- raise Bolt::Error.new(
675
- "#{e.message}; unable to create plan directory '#{dir_path}'",
676
- 'bolt/existing-file-error'
677
- )
678
- end
679
-
680
- plan_path = dir_path + "#{basename}.yaml"
681
-
682
- plan_template = <<~PLAN
683
- # This is the structure of a simple plan. To learn more about writing
684
- # YAML plans, see the documentation: http://pup.pt/bolt-yaml-plans
685
-
686
- # The description sets the description of the plan that will appear
687
- # in 'bolt plan show' output.
688
- description: A plan created with bolt plan new
689
-
690
- # The parameters key defines the parameters that can be passed to
691
- # the plan.
692
- parameters:
693
- targets:
694
- type: TargetSpec
695
- description: A list of targets to run actions on
696
- default: localhost
697
-
698
- # The steps key defines the actions the plan will take in order.
699
- steps:
700
- - message: Hello from #{plan_name}
701
- - name: command_step
702
- command: whoami
703
- targets: $targets
704
-
705
- # The return key sets the return value of the plan.
706
- return: $command_step
707
- PLAN
708
-
709
- begin
710
- File.write(plan_path, plan_template)
711
- rescue Errno::EACCES => e
712
- raise Bolt::FileError.new(
713
- "#{e.message}; unable to create plan",
714
- plan_path
715
- )
716
- end
717
-
718
- output = <<~OUTPUT
719
- Created plan '#{plan_name}' at '#{plan_path}'
720
-
721
- Show this plan with:
722
- bolt plan show #{plan_name}
723
- Run this plan with:
724
- bolt plan run #{plan_name}
725
- OUTPUT
726
-
727
- outputter.print_message(output)
728
-
729
- 0
730
- end
731
-
732
645
  def run_plan(plan_name, plan_arguments, nodes, options)
733
646
  unless nodes.empty?
734
647
  if plan_arguments['nodes'] || plan_arguments['targets']
@@ -768,7 +681,9 @@ module Bolt
768
681
 
769
682
  executor.subscribe(log_outputter)
770
683
  executor.start_plan(plan_context)
771
- result = pal.run_plan(plan_name, plan_arguments, executor, inventory, puppetdb_client)
684
+ result = outputter.spin do
685
+ pal.run_plan(plan_name, plan_arguments, executor, inventory, puppetdb_client)
686
+ end
772
687
 
773
688
  # If a non-bolt exception bubbles up the plan won't get finished
774
689
  executor.finish_plan(result)
@@ -820,88 +735,22 @@ module Bolt
820
735
  end
821
736
 
822
737
  def list_modules
738
+ assert_puppetfile_or_module_command(config.project.modules)
823
739
  outputter.print_module_list(pal.list_modules)
824
740
  end
825
741
 
826
742
  def generate_types
743
+ assert_puppetfile_or_module_command(config.project.modules)
827
744
  # generate_types will surface a nice error with helpful message if it fails
828
745
  pal.generate_types
829
746
  0
830
747
  end
831
748
 
832
- # Initializes a specified directory as a Bolt project and installs any modules
833
- # specified by the user, along with their dependencies
834
- def initialize_project
835
- # Dir.pwd will return backslashes on Windows, but Pathname always uses
836
- # forward slashes to concatenate paths. This results in paths like
837
- # C:\User\Administrator/modules, which fail module install. This ensure
838
- # forward slashes in the cwd path.
839
- dir = File.expand_path(Dir.pwd)
840
- name = options[:object] || File.basename(dir)
841
- if name !~ Bolt::Module::MODULE_NAME_REGEX
842
- if options[:object]
843
- raise Bolt::ValidationError, "The provided project name '#{name}' is invalid; "\
844
- "project name must begin with a lowercase letter and can include lowercase "\
845
- "letters, numbers, and underscores."
846
- else
847
- raise Bolt::ValidationError, "The current directory name '#{name}' is an invalid "\
848
- "project name. Please specify a name using 'bolt project init <name>'."
849
- end
850
- end
851
-
852
- project = Pathname.new(dir)
853
- old_config = project + 'bolt.yaml'
854
- config = project + 'bolt-project.yaml'
855
- puppetfile = project + 'Puppetfile'
856
- moduledir = project + 'modules'
857
-
858
- # Warn the user if the project directory already exists. We don't error
859
- # here since users might not have installed any modules yet. If both
860
- # bolt.yaml and bolt-project.yaml exist, this will just warn about
861
- # bolt-project.yaml and subsequent Bolt actions will warn about both files
862
- # existing.
863
- if config.exist?
864
- @logger.warn "Found existing project directory at #{project}. Skipping file creation."
865
- elsif old_config.exist?
866
- @logger.warn "Found existing #{old_config.basename} at #{project}. "\
867
- "#{old_config.basename} is deprecated, please rename to #{config.basename}."
868
- end
869
-
870
- # If modules were specified, first check if there is already a Puppetfile
871
- # at the project directory, erroring if there is. If there is no
872
- # Puppetfile, install the specified modules. The module installer will
873
- # resolve dependencies, generate a Puppetfile, and install the modules.
874
- if options[:modules]
875
- if puppetfile.exist?
876
- raise Bolt::CLIError,
877
- "Found existing Puppetfile at #{puppetfile}, unable to initialize "\
878
- "project with modules."
879
- end
880
-
881
- installer = Bolt::ModuleInstaller.new(outputter, pal)
882
- installer.install(options[:modules], puppetfile, moduledir)
883
- end
884
-
885
- # If either bolt.yaml or bolt-project.yaml exist, the user has already
886
- # been warned and we can just finish project creation. Otherwise, create a
887
- # bolt-project.yaml with the project name in it.
888
- unless config.exist? || old_config.exist?
889
- begin
890
- content = { 'name' => name }
891
- File.write(config.to_path, content.to_yaml)
892
- outputter.print_message "Successfully created Bolt project at #{project}"
893
- rescue StandardError => e
894
- raise Bolt::FileError.new("Could not create bolt-project.yaml at #{project}: #{e.message}", nil)
895
- end
896
- end
897
-
898
- 0
899
- end
900
-
901
749
  # Installs modules declared in the project configuration file.
902
750
  #
903
- def install_project_modules(project, force, resolve)
751
+ def install_project_modules(project, config, force, resolve)
904
752
  assert_project_file(project)
753
+ assert_puppetfile_or_module_command(project.modules)
905
754
 
906
755
  unless project.modules
907
756
  outputter.print_message "Project configuration file #{project.project_file} does not "\
@@ -909,29 +758,55 @@ module Bolt
909
758
  return 0
910
759
  end
911
760
 
761
+ if resolve != false && config.any?
762
+ @logger.warn(
763
+ "Detected configuration for 'module-install'. This configuration is currently "\
764
+ "only supported when installing modules, not when resolving module dependencies. "\
765
+ "For more information, see https://pup.pt/bolt-module-install"
766
+ )
767
+ end
768
+
769
+ modules = project.modules || []
912
770
  installer = Bolt::ModuleInstaller.new(outputter, pal)
913
771
 
914
- ok = installer.install(project.modules,
915
- project.puppetfile,
916
- project.managed_moduledir,
917
- force: force,
918
- resolve: resolve)
772
+ ok = outputter.spin do
773
+ installer.install(modules,
774
+ project.puppetfile,
775
+ project.managed_moduledir,
776
+ config,
777
+ force: force,
778
+ resolve: resolve)
779
+ end
780
+
919
781
  ok ? 0 : 1
920
782
  end
921
783
 
922
784
  # Adds a single module to the project.
923
785
  #
924
- def add_project_module(name, project)
786
+ def add_project_module(name, project, config)
925
787
  assert_project_file(project)
788
+ assert_puppetfile_or_module_command(project.modules)
789
+
790
+ if config.any?
791
+ @logger.warn(
792
+ "Detected configuration for 'module-install'. This configuration is currently "\
793
+ "only supported when installing modules, not when resolving module dependencies. "\
794
+ "For more information, see https://pup.pt/bolt-module-install"
795
+ )
796
+ end
926
797
 
927
798
  modules = project.modules || []
928
799
  installer = Bolt::ModuleInstaller.new(outputter, pal)
929
800
 
930
- ok = installer.add(name,
931
- modules,
932
- project.puppetfile,
933
- project.managed_moduledir,
934
- project.project_file)
801
+ ok = outputter.spin do
802
+ installer.add(name,
803
+ modules,
804
+ project.puppetfile,
805
+ project.managed_moduledir,
806
+ project.project_file,
807
+ config)
808
+ end
809
+
935
810
  ok ? 0 : 1
936
811
  end
937
812
 
@@ -940,11 +815,13 @@ module Bolt
940
815
  def assert_project_file(project)
941
816
  unless project.project_file?
942
817
  msg = if project.config_file.exist?
818
+ command = Bolt::Util.powershell? ? 'Update-BoltProject' : 'bolt project migrate'
943
819
  "Detected Bolt configuration file #{project.config_file}, unable to install "\
944
- "modules. To update to a project configuration file, run 'bolt project migrate'."
820
+ "modules. To update to a project configuration file, run '#{command}'."
945
821
  else
822
+ command = Bolt::Util.powershell? ? 'New-BoltProject' : 'bolt project init'
946
823
  "Could not find project configuration file #{project.project_file}, unable "\
947
- "to install modules. To create a Bolt project, run 'bolt project init'."
824
+ "to install modules. To create a Bolt project, run '#{command}'."
948
825
  end
949
826
 
950
827
  raise Bolt::Error.new(msg, 'bolt/missing-project-config-error')
@@ -953,10 +830,15 @@ module Bolt
953
830
 
954
831
  # Loads a Puppetfile and installs its modules.
955
832
  #
956
- def install_puppetfile(config, puppetfile, moduledir)
833
+ def install_puppetfile(puppetfile_config, puppetfile, moduledir)
834
+ assert_puppetfile_or_module_command(config.project.modules)
835
+
957
836
  outputter.print_message("Installing modules from Puppetfile")
958
837
  installer = Bolt::ModuleInstaller.new(outputter, pal)
959
- ok = installer.install_puppetfile(puppetfile, moduledir, config)
838
+ ok = outputter.spin do
839
+ installer.install_puppetfile(puppetfile, moduledir, puppetfile_config)
840
+ end
841
+
960
842
  ok ? 0 : 1
961
843
  end
962
844
 
@@ -964,17 +846,36 @@ module Bolt
964
846
  # modules being configured.
965
847
  #
966
848
  def assert_puppetfile_or_module_command(modules)
849
+ if Bolt::Util.powershell?
850
+ case options[:action]
851
+ when 'generate-types'
852
+ old_command = 'Register-BoltPuppetfileTypes'
853
+ new_command = 'Register-BoltModuleTypes'
854
+ when 'install'
855
+ old_command = 'Install-BoltPuppetfile'
856
+ new_command = 'Install-BoltModule'
857
+ when 'show', 'show-modules'
858
+ old_command = 'Get-BoltPuppetfileModules'
859
+ new_command = 'Get-BoltModule'
860
+ end
861
+ else
862
+ old_command = "bolt puppetfile #{options[:action]}"
863
+ new_command = if options[:action] == 'show-modules'
864
+ 'bolt module show'
865
+ else
866
+ "bolt module #{options[:action]}"
867
+ end
868
+ end
869
+
967
870
  if modules && options[:subcommand] == 'puppetfile'
968
871
  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'."
872
+ "Unable to use command '#{old_command}' when 'modules' is configured in "\
873
+ "bolt-project.yaml. Use '#{new_command}' instead."
973
874
  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."
875
+ msg = "Unable to use command '#{new_command}' when 'modules' is not configured in "\
876
+ "bolt-project.yaml. "
877
+ msg += "Use '#{old_command}' instead." if options[:action] != 'add'
878
+ raise Bolt::CLIError, msg
978
879
  end
979
880
  end
980
881
 
@@ -1043,7 +944,11 @@ module Bolt
1043
944
  end
1044
945
 
1045
946
  def outputter
1046
- @outputter ||= Bolt::Outputter.for_format(config.format, config.color, options[:verbose], config.trace)
947
+ @outputter ||= Bolt::Outputter.for_format(config.format,
948
+ config.color,
949
+ options[:verbose],
950
+ config.trace,
951
+ config.spinner)
1047
952
  end
1048
953
 
1049
954
  def log_outputter