foreplay 0.9.13 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +1 -1
- data/lib/foreplay/cli.rb +47 -45
- data/lib/foreplay/engine/defaults.rb +60 -0
- data/lib/foreplay/engine/logger.rb +50 -46
- data/lib/foreplay/engine/port.rb +65 -60
- data/lib/foreplay/engine/remote/check.rb +44 -38
- data/lib/foreplay/engine/remote/step.rb +34 -28
- data/lib/foreplay/engine/remote.rb +93 -86
- data/lib/foreplay/engine/role.rb +19 -15
- data/lib/foreplay/engine/secrets.rb +28 -24
- data/lib/foreplay/engine/server.rb +66 -55
- data/lib/foreplay/engine/step.rb +100 -96
- data/lib/foreplay/engine.rb +57 -106
- data/lib/foreplay/launcher.rb +9 -7
- data/lib/foreplay/setup.rb +26 -24
- data/lib/foreplay/version.rb +1 -1
- data/lib/foreplay.rb +14 -11
- data/spec/lib/foreplay/deploy_spec.rb +6 -2
- metadata +3 -4
- data/foreplay.rake +0 -28
- data/lib/foreplay/foreplay.rb +0 -9
|
@@ -1,92 +1,99 @@
|
|
|
1
1
|
require 'net/ssh'
|
|
2
2
|
require 'net/ssh/shell'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
4
|
+
require 'foreplay/engine/remote/check'
|
|
5
|
+
require 'foreplay/engine/remote/step'
|
|
6
|
+
|
|
7
|
+
module Foreplay
|
|
8
|
+
class Engine
|
|
9
|
+
class Remote
|
|
10
|
+
include Foreplay
|
|
11
|
+
attr_reader :server, :steps, :instructions
|
|
12
|
+
|
|
13
|
+
def initialize(s, st, i)
|
|
14
|
+
@server = s
|
|
15
|
+
@steps = st
|
|
16
|
+
@instructions = i
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def deploy
|
|
20
|
+
output = ''
|
|
21
|
+
|
|
22
|
+
log "Connecting to #{host} on port #{port}", host: host
|
|
23
|
+
|
|
24
|
+
# SSH connection
|
|
25
|
+
session = start_session(host, user, options)
|
|
26
|
+
|
|
27
|
+
log "Successfully connected to #{host} on port #{port}", host: host
|
|
28
|
+
|
|
29
|
+
session.shell do |sh|
|
|
30
|
+
steps.each { |step| output += Foreplay::Engine::Remote::Step.new(host, sh, step, instructions).execute }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
session.close
|
|
34
|
+
output
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Deployment check: just say what we would have done
|
|
38
|
+
def check
|
|
39
|
+
Foreplay::Engine::Remote::Check.new(host, steps, instructions).perform
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def user
|
|
43
|
+
@user = instructions['user']
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def host
|
|
47
|
+
@host ||= host_port[0]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def port
|
|
51
|
+
@port ||= (host_port[1] || 22)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def host_port
|
|
55
|
+
@host_port ||= server.split(':') # Parse host + port
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def options
|
|
59
|
+
return @options if @options
|
|
60
|
+
|
|
61
|
+
@options = { verbose: :warn, port: port }
|
|
62
|
+
password = instructions['password']
|
|
63
|
+
|
|
64
|
+
if password.blank?
|
|
65
|
+
@options[:key_data] = [private_key]
|
|
66
|
+
else
|
|
67
|
+
@options[:password] = password
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
@options
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def private_key
|
|
74
|
+
pk = instructions['private_key']
|
|
75
|
+
pk.blank? ? private_key_from_file : pk
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def private_key_from_file
|
|
79
|
+
keyfile = instructions['keyfile']
|
|
80
|
+
keyfile.sub! '~', ENV['HOME'] || '/' unless keyfile.blank? # Remote shell won't expand this for us
|
|
81
|
+
|
|
82
|
+
terminate(
|
|
83
|
+
'No authentication methods supplied. '\
|
|
84
|
+
'You must supply a private key, key file or password in the configuration file'
|
|
85
|
+
) if keyfile.blank?
|
|
86
|
+
|
|
87
|
+
# Get the key from the key file
|
|
88
|
+
log "Using private key from #{keyfile}"
|
|
89
|
+
File.read keyfile
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def start_session(host, user, options)
|
|
93
|
+
Net::SSH.start(host, user, options)
|
|
94
|
+
rescue SocketError => e
|
|
95
|
+
terminate "There was a problem starting an ssh session on #{host}:\n#{e.message}"
|
|
96
|
+
end
|
|
26
97
|
end
|
|
27
|
-
|
|
28
|
-
session.close
|
|
29
|
-
output
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Deployment check: just say what we would have done
|
|
33
|
-
def check
|
|
34
|
-
Foreplay::Engine::Remote::Check.new(host, steps, instructions).perform
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def user
|
|
38
|
-
@user = instructions['user']
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def host
|
|
42
|
-
@host ||= host_port[0]
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def port
|
|
46
|
-
@port ||= (host_port[1] || 22)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def host_port
|
|
50
|
-
@host_port ||= server.split(':') # Parse host + port
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def options
|
|
54
|
-
return @options if @options
|
|
55
|
-
|
|
56
|
-
@options = { verbose: :warn, port: port }
|
|
57
|
-
password = instructions['password']
|
|
58
|
-
|
|
59
|
-
if password.blank?
|
|
60
|
-
@options[:key_data] = [private_key]
|
|
61
|
-
else
|
|
62
|
-
@options[:password] = password
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
@options
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def private_key
|
|
69
|
-
pk = instructions['private_key']
|
|
70
|
-
pk.blank? ? private_key_from_file : pk
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def private_key_from_file
|
|
74
|
-
keyfile = instructions['keyfile']
|
|
75
|
-
keyfile.sub! '~', ENV['HOME'] || '/' unless keyfile.blank? # Remote shell won't expand this for us
|
|
76
|
-
|
|
77
|
-
terminate(
|
|
78
|
-
'No authentication methods supplied. '\
|
|
79
|
-
'You must supply a private key, key file or password in the configuration file'
|
|
80
|
-
) if keyfile.blank?
|
|
81
|
-
|
|
82
|
-
# Get the key from the key file
|
|
83
|
-
log "Using private key from #{keyfile}"
|
|
84
|
-
File.read keyfile
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def start_session(host, user, options)
|
|
88
|
-
Net::SSH.start(host, user, options)
|
|
89
|
-
rescue SocketError => e
|
|
90
|
-
terminate "There was a problem starting an ssh session on #{host}:\n#{e.message}"
|
|
91
98
|
end
|
|
92
99
|
end
|
data/lib/foreplay/engine/role.rb
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
module Foreplay
|
|
2
|
+
class Engine
|
|
3
|
+
class Role
|
|
4
|
+
attr_reader :environment, :mode, :instructions, :servers
|
|
5
|
+
def initialize(e, m, i)
|
|
6
|
+
@environment = e
|
|
7
|
+
@mode = m
|
|
8
|
+
@instructions = i
|
|
9
|
+
@servers = @instructions['servers']
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
preposition = mode == :deploy ? 'to' : 'for'
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
return if @servers.length == 1
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
puts "#{mode.capitalize}ing #{instructions['name'].yellow} #{preposition} #{@servers.join(', ').yellow} "\
|
|
16
|
+
"for the #{instructions['role'].dup.yellow} role in the #{environment.dup.yellow} environment..."
|
|
17
|
+
end
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
def threads
|
|
20
|
+
servers.map do |server|
|
|
21
|
+
Thread.new { Foreplay::Engine::Server.new(environment, mode, instructions, server).execute }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
20
24
|
end
|
|
21
25
|
end
|
|
22
26
|
end
|
|
@@ -1,34 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
module Foreplay
|
|
2
|
+
class Engine
|
|
3
|
+
class Secrets
|
|
4
|
+
attr_reader :environment, :secret_locations
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
def initialize(e, sl)
|
|
7
|
+
@environment = e
|
|
8
|
+
@secret_locations = sl
|
|
9
|
+
end
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
def fetch
|
|
12
|
+
return unless secret_locations
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
secrets = {}
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
secret_locations.each do |secret_location|
|
|
17
|
+
secrets.merge! fetch_from(secret_location) || {}
|
|
18
|
+
end
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
secrets
|
|
21
|
+
end
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
def fetch_from(secret_location)
|
|
24
|
+
url = secret_location['url'] || return
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
headers = secret_location['headers']
|
|
27
|
+
header_string = headers.map { |k, v| " -H \"#{k}: #{v}\"" }.join if headers.is_a? Hash
|
|
28
|
+
command = "curl -k -L#{header_string} #{url}".fake_erb
|
|
29
|
+
secrets_all = YAML.load(`#{command}`)
|
|
30
|
+
secrets = secrets_all[environment]
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
secrets if secrets.is_a? Hash
|
|
33
|
+
rescue Psych::SyntaxError
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
end
|
|
33
37
|
end
|
|
34
38
|
end
|
|
@@ -1,68 +1,79 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
module Foreplay
|
|
2
|
+
class Engine
|
|
3
|
+
class Server
|
|
4
|
+
include Foreplay::Engine::Port
|
|
5
|
+
attr_reader :environment, :mode, :instructions, :server
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
def initialize(e, m, i, s)
|
|
8
|
+
@environment = e
|
|
9
|
+
@mode = m
|
|
10
|
+
@instructions = i
|
|
11
|
+
@server = s
|
|
12
|
+
end
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Foreplay::Engine::Remote.new(server, steps, instructions).__send__ mode
|
|
17
|
-
end
|
|
14
|
+
def execute
|
|
15
|
+
execute_announce
|
|
16
|
+
foreman
|
|
17
|
+
env
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
puts "#{mode.capitalize}ing #{name.yellow} #{preposition} #{host.yellow} "\
|
|
22
|
-
"for the #{role.dup.yellow} role in the #{environment.dup.yellow} environment"
|
|
23
|
-
end
|
|
19
|
+
Foreplay::Engine::Remote.new(server, steps, instructions).__send__ mode
|
|
20
|
+
end
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
'log' => "$HOME/#{path}/#{current_port}/log"
|
|
31
|
-
)
|
|
32
|
-
end
|
|
22
|
+
def execute_announce
|
|
23
|
+
preposition = mode == :deploy ? 'to' : 'for'
|
|
24
|
+
puts "#{mode.capitalize}ing #{name.yellow} #{preposition} #{host.yellow} "\
|
|
25
|
+
"for the #{role.dup.yellow} role in the #{environment.dup.yellow} environment"
|
|
26
|
+
end
|
|
33
27
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
'HOME' => '$HOME',
|
|
37
|
-
'SHELL' => '$SHELL',
|
|
38
|
-
'PATH' => '$PATH:`which bundle`'
|
|
39
|
-
)
|
|
40
|
-
end
|
|
28
|
+
def foreman
|
|
29
|
+
instructions['foreman'] = {} unless instructions.key? 'foreman'
|
|
41
30
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
31
|
+
instructions['foreman'].merge!(
|
|
32
|
+
'app' => current_service,
|
|
33
|
+
'port' => current_port,
|
|
34
|
+
'user' => user,
|
|
35
|
+
'log' => "$HOME/#{path}/#{current_port}/log"
|
|
36
|
+
)
|
|
37
|
+
end
|
|
45
38
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
end
|
|
39
|
+
def env
|
|
40
|
+
instructions['env'] = {} unless instructions.key? 'env'
|
|
49
41
|
|
|
50
|
-
|
|
51
|
-
|
|
42
|
+
instructions['env'].merge!(
|
|
43
|
+
'HOME' => '$HOME',
|
|
44
|
+
'SHELL' => '$SHELL',
|
|
45
|
+
'PATH' => '$PATH:`which bundle`',
|
|
46
|
+
'GEM_HOME' => '$HOME/.rvm/gems/`rvm tools identifier`',
|
|
47
|
+
'RAILS_ENV' => environment
|
|
48
|
+
)
|
|
49
|
+
end
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
def role
|
|
52
|
+
@role ||= instructions['role']
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def user
|
|
56
|
+
@user ||= instructions['user']
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def path
|
|
60
|
+
return @path if @path
|
|
61
|
+
|
|
62
|
+
@path = instructions['path']
|
|
63
|
+
@path.gsub! '%u', user
|
|
64
|
+
@path.gsub! '%a', name
|
|
65
|
+
@path
|
|
66
|
+
end
|
|
58
67
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
def steps
|
|
69
|
+
@steps ||= YAML.load(
|
|
70
|
+
ERB.new(
|
|
71
|
+
File.read(
|
|
72
|
+
"#{File.dirname(__FILE__)}/steps.yml"
|
|
73
|
+
)
|
|
74
|
+
).result(binding)
|
|
64
75
|
)
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
end
|
|
77
|
+
end
|
|
67
78
|
end
|
|
68
79
|
end
|
data/lib/foreplay/engine/step.rb
CHANGED
|
@@ -1,117 +1,121 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def commands
|
|
12
|
-
return @commands if @commands
|
|
13
|
-
|
|
14
|
-
# Each step can be (1) a command or (2) a series of values to add to a file
|
|
15
|
-
if step.key?('key')
|
|
16
|
-
if instructions.key?(step['key'])
|
|
17
|
-
build_commands
|
|
18
|
-
else
|
|
19
|
-
@commands = []
|
|
1
|
+
module Foreplay
|
|
2
|
+
class Engine
|
|
3
|
+
class Step
|
|
4
|
+
include Foreplay
|
|
5
|
+
attr_reader :host, :step, :instructions
|
|
6
|
+
|
|
7
|
+
def initialize(h, s, i)
|
|
8
|
+
@host = h
|
|
9
|
+
@step = s
|
|
10
|
+
@instructions = i
|
|
20
11
|
end
|
|
21
|
-
else
|
|
22
|
-
# ...or just execute the command specified
|
|
23
|
-
@commands = [command]
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
@commands
|
|
27
|
-
end
|
|
28
12
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
13
|
+
def commands
|
|
14
|
+
return @commands if @commands
|
|
15
|
+
|
|
16
|
+
# Each step can be (1) a command or (2) a series of values to add to a file
|
|
17
|
+
if step.key?('key')
|
|
18
|
+
if instructions.key?(step['key'])
|
|
19
|
+
build_commands
|
|
20
|
+
else
|
|
21
|
+
@commands = []
|
|
22
|
+
end
|
|
23
|
+
else
|
|
24
|
+
# ...or just execute the command specified
|
|
25
|
+
@commands = [command]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
@commands
|
|
29
|
+
end
|
|
38
30
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
31
|
+
def build_commands
|
|
32
|
+
step['silent'] = true
|
|
33
|
+
|
|
34
|
+
if header?
|
|
35
|
+
@commands = ["echo \"#{header}\" > #{filename}"]
|
|
36
|
+
redirect
|
|
37
|
+
else
|
|
38
|
+
@commands = []
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if instructions[key].is_a? Hash
|
|
42
|
+
build_commands_from_hash
|
|
43
|
+
else
|
|
44
|
+
build_commands_from_string
|
|
45
|
+
end
|
|
46
|
+
end
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
def build_commands_from_hash
|
|
49
|
+
instructions[key].each do |k, v|
|
|
50
|
+
@commands << "echo \"#{before}#{k}#{delimiter}#{v}#{after}\" #{redirect} #{filename}"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
def build_commands_from_string
|
|
55
|
+
@commands << "echo \"#{before}#{delimiter}#{instructions[key]}#{after}\" #{redirect} #{filename}"
|
|
56
|
+
end
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
def redirect
|
|
59
|
+
if @redirect
|
|
60
|
+
'>>'
|
|
61
|
+
else
|
|
62
|
+
@redirect = true
|
|
63
|
+
'>'
|
|
64
|
+
end
|
|
65
|
+
end
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
def command
|
|
68
|
+
@command ||= step['command']
|
|
69
|
+
end
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
def filename
|
|
72
|
+
@filename ||= "#{path}#{prefix}#{key}#{suffix}"
|
|
73
|
+
end
|
|
72
74
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
def key
|
|
76
|
+
@key ||= step['key']
|
|
77
|
+
end
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
def prefix
|
|
80
|
+
@prefix ||= step['prefix'] || ''
|
|
81
|
+
end
|
|
80
82
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
def suffix
|
|
84
|
+
@suffix ||= step['suffix'] || ''
|
|
85
|
+
end
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
def path
|
|
88
|
+
@path ||= step['path'] || ''
|
|
89
|
+
end
|
|
88
90
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
def before
|
|
92
|
+
@before ||= step['before'] || ''
|
|
93
|
+
end
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
95
|
+
def delimiter
|
|
96
|
+
@delimiter ||= step['delimiter'] || ''
|
|
97
|
+
end
|
|
96
98
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
def after
|
|
100
|
+
@after ||= step['after'] || ''
|
|
101
|
+
end
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
def header
|
|
104
|
+
@header ||= step['header']
|
|
105
|
+
end
|
|
104
106
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
def header?
|
|
108
|
+
header.present?
|
|
109
|
+
end
|
|
108
110
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
111
|
+
def silent
|
|
112
|
+
@silent ||= step['silent']
|
|
113
|
+
end
|
|
112
114
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
def announce
|
|
116
|
+
log "#{(step['commentary'] || command).yellow}", host: host, silent: silent
|
|
117
|
+
log command.cyan, host: host, silent: silent if instructions['verbose'] && step['commentary'] && command
|
|
118
|
+
end
|
|
119
|
+
end
|
|
116
120
|
end
|
|
117
121
|
end
|