bolt 3.1.0 → 3.6.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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +11 -11
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +24 -0
  4. data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -1
  5. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +20 -2
  6. data/bolt-modules/boltlib/lib/puppet/functions/run_container.rb +162 -0
  7. data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +2 -2
  8. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +44 -5
  9. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
  10. data/bolt-modules/boltlib/types/planresult.pp +1 -0
  11. data/bolt-modules/file/lib/puppet/functions/file/read.rb +3 -2
  12. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +20 -2
  13. data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
  14. data/lib/bolt/analytics.rb +4 -8
  15. data/lib/bolt/apply_result.rb +1 -1
  16. data/lib/bolt/bolt_option_parser.rb +6 -3
  17. data/lib/bolt/cli.rb +121 -36
  18. data/lib/bolt/config.rb +15 -7
  19. data/lib/bolt/config/options.rb +62 -12
  20. data/lib/bolt/config/transport/lxd.rb +23 -0
  21. data/lib/bolt/config/transport/options.rb +8 -1
  22. data/lib/bolt/config/transport/podman.rb +33 -0
  23. data/lib/bolt/container_result.rb +105 -0
  24. data/lib/bolt/error.rb +15 -0
  25. data/lib/bolt/executor.rb +37 -18
  26. data/lib/bolt/inventory/options.rb +9 -0
  27. data/lib/bolt/inventory/target.rb +16 -0
  28. data/lib/bolt/logger.rb +8 -0
  29. data/lib/bolt/module_installer.rb +2 -2
  30. data/lib/bolt/module_installer/puppetfile.rb +2 -2
  31. data/lib/bolt/module_installer/specs/forge_spec.rb +2 -2
  32. data/lib/bolt/module_installer/specs/git_spec.rb +2 -2
  33. data/lib/bolt/node/output.rb +14 -4
  34. data/lib/bolt/outputter/human.rb +259 -90
  35. data/lib/bolt/outputter/json.rb +3 -1
  36. data/lib/bolt/outputter/logger.rb +17 -0
  37. data/lib/bolt/pal.rb +24 -4
  38. data/lib/bolt/pal/yaml_plan.rb +1 -2
  39. data/lib/bolt/pal/yaml_plan/evaluator.rb +5 -141
  40. data/lib/bolt/pal/yaml_plan/step.rb +91 -31
  41. data/lib/bolt/pal/yaml_plan/step/command.rb +21 -13
  42. data/lib/bolt/pal/yaml_plan/step/download.rb +15 -16
  43. data/lib/bolt/pal/yaml_plan/step/eval.rb +11 -11
  44. data/lib/bolt/pal/yaml_plan/step/message.rb +13 -4
  45. data/lib/bolt/pal/yaml_plan/step/plan.rb +19 -15
  46. data/lib/bolt/pal/yaml_plan/step/resources.rb +82 -21
  47. data/lib/bolt/pal/yaml_plan/step/script.rb +36 -17
  48. data/lib/bolt/pal/yaml_plan/step/task.rb +19 -16
  49. data/lib/bolt/pal/yaml_plan/step/upload.rb +16 -17
  50. data/lib/bolt/pal/yaml_plan/transpiler.rb +3 -3
  51. data/lib/bolt/plan_creator.rb +1 -1
  52. data/lib/bolt/plugin.rb +13 -11
  53. data/lib/bolt/project_manager.rb +1 -1
  54. data/lib/bolt/project_manager/module_migrator.rb +1 -1
  55. data/lib/bolt/result.rb +5 -14
  56. data/lib/bolt/shell.rb +16 -0
  57. data/lib/bolt/shell/bash.rb +68 -30
  58. data/lib/bolt/shell/bash/tmpdir.rb +2 -2
  59. data/lib/bolt/shell/powershell.rb +28 -11
  60. data/lib/bolt/task.rb +1 -1
  61. data/lib/bolt/transport/docker.rb +1 -1
  62. data/lib/bolt/transport/docker/connection.rb +21 -32
  63. data/lib/bolt/transport/lxd.rb +26 -0
  64. data/lib/bolt/transport/lxd/connection.rb +99 -0
  65. data/lib/bolt/transport/orch.rb +13 -5
  66. data/lib/bolt/transport/podman.rb +19 -0
  67. data/lib/bolt/transport/podman/connection.rb +98 -0
  68. data/lib/bolt/transport/ssh/connection.rb +1 -1
  69. data/lib/bolt/transport/winrm/connection.rb +1 -1
  70. data/lib/bolt/util.rb +42 -0
  71. data/lib/bolt/version.rb +1 -1
  72. data/lib/bolt_server/transport_app.rb +16 -1
  73. data/lib/bolt_spec/plans/action_stubs.rb +1 -1
  74. data/lib/bolt_spec/plans/action_stubs/command_stub.rb +8 -1
  75. data/lib/bolt_spec/plans/action_stubs/script_stub.rb +8 -1
  76. data/lib/bolt_spec/plans/mock_executor.rb +91 -7
  77. data/modules/puppet_connect/plans/test_input_data.pp +22 -0
  78. metadata +12 -2
