leap_cli 1.7.4 → 1.8
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/bin/leap +6 -13
- data/lib/leap/platform.rb +2 -0
- data/lib/leap_cli.rb +2 -1
- data/lib/leap_cli/bootstrap.rb +197 -0
- data/lib/leap_cli/commands/common.rb +61 -0
- data/lib/leap_cli/commands/new.rb +5 -1
- data/lib/leap_cli/commands/pre.rb +1 -66
- data/lib/leap_cli/config/environment.rb +180 -0
- data/lib/leap_cli/config/manager.rb +100 -197
- data/lib/leap_cli/config/node.rb +2 -2
- data/lib/leap_cli/config/object.rb +56 -43
- data/lib/leap_cli/config/object_list.rb +6 -3
- data/lib/leap_cli/config/provider.rb +11 -0
- data/lib/leap_cli/config/secrets.rb +14 -1
- data/lib/leap_cli/config/tag.rb +2 -2
- data/lib/leap_cli/leapfile.rb +1 -0
- data/lib/leap_cli/log.rb +1 -0
- data/lib/leap_cli/logger.rb +16 -12
- data/lib/leap_cli/markdown_document_listener.rb +3 -1
- data/lib/leap_cli/path.rb +12 -0
- data/lib/leap_cli/remote/leap_plugin.rb +9 -34
- data/lib/leap_cli/remote/puppet_plugin.rb +0 -40
- data/lib/leap_cli/remote/tasks.rb +9 -34
- data/lib/leap_cli/ssh_key.rb +5 -2
- data/lib/leap_cli/version.rb +2 -2
- metadata +5 -18
- data/lib/leap_cli/commands/ca.rb +0 -518
- data/lib/leap_cli/commands/clean.rb +0 -16
- data/lib/leap_cli/commands/compile.rb +0 -340
- data/lib/leap_cli/commands/db.rb +0 -65
- data/lib/leap_cli/commands/deploy.rb +0 -368
- data/lib/leap_cli/commands/env.rb +0 -76
- data/lib/leap_cli/commands/facts.rb +0 -100
- data/lib/leap_cli/commands/inspect.rb +0 -144
- data/lib/leap_cli/commands/list.rb +0 -132
- data/lib/leap_cli/commands/node.rb +0 -165
- data/lib/leap_cli/commands/node_init.rb +0 -169
- data/lib/leap_cli/commands/ssh.rb +0 -220
- data/lib/leap_cli/commands/test.rb +0 -74
- data/lib/leap_cli/commands/user.rb +0 -136
- data/lib/leap_cli/commands/util.rb +0 -50
- data/lib/leap_cli/commands/vagrant.rb +0 -197
data/lib/leap_cli/config/node.rb
CHANGED
@@ -11,33 +11,6 @@ require 'ya2yaml' # pure ruby yaml
|
|
11
11
|
module LeapCli
|
12
12
|
module Config
|
13
13
|
|
14
|
-
#
|
15
|
-
# A proxy for Manager that binds to a particular object
|
16
|
-
# (so that we can bind to a particular environment)
|
17
|
-
#
|
18
|
-
class ManagerBinding
|
19
|
-
def initialize(manager, object)
|
20
|
-
@manager = manager
|
21
|
-
@object = object
|
22
|
-
end
|
23
|
-
|
24
|
-
def services
|
25
|
-
@manager.env(@object.environment).services
|
26
|
-
end
|
27
|
-
|
28
|
-
def tags
|
29
|
-
@manager.env(@object.environment).tags
|
30
|
-
end
|
31
|
-
|
32
|
-
def provider
|
33
|
-
@manager.env(@object.environment).provider
|
34
|
-
end
|
35
|
-
|
36
|
-
def method_missing(*args)
|
37
|
-
@manager.send(*args)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
14
|
#
|
42
15
|
# This class represents the configuration for a single node, service, or tag.
|
43
16
|
# Also, all the nested hashes are also of this type.
|
@@ -46,21 +19,27 @@ module LeapCli
|
|
46
19
|
#
|
47
20
|
class Object < Hash
|
48
21
|
|
22
|
+
attr_reader :env
|
49
23
|
attr_reader :node
|
50
24
|
|
51
|
-
def initialize(
|
52
|
-
|
53
|
-
|
54
|
-
@
|
55
|
-
|
56
|
-
# an object that is a node as @node equal to self, otherwise all the child objects point back to the top level node.
|
25
|
+
def initialize(environment=nil, node=nil)
|
26
|
+
raise ArgumentError unless environment.nil? || environment.is_a?(Config::Environment)
|
27
|
+
@env = environment
|
28
|
+
# an object that is a node as @node equal to self, otherwise all the
|
29
|
+
# child objects point back to the top level node.
|
57
30
|
@node = node || self
|
58
31
|
end
|
59
32
|
|
60
33
|
def manager
|
61
|
-
|
34
|
+
@env.manager
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# TODO: deprecate node.global()
|
39
|
+
#
|
40
|
+
def global
|
41
|
+
@env
|
62
42
|
end
|
63
|
-
alias :global :manager
|
64
43
|
|
65
44
|
def environment=(e)
|
66
45
|
self.store('environment', e)
|
@@ -70,6 +49,11 @@ module LeapCli
|
|
70
49
|
self['environment']
|
71
50
|
end
|
72
51
|
|
52
|
+
def duplicate(env)
|
53
|
+
new_object = self.deep_dup
|
54
|
+
new_object.set_environment(env, new_object)
|
55
|
+
end
|
56
|
+
|
73
57
|
#
|
74
58
|
# export YAML
|
75
59
|
#
|
@@ -85,12 +69,23 @@ module LeapCli
|
|
85
69
|
#
|
86
70
|
# export JSON
|
87
71
|
#
|
88
|
-
def dump_json(
|
72
|
+
def dump_json(options={})
|
89
73
|
evaluate(@node)
|
90
|
-
if options
|
91
|
-
self.to_json
|
74
|
+
if options[:format] == :compact
|
75
|
+
return self.to_json
|
92
76
|
else
|
93
|
-
|
77
|
+
excluded = {}
|
78
|
+
if options[:exclude]
|
79
|
+
options[:exclude].each do |key|
|
80
|
+
excluded[key] = self[key]
|
81
|
+
self.delete(key)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
json_str = JSON.sorted_generate(self)
|
85
|
+
if excluded.any?
|
86
|
+
self.merge!(excluded)
|
87
|
+
end
|
88
|
+
return json_str
|
94
89
|
end
|
95
90
|
end
|
96
91
|
|
@@ -186,6 +181,10 @@ module LeapCli
|
|
186
181
|
mode = :subtract
|
187
182
|
old_value = self.fetch '-'+key, nil
|
188
183
|
self.delete('-'+key)
|
184
|
+
elsif self.has_key?('!'+key)
|
185
|
+
mode = :replace
|
186
|
+
old_value = self.fetch '!'+key, nil
|
187
|
+
self.delete('!'+key)
|
189
188
|
else
|
190
189
|
mode = :normal
|
191
190
|
old_value = self.fetch key, nil
|
@@ -197,9 +196,13 @@ module LeapCli
|
|
197
196
|
old_value = true if old_value == "true"
|
198
197
|
old_value = false if old_value == "false"
|
199
198
|
|
199
|
+
# force replace?
|
200
|
+
if mode == :replace && prefer_self
|
201
|
+
value = old_value
|
202
|
+
|
200
203
|
# merge hashes
|
201
|
-
|
202
|
-
value = Config::Object.new(@
|
204
|
+
elsif old_value.is_a?(Hash) || new_value.is_a?(Hash)
|
205
|
+
value = Config::Object.new(@env, @node)
|
203
206
|
old_value.is_a?(Hash) ? value.deep_merge!(old_value) : (value[key] = old_value if !old_value.nil?)
|
204
207
|
new_value.is_a?(Hash) ? value.deep_merge!(new_value, prefer_self) : (value[key] = new_value if !new_value.nil?)
|
205
208
|
|
@@ -250,6 +253,16 @@ module LeapCli
|
|
250
253
|
self
|
251
254
|
end
|
252
255
|
|
256
|
+
def set_environment(env, node)
|
257
|
+
@env = env
|
258
|
+
@node = node
|
259
|
+
self.each do |key, value|
|
260
|
+
if value.is_a?(Config::Object)
|
261
|
+
value.set_environment(env, node)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
253
266
|
#
|
254
267
|
# like a reverse deep merge
|
255
268
|
# (self takes precedence)
|
@@ -286,7 +299,7 @@ module LeapCli
|
|
286
299
|
keys.each do |key|
|
287
300
|
obj = fetch_value(key, context)
|
288
301
|
if is_required_value_not_set?(obj)
|
289
|
-
Util::log 0, :warning, "required
|
302
|
+
Util::log 0, :warning, "required property \"#{key}\" is not set in node \"#{node.name}\"."
|
290
303
|
elsif obj.is_a? Config::Object
|
291
304
|
obj.evaluate_everything(context)
|
292
305
|
end
|
@@ -301,7 +314,7 @@ module LeapCli
|
|
301
314
|
@late_eval_list.each do |key, value|
|
302
315
|
self[key] = context.evaluate_ruby(key, value)
|
303
316
|
if is_required_value_not_set?(self[key])
|
304
|
-
Util::log 0, :warning, "required
|
317
|
+
Util::log 0, :warning, "required property \"#{key}\" is not set in node \"#{node.name}\"."
|
305
318
|
end
|
306
319
|
end
|
307
320
|
end
|
@@ -167,14 +167,17 @@ module LeapCli
|
|
167
167
|
end
|
168
168
|
|
169
169
|
#
|
170
|
-
#
|
170
|
+
# Applies inherit_from! to all objects.
|
171
171
|
#
|
172
|
-
|
172
|
+
# 'env' specifies what environment should be for
|
173
|
+
# each object in the list.
|
174
|
+
#
|
175
|
+
def inherit_from!(object_list, env)
|
173
176
|
object_list.each do |name, object|
|
174
177
|
if self[name]
|
175
178
|
self[name].inherit_from!(object)
|
176
179
|
else
|
177
|
-
self[name] = object.
|
180
|
+
self[name] = object.duplicate(env)
|
178
181
|
end
|
179
182
|
end
|
180
183
|
end
|
@@ -4,8 +4,19 @@
|
|
4
4
|
|
5
5
|
module LeapCli; module Config
|
6
6
|
class Provider < Object
|
7
|
+
attr_reader :environment
|
8
|
+
def set_env(e)
|
9
|
+
if e == 'default'
|
10
|
+
@environment = nil
|
11
|
+
else
|
12
|
+
@environment = e
|
13
|
+
end
|
14
|
+
end
|
7
15
|
def provider
|
8
16
|
self
|
9
17
|
end
|
18
|
+
def validate!
|
19
|
+
# nothing here yet :(
|
20
|
+
end
|
10
21
|
end
|
11
22
|
end; end
|
@@ -27,6 +27,15 @@ module LeapCli; module Config
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
# searches over all keys matching the regexp, checking to see if the value
|
31
|
+
# has been already used by any of them.
|
32
|
+
def taken?(regexp, value, environment)
|
33
|
+
self.keys.grep(regexp).each do |key|
|
34
|
+
return true if self.retrieve(key, environment) == value
|
35
|
+
end
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
|
30
39
|
def set_without_block(key, value, environment)
|
31
40
|
set_with_block(key, environment) {value}
|
32
41
|
end
|
@@ -56,7 +65,11 @@ module LeapCli; module Config
|
|
56
65
|
if clean
|
57
66
|
self.each_key do |environment|
|
58
67
|
if pinned_env.nil? || pinned_env == environment
|
59
|
-
self[environment]
|
68
|
+
env = self[environment]
|
69
|
+
if env.nil?
|
70
|
+
raise StandardError.new("secrets.json file seems corrupted. No such environment '#{environment}'")
|
71
|
+
end
|
72
|
+
env.each_key do |key|
|
60
73
|
unless @discovered_keys[environment] && @discovered_keys[environment][key]
|
61
74
|
self[environment].delete(key)
|
62
75
|
end
|
data/lib/leap_cli/config/tag.rb
CHANGED
data/lib/leap_cli/leapfile.rb
CHANGED
data/lib/leap_cli/log.rb
CHANGED
@@ -83,6 +83,7 @@ module LeapCli
|
|
83
83
|
when :fatal_error then ['fatal error:', :red, :bold]
|
84
84
|
when :warning then ['warning:', :yellow, :bold]
|
85
85
|
when :info then ['info', :cyan, :bold]
|
86
|
+
when :note then ['NOTE:', :cyan, :bold]
|
86
87
|
when :updated then ['updated', :cyan, :bold]
|
87
88
|
when :updating then ['updating', :cyan, :bold]
|
88
89
|
when :created then ['created', :green, :bold]
|
data/lib/leap_cli/logger.rb
CHANGED
@@ -113,7 +113,7 @@ module LeapCli
|
|
113
113
|
{ :match => /sh: .+: command not found/, :color => :magenta, :match_level => 1, :priority => -30 },
|
114
114
|
|
115
115
|
# IMPORTANT
|
116
|
-
{ :match => /^
|
116
|
+
{ :match => /^(E|e)rr ::/, :color => :red, :match_level => 0, :priority => -10, :exit => 1},
|
117
117
|
{ :match => /^ERROR:/, :color => :red, :priority => -10, :exit => 1},
|
118
118
|
{ :match => /.*/, :color => :blue, :match_level => 0, :priority => -20 },
|
119
119
|
|
@@ -129,19 +129,23 @@ module LeapCli
|
|
129
129
|
{ :match => /WARNING: The following packages cannot be authenticated!/, :color => :red, :level => 0, :priority => -10},
|
130
130
|
|
131
131
|
# PUPPET
|
132
|
-
{ :match => /^
|
133
|
-
{ :match => /^
|
134
|
-
{ :match => /^
|
135
|
-
{ :match => /^
|
136
|
-
{ :match => /^
|
137
|
-
{ :match => /^
|
138
|
-
{ :match =>
|
132
|
+
{ :match => /^(W|w)arning: Not collecting exported resources without storeconfigs/, :level => 2, :color => :yellow, :priority => -10},
|
133
|
+
{ :match => /^(W|w)arning: Found multiple default providers for vcsrepo:/, :level => 2, :color => :yellow, :priority => -10},
|
134
|
+
{ :match => /^(W|w)arning: .*is deprecated.*$/, :level => 2, :color => :yellow, :priority => -10},
|
135
|
+
{ :match => /^(W|w)arning: Scope.*$/, :level => 2, :color => :yellow, :priority => -10},
|
136
|
+
{ :match => /^(N|n)otice:/, :level => 1, :color => :cyan, :priority => -20},
|
137
|
+
{ :match => /^(N|n)otice:.*executed successfully$/, :level => 2, :color => :cyan, :priority => -15},
|
138
|
+
{ :match => /^(W|w)arning:/, :level => 0, :color => :yellow, :priority => -20},
|
139
|
+
{ :match => /^Duplicate declaration:/, :level => 0, :color => :red, :priority => -20},
|
140
|
+
{ :match => /Finished catalog run/, :level => 0, :color => :green, :priority => -10},
|
139
141
|
{ :match => /^APPLY COMPLETE \(changes made\)/, :level => 0, :color => :green, :priority => -10},
|
140
|
-
{ :match => /^APPLY COMPLETE \(no changes\)/,
|
142
|
+
{ :match => /^APPLY COMPLETE \(no changes\)/, :level => 0, :color => :green, :priority => -10},
|
141
143
|
|
142
144
|
# PUPPET FATAL ERRORS
|
143
|
-
{ :match => /^
|
145
|
+
{ :match => /^(E|e)rr(or|):/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
146
|
+
{ :match => /^Wrapped exception:/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
144
147
|
{ :match => /^Failed to parse template/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
148
|
+
{ :match => /^Execution of.*returned/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
145
149
|
{ :match => /^Parameter matches failed:/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
146
150
|
{ :match => /^Syntax error/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
147
151
|
{ :match => /^Cannot reassign variable/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
@@ -155,8 +159,8 @@ module LeapCli
|
|
155
159
|
{ :match => /\d+ tests: \d+ passes, \d+ skips, 0 warnings, 0 failures, 0 errors/, :color => :blue, :priority => -20},
|
156
160
|
|
157
161
|
# LOG SUPPRESSION
|
158
|
-
{ :match => /^
|
159
|
-
{ :match => /^
|
162
|
+
{ :match => /^(W|w)arning: You cannot collect without storeconfigs being set/, :level => 2, :priority => 10},
|
163
|
+
{ :match => /^(W|w)arning: You cannot collect exported resources without storeconfigs being set/, :level => 2, :priority => 10}
|
160
164
|
]
|
161
165
|
|
162
166
|
def self.sorted_formatters
|
@@ -29,6 +29,8 @@ module LeapCli
|
|
29
29
|
# Gives you the program description
|
30
30
|
def program_desc(desc)
|
31
31
|
@io.puts "@title = 'Command Line Reference'"
|
32
|
+
@io.puts "@summary = 'A copy of leap --help'"
|
33
|
+
|
32
34
|
#@io.puts "# #{File.basename($0)} - #{desc}"
|
33
35
|
@io.puts
|
34
36
|
end
|
@@ -98,7 +100,7 @@ module LeapCli
|
|
98
100
|
@commands.push(name)
|
99
101
|
#@io.puts "#{@nest}## Command: <tt>#{([name] + aliases).join('|')} #{@arg_name_formatter.format(arg_name,arg_options)}</tt>"
|
100
102
|
@io.puts
|
101
|
-
@io.puts "#{@nest}# #{@commands.join ' '} #{@arg_name_formatter.format(arg_name,arg_options)}"
|
103
|
+
@io.puts "#{@nest}# #{@commands.join ' '} #{@arg_name_formatter.format(arg_name, arg_options, [])}"
|
102
104
|
@io.puts
|
103
105
|
@io.puts String(desc).strip
|
104
106
|
@io.puts
|
data/lib/leap_cli/path.rb
CHANGED
@@ -99,4 +99,16 @@ module LeapCli; module Path
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
+
def self.vagrant_ssh_priv_key_file
|
103
|
+
File.join(LEAP_CLI_BASE_DIR, 'vendor', 'vagrant_ssh_keys', 'vagrant.key')
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.vagrant_ssh_pub_key_file
|
107
|
+
File.join(LEAP_CLI_BASE_DIR, 'vendor', 'vagrant_ssh_keys', 'vagrant.pub')
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.node_init_script
|
111
|
+
File.join(@platform, 'bin', 'node_init')
|
112
|
+
end
|
113
|
+
|
102
114
|
end; end
|
@@ -9,10 +9,6 @@ module LeapCli; module Remote; module LeapPlugin
|
|
9
9
|
"puppet rsync lsb-release locales"
|
10
10
|
end
|
11
11
|
|
12
|
-
def required_wheezy_packages
|
13
|
-
"puppet ruby-hiera-puppet rsync lsb-release locales"
|
14
|
-
end
|
15
|
-
|
16
12
|
def log(*args, &block)
|
17
13
|
LeapCli::Util::log(*args, &block)
|
18
14
|
end
|
@@ -61,15 +57,19 @@ module LeapCli; module Remote; module LeapPlugin
|
|
61
57
|
end
|
62
58
|
end
|
63
59
|
|
64
|
-
|
65
|
-
|
60
|
+
#
|
61
|
+
# dumps debugging information
|
62
|
+
# #
|
63
|
+
def debug
|
64
|
+
run "#{Leap::Platform.leap_dir}/bin/debug.sh"
|
66
65
|
end
|
67
66
|
|
68
67
|
#
|
69
68
|
# dumps the recent deploy history to the console
|
70
69
|
#
|
71
|
-
def history
|
72
|
-
|
70
|
+
def history(lines)
|
71
|
+
command = "(test -s /var/log/leap/deploy-summary.log && tail -n #{lines} /var/log/leap/deploy-summary.log) || (test -s /var/log/leap/deploy-summary.log.1 && tail -n #{lines} /var/log/leap/deploy-summary.log.1) || (echo 'no history')"
|
72
|
+
run command
|
73
73
|
end
|
74
74
|
|
75
75
|
#
|
@@ -189,29 +189,4 @@ module LeapCli; module Remote; module LeapPlugin
|
|
189
189
|
STDOUT.flush
|
190
190
|
end
|
191
191
|
|
192
|
-
|
193
|
-
# run "mkdir -p #{dir}"
|
194
|
-
#end
|
195
|
-
|
196
|
-
#def chown_root(dir)
|
197
|
-
# run "chown root -R #{dir} && chmod -R ag-rwx,u+rwX #{dir}"
|
198
|
-
#end
|
199
|
-
|
200
|
-
#def logrun(cmd)
|
201
|
-
# @streamer ||= LeapCli::Remote::LogStreamer.new
|
202
|
-
# run cmd do |channel, stream, data|
|
203
|
-
# @streamer.collect_output(channel[:host], data)
|
204
|
-
# end
|
205
|
-
#end
|
206
|
-
|
207
|
-
# return_code = nil
|
208
|
-
# run "something; echo return code: $?" do |channel, stream, data|
|
209
|
-
# if data =~ /return code: (\d+)/
|
210
|
-
# return_code = $1.to_i
|
211
|
-
# else
|
212
|
-
# Capistrano::Configuration.default_io_proc.call(channel, stream, data)
|
213
|
-
# end
|
214
|
-
# end
|
215
|
-
# puts "finished with return code: #{return_code}"
|
216
|
-
|
217
|
-
end; end; end
|
192
|
+
end; end; end
|
@@ -24,43 +24,3 @@ module LeapCli; module Remote; module PuppetPlugin
|
|
24
24
|
end
|
25
25
|
|
26
26
|
end; end; end
|
27
|
-
|
28
|
-
|
29
|
-
# def puppet(command = :noop)
|
30
|
-
# #puppet_cmd = "cd #{puppet_destination} && #{sudo_cmd} #{puppet_command} --modulepath=#{puppet_lib} #{puppet_parameters}"
|
31
|
-
# puppet_cmd = "cd #{puppet_destination} && #{sudo_cmd} #{puppet_command} #{puppet_parameters}"
|
32
|
-
# flag = command == :noop ? '--noop' : ''
|
33
|
-
|
34
|
-
# writer = if puppet_stream_output
|
35
|
-
# SupplyDrop::Writer::Streaming.new(logger)
|
36
|
-
# else
|
37
|
-
# SupplyDrop::Writer::Batched.new(logger)
|
38
|
-
# end
|
39
|
-
|
40
|
-
# writer = SupplyDrop::Writer::File.new(writer, puppet_write_to_file) unless puppet_write_to_file.nil?
|
41
|
-
|
42
|
-
# begin
|
43
|
-
# exitcode = nil
|
44
|
-
# run "#{puppet_cmd} #{flag}; echo exitcode:$?" do |channel, stream, data|
|
45
|
-
# if data =~ /exitcode:(\d+)/
|
46
|
-
# exitcode = $1
|
47
|
-
# writer.collect_output(channel[:host], "Puppet #{command} complete (#{exitcode_description(exitcode)}).\n")
|
48
|
-
# else
|
49
|
-
# writer.collect_output(channel[:host], data)
|
50
|
-
# end
|
51
|
-
# end
|
52
|
-
# ensure
|
53
|
-
# writer.all_output_collected
|
54
|
-
# end
|
55
|
-
# end
|
56
|
-
|
57
|
-
# def exitcode_description(code)
|
58
|
-
# case code
|
59
|
-
# when "0" then "no changes"
|
60
|
-
# when "2" then "changes made"
|
61
|
-
# when "4" then "failed"
|
62
|
-
# when "6" then "changes and failures"
|
63
|
-
# else code
|
64
|
-
# end
|
65
|
-
# end
|
66
|
-
|