bolt 2.44.0 → 3.4.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +11 -9
  3. data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -1
  4. data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +25 -0
  5. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +20 -2
  6. data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +2 -2
  7. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +44 -5
  8. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
  9. data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +7 -3
  10. data/bolt-modules/file/lib/puppet/functions/file/read.rb +3 -2
  11. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +20 -2
  12. data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
  13. data/lib/bolt/apply_result.rb +1 -1
  14. data/lib/bolt/bolt_option_parser.rb +9 -123
  15. data/lib/bolt/cli.rb +125 -127
  16. data/lib/bolt/config.rb +39 -214
  17. data/lib/bolt/config/options.rb +34 -125
  18. data/lib/bolt/config/transport/local.rb +1 -0
  19. data/lib/bolt/config/transport/lxd.rb +23 -0
  20. data/lib/bolt/config/transport/options.rb +9 -2
  21. data/lib/bolt/executor.rb +20 -5
  22. data/lib/bolt/logger.rb +9 -1
  23. data/lib/bolt/module_installer.rb +2 -2
  24. data/lib/bolt/module_installer/puppetfile.rb +2 -2
  25. data/lib/bolt/module_installer/specs/forge_spec.rb +2 -2
  26. data/lib/bolt/module_installer/specs/git_spec.rb +2 -2
  27. data/lib/bolt/node/output.rb +14 -4
  28. data/lib/bolt/outputter/human.rb +52 -24
  29. data/lib/bolt/outputter/json.rb +16 -16
  30. data/lib/bolt/pal.rb +26 -5
  31. data/lib/bolt/pal/yaml_plan.rb +1 -2
  32. data/lib/bolt/pal/yaml_plan/evaluator.rb +5 -153
  33. data/lib/bolt/pal/yaml_plan/step.rb +91 -52
  34. data/lib/bolt/pal/yaml_plan/step/command.rb +21 -13
  35. data/lib/bolt/pal/yaml_plan/step/download.rb +15 -16
  36. data/lib/bolt/pal/yaml_plan/step/eval.rb +11 -11
  37. data/lib/bolt/pal/yaml_plan/step/message.rb +13 -4
  38. data/lib/bolt/pal/yaml_plan/step/plan.rb +19 -15
  39. data/lib/bolt/pal/yaml_plan/step/resources.rb +82 -21
  40. data/lib/bolt/pal/yaml_plan/step/script.rb +36 -17
  41. data/lib/bolt/pal/yaml_plan/step/task.rb +19 -16
  42. data/lib/bolt/pal/yaml_plan/step/upload.rb +16 -17
  43. data/lib/bolt/pal/yaml_plan/transpiler.rb +3 -3
  44. data/lib/bolt/plan_creator.rb +1 -1
  45. data/lib/bolt/plugin/module.rb +0 -23
  46. data/lib/bolt/plugin/puppet_connect_data.rb +45 -3
  47. data/lib/bolt/project.rb +16 -56
  48. data/lib/bolt/project_manager.rb +5 -4
  49. data/lib/bolt/project_manager/module_migrator.rb +7 -6
  50. data/lib/bolt/result.rb +10 -11
  51. data/lib/bolt/shell.rb +16 -0
  52. data/lib/bolt/shell/bash.rb +61 -31
  53. data/lib/bolt/shell/bash/tmpdir.rb +2 -2
  54. data/lib/bolt/shell/powershell.rb +35 -14
  55. data/lib/bolt/shell/powershell/snippets.rb +37 -150
  56. data/lib/bolt/task.rb +1 -1
  57. data/lib/bolt/transport/base.rb +0 -9
  58. data/lib/bolt/transport/docker.rb +1 -125
  59. data/lib/bolt/transport/docker/connection.rb +86 -161
  60. data/lib/bolt/transport/local.rb +1 -9
  61. data/lib/bolt/transport/lxd.rb +26 -0
  62. data/lib/bolt/transport/lxd/connection.rb +99 -0
  63. data/lib/bolt/transport/orch.rb +13 -5
  64. data/lib/bolt/transport/ssh/connection.rb +1 -1
  65. data/lib/bolt/transport/winrm/connection.rb +1 -1
  66. data/lib/bolt/util.rb +8 -0
  67. data/lib/bolt/version.rb +1 -1
  68. data/lib/bolt_server/transport_app.rb +61 -33
  69. data/lib/bolt_spec/bolt_context.rb +9 -4
  70. data/lib/bolt_spec/plans.rb +1 -109
  71. data/lib/bolt_spec/plans/action_stubs.rb +1 -1
  72. data/lib/bolt_spec/plans/action_stubs/command_stub.rb +8 -1
  73. data/lib/bolt_spec/plans/action_stubs/script_stub.rb +8 -1
  74. data/lib/bolt_spec/plans/mock_executor.rb +4 -0
  75. data/modules/aggregate/plans/count.pp +21 -0
  76. data/modules/aggregate/plans/targets.pp +21 -0
  77. data/modules/puppet_connect/plans/test_input_data.pp +67 -0
  78. data/modules/puppetdb_fact/plans/init.pp +10 -0
  79. metadata +7 -3
  80. data/modules/aggregate/plans/nodes.pp +0 -36
