bolt 2.27.0 → 2.32.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 +4 -4
- data/Puppetfile +13 -12
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +2 -2
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +44 -1
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +3 -0
- data/guides/module.txt +19 -0
- data/guides/modulepath.txt +25 -0
- data/lib/bolt/applicator.rb +14 -14
- data/lib/bolt/bolt_option_parser.rb +74 -22
- data/lib/bolt/catalog.rb +1 -1
- data/lib/bolt/cli.rb +178 -127
- data/lib/bolt/config.rb +13 -1
- data/lib/bolt/config/modulepath.rb +30 -0
- data/lib/bolt/config/options.rb +38 -9
- data/lib/bolt/config/transport/options.rb +1 -1
- data/lib/bolt/executor.rb +1 -1
- data/lib/bolt/inventory.rb +11 -10
- data/lib/bolt/logger.rb +26 -19
- data/lib/bolt/module_installer.rb +197 -0
- data/lib/bolt/{puppetfile → module_installer}/installer.rb +3 -2
- data/lib/bolt/module_installer/puppetfile.rb +117 -0
- data/lib/bolt/module_installer/puppetfile/forge_module.rb +54 -0
- data/lib/bolt/module_installer/puppetfile/git_module.rb +37 -0
- data/lib/bolt/module_installer/puppetfile/module.rb +26 -0
- data/lib/bolt/module_installer/resolver.rb +76 -0
- data/lib/bolt/module_installer/specs.rb +93 -0
- data/lib/bolt/module_installer/specs/forge_spec.rb +84 -0
- data/lib/bolt/module_installer/specs/git_spec.rb +178 -0
- data/lib/bolt/outputter.rb +2 -45
- data/lib/bolt/outputter/human.rb +78 -18
- data/lib/bolt/outputter/json.rb +22 -7
- data/lib/bolt/outputter/logger.rb +2 -2
- data/lib/bolt/pal.rb +29 -25
- data/lib/bolt/plugin.rb +1 -1
- data/lib/bolt/plugin/module.rb +1 -1
- data/lib/bolt/project.rb +32 -22
- data/lib/bolt/project_migrator.rb +80 -0
- data/lib/bolt/project_migrator/base.rb +39 -0
- data/lib/bolt/project_migrator/config.rb +67 -0
- data/lib/bolt/project_migrator/inventory.rb +67 -0
- data/lib/bolt/project_migrator/modules.rb +200 -0
- data/lib/bolt/shell/bash.rb +4 -3
- data/lib/bolt/transport/base.rb +4 -4
- data/lib/bolt/transport/ssh/connection.rb +1 -1
- data/lib/bolt/util.rb +51 -10
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/acl.rb +2 -2
- data/lib/bolt_server/base_config.rb +3 -3
- data/lib/bolt_server/file_cache.rb +11 -11
- data/lib/bolt_server/schemas/partials/task.json +17 -2
- data/lib/bolt_server/transport_app.rb +93 -13
- data/lib/bolt_spec/bolt_context.rb +8 -6
- data/lib/bolt_spec/plans.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +1 -1
- data/lib/bolt_spec/run.rb +1 -1
- metadata +30 -11
- data/lib/bolt/project_migrate.rb +0 -138
- data/lib/bolt/puppetfile.rb +0 -160
- data/lib/bolt/puppetfile/module.rb +0 -66
- data/lib/bolt_server/pe/pal.rb +0 -67
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
require 'bolt/error'
|
7
|
+
|
8
|
+
# This class represents a Git module specification.
|
9
|
+
#
|
10
|
+
module Bolt
|
11
|
+
class ModuleInstaller
|
12
|
+
class Specs
|
13
|
+
class GitSpec
|
14
|
+
NAME_REGEX = %r{\A(?:[a-z][a-z0-9_]*[-/])?(?<name>[a-z][a-z0-9_]*)\z}.freeze
|
15
|
+
REQUIRED_KEYS = Set.new(%w[git ref]).freeze
|
16
|
+
|
17
|
+
attr_reader :git, :ref, :type
|
18
|
+
|
19
|
+
def initialize(init_hash)
|
20
|
+
@name = parse_name(init_hash['name'])
|
21
|
+
@git, @repo = parse_git(init_hash['git'])
|
22
|
+
@ref = init_hash['ref']
|
23
|
+
@type = :git
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.implements?(hash)
|
27
|
+
REQUIRED_KEYS == hash.keys.to_set
|
28
|
+
end
|
29
|
+
|
30
|
+
# Parses the name into owner and name segments, and formats the full
|
31
|
+
# name.
|
32
|
+
#
|
33
|
+
private def parse_name(name)
|
34
|
+
return unless name
|
35
|
+
|
36
|
+
unless (match = name.match(NAME_REGEX))
|
37
|
+
raise Bolt::ValidationError,
|
38
|
+
"Invalid name for Git module specification: #{name}. Name must match "\
|
39
|
+
"'name' or 'owner/name', must start with a lowercase letter, and may "\
|
40
|
+
"only include lowercase letters, digits, and underscores."
|
41
|
+
end
|
42
|
+
|
43
|
+
match[:name]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Gets the repo from the git URL.
|
47
|
+
#
|
48
|
+
private def parse_git(git)
|
49
|
+
repo = if git.start_with?('git@github.com:')
|
50
|
+
git.split('git@github.com:').last.split('.git').first
|
51
|
+
elsif git.start_with?('https://github.com')
|
52
|
+
git.split('https://github.com/').last.split('.git').first
|
53
|
+
else
|
54
|
+
raise Bolt::ValidationError,
|
55
|
+
"Invalid git source: #{git}. Only GitHub modules are supported."
|
56
|
+
end
|
57
|
+
|
58
|
+
[git, repo]
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns true if the specification is satisfied by the module.
|
62
|
+
#
|
63
|
+
def satisfied_by?(mod)
|
64
|
+
@type == mod.type && @git == mod.git
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns a hash matching the module spec in bolt-project.yaml
|
68
|
+
#
|
69
|
+
def to_hash
|
70
|
+
{
|
71
|
+
'git' => @git,
|
72
|
+
'ref' => @ref
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns a PuppetfileResolver::Model::GitModule object for resolving.
|
77
|
+
#
|
78
|
+
def to_resolver_module
|
79
|
+
require 'puppetfile-resolver'
|
80
|
+
|
81
|
+
PuppetfileResolver::Puppetfile::GitModule.new(name).tap do |mod|
|
82
|
+
mod.remote = @git
|
83
|
+
mod.ref = sha
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Resolves the module's title from the module metadata. This is lazily
|
88
|
+
# resolved since Bolt does not always need to know a Git module's name.
|
89
|
+
#
|
90
|
+
def name
|
91
|
+
@name ||= begin
|
92
|
+
url = "https://raw.githubusercontent.com/#{@repo}/#{sha}/metadata.json"
|
93
|
+
response = make_request(:Get, url)
|
94
|
+
|
95
|
+
case response
|
96
|
+
when Net::HTTPOK
|
97
|
+
body = JSON.parse(response.body)
|
98
|
+
|
99
|
+
unless body.key?('name')
|
100
|
+
raise Bolt::Error.new(
|
101
|
+
"Missing name in metadata.json at #{git}. This is not a valid module.",
|
102
|
+
"bolt/missing-module-name-error"
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
parse_name(body['name'])
|
107
|
+
else
|
108
|
+
raise Bolt::Error.new(
|
109
|
+
"Missing metadata.json at #{git}. This is not a valid module.",
|
110
|
+
"bolt/missing-module-metadata-error"
|
111
|
+
)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Resolves the SHA for the specified ref. This is lazily resolved since
|
117
|
+
# Bolt does not always need to know a Git module's SHA.
|
118
|
+
#
|
119
|
+
def sha
|
120
|
+
@sha ||= begin
|
121
|
+
url = "https://api.github.com/repos/#{@repo}/commits/#{ref}"
|
122
|
+
headers = ENV['GITHUB_TOKEN'] ? { "Authorization" => "token #{ENV['GITHUB_TOKEN']}" } : {}
|
123
|
+
response = make_request(:Get, url, headers)
|
124
|
+
|
125
|
+
case response
|
126
|
+
when Net::HTTPOK
|
127
|
+
body = JSON.parse(response.body)
|
128
|
+
body['sha']
|
129
|
+
when Net::HTTPUnauthorized
|
130
|
+
raise Bolt::Error.new(
|
131
|
+
"Invalid token at GITHUB_TOKEN, unable to resolve git modules.",
|
132
|
+
"bolt/invalid-git-token-error"
|
133
|
+
)
|
134
|
+
when Net::HTTPForbidden
|
135
|
+
message = "GitHub API rate limit exceeded, unable to resolve git modules. "
|
136
|
+
|
137
|
+
unless ENV['GITHUB_TOKEN']
|
138
|
+
message += "To increase your rate limit, set the GITHUB_TOKEN environment "\
|
139
|
+
"variable with a GitHub personal access token."
|
140
|
+
end
|
141
|
+
|
142
|
+
raise Bolt::Error.new(message, 'bolt/github-api-rate-limit-error')
|
143
|
+
when Net::HTTPNotFound
|
144
|
+
raise Bolt::Error.new(
|
145
|
+
"#{git} is not a git repository.",
|
146
|
+
"bolt/missing-git-repository-error"
|
147
|
+
)
|
148
|
+
else
|
149
|
+
raise Bolt::Error.new(
|
150
|
+
"Ref #{ref} at #{git} is not a commit, tag, or branch.",
|
151
|
+
"bolt/invalid-git-ref-error"
|
152
|
+
)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Makes a generic HTTP request.
|
158
|
+
#
|
159
|
+
private def make_request(verb, url, headers = {})
|
160
|
+
require 'net/http'
|
161
|
+
|
162
|
+
uri = URI.parse(url)
|
163
|
+
opts = { use_ssl: uri.scheme == 'https' }
|
164
|
+
|
165
|
+
Net::HTTP.start(uri.host, uri.port, opts) do |client|
|
166
|
+
request = Net::HTTP.const_get(verb).new(uri, headers)
|
167
|
+
client.request(request)
|
168
|
+
end
|
169
|
+
rescue StandardError => e
|
170
|
+
raise Bolt::Error.new(
|
171
|
+
"Failed to connect to #{uri}: #{e.message}",
|
172
|
+
"bolt/http-connect-error"
|
173
|
+
)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
data/lib/bolt/outputter.rb
CHANGED
@@ -27,55 +27,12 @@ module Bolt
|
|
27
27
|
string.gsub(/^/, indent.to_s)
|
28
28
|
end
|
29
29
|
|
30
|
-
def print_message_event(event)
|
31
|
-
print_message(stringify(event[:message]))
|
32
|
-
end
|
33
|
-
|
34
30
|
def print_message
|
35
31
|
raise NotImplementedError, "print_message() must be implemented by the outputter class"
|
36
32
|
end
|
37
33
|
|
38
|
-
def
|
39
|
-
|
40
|
-
if formatted.is_a?(Hash) || formatted.is_a?(Array)
|
41
|
-
::JSON.pretty_generate(formatted)
|
42
|
-
else
|
43
|
-
formatted
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def format_message(message)
|
48
|
-
case message
|
49
|
-
when Array
|
50
|
-
message.map { |item| format_message(item) }
|
51
|
-
when Bolt::ApplyResult
|
52
|
-
format_apply_result(message)
|
53
|
-
when Bolt::Result, Bolt::ResultSet
|
54
|
-
# This is equivalent to to_s, but formattable
|
55
|
-
message.to_data
|
56
|
-
when Bolt::RunFailure
|
57
|
-
formatted_resultset = message.result_set.to_data
|
58
|
-
message.to_h.merge('result_set' => formatted_resultset)
|
59
|
-
when Hash
|
60
|
-
message.each_with_object({}) do |(k, v), h|
|
61
|
-
h[format_message(k)] = format_message(v)
|
62
|
-
end
|
63
|
-
when Integer, Float, NilClass
|
64
|
-
message
|
65
|
-
else
|
66
|
-
message.to_s
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def format_apply_result(result)
|
71
|
-
logs = result.resource_logs&.map do |log|
|
72
|
-
# Omit low-level info/debug messages
|
73
|
-
next if %w[info debug].include?(log['level'])
|
74
|
-
indent(2, format_log(log))
|
75
|
-
end
|
76
|
-
hash = result.to_data
|
77
|
-
hash['logs'] = logs unless logs.empty?
|
78
|
-
hash
|
34
|
+
def print_error
|
35
|
+
raise NotImplementedError, "print_error() must be implemented by the outputter class"
|
79
36
|
end
|
80
37
|
end
|
81
38
|
end
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -5,9 +5,12 @@ require 'bolt/pal'
|
|
5
5
|
module Bolt
|
6
6
|
class Outputter
|
7
7
|
class Human < Bolt::Outputter
|
8
|
-
COLORS = {
|
9
|
-
|
10
|
-
|
8
|
+
COLORS = {
|
9
|
+
red: "31",
|
10
|
+
green: "32",
|
11
|
+
yellow: "33",
|
12
|
+
cyan: "36"
|
13
|
+
}.freeze
|
11
14
|
|
12
15
|
def print_head; end
|
13
16
|
|
@@ -31,6 +34,10 @@ module Bolt
|
|
31
34
|
string.sub(/\s\z/, '')
|
32
35
|
end
|
33
36
|
|
37
|
+
def wrap(string, width = 80)
|
38
|
+
string.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n")
|
39
|
+
end
|
40
|
+
|
34
41
|
def handle_event(event)
|
35
42
|
case event[:type]
|
36
43
|
when :enable_default_output
|
@@ -38,7 +45,7 @@ module Bolt
|
|
38
45
|
when :disable_default_output
|
39
46
|
@disable_depth += 1
|
40
47
|
when :message
|
41
|
-
|
48
|
+
print_message(event[:message])
|
42
49
|
end
|
43
50
|
|
44
51
|
if enabled?
|
@@ -48,9 +55,9 @@ module Bolt
|
|
48
55
|
when :node_result
|
49
56
|
print_result(event[:result]) if @verbose
|
50
57
|
when :step_start
|
51
|
-
print_step_start(event) if plan_logging?
|
58
|
+
print_step_start(**event) if plan_logging?
|
52
59
|
when :step_finish
|
53
|
-
print_step_finish(event) if plan_logging?
|
60
|
+
print_step_finish(**event) if plan_logging?
|
54
61
|
when :plan_start
|
55
62
|
print_plan_start(event)
|
56
63
|
when :plan_finish
|
@@ -188,7 +195,7 @@ module Bolt
|
|
188
195
|
@stream.puts total_msg
|
189
196
|
end
|
190
197
|
|
191
|
-
def print_table(results)
|
198
|
+
def print_table(results, padding_left = 0, padding_right = 3)
|
192
199
|
# lazy-load expensive gem code
|
193
200
|
require 'terminal-table'
|
194
201
|
|
@@ -198,8 +205,8 @@ module Bolt
|
|
198
205
|
border_x: '',
|
199
206
|
border_y: '',
|
200
207
|
border_i: '',
|
201
|
-
padding_left:
|
202
|
-
padding_right:
|
208
|
+
padding_left: padding_left,
|
209
|
+
padding_right: padding_right,
|
203
210
|
border_top: false,
|
204
211
|
border_bottom: false
|
205
212
|
}
|
@@ -241,7 +248,7 @@ module Bolt
|
|
241
248
|
task_info << "MODULE:\n"
|
242
249
|
|
243
250
|
path = task.files.first['path'].chomp("/tasks/#{task.files.first['name']}")
|
244
|
-
task_info << if path.start_with?(Bolt::
|
251
|
+
task_info << if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH)
|
245
252
|
"built-in module"
|
246
253
|
else
|
247
254
|
path
|
@@ -271,7 +278,7 @@ module Bolt
|
|
271
278
|
plan_info << "MODULE:\n"
|
272
279
|
|
273
280
|
path = plan['module']
|
274
|
-
plan_info << if path.start_with?(Bolt::
|
281
|
+
plan_info << if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH)
|
275
282
|
"built-in module"
|
276
283
|
else
|
277
284
|
path
|
@@ -299,9 +306,9 @@ module Bolt
|
|
299
306
|
def print_module_list(module_list)
|
300
307
|
module_list.each do |path, modules|
|
301
308
|
if (mod = modules.find { |m| m[:internal_module_group] })
|
302
|
-
@stream.puts(mod[:internal_module_group])
|
309
|
+
@stream.puts(colorize(:cyan, mod[:internal_module_group]))
|
303
310
|
else
|
304
|
-
@stream.puts(path)
|
311
|
+
@stream.puts(colorize(:cyan, path))
|
305
312
|
end
|
306
313
|
|
307
314
|
if modules.empty?
|
@@ -317,17 +324,35 @@ module Bolt
|
|
317
324
|
[m[:name], version]
|
318
325
|
end
|
319
326
|
|
320
|
-
print_table(module_info)
|
327
|
+
print_table(module_info, 2, 1)
|
321
328
|
end
|
322
329
|
|
323
330
|
@stream.write("\n")
|
324
331
|
end
|
325
332
|
end
|
326
333
|
|
327
|
-
def print_targets(
|
328
|
-
|
329
|
-
|
330
|
-
|
334
|
+
def print_targets(target_list, inventoryfile)
|
335
|
+
adhoc = colorize(:yellow, "(Not found in inventory file)")
|
336
|
+
|
337
|
+
targets = []
|
338
|
+
targets += target_list[:inventory].map { |target| [target.name, nil] }
|
339
|
+
targets += target_list[:adhoc].map { |target| [target.name, adhoc] }
|
340
|
+
|
341
|
+
if targets.any?
|
342
|
+
print_table(targets, 0, 2)
|
343
|
+
@stream.puts
|
344
|
+
end
|
345
|
+
|
346
|
+
@stream.puts "INVENTORY FILE:"
|
347
|
+
if File.exist?(inventoryfile)
|
348
|
+
@stream.puts inventoryfile
|
349
|
+
else
|
350
|
+
@stream.puts wrap("Tried to load inventory from #{inventoryfile}, but the file does not exist")
|
351
|
+
end
|
352
|
+
|
353
|
+
@stream.puts "\nTARGET COUNT:"
|
354
|
+
@stream.puts "#{targets.count} total, #{target_list[:inventory].count} from inventory, "\
|
355
|
+
"#{target_list[:adhoc].count} adhoc"
|
331
356
|
end
|
332
357
|
|
333
358
|
def print_target_info(targets)
|
@@ -394,6 +419,41 @@ module Bolt
|
|
394
419
|
@stream.puts(message)
|
395
420
|
end
|
396
421
|
|
422
|
+
def print_error(message)
|
423
|
+
@stream.puts(colorize(:red, message))
|
424
|
+
end
|
425
|
+
|
426
|
+
def print_prompt(prompt)
|
427
|
+
@stream.print(colorize(:cyan, indent(4, prompt)))
|
428
|
+
end
|
429
|
+
|
430
|
+
def print_prompt_error(message)
|
431
|
+
@stream.puts(colorize(:red, indent(4, message)))
|
432
|
+
end
|
433
|
+
|
434
|
+
def print_action_step(step)
|
435
|
+
first, *remaining = wrap(step, 76).lines
|
436
|
+
|
437
|
+
first = indent(2, "→ #{first}")
|
438
|
+
remaining = remaining.map { |line| indent(4, line) }
|
439
|
+
step = [first, *remaining, "\n"].join
|
440
|
+
|
441
|
+
@stream.puts(step)
|
442
|
+
end
|
443
|
+
|
444
|
+
def print_action_error(error)
|
445
|
+
# Running everything through 'wrap' messes with newlines. Separating
|
446
|
+
# into lines and wrapping each individually ensures separate errors are
|
447
|
+
# distinguishable.
|
448
|
+
first, *remaining = error.lines
|
449
|
+
first = colorize(:red, indent(2, "→ #{wrap(first, 76)}"))
|
450
|
+
wrapped = remaining.map { |l| wrap(l) }
|
451
|
+
to_print = wrapped.map { |line| colorize(:red, indent(4, line)) }
|
452
|
+
step = [first, *to_print, "\n"].join
|
453
|
+
|
454
|
+
@stream.puts(step)
|
455
|
+
end
|
456
|
+
|
397
457
|
def duration_to_string(duration)
|
398
458
|
hrs = (duration / 3600).floor
|
399
459
|
mins = ((duration % 3600) / 60).floor
|
data/lib/bolt/outputter/json.rb
CHANGED
@@ -22,7 +22,7 @@ module Bolt
|
|
22
22
|
when :node_result
|
23
23
|
print_result(event[:result])
|
24
24
|
when :message
|
25
|
-
|
25
|
+
print_message(event[:message])
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
@@ -48,7 +48,7 @@ module Bolt
|
|
48
48
|
|
49
49
|
def print_task_info(task)
|
50
50
|
path = task.files.first['path'].chomp("/tasks/#{task.files.first['name']}")
|
51
|
-
module_dir = if path.start_with?(Bolt::
|
51
|
+
module_dir = if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH)
|
52
52
|
"built-in module"
|
53
53
|
else
|
54
54
|
path
|
@@ -62,7 +62,7 @@ module Bolt
|
|
62
62
|
|
63
63
|
def print_plan_info(plan)
|
64
64
|
path = plan.delete('module')
|
65
|
-
plan['module_dir'] = if path.start_with?(Bolt::
|
65
|
+
plan['module_dir'] = if path.start_with?(Bolt::Config::Modulepath::MODULES_PATH)
|
66
66
|
"built-in module"
|
67
67
|
else
|
68
68
|
path
|
@@ -97,13 +97,22 @@ module Bolt
|
|
97
97
|
def print_puppetfile_result(success, puppetfile, moduledir)
|
98
98
|
@stream.puts({ "success": success,
|
99
99
|
"puppetfile": puppetfile,
|
100
|
-
"moduledir": moduledir }.to_json)
|
100
|
+
"moduledir": moduledir.to_s }.to_json)
|
101
101
|
end
|
102
102
|
|
103
|
-
def print_targets(
|
103
|
+
def print_targets(target_list, inventoryfile)
|
104
104
|
@stream.puts ::JSON.pretty_generate(
|
105
|
-
"
|
106
|
-
|
105
|
+
"inventory": {
|
106
|
+
"targets": target_list[:inventory].map(&:name),
|
107
|
+
"count": target_list[:inventory].count,
|
108
|
+
"file": inventoryfile.to_s
|
109
|
+
},
|
110
|
+
"adhoc": {
|
111
|
+
"targets": target_list[:adhoc].map(&:name),
|
112
|
+
"count": target_list[:adhoc].count
|
113
|
+
},
|
114
|
+
"targets": target_list.values.flatten.map(&:name),
|
115
|
+
"count": target_list.values.flatten.count
|
107
116
|
)
|
108
117
|
end
|
109
118
|
|
@@ -135,6 +144,12 @@ module Bolt
|
|
135
144
|
def print_message(message)
|
136
145
|
$stderr.puts(message)
|
137
146
|
end
|
147
|
+
alias print_error print_message
|
148
|
+
|
149
|
+
def print_action_step(step)
|
150
|
+
$stderr.puts(step)
|
151
|
+
end
|
152
|
+
alias print_action_error print_action_step
|
138
153
|
end
|
139
154
|
end
|
140
155
|
end
|