bolt 2.14.0 → 2.15.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bolt might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fbe6053f1a3801adb0f1d0684f1e4616311c93bf0513a74cde2c54c82d5de503
4
- data.tar.gz: b51e4068dcceb096d5e6cec4a79d692b84d1472abe57065de1a219d8ed84a4ad
3
+ metadata.gz: 696f0fbeea26bd6bccad0ab2935e8174b871148bcae962438996e0822a216a7f
4
+ data.tar.gz: fc865179b5b437a6474e45d1c94139b892cec331fb85511c81ef19b41af4a806
5
5
  SHA512:
6
- metadata.gz: ea53a6643459311d007ad7b8c2a60e5d0d4af6b0005b037295edff063a2e1f0236054d9cb5c14cec039ee8d0eb0ffa94e887b6a1ba64c3bb196f2a10cd90ce6d
7
- data.tar.gz: 7bfde56e613103081c5ce73095c82b897f5db8b973365e6fbd28dfc7f57873fce518d37fe55bb76e36f36ab9dc802a12e545621bbff44f3a389ff071898bbc2c
6
+ metadata.gz: e0bf903cf991732261e779a665de009b9e45a08c911cab88df738201ebdc8a75b5d4e82cc7585956215b0e93f30b21142a37401f94d5ce8e0d7e41c8014bf499
7
+ data.tar.gz: ea88a182be0b972869342ef16423089338b13f9e0f540b13da4b133c231f80597646c4f920d254184e5c5b276c08ec6ec6eff0c79aeb456dfd777e68ba4433fc
data/Puppetfile CHANGED
@@ -33,7 +33,7 @@ mod 'puppetlabs-ruby_plugin_helper', '0.1.0'
33
33
  mod 'puppetlabs-aws_inventory', '0.5.0'
34
34
  mod 'puppetlabs-azure_inventory', '0.3.0'
35
35
  mod 'puppetlabs-gcloud_inventory', '0.1.1'
36
- mod 'puppetlabs-pkcs7', '0.1.0'
36
+ mod 'puppetlabs-pkcs7', '0.1.1'
37
37
  mod 'puppetlabs-terraform', '0.5.0'
38
38
  mod 'puppetlabs-vault', '0.3.0'
39
39
  mod 'puppetlabs-yaml', '0.2.0'