@@ -54,7 +54,7 @@ module Bolt
54
54
  unless missing_keys.empty?
55
55
  if result['_output']
56
56
  # rubocop:disable Layout/LineLength
57
- msg = "Report result contains an '_output' key. Catalog application may have printed extraneous output to stdout: #{result['_output']}"
57
+ msg = "Report result contains an '_output' key. Catalog application might have printed extraneous output to stdout: #{result['_output']}"
58
58
  # rubocop:enable Layout/LineLength
59
59
  else
60
60
  msg = "Report did not contain all expected keys missing: #{missing_keys.join(', ')}"
@@ -6,15 +6,15 @@ require 'optparse'
6
6
 
7
7
  module Bolt
8
8
  class BoltOptionParser < OptionParser
9
- PROJECT_PATHS = %w[project configfile boltdir].freeze
10
- OPTIONS = { inventory: %w[targets query rerun description],
9
+ PROJECT_PATHS = %w[project].freeze
10
+ OPTIONS = { inventory: %w[targets query rerun],
11
11
  authentication: %w[user password password-prompt private-key host-key-check ssl ssl-verify],
12
12
  escalation: %w[run-as sudo-password sudo-password-prompt sudo-executable],
13
13
  run_context: %w[concurrency inventoryfile save-rerun cleanup],
14
14
  global_config_setters: PROJECT_PATHS + %w[modulepath],
15
15
  transports: %w[transport connect-timeout tty native-ssh ssh-command copy-command],
16
- display: %w[format color verbose trace],
17
- global: %w[help version debug log-level clear-cache] }.freeze
16
+ display: %w[format color verbose trace stream],
17
+ global: %w[help version log-level clear-cache] }.freeze
18
18
 
19
19
  ACTION_OPTS = OPTIONS.values.flatten.freeze
20
20
 
@@ -114,21 +114,6 @@ module Bolt
114
114
  { flags: OPTIONS[:global],
115
115
  banner: PROJECT_HELP }
116
116
  end
117
- when 'puppetfile'
118
- case action
119
- when 'install'
120
- { flags: OPTIONS[:global] + OPTIONS[:global_config_setters] + %w[puppetfile],
121
- banner: PUPPETFILE_INSTALL_HELP }
122
- when 'show-modules'
123
- { flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
124
- banner: PUPPETFILE_SHOWMODULES_HELP }
125
- when 'generate-types'
126
- { flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
127
- banner: PUPPETFILE_GENERATETYPES_HELP }
128
- else
129
- { flags: OPTIONS[:global],
130
- banner: PUPPETFILE_HELP }
131
- end
132
117
  when 'script'
133
118
  case action
134
119
  when 'run'
@@ -192,7 +177,6 @@ module Bolt
192
177
  module Manage Bolt project modules
193
178
  plan Convert, create, show, and run Bolt plans
194
179
  project Create and migrate Bolt projects
195
- puppetfile Install and list modules and generate type references
196
180
  script Upload a local script and run it remotely
197
181
  secret Create encryption keys and encrypt and decrypt values
198
182
  task Show and run Bolt tasks
@@ -470,7 +454,7 @@ module Bolt
470
454
  DESCRIPTION
471
455
  Convert a YAML plan to a Puppet language plan and print the converted plan to stdout.
472
456
 
473
- Converting a YAML plan may result in a plan that is syntactically
457
+ Converting a YAML plan might result in a plan that is syntactically
474
458
  correct but has different behavior. Always verify a converted plan's
475
459
  functionality. Note that the converted plan is not written to a file.
476
460
 
@@ -576,55 +560,6 @@ module Bolt
576
560
  Migrate a Bolt project to use current best practices and the latest version of configuration files.
577
561
  HELP
578
562
 