data/lib/bolt/config.rb CHANGED
@@ -169,6 +169,7 @@ module Bolt
169
169
  @config_files = []
170
170
 
171
171
  default_data = {
172
+ 'analytics' => true,
172
173
  'apply-settings' => {},
173
174
  'color' => true,
174
175
  'compile-concurrency' => Etc.nprocessors,
@@ -263,6 +264,8 @@ module Bolt
263
264
  # Disabled warnings are concatenated
264
265
  when 'disable-warnings'
265
266
  val1.concat(val2)
267
+ when 'analytics'
268
+ val1 && val2
266
269
  # All other values are overwritten
267
270
  else
268
271
  val2
@@ -328,13 +331,6 @@ module Bolt
328
331
  end
329
332
 
330
333
  def validate
331
- if @data['future']
332
- Bolt::Logger.warn(
333
- "future_option",
334
- "Configuration option 'future' no longer exposes future behavior."
335
- )
336
- end
337
-
338
334
  if @data['modulepath']&.include?(@project.managed_moduledir.to_s)
339
335
  raise Bolt::ValidationError,
340
336
  "Found invalid path in modulepath: #{@project.managed_moduledir}. This path "\
@@ -395,6 +391,10 @@ module Bolt
395
391
  @data['format'] = value
396
392
  end
397
393
 
394
+ def future
395
+ @data['future']
396
+ end
397
+
398
398
  def trace
399
399
  @data['trace']
400
400
  end
@@ -419,6 +419,10 @@ module Bolt
419
419
  @data['spinner']
420
420
  end
421
421
 
422
+ def stream
423
+ @data['stream']
424
+ end
425
+
422
426
  def inventoryfile
423
427
  @data['inventoryfile']
424
428
  end
@@ -455,6 +459,10 @@ module Bolt
455
459
  Set.new(@project.disable_warnings + @data['disable-warnings'])
456
460
  end
457
461
 
462
+ def analytics
463
+ @data['analytics']
464
+ end
465
+
458
466
  # Check if there is a case-insensitive match to the path
459
467
  def check_path_case(type, paths)
460
468
  return if paths.nil?
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bolt/config/transport/ssh'
4
- require 'bolt/config/transport/winrm'
5
- require 'bolt/config/transport/orch'
6
- require 'bolt/config/transport/local'
7
3
  require 'bolt/config/transport/docker'
4
+ require 'bolt/config/transport/local'
5
+ require 'bolt/config/transport/lxd'
6
+ require 'bolt/config/transport/orch'
7
+ require 'bolt/config/transport/podman'
8
8
  require 'bolt/config/transport/remote'
9
+ require 'bolt/config/transport/ssh'
10
+ require 'bolt/config/transport/winrm'
9
11
 
10
12
  module Bolt
11
13
  class Config
@@ -13,12 +15,14 @@ module Bolt
13
15
  # Transport config classes. Used to load default transport config which
14
16
  # gets passed along to the inventory.
15
17
  TRANSPORT_CONFIG = {
16
- 'ssh' => Bolt::Config::Transport::SSH,
17
- 'winrm' => Bolt::Config::Transport::WinRM,
18
- 'pcp' => Bolt::Config::Transport::Orch,
19
- 'local' => Bolt::Config::Transport::Local,
20
18
  'docker' => Bolt::Config::Transport::Docker,
21
- 'remote' => Bolt::Config::Transport::Remote
19
+ 'local' => Bolt::Config::Transport::Local,
20
+ 'lxd' => Bolt::Config::Transport::LXD,
21
+ 'pcp' => Bolt::Config::Transport::Orch,
22
+ 'podman' => Bolt::Config::Transport::Podman,
23
+ 'remote' => Bolt::Config::Transport::Remote,
24
+ 'ssh' => Bolt::Config::Transport::SSH,
25
+ 'winrm' => Bolt::Config::Transport::WinRM
22
26
  }.freeze
23
27
 
24
28
  # Plugin definition. This is used by the JSON schemas to indicate that an option
@@ -53,6 +57,13 @@ module Bolt
53
57
  # Definitions used to validate config options.
54
58
  # https://github.com/puppetlabs/bolt/blob/main/schemas/README.md
55
59
  OPTIONS = {
60
+ "analytics" => {
61
+ description: "Whether to disable analytics. Setting this option to 'false' in the system-wide "\
62
+ "or user-level configuration will disable analytics for all projects, even if this "\
63
+ "option is set to 'true' at the project level.",
64
+ type: [TrueClass, FalseClass],
65
+ _example: false
66
+ },
56
67
  "apply-settings" => {
57
68
  description: "A map of Puppet settings to use when applying Puppet code using the `apply` "\
58
69
  "plan function or the `bolt apply` command.",
@@ -131,6 +142,20 @@ module Bolt
131
142
  _example: "json",
132
143
  _default: "human"
133
144
  },
145
+ "future" => {
146
+ description: "Enable new Bolt features that may include breaking changes.",
147
+ type: Hash,
148
+ properties: {
149
+ "file_paths" => {
150
+ description: "Load scripts from the `scripts/` directory of a module",
151
+ type: [TrueClass, FalseClass],
152
+ _example: true,
153
+ _default: false
154
+ }
155
+ },
156
+ _plugin: false,
157
+ _example: { 'file_paths' => true }
158
+ },
134
159
  "hiera-config" => {
135
160
  description: "The path to the Hiera configuration file.",
136
161
  type: String,
@@ -423,7 +448,14 @@ module Bolt
423
448
  _example: false,
424
449
  _default: true
425
450
  },
426
-
451
+ "stream" => {
452
+ description: "Whether to stream output from scripts and commands to the console. "\
453
+ "**This option is experimental**.",
454
+ type: [TrueClass, FalseClass],
455
+ _plugin: false,
456
+ _default: false,
457
+ _example: true
458
+ },
427
459
  "tasks" => {
428
460
  description: "A list of task names and glob patterns to filter the project's tasks by. This option is used "\
429
461
  "to limit the visibility of tasks for users of the project. For example, project authors "\
@@ -440,8 +472,8 @@ module Bolt
440
472
  },
441
473
  "trusted-external-command" => {
442
474
  description: "The path to an executable on the Bolt controller that can produce external trusted facts. "\
443
- "**External trusted facts are experimental in both Puppet and Bolt and this API may change or "\
444
- "be removed.**",
475
+ "**External trusted facts are experimental in both Puppet and Bolt and this API might "\
476
+ "change or be removed.**",
445
477
  type: String,
446
478
  _plugin: false,
447
479
  _example: "/etc/puppetlabs/facts/trusted_external.sh"
@@ -475,12 +507,25 @@ module Bolt
475
507
  _plugin: true,
476
508
  _example: { "cleanup" => false, "tmpdir" => "/tmp/bolt" }
477
509
  },
510
+ "lxd" => {
511
+ description: "A map of configuration options for the LXD transport. The LXD transport is "\
512
+ "experimental and might include breaking changes between minor versions.",
513
+ type: Hash,
514
+ _plugin: true,
515
+ _example: { cleanup: false }
516
+ },
478
517
  "pcp" => {
479
518
  description: "A map of configuration options for the pcp transport.",
480
519
  type: Hash,
481
520
  _plugin: true,
482
521
  _example: { "job-poll-interval" => 15, "job-poll-timeout" => 30 }
483
522
  },
523
+ "podman" => {
524
+ description: "A map of configuration options for the podman transport.",
525
+ type: Hash,
526
+ _plugin: true,
527
+ _example: { "cleanup" => false, "tmpdir" => "/mount/tmp" }
528
+ },
484
529
  "remote" => {
485
530
  description: "A map of configuration options for the remote transport.",
486
531
  type: Hash,
@@ -516,6 +561,7 @@ module Bolt
516
561
 
517
562
  # Options that are available in a bolt-defaults.yaml file
518
563
  DEFAULTS_OPTIONS = %w[
564
+ analytics
519
565
  color
520
566
  compile-concurrency
521
567
  concurrency
@@ -530,16 +576,19 @@ module Bolt
530
576
  puppetdb
531
577
  save-rerun
532
578
  spinner
579
+ stream
533
580
  ].freeze
534
581
 
535
582
  # Options that are available in a bolt-project.yaml file
536
583
  PROJECT_OPTIONS = %w[
584
+ analytics
537
585
  apply-settings
538
586
  color
539
587
  compile-concurrency
540
588
  concurrency
541
589
  disable-warnings
542
590
  format
591
+ future
543
592
  hiera-config
544
593
  log
545
594
  modulepath
@@ -553,6 +602,7 @@ module Bolt
553
602
  puppetdb
554
603
  save-rerun
555
604
  spinner
605
+ stream
556
606
  tasks
557
607
  trusted-external-command
558
608
  ].freeze
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/error'
4
+ require 'bolt/config/transport/base'
5
+
6
+ module Bolt
7
+ class Config
8
+ module Transport
9
+ class LXD < Base
10
+ OPTIONS = %w[
11
+ cleanup
12
+ remote
13
+ tmpdir
14
+ ].freeze
15
+
16
+ DEFAULTS = {
17
+ 'cleanup' => true,
18
+ 'remote' => 'local'
19
+ }.freeze
20
+ end
21
+ end
22
+ end
23
+ end
@@ -32,7 +32,7 @@ module Bolt
32
32
  "cleanup" => {
33
33
  type: [TrueClass, FalseClass],
34
34
  description: "Whether to clean up temporary files created on targets. When running commands on a target, "\
35
- "Bolt may create temporary files. After completing the command, these files are "\
35
+ "Bolt might create temporary files. After completing the command, these files are "\
36
36
  "automatically deleted. This value can be set to 'false' if you wish to leave these "\
37
37
  "temporary files on the target.",
38
38
  _plugin: true,
@@ -266,6 +266,13 @@ module Bolt
266
266
  _plugin: true,
267
267
  _example: "BOLT.PRODUCTION"
268
268
  },
269
+ "remote" => {
270
+ type: String,
271
+ description: "The LXD remote host to use.",
272
+ _default: "local",
273
+ _plugin: false,
274
+ _example: 'myremote'
275
+ },
269
276
  "run-as" => {
270
277
  type: String,
271
278
  description: "The user to run commands as after login. The run-as user must be different than the "\
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/error'
4
+ require 'bolt/config/transport/base'
5
+
6
+ module Bolt
7
+ class Config
8
+ module Transport
9
+ class Podman < Base
10
+ OPTIONS = %w[
11
+ cleanup
12
+ host
13
+ interpreters
14
+ shell-command
15
+ tmpdir
16
+ tty
17
+ ].freeze
18
+
19
+ DEFAULTS = {
20
+ 'cleanup' => true
21
+ }.freeze
22
+
23
+ private def validate
24
+ super
25
+
26
+ if @config['interpreters']
27
+ @config['interpreters'] = normalize_interpreters(@config['interpreters'])
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'bolt/error'
5
+ require 'bolt/result'
6
+
7
+ module Bolt
8
+ class ContainerResult
9
+ attr_reader :value, :object
10
+
11
+ def self.from_exception(exception, exit_code, image, position: [])
12
+ details = Bolt::Result.create_details(position)
13
+ error = {
14
+ 'kind' => 'puppetlabs.tasks/container-error',
15
+ 'issue_code' => 'CONTAINER_ERROR',
16
+ 'msg' => "Error running container '#{image}': #{exception}",
17
+ 'details' => details
18
+ }
19
+ error['details']['exit_code'] = exit_code
20
+ ContainerResult.new({ '_error' => error }, object: image)
21
+ end
22
+
23
+ def _pcore_init_hash
24
+ { 'value' => @value,
25
+ 'object' => @image }
26
+ end
27
+
28
+ # First argument can't be named given the way that Puppet deserializes variables
29
+ def initialize(value = nil, object: nil)
30
+ @value = value || {}
31
+ @object = object
32
+ end
33
+
34
+ def eql?(other)
35
+ self.class == other.class &&
36
+ value == other.value
37
+ end
38
+ alias == eql?
39
+
40
+ def [](key)
41
+ value[key]
42
+ end
43
+
44
+ def to_json(opts = nil)
45
+ to_data.to_json(opts)
46
+ end
47
+ alias to_s to_json
48
+
49
+ # This is the value with all non-UTF-8 characters removed, suitable for
50
+ # printing or converting to JSON. It *should* only be possible to have
51
+ # non-UTF-8 characters in stdout/stderr keys as they are not allowed from
52
+ # tasks but we scrub the whole thing just in case.
53
+ def safe_value
54
+ Bolt::Util.walk_vals(value) do |val|
55
+ if val.is_a?(String)
56
+ # Replace invalid bytes with hex codes, ie. \xDE\xAD\xBE\xEF
57
+ val.scrub { |c| c.bytes.map { |b| "\\x" + b.to_s(16).upcase }.join }
58
+ else
59
+ val
60
+ end
61
+ end
62
+ end
63
+
64
+ def stdout
65
+ value['stdout']
66
+ end
67
+
68
+ def stderr
69
+ value['stderr']
70
+ end
71
+
72
+ def to_data
73
+ {
74
+ "object" => object,
75
+ "status" => status,
76
+ "value" => safe_value
77
+ }
78
+ end
79
+
80
+ def status
81
+ ok? ? 'success' : 'failure'
82
+ end
83
+
84
+ def ok?
85
+ error_hash.nil?
86
+ end
87
+ alias ok ok?
88
+ alias success? ok?
89
+
90
+ # This allows access to errors outside puppet compilation
91
+ # it should be prefered over error in bolt code
92
+ def error_hash
93
+ value['_error']
94
+ end
95
+
96
+ # Warning: This will fail outside of a compilation.
97
+ # Use error_hash inside bolt.
98
+ # Is it crazy for this to behave differently outside a compiler?
99
+ def error
100
+ if error_hash
101
+ Puppet::DataTypes::Error.from_asserted_hash(error_hash)
102
+ end
103
+ end
104
+ end
105
+ end
data/lib/bolt/error.rb CHANGED
@@ -61,6 +61,21 @@ module Bolt
61
61
  end
62
62
  end
63
63
 
64
+ class ContainerFailure < Bolt::Error
65
+ attr_reader :result
66
+
67
+ def initialize(result)
68
+ details = {
69
+ 'value' => result.value,
70
+ 'object' => result.object
71
+ }
72
+ message = "Plan aborted: Running container '#{result.object}' failed."
73
+ super(message, 'bolt/container-failure', details)
74
+ @result = result
75
+ @error_code = 2
76
+ end
77
+ end
78
+
64
79
  class RunFailure < Bolt::Error
65
80
  attr_reader :result_set
66
81
 
data/lib/bolt/executor.rb CHANGED
@@ -4,6 +4,7 @@
4
4
  require 'English'
5
5
  require 'json'
6
6
  require 'logging'
7
+ require 'pathname'
7
8
  require 'set'
8
9
  require 'bolt/analytics'
9
10
  require 'bolt/result'
@@ -11,35 +12,39 @@ require 'bolt/config'
11
12
  require 'bolt/result_set'
12
13
  require 'bolt/puppetdb'
13
14
  # Load transports
14
- require 'bolt/transport/ssh'
15
- require 'bolt/transport/winrm'
16
- require 'bolt/transport/orch'
17
- require 'bolt/transport/local'
18
15
  require 'bolt/transport/docker'
16
+ require 'bolt/transport/local'
17
+ require 'bolt/transport/lxd'
18
+ require 'bolt/transport/orch'
19
+ require 'bolt/transport/podman'
19
20
  require 'bolt/transport/remote'
21
+ require 'bolt/transport/ssh'
22
+ require 'bolt/transport/winrm'
20
23
  require 'bolt/yarn'
21
24
 
22
25
  module Bolt
23
26
  TRANSPORTS = {
24
- ssh: Bolt::Transport::SSH,
25
- winrm: Bolt::Transport::WinRM,
26
- pcp: Bolt::Transport::Orch,
27
- local: Bolt::Transport::Local,
28
27
  docker: Bolt::Transport::Docker,
29
- remote: Bolt::Transport::Remote
28
+ local: Bolt::Transport::Local,
29
+ lxd: Bolt::Transport::LXD,
30
+ pcp: Bolt::Transport::Orch,
31
+ podman: Bolt::Transport::Podman,
32
+ remote: Bolt::Transport::Remote,
33
+ ssh: Bolt::Transport::SSH,
34
+ winrm: Bolt::Transport::WinRM
30
35
  }.freeze
31
36
 
32
37
  class Executor
33
- attr_reader :noop, :transports, :in_parallel
38
+ attr_reader :noop, :transports, :in_parallel, :future
34
39
  attr_accessor :run_as
35
40
 
36
41
  def initialize(concurrency = 1,
37
42
  analytics = Bolt::Analytics::NoopClient.new,
38
43
  noop = false,
39
- modified_concurrency = false)
44
+ modified_concurrency = false,
45
+ future = {})
40
46
  # lazy-load expensive gem code
41
47
  require 'concurrent'
42
-
43
48
  @analytics = analytics
44
49
  @logger = Bolt::Logger.logger(self)
45
50
 
@@ -62,6 +67,7 @@ module Bolt
62
67
  @noop = noop
63
68
  @run_as = nil
64
69
  @in_parallel = false
70
+ @future = future
65
71
  @pool = if concurrency > 0
66
72
  Concurrent::ThreadPoolExecutor.new(name: 'exec', max_threads: concurrency)
67
73
  else
@@ -121,8 +127,8 @@ module Bolt
121
127
  def queue_execute(targets)
122
128
  if @warn_concurrency && targets.length > @concurrency
123
129
  @warn_concurrency = false
124
- msg = "The ulimit is low, which may cause file limit issues. Default concurrency has been set to "\
125
- "'#{@concurrency}' to mitigate those issues, which may cause Bolt to run slow. "\
130
+ msg = "The ulimit is low, which might cause file limit issues. Default concurrency has been set to "\
131
+ "'#{@concurrency}' to mitigate those issues, which might cause Bolt to run slow. "\
126
132
  "Disable this warning by configuring ulimit using 'ulimit -n <limit>' in your shell "\
127
133
  "configuration, or by configuring Bolt's concurrency. "\
128
134
  "See https://puppet.com/docs/bolt/latest/bolt_known_issues.html for details."
@@ -215,7 +221,7 @@ module Bolt
215
221
  results
216
222
  end
217
223
 
218
- def report_transport(transport, count)
224
+ private def report_transport(transport, count)
219
225
  name = transport.class.name.split('::').last.downcase
220
226
  unless @reported_transports.include?(name)
221
227
  @analytics&.event('Transport', 'initialize', label: name, value: count)
@@ -231,6 +237,11 @@ module Bolt
231
237
  @analytics.report_bundled_content(mode, name)
232
238
  end
233
239
 
240
+ def report_file_source(plan_function, source)
241
+ label = Pathname.new(source).absolute? ? 'absolute' : 'module'
242
+ @analytics&.event('Plan', plan_function, label: label)
243
+ end
244
+
234
245
  def report_apply(statement_count, resource_counts)
235
246
  data = { statement_count: statement_count }
236
247
 
@@ -468,7 +479,7 @@ module Bolt
468
479
  Time.now
469
480
  end
470
481
 
471
- def wait_until(timeout, retry_interval)
482
+ private def wait_until(timeout, retry_interval)
472
483
  start = wait_now
473
484
  until yield
474
485
  raise(TimeoutError, 'Timed out waiting for target') if (wait_now - start).to_i >= timeout
@@ -477,22 +488,30 @@ module Bolt
477
488
  end
478
489
 
479
490
  def prompt(prompt, options)
480
- @prompting = true
481
491
  unless $stdin.tty?
492
+ return options[:default] if options[:default]
482
493
  raise Bolt::Error.new('STDIN is not a tty, unable to prompt', 'bolt/no-tty-error')
483
494
  end
484
495
 
485
- $stderr.print("#{prompt}: ")
496
+ @prompting = true
497
+
498
+ if options[:default] && !options[:sensitive]
499
+ $stderr.print("#{prompt} [#{options[:default]}]: ")
500
+ else
501
+ $stderr.print("#{prompt}: ")
502
+ end
486
503
 
487
504
  value = if options[:sensitive]
488
505
  $stdin.noecho(&:gets).to_s.chomp
489
506
  else
490
507
  $stdin.gets.to_s.chomp
491
508
  end
509
+
492
510
  @prompting = false
493
511
 
494
512
  $stderr.puts if options[:sensitive]
495
513
 
514
+ value = options[:default] if value.empty?
496
515
  value
497
516
  end
498
517