@@ -9,7 +9,7 @@ Puppet::Functions.create_function(:'file::exists', Puppet::Functions::InternalFu
9
9
  # @example Check a file on disk
10
10
  # file::exists('/tmp/i_dumped_this_here')
11
11
  # @example check a file from the modulepath
12
- # file::exists('example/files/VERSION')
12
+ # file::exists('example/VERSION')
13
13
  dispatch :exists do
14
14
  scope_param
15
15
  required_param 'String', :filename
@@ -8,7 +8,7 @@ Puppet::Functions.create_function(:'file::read', Puppet::Functions::InternalFunc
8
8
  # @example Read a file from disk
9
9
  # file::read('/tmp/i_dumped_this_here')
10
10
  # @example Read a file from the modulepath
11
- # file::read('example/files/VERSION')
11
+ # file::read('example/VERSION')
12
12
  dispatch :read do
13
13
  scope_param
14
14
  required_param 'String', :filename
@@ -9,7 +9,7 @@ Puppet::Functions.create_function(:'file::readable', Puppet::Functions::Internal
9
9
  # @example Check a file on disk
10
10
  # file::readable('/tmp/i_dumped_this_here')
11
11
  # @example check a file from the modulepath
12
- # file::readable('example/files/VERSION')
12
+ # file::readable('example/VERSION')
13
13
  dispatch :readable do
14
14
  scope_param
15
15
  required_param 'String', :filename
@@ -106,6 +106,16 @@ module Bolt
106
106
  out, err, stat = Open3.capture3('ruby', bolt_catalog_exe, 'compile', stdin_data: catalog_input.to_json)
107
107
  ENV['PATH'] = old_path
108
108
 
109
+ # If bolt_catalog does not return valid JSON, we should print stderr to
110
+ # see what happened
111
+ print_logs = stat.success?
112
+ result = begin
113
+ JSON.parse(out)
114
+ rescue JSON::ParserError
115
+ print_logs = true
116
+ { 'message' => "Something's gone terribly wrong! STDERR is logged." }
117
+ end
118
+
109
119
  # Any messages logged by Puppet will be on stderr as JSON hashes, so we
110
120
  # parse those and store them here. Any message on stderr that is not
111
121
  # properly JSON formatted is assumed to be an error message. If
@@ -119,17 +129,15 @@ module Bolt
119
129
  { 'level' => 'err', 'message' => line }
120
130
  end
121
131
 
122
- result = JSON.parse(out)
123
- if stat.success?
132
+ if print_logs
124
133
  logs.each do |log|
125
134
  bolt_level = Bolt::Util::PuppetLogLevel::MAPPING[log['level'].to_sym]
126
135
  message = log['message'].chomp
127
136
  @logger.send(bolt_level, "#{target.name}: #{message}")
128
137
  end
129
- result
130
- else
131
- raise ApplyError.new(target.name, result['message'])
132
138
  end
139
+ raise ApplyError.new(target.name, result['message']) unless stat.success?
140
+ result
133
141
  end
134
142
 
135
143
  def validate_hiera_config(hiera_config)
@@ -25,6 +25,11 @@ module Bolt
25
25
  class Config
26
26
  attr_reader :config_files, :warnings, :data, :transports, :project, :modified_concurrency
27
27
 
28
+ BOLT_CONFIG_NAME = 'bolt.yaml'
29
+ BOLT_DEFAULTS_NAME = 'bolt-defaults.yaml'
30
+
31
+ # Transport config classes. Used to load default transport config which
32
+ # gets passed along to the inventory.
28
33
  TRANSPORT_CONFIG = {
29
34
  'ssh' => Bolt::Config::Transport::SSH,
30
35
  'winrm' => Bolt::Config::Transport::WinRM,
@@ -34,50 +39,84 @@ module Bolt
34
39
  'remote' => Bolt::Config::Transport::Remote
35
40
  }.freeze
36
41
 
37
- TRANSPORT_OPTION = { 'transport' => 'The default transport to use when the '\
38
- 'transport for a target is not specified in the URL.' }.freeze
42
+ # Options that configure Bolt. These options are used in bolt.yaml and
43
+ # bolt-defaults.yaml.
44
+ BOLT_CONFIG = {
45
+ "color" => "Whether to use colored output when printing messages to the console.",
46
+ "compile-concurrency" => "The maximum number of simultaneous manifest block compiles.",
47
+ "concurrency" => "The number of threads to use when executing on remote targets.",
48
+ "format" => "The format to use when printing results. Options are `human` and `json`.",
49
+ "plugin_hooks" => "Which plugins a specific hook should use.",
50
+ "plugins" => "A map of plugins and their configuration data.",
51
+ "puppetdb" => "A map containing options for configuring the Bolt PuppetDB client.",
52
+ "puppetfile" => "A map containing options for the `bolt puppetfile install` command.",
53
+ "save-rerun" => "Whether to update `.rerun.json` in the Bolt project directory. If "\
54
+ "your target names include passwords, set this value to `false` to avoid "\
55
+ "writing passwords to disk."
56
+ }.freeze
39
57
 
40
- DEFAULT_TRANSPORT_OPTION = { 'transport' => 'ssh' }.freeze
58
+ # These options are only available to bolt-defaults.yaml.
59
+ DEFAULTS_CONFIG = {
60
+ "inventory-config" => "A map of default configuration options for the inventory. This includes options "\
61
+ "for setting the default transport to use when connecting to targets, as well as "\
62
+ "options for configuring the default behavior of each transport."
63
+ }.freeze
41
64
 
42
- CONFIG_IN_INVENTORY = TRANSPORT_CONFIG.merge(TRANSPORT_OPTION)
65
+ # Options that configure the inventory, specifically the default transport
66
+ # used by targets and the transports themselves. These options are used in
67
+ # bolt.yaml, inventory.yaml, and under the inventory-config key in
68
+ # bolt-defaults.yaml.
69
+ INVENTORY_CONFIG = {
70
+ "transport" => "The default transport to use when the transport for a target is not specified in the URI.",
71
+ "docker" => "A map of configuration options for the docker transport.",
72
+ "local" => "A map of configuration options for the local transport.",
73
+ "pcp" => "A map of configuration options for the pcp transport.",
74
+ "remote" => "A map of configuration options for the remote transport.",
75
+ "ssh" => "A map of configuration options for the ssh transport.",
76
+ "winrm" => "A map of configuration options for the winrm transport."
77
+ }.freeze
43
78
 
44
- # NOTE: All configuration options should have a corresponding schema property
45
- # in schemas/bolt-config.schema.json
46
- OPTIONS = {
79
+ # Options that configure the project, such as paths to files used for a
80
+ # specific project. These settings are used in bolt.yaml and bolt-project.yaml.
81
+ PROJECT_CONFIG = {
47
82
  "apply_settings" => "A map of Puppet settings to use when applying Puppet code",
48
- "color" => "Whether to use colored output when printing messages to the console.",
49
- "compile-concurrency" => "The maximum number of simultaneous manifest block compiles.",
50
- "concurrency" => "The number of threads to use when executing on remote targets.",
51
- "format" => "The format to use when printing results. Options are `human` and `json`.",
52
83
  "hiera-config" => "The path to your Hiera config.",
53
84
  "inventoryfile" => "The path to a structured data inventory file used to refer to groups of "\
54
85
  "targets on the command line and from plans.",
55
86
  "log" => "The configuration of the logfile output. Configuration can be set for "\
56
87
  "`console` and the path to a log file, such as `~/.puppetlabs/bolt/debug.log`.",
57
88
  "modulepath" => "An array of directories that Bolt loads content (e.g. plans and tasks) from.",
58
- "plugin_hooks" => "Which plugins a specific hook should use.",
59
- "plugins" => "A map of plugins and their configuration data.",
60
- "puppetdb" => "A map containing options for configuring the Bolt PuppetDB client.",
61
- "puppetfile" => "A map containing options for the `bolt puppetfile install` command.",
62
- "save-rerun" => "Whether to update `.rerun.json` in the Bolt project directory. If "\
63
- "your target names include passwords, set this value to `false` to avoid "\
64
- "writing passwords to disk.",
65
- "transport" => "The default transport to use when the transport for a target is not specified "\
66
- "in the URL or inventory.",
67
89
  "trusted-external-command" => "The path to an executable on the Bolt controller that can produce "\
68
90
  "external trusted facts. **External trusted facts are experimental in both "\
69
91
  "Puppet and Bolt and this API may change or be removed.**"
70
92
  }.freeze
71
93
 
94
+ # A combined map of all configuration options that can be set in this class.
95
+ # Includes all options except 'inventory-config', which is munged when loading
96
+ # a bolt-defaults.yaml file.
97
+ OPTIONS = BOLT_CONFIG.merge(INVENTORY_CONFIG).merge(PROJECT_CONFIG).freeze
98
+
99
+ # Default values for select options. These do not set the default values in Bolt
100
+ # and are only used for documentation.
72
101
  DEFAULT_OPTIONS = {
73
- "color" => true,
102
+ "color" => true,
74
103
  "compile-concurrency" => "Number of cores",
75
- "concurrency" => "100 or one-third of the ulimit, whichever is lower",
76
- "format" => "human",
77
- "hiera-config" => "Boltdir/hiera.yaml",
78
- "inventoryfile" => "Boltdir/inventory.yaml",
79
- "modulepath" => ["Boltdir/modules", "Boltdir/site-modules", "Boltdir/site"],
80
- "save-rerun" => true
104
+ "concurrency" => "100 or one-seventh of the ulimit, whichever is lower",
105
+ "format" => "human",
106
+ "hiera-config" => "Boltdir/hiera.yaml",
107
+ "inventoryfile" => "Boltdir/inventory.yaml",
108
+ "modulepath" => ["Boltdir/modules", "Boltdir/site-modules", "Boltdir/site"],
109
+ "save-rerun" => true,
110
+ "transport" => "ssh"
111
+ }.freeze
112
+
113
+ PUPPETDB_OPTIONS = {
114
+ "cacert" => "The path to the ca certificate for PuppetDB.",
115
+ "cert" => "The path to the client certificate file to use for authentication.",
116
+ "key" => "The private key for the certificate.",
117
+ "server_urls" => "An array containing the PuppetDB host to connect to. Include the protocol `https` and "\
118
+ "the port, which is usually `8081`. For example, `https://my-master.example.com:8081`.",
119
+ "token" => "The path to the PE RBAC Token."
81
120
  }.freeze
82
121
 
83
122
  PUPPETFILE_OPTIONS = {
@@ -121,57 +160,144 @@ module Bolt
121
160
  Bolt::Util.read_optional_yaml_hash(project.config_file, 'config')
122
161
  end
123
162
 
124
- data = { filepath: project.config_file, data: conf }
125
-
126
- data = load_defaults(project).push(data).select { |config| config[:data]&.any? }
163
+ data = load_defaults(project).push(
164
+ filepath: project.config_file,
165
+ data: conf,
166
+ warnings: []
167
+ )
127
168
 
128
169
  new(project, data, overrides)
129
170
  end
130
171
 
131
172
  def self.from_file(configfile, overrides = {})
132
173
  project = Bolt::Project.create_project(Pathname.new(configfile).expand_path.dirname)
174
+
133
175
  conf = if project.project_file == project.config_file
134
176
  project.data
135
177
  else
136
178
  Bolt::Util.read_yaml_hash(configfile, 'config')
137
179
  end
138
- data = { filepath: project.config_file, data: conf }
139
- data = load_defaults(project).push(data).select { |config| config[:data]&.any? }
180
+
181
+ data = load_defaults(project).push(
182
+ filepath: project.config_file,
183
+ data: conf,
184
+ warnings: []
185
+ )
140
186
 
141
187
  new(project, data, overrides)
142
188
  end
143
189
 
144
- def self.load_defaults(project)
190
+ def self.system_path
145
191
  # Lazy-load expensive gem code
146
192
  require 'win32/dir' if Bolt::Util.windows?
147
193
 
148
- # Don't load /etc/puppetlabs/bolt/bolt.yaml twice
149
- confs = if project.path == Bolt::Project.system_path
150
- []
151
- else
152
- system_path = Pathname.new(File.join(Bolt::Project.system_path, 'bolt.yaml'))
153
- [{ filepath: system_path, data: Bolt::Util.read_optional_yaml_hash(system_path, 'config') }]
154
- end
155
-
156
- user_path = begin
157
- Pathname.new(File.expand_path(File.join('~', '.puppetlabs', 'etc', 'bolt', 'bolt.yaml')))
158
- rescue ArgumentError
159
- nil
160
- end
161
-
162
- confs << { filepath: user_path, data: Bolt::Util.read_optional_yaml_hash(user_path, 'config') } if user_path
194
+ if Bolt::Util.windows?
195
+ Pathname.new(File.join(Dir::COMMON_APPDATA, 'PuppetLabs', 'bolt', 'etc'))
196
+ else
197
+ Pathname.new(File.join('/etc', 'puppetlabs', 'bolt'))
198
+ end
199
+ end
200
+
201
+ def self.user_path
202
+ Pathname.new(File.expand_path(File.join('~', '.puppetlabs', 'etc', 'bolt')))
203
+ rescue StandardError
204
+ nil
205
+ end
206
+
207
+ # Loads a 'bolt-defaults.yaml' file, which contains default configuration that applies to all
208
+ # projects. This file does not allow project-specific configuration such as 'hiera-config' and
209
+ # 'inventoryfile', and nests all default inventory configuration under an 'inventory-config' key.
210
+ def self.load_bolt_defaults_yaml(dir)
211
+ filepath = dir + BOLT_DEFAULTS_NAME
212
+ data = Bolt::Util.read_yaml_hash(filepath, 'config')
213
+ warnings = []
214
+
215
+ # Warn if 'bolt.yaml' detected in same directory.
216
+ if File.exist?(bolt_yaml = dir + BOLT_CONFIG_NAME)
217
+ warnings.push(
218
+ msg: "Detected multiple configuration files: ['#{bolt_yaml}', '#{filepath}']. '#{bolt_yaml}' "\
219
+ "will be ignored."
220
+ )
221
+ end
222
+
223
+ # Remove project-specific config such as hiera-config, etc.
224
+ project_config = data.slice(*PROJECT_CONFIG.keys)
225
+
226
+ if project_config.any?
227
+ data.reject! { |key, _| project_config.include?(key) }
228
+ warnings.push(
229
+ msg: "Unsupported project configuration detected in '#{filepath}': #{project_config.keys}. "\
230
+ "Project configuration should be set in 'bolt-project.yaml'."
231
+ )
232
+ end
233
+
234
+ # Remove top-level transport config such as transport, ssh, etc.
235
+ transport_config = data.slice(*INVENTORY_CONFIG.keys)
236
+
237
+ if transport_config.any?
238
+ data.reject! { |key, _| transport_config.include?(key) }
239
+ warnings.push(
240
+ msg: "Unsupported inventory configuration detected in '#{filepath}': #{transport_config.keys}. "\
241
+ "Transport configuration should be set under the 'inventory-config' option or "\
242
+ "in 'inventory.yaml'."
243
+ )
244
+ end
245
+
246
+ # Move data under transport-config to top-level so it can be easily merged with
247
+ # config from other sources.
248
+ if data.key?('inventory-config')
249
+ data = data.merge(data.delete('inventory-config'))
250
+ end
251
+
252
+ { filepath: filepath, data: data, warnings: warnings }
253
+ end
254
+
255
+ # Loads a 'bolt.yaml' file, the legacy configuration file. There's no special munging needed
256
+ # here since Bolt::Config will just ignore any invalid keys.
257
+ def self.load_bolt_yaml(dir)
258
+ filepath = dir + BOLT_CONFIG_NAME
259
+ data = Bolt::Util.read_yaml_hash(filepath, 'config')
260
+ warnings = [msg: "Configuration file #{filepath} is deprecated and will be removed in a future version "\
261
+ "of Bolt. Use '#{dir + BOLT_DEFAULTS_NAME}' instead."]
262
+
263
+ { filepath: filepath, data: data, warnings: warnings }
264
+ end
265
+
266
+ def self.load_defaults(project)
267
+ confs = []
268
+
269
+ # Load system-level config. Prefer a 'bolt-defaults.yaml' file, but fall back to the
270
+ # legacy 'bolt.yaml' file. If the project-level config file is also the system-level
271
+ # config file, don't load it a second time.
272
+ if File.exist?(system_path + BOLT_DEFAULTS_NAME)
273
+ confs << load_bolt_defaults_yaml(system_path)
274
+ elsif File.exist?(system_path + BOLT_CONFIG_NAME) &&
275
+ (system_path + BOLT_CONFIG_NAME) != project.config_file
276
+ confs << load_bolt_yaml(system_path)
277
+ end
278
+
279
+ # Load user-level config if there is a homedir. Prefer a 'bolt-defaults.yaml' file, but
280
+ # fall back to the legacy 'bolt.yaml' file.
281
+ if user_path
282
+ if File.exist?(user_path + BOLT_DEFAULTS_NAME)
283
+ confs << load_bolt_defaults_yaml(user_path)
284
+ elsif File.exist?(user_path + BOLT_CONFIG_NAME)
285
+ confs << load_bolt_yaml(user_path)
286
+ end
287
+ end
288
+
163
289
  confs
164
290
  end
165
291
 
166
292
  def initialize(project, config_data, overrides = {})
167
293
  unless config_data.is_a?(Array)
168
- config_data = [{ filepath: project.config_file, data: config_data }]
294
+ config_data = [{ filepath: project.config_file, data: config_data, warnings: [] }]
169
295
  end
170
296
 
171
- @logger = Logging.logger[self]
172
- @project = project
173
- @warnings = @project.warnings.dup
174
- @transports = {}
297
+ @logger = Logging.logger[self]
298
+ @project = project
299
+ @warnings = @project.warnings.dup
300
+ @transports = {}
175
301
  @config_files = []
176
302
 
177
303
  default_data = {
@@ -189,9 +315,13 @@ module Bolt
189
315
  'transport' => 'ssh'
190
316
  }
191
317
 
192
- loaded_data = config_data.map do |config|
193
- @config_files.push(config[:filepath])
194
- config[:data]
318
+ loaded_data = config_data.each_with_object([]) do |data, acc|
319
+ @warnings.concat(data[:warnings]) if data[:warnings].any?
320
+
321
+ if data[:data].any?
322
+ @config_files.push(data[:filepath])
323
+ acc.push(data[:data])
324
+ end
195
325
  end
196
326
 
197
327
  override_data = normalize_overrides(overrides)
@@ -356,7 +486,7 @@ module Bolt
356
486
  raise Bolt::ValidationError, "Compilation is CPU-intensive, set concurrency less than #{compile_limit}"
357
487
  end
358
488
 
359
- unless %w[human json].include? format
489
+ if (format == 'rainbow' && Bolt::Util.windows?) || !(%w[human json rainbow].include? format)
360
490
  raise Bolt::ValidationError, "Unsupported format: '#{format}'"
361
491
  end
362
492
 
@@ -16,7 +16,7 @@ module Bolt
16
16
  DATA_KEYS = %w[config facts vars features plugin_hooks].freeze
17
17
  TARGET_KEYS = DATA_KEYS + %w[name alias uri]
18
18
  GROUP_KEYS = DATA_KEYS + %w[name groups targets]
19
- CONFIG_KEYS = Bolt::Config::CONFIG_IN_INVENTORY.keys
19
+ CONFIG_KEYS = Bolt::Config::INVENTORY_CONFIG.keys
20
20
 
21
21
  def initialize(input, plugins)
22
22
  @logger = Logging.logger[self]
@@ -8,6 +8,8 @@ module Bolt
8
8
  Bolt::Outputter::Human.new(color, verbose, trace)
9
9
  when 'json'
10
10
  Bolt::Outputter::JSON.new(color, verbose, trace)
11
+ when 'rainbow'
12
+ Bolt::Outputter::Rainbow.new(color, verbose, trace)
11
13
  when nil
12
14
  raise "Cannot use outputter before parsing."
13
15
  end
@@ -24,4 +26,5 @@ end
24
26
 
25
27
  require 'bolt/outputter/human'
26
28
  require 'bolt/outputter/json'
29
+ require 'bolt/outputter/rainbow'
27
30
  require 'bolt/outputter/logger'
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/pal'
4
+ require 'paint'
5
+
6
+ module Bolt
7
+ class Outputter
8
+ class Rainbow < Bolt::Outputter::Human
9
+ def initialize(color, verbose, trace, stream = $stdout)
10
+ super
11
+ @line_color = 0
12
+ @color = 0
13
+ @state = :normal
14
+ end
15
+
16
+ # The algorithm is from lolcat (https://github.com/busyloop/lolcat)
17
+ # lolcat is released with WTFPL
18
+ def rainbow
19
+ red = Math.sin(0.3 * @color + 0) * 127 + 128
20
+ green = Math.sin(0.3 * @color + 2 * Math::PI / 3) * 127 + 128
21
+ blue = Math.sin(0.3 * @color + 4 * Math::PI / 3) * 127 + 128
22
+ @color += 1 / 8.0
23
+ format("%<red>02X%<green>02X%<blue>02X", red: red, green: green, blue: blue)
24
+ end
25
+
26
+ def colorize(color, string)
27
+ if @color && @stream.isatty
28
+ if %i[green rainbow].include?(color)
29
+ a = string.chars.map do |c|
30
+ case @state
31
+ when :normal
32
+ if c == "\e"
33
+ @state = :ansi
34
+ elsif c == "\n"
35
+ @line_color += 1
36
+ @color = @line_color
37
+ c
38
+ else
39
+ Paint[c, rainbow]
40
+ end
41
+ when :ansi
42
+ @state = :normal if c == 'm'
43
+ end
44
+ end
45
+ a.join('')
46
+ else
47
+ "\033[#{COLORS[color]}m#{string}\033[0m"
48
+ end
49
+ else
50
+ string
51
+ end
52
+ end
53
+
54
+ def print_summary(results, elapsed_time = nil)
55
+ ok_set = results.ok_set
56
+ unless ok_set.empty?
57
+ @stream.puts colorize(:rainbow, format('Successful on %<size>d target%<plural>s: %<names>s',
58
+ size: ok_set.size,
59
+ plural: ok_set.size == 1 ? '' : 's',
60
+ names: ok_set.targets.map(&:safe_name).join(',')))
61
+ end
62
+
63
+ error_set = results.error_set
64
+ unless error_set.empty?
65
+ @stream.puts colorize(:red,
66
+ format('Failed on %<size>d target%<plural>s: %<names>s',
67
+ size: error_set.size,
68
+ plural: error_set.size == 1 ? '' : 's',
69
+ names: error_set.targets.map(&:safe_name).join(',')))
70
+ end
71
+
72
+ total_msg = format('Ran on %<size>d target%<plural>s',
73
+ size: results.size,
74
+ plural: results.size == 1 ? '' : 's')
75
+ total_msg << " in #{duration_to_string(elapsed_time)}" unless elapsed_time.nil?
76
+ @stream.puts colorize(:rainbow, total_msg)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'pathname'
4
4
  require 'bolt/pal'
5
+ require 'bolt/config'
5
6
 
6
7
  module Bolt
7
8
  class Project
@@ -19,15 +20,7 @@ module Bolt
19
20
  create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user')
20
21
  # If homedir isn't defined use the system config path
21
22
  rescue ArgumentError
22
- create_project(system_path, 'system')
23
- end
24
-
25
- def self.system_path
26
- if Bolt::Util.windows?
27
- File.join(Dir::COMMON_APPDATA, 'PuppetLabs', 'bolt', 'etc')
28
- else
29
- File.join('/etc', 'puppetlabs', 'bolt')
30
- end
23
+ create_project(Bolt::Config.system_path, 'system')
31
24
  end
32
25
 
33
26
  # Search recursively up the directory hierarchy for the Project. Look for a
@@ -74,13 +67,13 @@ module Bolt
74
67
  @resource_types = @path + '.resource_types'
75
68
  @type = type
76
69
 
77
- tc = Bolt::Config::CONFIG_IN_INVENTORY.keys & raw_data.keys
70
+ tc = Bolt::Config::INVENTORY_CONFIG.keys & raw_data.keys
78
71
  if tc.any?
79
72
  msg = "Transport configuration isn't supported in bolt-project.yaml. Ignoring keys #{tc}"
80
73
  @warnings << { msg: msg }
81
74
  end
82
75
 
83
- @data = raw_data.reject { |k, _| Bolt::Config::CONFIG_IN_INVENTORY.keys.include?(k) }
76
+ @data = raw_data.reject { |k, _| Bolt::Config::INVENTORY_CONFIG.keys.include?(k) }
84
77
 
85
78
  # Once bolt.yaml deprecation is removed, this attribute should be removed
86
79
  # and replaced with .project_file in lib/bolt/config.rb
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '2.14.0'
4
+ VERSION = '2.15.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bolt
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.14.0
4
+ version: 2.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-15 00:00:00.000000000 Z
11
+ date: 2020-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -178,6 +178,20 @@ dependencies:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
180
  version: '0.4'
181
+ - !ruby/object:Gem::Dependency
182
+ name: paint
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '2.2'
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: '2.2'
181
195
  - !ruby/object:Gem::Dependency
182
196
  name: puppet
183
197
  requirement: !ruby/object:Gem::Requirement
@@ -467,6 +481,7 @@ files:
467
481
  - lib/bolt/outputter/human.rb
468
482
  - lib/bolt/outputter/json.rb
469
483
  - lib/bolt/outputter/logger.rb
484
+ - lib/bolt/outputter/rainbow.rb
470
485
  - lib/bolt/pal.rb
471
486
  - lib/bolt/pal/issues.rb
472
487
  - lib/bolt/pal/logging.rb