579
- PUPPETFILE_HELP = <<~HELP
580
- NAME
581
- puppetfile
582
-
583
- USAGE
584
- bolt puppetfile <action> [options]
585
-
586
- DESCRIPTION
587
- Install and list modules and generate type references
588
-
589
- ACTIONS
590
- generate-types Generate type references to register in plans
591
- install Install modules from a Puppetfile into a project
592
- show-modules List modules available to the Bolt project
593
- HELP
594
-
595
- PUPPETFILE_GENERATETYPES_HELP = <<~HELP
596
- NAME
597
- generate-types
598
-
599
- USAGE
600
- bolt puppetfile generate-types [options]
601
-
602
- DESCRIPTION
603
- Generate type references to register in plans.
604
- HELP
605
-
606
- PUPPETFILE_INSTALL_HELP = <<~HELP
607
- NAME
608
- install
609
-
610
- USAGE
611
- bolt puppetfile install [options]
612
-
613
- DESCRIPTION
614
- Install modules from a Puppetfile into a project
615
- HELP
616
-
617
- PUPPETFILE_SHOWMODULES_HELP = <<~HELP
618
- NAME
619
- show-modules
620
-
621
- USAGE
622
- bolt puppetfile show-modules [options]
623
-
624
- DESCRIPTION
625
- List modules available to the Bolt project.
626
- HELP
627
-
628
563
  SCRIPT_HELP = <<~HELP
629
564
  NAME
630
565
  script
@@ -772,7 +707,7 @@ module Bolt
772
707
  "Or read a target list from an input file '@<file>' or stdin '-'.",
773
708
  'Example: --targets localhost,target_group,ssh://nix.com:23,winrm://windows.puppet.com',
774
709
  'URI format is [protocol://]host[:port]',
775
- "SSH is the default protocol; may be #{TRANSPORTS.keys.join(', ')}",
710
+ "SSH is the default protocol; can be #{TRANSPORTS.keys.join(', ')}",
776
711
  'For Windows targets, specify the winrm:// protocol if it has not be configured',
777
712
  'For SSH, port defaults to `22`',
778
713
  'For WinRM, port defaults to `5985` or `5986` based on the --[no-]ssl setting') do |targets|
@@ -791,15 +726,6 @@ module Bolt
791
726
  define('--noop', 'See what changes Bolt will make without actually executing the changes') do |_|
792
727
  @options[:noop] = true
793
728
  end
794
- define('--description DESCRIPTION',
795
- 'Deprecated. Description to use for the job') do |description|
796
- Bolt::Logger.deprecate(
797
- "description_cli_option",
798
- "Command line option '--description' is deprecated, and will be "\
799
- "removed in Bolt 3.0."
800
- )
801
- @options[:description] = description
802
- end
803
729
  define('--params PARAMETERS',
804
730
  "Parameters to a task or plan as json, a json file '@<file>', or on stdin '-'") do |params|
805
731
  @options[:task_options] = parse_params(params)
@@ -877,31 +803,10 @@ module Bolt
877
803
  File.expand_path(moduledir)
878
804
  end
879
805
  end
880
- define('--boltdir PATH',
881
- 'Deprecated. Specify what project to load config from (default:',
882
- 'autodiscovered from current working dir)') do |path|
883
- Bolt::Logger.deprecate(
884
- "boltdir_cli_option",
885
- "Command line option '--boltdir' is deprecated, use '--project' instead."
886
- )
887
- @options[:boltdir] = path
888
- end
889
806
  define('--project PATH',
890
807
  'Path to load the Bolt project from (default: autodiscovered from current dir)') do |path|
891
808
  @options[:project] = path
892
809
  end
893
- define('--configfile PATH',
894
- 'Deprecated. Specify where to load config from (default:',
895
- '~/.puppetlabs/bolt/bolt.yaml). Directory containing bolt.yaml will be',
896
- 'used as the project directory.') do |path|
897
- Bolt::Logger.deprecate(
898
- "configfile_cli_option",
899
- "Command line option '--configfile' is deprecated, and " \
900
- "will be removed in Bolt 3.0. Use '--project' and provide the "\
901
- "directory path instead."
902
- )
903
- @options[:configfile] = path
904
- end
905
810
  define('--hiera-config PATH',
906
811
  'Specify where to load Hiera config from (default: ~/.puppetlabs/bolt/hiera.yaml)') do |path|
907
812
  @options[:'hiera-config'] = File.expand_path(path)
@@ -913,19 +818,6 @@ module Bolt
913
818
  end
914
819
  @options[:inventoryfile] = File.expand_path(path)
915
820
  end
916
- define('--puppetfile PATH',
917
- 'Deprecated. Specify a Puppetfile to use when installing modules.',
918
- ' (default: ~/.puppetlabs/bolt/Puppetfile)',
919
- 'Modules are installed in the current project.') do |path|
920
- command = Bolt::Util.powershell? ? 'Update-BoltProject' : 'bolt project migrate'
921
- Bolt::Logger.deprecate(
922
- "puppetfile_cli_option",
923
- "Command line option '--puppetfile' is deprecated, and will be removed "\
924
- "in Bolt 3.0. You can migrate to using the new module management "\
925
- "workflow using '#{command}'."
926
- )
927
- @options[:puppetfile_path] = File.expand_path(path)
928
- end
929
821
  define('--[no-]save-rerun', 'Whether to update the rerun file after this command.') do |save|
930
822
  @options[:'save-rerun'] = save
931
823
  end
@@ -996,6 +888,9 @@ module Bolt
996
888
  define('-v', '--[no-]verbose', 'Display verbose logging') do |value|
997
889
  @options[:verbose] = value
998
890
  end
891
+ define('--stream', 'Stream output from scripts and commands to the console') do |_|
892
+ @options[:stream] = true
893
+ end
999
894
  define('--trace', 'Display error stack traces') do |_|
1000
895
  @options[:trace] = true
1001
896
  end
@@ -1018,15 +913,6 @@ module Bolt
1018
913
  puts Bolt::VERSION
1019
914
  raise Bolt::CLIExit
1020
915
  end
1021
- define('--debug', 'Display debug logging') do |_|
1022
- @options[:debug] = true
1023
- # We don't actually set '--log-level debug' here, but once the options are evaluated by
1024
- # the config class the end result is the same.
1025
- Bolt::Logger.deprecate(
1026
- "debug_cli_option",
1027
- "Command line option '--debug' is deprecated, set '--log-level debug' instead."
1028
- )
1029
- end
1030
916
  define('--log-level LEVEL',
1031
917
  "Set the log level for the console. Available options are",
1032
918
  "trace, debug, info, warn, error, fatal, any.") do |level|
data/lib/bolt/cli.rb CHANGED
@@ -33,19 +33,18 @@ module Bolt
33
33
 
34
34
  class CLI
35
35
  COMMANDS = {
36
- 'command' => %w[run],
37
- 'script' => %w[run],
38
- 'task' => %w[show run],
39
- 'plan' => %w[show run convert new],
40
- 'file' => %w[download upload],
41
- 'puppetfile' => %w[install show-modules generate-types],
42
- 'secret' => %w[encrypt decrypt createkeys],
43
- 'inventory' => %w[show],
44
- 'group' => %w[show],
45
- 'project' => %w[init migrate],
46
- 'module' => %w[add generate-types install show],
47
- 'apply' => %w[],
48
- 'guide' => %w[]
36
+ 'command' => %w[run],
37
+ 'script' => %w[run],
38
+ 'task' => %w[show run],
39
+ 'plan' => %w[show run convert new],
40
+ 'file' => %w[download upload],
41
+ 'secret' => %w[encrypt decrypt createkeys],
42
+ 'inventory' => %w[show],
43
+ 'group' => %w[show],
44
+ 'project' => %w[init migrate],
45
+ 'module' => %w[add generate-types install show],
46
+ 'apply' => %w[],
47
+ 'guide' => %w[]
49
48
  }.freeze
50
49
 
51
50
  attr_reader :config, :options
@@ -97,6 +96,48 @@ module Bolt
97
96
  finalize_setup
98
97
  end
99
98
 
99
+ # Prints a welcome message when users first install Bolt and run `bolt`, `bolt help` or `bolt --help`
100
+ def welcome_message
101
+ bolt = <<~BOLT
102
+ `.::-`
103
+ `.-:///////-.`
104
+ `-:////:. `-:///:- /ooo. .ooo/
105
+ `.-:///::///:-` `-//: ymmm- :mmmy .---.
106
+ :///:-. `.:////. -//: ymmm- :mmmy +mmm+
107
+ ://. ///. -//: ymmm--/++/- `-/++/:` :mmmy-:smmms::-
108
+ ://. ://. .://: ymmmdmmmmmmdo` .smmmmmmmmh: :mmmysmmmmmmmms
109
+ ://. ://:///:-. ymmmh/--/hmmmy -mmmd/-.:hmmm+:mmmy.-smmms--.
110
+ ://:.` .-////:-` ymmm- ymmm:hmmm- `dmmm/mmmy +mmm+
111
+ `-:///:-..:///:-.` ymmm- ommm/dmmm` hmmm+mmmy +mmm+
112
+ `.-:////:-` ymmm+ /mmmm.ommms` /mmmh:mmmy +mmmo
113
+ `-.` ymmmmmhhmmmmd: ommmmhydmmmy`:mmmy -mmmmdhd
114
+ oyyy+shddhs/` .+shddhy+- -yyyo .ohddhs
115
+
116
+
117
+ BOLT
118
+ example_cmd = if Bolt::Util.windows?
119
+ "Invoke-BoltCommand -Command 'hostname' -Targets localhost"
120
+ else
121
+ "bolt command run 'hostname' --target localhost"
122
+ end
123
+ prev_cmd = String.new("bolt")
124
+ prev_cmd << " #{@argv[0]}" unless @argv.empty?
125
+
126
+ message = <<~MSG
127
+ 🎉 Welcome to Bolt #{VERSION}
128
+ 😌 We're here to help bring order to the chaos
129
+ 📖 Find our documentation at https://bolt.guide
130
+ 🙋 Ask a question in #bolt on https://slack.puppet.com/
131
+ 🔩 Contribute at https://github.com/puppetlabs/bolt/
132
+ 💡 Not sure where to start? Try "#{example_cmd}"
133
+
134
+ We only print this message once. Run "#{prev_cmd}" again for help text.
135
+ MSG
136
+
137
+ $stdout.print "\033[36m#{bolt}\033[0m"
138
+ $stdout.print message
139
+ end
140
+
100
141
  # Parses the command and validates options. All errors that are raised here
101
142
  # are not handled by the outputter, as it relies on config being loaded.
102
143
  def parse_command
@@ -108,6 +149,16 @@ module Bolt
108
149
  # help text
109
150
  options[:subcommand] = nil unless COMMANDS.include?(options[:subcommand])
110
151
 
152
+ if Bolt::Util.first_run?
153
+ FileUtils.mkdir_p(Bolt::Util.first_runs_free.dirname)
154
+ FileUtils.touch(Bolt::Util.first_runs_free)
155
+
156
+ if options[:subcommand].nil? && $stdout.isatty
157
+ welcome_message
158
+ raise Bolt::CLIExit
159
+ end
160
+ end
161
+
111
162
  # Update the parser for the subcommand (or lack thereof)
112
163
  parser.update
113
164
  puts parser.help
@@ -155,25 +206,19 @@ module Bolt
155
206
  # Loads the project and configuration. All errors that are raised here are not
156
207
  # handled by the outputter, as it relies on config being loaded.
157
208
  def load_config
158
- @config = if ENV['BOLT_PROJECT']
159
- project = Bolt::Project.create_project(ENV['BOLT_PROJECT'], 'environment')
160
- Bolt::Config.from_project(project, options)
161
- elsif options[:configfile]
162
- Bolt::Config.from_file(options[:configfile], options)
209
+ project = if ENV['BOLT_PROJECT']
210
+ Bolt::Project.create_project(ENV['BOLT_PROJECT'], 'environment')
211
+ elsif options[:project]
212
+ dir = Pathname.new(options[:project])
213
+ if (dir + Bolt::Project::BOLTDIR_NAME).directory?
214
+ Bolt::Project.create_project(dir + Bolt::Project::BOLTDIR_NAME)
215
+ else
216
+ Bolt::Project.create_project(dir)
217
+ end
163
218
  else
164
- cli_flag = options[:project] || options[:boltdir]
165
- project = if cli_flag
166
- dir = Pathname.new(cli_flag)
167
- if (dir + Bolt::Project::BOLTDIR_NAME).directory?
168
- Bolt::Project.create_project(dir + Bolt::Project::BOLTDIR_NAME)
169
- else
170
- Bolt::Project.create_project(dir)
171
- end
172
- else
173
- Bolt::Project.find_boltdir(Dir.pwd)
174
- end
175
- Bolt::Config.from_project(project, options)
219
+ Bolt::Project.find_boltdir(Dir.pwd)
176
220
  end
221
+ @config = Bolt::Config.from_project(project, options)
177
222
  rescue Bolt::Error => e
178
223
  fatal_error(e)
179
224
  raise e
@@ -182,6 +227,7 @@ module Bolt
182
227
  # Completes the setup process by configuring Bolt and log messages
183
228
  def finalize_setup
184
229
  Bolt::Logger.configure(config.log, config.color, config.disable_warnings)
230
+ Bolt::Logger.stream = config.stream
185
231
  Bolt::Logger.analytics = analytics
186
232
  Bolt::Logger.flush_queue
187
233
 
@@ -210,9 +256,8 @@ module Bolt
210
256
 
211
257
  return unless !stdout.empty? && stdout.to_i < 3
212
258
 
213
- msg = "Detected PowerShell 2 on controller. PowerShell 2 is deprecated and "\
214
- "support will be removed in Bolt 3.0."
215
- Bolt::Logger.deprecate("powershell_2_controller", msg)
259
+ msg = "Detected PowerShell 2 on controller. PowerShell 2 is unsupported."
260
+ Bolt::Logger.deprecation_warning("powershell_2_controller", msg)
216
261
  end
217
262
  end
218
263
 
@@ -220,7 +265,7 @@ module Bolt
220
265
  target_opts = options.keys.select { |opt| %i[query rerun targets].include?(opt) }
221
266
  target_string = "'--targets', '--rerun', or '--query'"
222
267
  if target_opts.length > 1
223
- raise Bolt::CLIError, "Only one targeting option #{target_string} may be specified"
268
+ raise Bolt::CLIError, "Only one targeting option #{target_string} can be specified"
224
269
  elsif target_opts.empty? && options[:subcommand] != 'plan'
225
270
  raise Bolt::CLIError, "Command requires a targeting option: #{target_string}"
226
271
  end
@@ -257,11 +302,14 @@ module Bolt
257
302
  end
258
303
  end
259
304
 
260
- if %w[task plan].include?(options[:subcommand]) && options[:action] == 'run'
305
+ if %w[task plan script].include?(options[:subcommand]) && options[:action] == 'run'
261
306
  if options[:object].nil?
262
307
  raise Bolt::CLIError, "Must specify a #{options[:subcommand]} to run"
263
308
  end
264
- # This may mean that we parsed a parameter as the object
309
+ end
310
+
311
+ # This may mean that we parsed a parameter as the object
312
+ if %w[task plan].include?(options[:subcommand]) && options[:action] == 'run'
265
313
  unless options[:object] =~ /\A([a-z][a-z0-9_]*)?(::[a-z][a-z0-9_]*)*\Z/
266
314
  raise Bolt::CLIError,
267
315
  "Invalid #{options[:subcommand]} '#{options[:object]}'"
@@ -306,26 +354,18 @@ module Bolt
306
354
  "Unknown argument(s) #{options[:leftovers].join(', ')}"
307
355
  end
308
356
 
309
- if options.slice(:boltdir, :configfile, :project).length > 1
310
- raise Bolt::CLIError, "Only one of '--boltdir', '--project', or '--configfile' may be specified"
311
- end
312
-
313
357
  if options[:noop] &&
314
358
  !(options[:subcommand] == 'task' && options[:action] == 'run') && options[:subcommand] != 'apply'
315
359
  raise Bolt::CLIError,
316
- "Option '--noop' may only be specified when running a task or applying manifest code"
360
+ "Option '--noop' can only be specified when running a task or applying manifest code"
317
361
  end
318
362
 
319
363
  if options[:env_vars]
320
364
  unless %w[command script].include?(options[:subcommand]) && options[:action] == 'run'
321
365
  raise Bolt::CLIError,
322
- "Option '--env-var' may only be specified when running a command or script"
366
+ "Option '--env-var' can only be specified when running a command or script"
323
367
  end
324
368
  end
325
-
326
- if options.key?(:debug) && options.key?(:log)
327
- raise Bolt::CLIError, "Only one of '--debug' or '--log-level' may be specified"
328
- end
329
369
  end
330
370
 
331
371
  def handle_parser_errors
@@ -370,7 +410,7 @@ module Bolt
370
410
  if inventory_source && conflicting_options.any?
371
411
  Bolt::Logger.warn(
372
412
  "cli_overrides",
373
- "CLI arguments #{conflicting_options.to_a} may be overridden by Inventory: #{inventory_source}"
413
+ "CLI arguments #{conflicting_options.to_a} might be overridden by Inventory: #{inventory_source}"
374
414
  )
375
415
  end
376
416
  end
@@ -388,7 +428,7 @@ module Bolt
388
428
  # Initialize inventory and targets. Errors here are better to catch early.
389
429
  # options[:target_args] will contain a string/array version of the targetting options this is passed to plans
390
430
  # options[:targets] will contain a resolved set of Target objects
391
- unless %w[guide module project puppetfile secret].include?(options[:subcommand]) ||
431
+ unless %w[guide module project secret].include?(options[:subcommand]) ||
392
432
  %w[convert new show].include?(options[:action])
393
433
  update_targets(options)
394
434
  end
@@ -443,15 +483,12 @@ module Bolt
443
483
  list_modules
444
484
  end
445
485
  return 0
446
- when 'show-modules'
447
- list_modules
448
- return 0
449
486
  when 'convert'
450
487
  pal.convert_plan(options[:object])
451
488
  return 0
452
489
  end
453
490
 
454
- message = 'There may be processes left executing on some nodes.'
491
+ message = 'There might be processes left executing on some nodes.'
455
492
 
456
493
  if %w[task plan].include?(options[:subcommand]) && options[:task_options] && !options[:params_parsed] && pal
457
494
  options[:task_options] = pal.parse_params(options[:subcommand], options[:object], options[:task_options])
@@ -495,17 +532,6 @@ module Bolt
495
532
  when 'generate-types'
496
533
  code = generate_types
497
534
  end
498
- when 'puppetfile'
499
- case options[:action]
500
- when 'generate-types'
501
- code = generate_types
502
- when 'install'
503
- code = install_puppetfile(
504
- config.puppetfile_config,
505
- config.puppetfile,
506
- config.modulepath.first
507
- )
508
- end
509
535
  when 'secret'
510
536
  code = Bolt::Secret.execute(plugins, outputter, options)
511
537
  when 'apply'
@@ -523,7 +549,6 @@ module Bolt
523
549
 
524
550
  elapsed_time = Benchmark.realtime do
525
551
  executor_opts = {}
526
- executor_opts[:description] = options[:description] if options.key?(:description)
527
552
  executor_opts[:env_vars] = options[:env_vars] if options.key?(:env_vars)
528
553
  executor.subscribe(outputter)
529
554
  executor.subscribe(log_outputter)
@@ -532,16 +557,14 @@ module Bolt
532
557
  when 'command'
533
558
  executor.run_command(targets, options[:object], executor_opts)
534
559
  when 'script'
535
- script = options[:object]
536
- validate_file('script', script)
537
- executor.run_script(targets, script, options[:leftovers], executor_opts)
560
+ script_path = find_file(options[:object])
561
+ executor.run_script(targets, script_path, options[:leftovers], executor_opts)
538
562
  when 'task'
539
563
  pal.run_task(options[:object],
540
564
  targets,
541
565
  options[:task_options],
542
566
  executor,
543
- inventory,
544
- options[:description])
567
+ inventory)
545
568
  when 'file'
546
569
  src = options[:object]
547
570
  dest = options[:leftovers].first
@@ -642,7 +665,7 @@ module Bolt
642
665
  if plan_arguments['nodes'] || plan_arguments['targets']
643
666
  key = plan_arguments.include?('nodes') ? 'nodes' : 'targets'
644
667
  raise Bolt::CLIError,
645
- "A plan's '#{key}' parameter may be specified using the --#{key} option, but in that " \
668
+ "A plan's '#{key}' parameter can be specified using the --#{key} option, but in that " \
646
669
  "case it must not be specified as a separate #{key}=<value> parameter nor included " \
647
670
  "in the JSON data passed in the --params option"
648
671
  end
@@ -664,7 +687,6 @@ module Bolt
664
687
 
665
688
  plan_context = { plan_name: plan_name,
666
689
  params: plan_arguments }
667
- plan_context[:description] = options[:description] if options[:description]
668
690
 
669
691
  executor = Bolt::Executor.new(config.concurrency, analytics, options[:noop], config.modified_concurrency)
670
692
  if %w[human rainbow].include?(options.fetch(:format, 'human'))
@@ -728,12 +750,10 @@ module Bolt
728
750
  end
729
751
 
730
752
  def list_modules
731
- assert_puppetfile_or_module_command(config.project.modules)
732
753
  outputter.print_module_list(pal.list_modules)
733
754
  end
734
755
 
735
756
  def generate_types
736
- assert_puppetfile_or_module_command(config.project.modules)
737
757
  # generate_types will surface a nice error with helpful message if it fails
738
758
  pal.generate_types(cache: true)
739
759
  0
@@ -743,19 +763,19 @@ module Bolt
743
763
  #
744
764
  def install_project_modules(project, config, force, resolve)
745
765
  assert_project_file(project)
746
- assert_puppetfile_or_module_command(project.modules)
747
766
 
748
- unless project.modules
749
- outputter.print_message "Project configuration file #{project.project_file} does not "\
750
- "specify any module dependencies. Nothing to do."
767
+ if project.modules.empty? && resolve != false
768
+ outputter.print_message(
769
+ "Project configuration file #{project.project_file} does not "\
770
+ "specify any module dependencies. Nothing to do."
771
+ )
751
772
  return 0
752
773
  end
753
774
 
754
- modules = project.modules || []
755
775
  installer = Bolt::ModuleInstaller.new(outputter, pal)
756
776
 
757
777
  ok = outputter.spin do
758
- installer.install(modules,
778
+ installer.install(project.modules,
759
779
  project.puppetfile,
760
780
  project.managed_moduledir,
761
781
  config,
@@ -770,14 +790,12 @@ module Bolt
770
790
  #
771
791
  def add_project_module(name, project, config)
772
792
  assert_project_file(project)
773
- assert_puppetfile_or_module_command(project.modules)
774
793
 
775
- modules = project.modules || []
776
794
  installer = Bolt::ModuleInstaller.new(outputter, pal)
777
795
 
778
796
  ok = outputter.spin do
779
797
  installer.add(name,
780
- modules,
798
+ project.modules,
781
799
  project.puppetfile,
782
800
  project.managed_moduledir,
783
801
  project.project_file,
@@ -808,8 +826,6 @@ module Bolt
808
826
  # Loads a Puppetfile and installs its modules.
809
827
  #
810
828
  def install_puppetfile(puppetfile_config, puppetfile, moduledir)
811
- assert_puppetfile_or_module_command(config.project.modules)
812
-
813
829
  outputter.print_message("Installing modules from Puppetfile")
814
830
  installer = Bolt::ModuleInstaller.new(outputter, pal)
815
831
  ok = outputter.spin do
@@ -819,47 +835,6 @@ module Bolt
819
835
  ok ? 0 : 1
820
836
  end
821
837
 
822
- # Raises an error if the 'puppetfile install' command is deprecated due to
823
- # modules being configured.
824
- #
825
- def assert_puppetfile_or_module_command(modules)
826
- if Bolt::Util.powershell?
827
- case options[:action]
828
- when 'generate-types'
829
- old_command = 'Register-BoltPuppetfileTypes'
830
- new_command = 'Register-BoltModuleTypes'
831
- when 'install'
832
- old_command = 'Install-BoltPuppetfile'
833
- new_command = 'Install-BoltModule'
834
- when 'show', 'show-modules'
835
- old_command = 'Get-BoltPuppetfileModules'
836
- new_command = 'Get-BoltModule'
837
- end
838
- else
839
- old_command = "bolt puppetfile #{options[:action]}"
840
- new_command = if options[:action] == 'show-modules'
841
- 'bolt module show'
842
- else
843
- "bolt module #{options[:action]}"
844
- end
845
- end
846
-
847
- if modules && options[:subcommand] == 'puppetfile'
848
- raise Bolt::CLIError,
849
- "Unable to use command '#{old_command}' when 'modules' is configured in "\
850
- "bolt-project.yaml. Use '#{new_command}' instead."
851
- elsif modules.nil? && options[:subcommand] == 'puppetfile'
852
- msg = "Command '#{old_command}' is deprecated and will be removed in Bolt 3.0. Update your project to use "\
853
- "the module management feature. For more information, see https://pup.pt/bolt-module-migrate."
854
- Bolt::Logger.deprecate("puppetfile_command", msg)
855
- elsif modules.nil? && options[:subcommand] == 'module'
856
- msg = "Unable to use command '#{new_command}' when 'modules' is not configured in "\
857
- "bolt-project.yaml. "
858
- msg += "Use '#{old_command}' instead." if options[:action] != 'add'
859
- raise Bolt::CLIError, msg
860
- end
861
- end
862
-
863
838
  def pal
864
839
  @pal ||= Bolt::PAL.new(Bolt::Config::Modulepath.new(config.modulepath),
865
840
  config.hiera_config,
@@ -920,6 +895,28 @@ module Bolt
920
895
  Bolt::Util.validate_file(type, path, allow_dir)
921
896
  end
922
897
 
898
+ # Returns the path to a file. If the path is an absolute or relative to
899
+ # a file, and the file exists, returns the path as-is. Otherwise, checks if
900
+ # the path is a Puppet file path and looks for the file in a module's files
901
+ # directory.
902
+ #
903
+ def find_file(path)
904
+ unless File.exist?(path) || Pathname.new(path).absolute?
905
+ modulepath = Bolt::Config::Modulepath.new(config.modulepath)
906
+ modules = Bolt::Module.discover(modulepath.full_modulepath, config.project)
907
+ mod, file = path.split(File::SEPARATOR, 2)
908
+
909
+ if modules[mod]
910
+ @logger.debug("Did not find file at #{File.expand_path(path)}, checking in module '#{mod}'")
911
+ path = File.join(modules[mod].path, 'files', file)
912
+ end
913
+ end
914
+
915
+ Bolt::Util.validate_file('script', path)
916
+
917
+ path
918
+ end
919
+
923
920
  def rerun
924
921
  @rerun ||= Bolt::Rerun.new(config.rerunfile, config.save_rerun)
925
922
  end
@@ -948,7 +945,7 @@ module Bolt
948
945
  # If the bundled content directory is empty, Bolt is likely installed as a gem.
949
946
  if ENV['BOLT_GEM'].nil? && incomplete_install?
950
947
  msg = <<~MSG.chomp
951
- Bolt may be installed as a gem. To use Bolt reliably and with all of its
948
+ Bolt might be installed as a gem. To use Bolt reliably and with all of its
952
949
  dependencies, uninstall the 'bolt' gem and install Bolt as a package:
953
950
  https://puppet.com/docs/bolt/latest/bolt_installing.html
954
951
 
@@ -979,7 +976,8 @@ module Bolt
979
976
  # Gem installs include the aggregate, canary, and puppetdb_fact modules, while
980
977
  # package installs include modules listed in the Bolt repo Puppetfile
981
978
  def incomplete_install?
982
- (Dir.children(Bolt::Config::Modulepath::MODULES_PATH) - %w[aggregate canary puppetdb_fact secure_env_vars]).empty?
979
+ builtin_module_list = %w[aggregate canary puppetdb_fact secure_env_vars puppet_connect]
980
+ (Dir.children(Bolt::Config::Modulepath::MODULES_PATH) - builtin_module_list).empty?
983
981
  end
984
982
 
985
983
  # Mimicks the output from Outputter::Human#fatal_error. This should be used to print