leap_cli 1.2.5
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.
- data/bin/leap +81 -0
- data/lib/core_ext/boolean.rb +14 -0
- data/lib/core_ext/hash.rb +35 -0
- data/lib/core_ext/json.rb +42 -0
- data/lib/core_ext/nil.rb +5 -0
- data/lib/core_ext/string.rb +14 -0
- data/lib/leap/platform.rb +52 -0
- data/lib/leap_cli/commands/ca.rb +430 -0
- data/lib/leap_cli/commands/clean.rb +16 -0
- data/lib/leap_cli/commands/compile.rb +134 -0
- data/lib/leap_cli/commands/deploy.rb +172 -0
- data/lib/leap_cli/commands/facts.rb +93 -0
- data/lib/leap_cli/commands/inspect.rb +140 -0
- data/lib/leap_cli/commands/list.rb +122 -0
- data/lib/leap_cli/commands/new.rb +126 -0
- data/lib/leap_cli/commands/node.rb +272 -0
- data/lib/leap_cli/commands/pre.rb +99 -0
- data/lib/leap_cli/commands/shell.rb +67 -0
- data/lib/leap_cli/commands/test.rb +55 -0
- data/lib/leap_cli/commands/user.rb +140 -0
- data/lib/leap_cli/commands/util.rb +50 -0
- data/lib/leap_cli/commands/vagrant.rb +201 -0
- data/lib/leap_cli/config/macros.rb +369 -0
- data/lib/leap_cli/config/manager.rb +369 -0
- data/lib/leap_cli/config/node.rb +37 -0
- data/lib/leap_cli/config/object.rb +336 -0
- data/lib/leap_cli/config/object_list.rb +174 -0
- data/lib/leap_cli/config/secrets.rb +43 -0
- data/lib/leap_cli/config/tag.rb +18 -0
- data/lib/leap_cli/constants.rb +7 -0
- data/lib/leap_cli/leapfile.rb +97 -0
- data/lib/leap_cli/load_paths.rb +15 -0
- data/lib/leap_cli/log.rb +166 -0
- data/lib/leap_cli/logger.rb +216 -0
- data/lib/leap_cli/markdown_document_listener.rb +134 -0
- data/lib/leap_cli/path.rb +84 -0
- data/lib/leap_cli/remote/leap_plugin.rb +204 -0
- data/lib/leap_cli/remote/puppet_plugin.rb +66 -0
- data/lib/leap_cli/remote/rsync_plugin.rb +35 -0
- data/lib/leap_cli/remote/tasks.rb +36 -0
- data/lib/leap_cli/requirements.rb +19 -0
- data/lib/leap_cli/ssh_key.rb +130 -0
- data/lib/leap_cli/util/remote_command.rb +110 -0
- data/lib/leap_cli/util/secret.rb +54 -0
- data/lib/leap_cli/util/x509.rb +32 -0
- data/lib/leap_cli/util.rb +431 -0
- data/lib/leap_cli/version.rb +9 -0
- data/lib/leap_cli.rb +46 -0
- data/lib/lib_ext/capistrano_connections.rb +16 -0
- data/lib/lib_ext/gli.rb +52 -0
- data/lib/lib_ext/markdown_document_listener.rb +122 -0
- data/vendor/certificate_authority/lib/certificate_authority/certificate.rb +200 -0
- data/vendor/certificate_authority/lib/certificate_authority/certificate_revocation_list.rb +77 -0
- data/vendor/certificate_authority/lib/certificate_authority/distinguished_name.rb +97 -0
- data/vendor/certificate_authority/lib/certificate_authority/extensions.rb +266 -0
- data/vendor/certificate_authority/lib/certificate_authority/key_material.rb +148 -0
- data/vendor/certificate_authority/lib/certificate_authority/ocsp_handler.rb +144 -0
- data/vendor/certificate_authority/lib/certificate_authority/pkcs11_key_material.rb +65 -0
- data/vendor/certificate_authority/lib/certificate_authority/revocable.rb +14 -0
- data/vendor/certificate_authority/lib/certificate_authority/serial_number.rb +10 -0
- data/vendor/certificate_authority/lib/certificate_authority/signing_entity.rb +16 -0
- data/vendor/certificate_authority/lib/certificate_authority/signing_request.rb +56 -0
- data/vendor/certificate_authority/lib/certificate_authority.rb +21 -0
- data/vendor/rsync_command/lib/rsync_command/ssh_options.rb +159 -0
- data/vendor/rsync_command/lib/rsync_command/thread_pool.rb +36 -0
- data/vendor/rsync_command/lib/rsync_command/version.rb +3 -0
- data/vendor/rsync_command/lib/rsync_command.rb +96 -0
- data/vendor/rsync_command/test/rsync_test.rb +74 -0
- data/vendor/rsync_command/test/ssh_options_test.rb +61 -0
- data/vendor/vagrant_ssh_keys/vagrant.key +27 -0
- data/vendor/vagrant_ssh_keys/vagrant.pub +1 -0
- metadata +345 -0
@@ -0,0 +1,431 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'pathname'
|
4
|
+
require 'erb'
|
5
|
+
require 'pty'
|
6
|
+
|
7
|
+
module LeapCli
|
8
|
+
module Util
|
9
|
+
extend self
|
10
|
+
|
11
|
+
##
|
12
|
+
## QUITTING
|
13
|
+
##
|
14
|
+
|
15
|
+
def exit_status(code=nil)
|
16
|
+
@exit_status = code if code
|
17
|
+
@exit_status
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# quit and print help
|
22
|
+
#
|
23
|
+
def help!(message=nil)
|
24
|
+
ENV['GLI_DEBUG'] = "false"
|
25
|
+
help_now!(message)
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# exit with error code and with a message that we are bailing out.
|
30
|
+
#
|
31
|
+
def bail!(*message)
|
32
|
+
if block_given?
|
33
|
+
LeapCli.log_level = 3
|
34
|
+
yield
|
35
|
+
elsif message
|
36
|
+
log 0, *message
|
37
|
+
end
|
38
|
+
log 0, :bail, ""
|
39
|
+
raise SystemExit.new(@exit_status || 1)
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# quit with message, but no additional error or warning about bailing.
|
44
|
+
#
|
45
|
+
def quit!(message='')
|
46
|
+
puts(message)
|
47
|
+
raise SystemExit.new(@exit_status || 0)
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# bails out with message if assertion is false.
|
52
|
+
#
|
53
|
+
def assert!(boolean, message=nil, &block)
|
54
|
+
if !boolean
|
55
|
+
bail!(message, &block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# assert that the command is available
|
61
|
+
#
|
62
|
+
def assert_bin!(cmd_name, msg=nil)
|
63
|
+
assert! `which #{cmd_name}`.strip.any? do
|
64
|
+
log :missing, "command '%s'" % cmd_name do
|
65
|
+
if msg
|
66
|
+
log msg
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# assert that the command is run without an error.
|
74
|
+
# if successful, return output.
|
75
|
+
#
|
76
|
+
def assert_run!(cmd, message=nil)
|
77
|
+
cmd = cmd + " 2>&1"
|
78
|
+
output = `#{cmd}`.strip
|
79
|
+
unless $?.success?
|
80
|
+
exit_status($?.exitstatus)
|
81
|
+
bail! do
|
82
|
+
log :run, cmd
|
83
|
+
log :failed, "(exit #{$?.exitstatus}) #{output}", :indent => 1
|
84
|
+
log message, :indent => 1 if message
|
85
|
+
end
|
86
|
+
else
|
87
|
+
log 2, :ran, cmd
|
88
|
+
end
|
89
|
+
return output
|
90
|
+
end
|
91
|
+
|
92
|
+
def assert_files_missing!(*files)
|
93
|
+
options = files.last.is_a?(Hash) ? files.pop : {}
|
94
|
+
base = options[:base] || Path.provider
|
95
|
+
file_list = files.collect { |file_path|
|
96
|
+
file_path = Path.named_path(file_path, base)
|
97
|
+
File.exists?(file_path) ? Path.relative_path(file_path, base) : nil
|
98
|
+
}.compact
|
99
|
+
if file_list.length > 1
|
100
|
+
bail! do
|
101
|
+
log :error, "Sorry, we can't continue because these files already exist: #{file_list.join(', ')}."
|
102
|
+
log options[:msg] if options[:msg]
|
103
|
+
end
|
104
|
+
elsif file_list.length == 1
|
105
|
+
bail! do
|
106
|
+
log :error, "Sorry, we can't continue because this file already exists: #{file_list.first}."
|
107
|
+
log options[:msg] if options[:msg]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def assert_config!(conf_path)
|
113
|
+
value = nil
|
114
|
+
begin
|
115
|
+
value = manager.instance_eval(conf_path)
|
116
|
+
#rescue NoMethodError
|
117
|
+
#rescue NameError
|
118
|
+
ensure
|
119
|
+
assert! !value.nil? && value != "REQUIRED" do
|
120
|
+
log :missing, "required configuration value for #{conf_path}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def assert_files_exist!(*files)
|
126
|
+
options = files.last.is_a?(Hash) ? files.pop : {}
|
127
|
+
file_list = files.collect { |file_path|
|
128
|
+
file_path = Path.named_path(file_path)
|
129
|
+
!File.exists?(file_path) ? Path.relative_path(file_path) : nil
|
130
|
+
}.compact
|
131
|
+
if file_list.length > 1
|
132
|
+
bail! do
|
133
|
+
log :missing, "these files: #{file_list.join(', ')}"
|
134
|
+
log options[:msg] if options[:msg]
|
135
|
+
end
|
136
|
+
elsif file_list.length == 1
|
137
|
+
bail! do
|
138
|
+
log :missing, "file #{file_list.first}"
|
139
|
+
log options[:msg] if options[:msg]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def file_exists?(*files)
|
145
|
+
files.each do |file_path|
|
146
|
+
file_path = Path.named_path(file_path)
|
147
|
+
if !File.exists?(file_path)
|
148
|
+
return false
|
149
|
+
end
|
150
|
+
end
|
151
|
+
return true
|
152
|
+
end
|
153
|
+
|
154
|
+
##
|
155
|
+
## FILES AND DIRECTORIES
|
156
|
+
##
|
157
|
+
|
158
|
+
#
|
159
|
+
# creates a directory if it doesn't already exist
|
160
|
+
#
|
161
|
+
def ensure_dir(dir)
|
162
|
+
dir = Path.named_path(dir)
|
163
|
+
unless File.directory?(dir)
|
164
|
+
assert_files_missing!(dir, :msg => "Cannot create directory #{dir}")
|
165
|
+
FileUtils.mkdir_p(dir, :mode => 0700)
|
166
|
+
unless dir =~ /\/$/
|
167
|
+
dir = dir + '/'
|
168
|
+
end
|
169
|
+
log :created, dir
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
##
|
174
|
+
## FILE READING, WRITING, DELETING, and MOVING
|
175
|
+
##
|
176
|
+
|
177
|
+
#
|
178
|
+
# All file read and write methods support using named paths in the place of an actual file path.
|
179
|
+
#
|
180
|
+
# To call using a named path, use a symbol in the place of filepath, like so:
|
181
|
+
#
|
182
|
+
# read_file(:known_hosts)
|
183
|
+
#
|
184
|
+
# In some cases, the named path will take an argument. In this case, set the filepath to be an array:
|
185
|
+
#
|
186
|
+
# write_file!([:user_ssh, 'bob'], ssh_key_str)
|
187
|
+
#
|
188
|
+
# To resolve a named path, use the shortcut helper 'path()'
|
189
|
+
#
|
190
|
+
# path([:user_ssh, 'bob']) ==> files/users/bob/bob_ssh_pub.key
|
191
|
+
#
|
192
|
+
|
193
|
+
def read_file!(filepath)
|
194
|
+
filepath = Path.named_path(filepath)
|
195
|
+
assert_files_exist!(filepath)
|
196
|
+
File.read(filepath)
|
197
|
+
end
|
198
|
+
|
199
|
+
def read_file(filepath)
|
200
|
+
filepath = Path.named_path(filepath)
|
201
|
+
if file_exists?(filepath)
|
202
|
+
File.read(filepath)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
#
|
207
|
+
# replace contents of a file, with an exclusive lock.
|
208
|
+
#
|
209
|
+
# 1. locks file
|
210
|
+
# 2. reads contents
|
211
|
+
# 3. yields contents
|
212
|
+
# 4. replaces file with return value of the block
|
213
|
+
#
|
214
|
+
def replace_file!(filepath, &block)
|
215
|
+
filepath = Path.named_path(filepath)
|
216
|
+
if !File.exists?(filepath)
|
217
|
+
content = yield(nil)
|
218
|
+
unless content.nil?
|
219
|
+
write_file!(filepath, content)
|
220
|
+
end
|
221
|
+
else
|
222
|
+
File.open(filepath, File::RDWR|File::CREAT, 0600) do |f|
|
223
|
+
f.flock(File::LOCK_EX)
|
224
|
+
old_content = f.read
|
225
|
+
new_content = yield(old_content)
|
226
|
+
if old_content == new_content
|
227
|
+
log :nochange, filepath, 2
|
228
|
+
else
|
229
|
+
f.rewind
|
230
|
+
f.write(new_content)
|
231
|
+
f.flush
|
232
|
+
f.truncate(f.pos)
|
233
|
+
log :updated, filepath
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def remove_file!(filepath)
|
240
|
+
filepath = Path.named_path(filepath)
|
241
|
+
if File.exists?(filepath)
|
242
|
+
if File.directory?(filepath)
|
243
|
+
remove_directory!(filepath)
|
244
|
+
else
|
245
|
+
begin
|
246
|
+
File.unlink(filepath)
|
247
|
+
log :removed, filepath
|
248
|
+
rescue Exception => exc
|
249
|
+
bail! do
|
250
|
+
log :failed, "to remove file #{filepath}"
|
251
|
+
log "error message: " + exc.to_s
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def remove_directory!(filepath)
|
259
|
+
filepath = Path.named_path(filepath)
|
260
|
+
if filepath !~ /^#{Regexp.escape(Path.provider)}/ || filepath =~ /\.\./
|
261
|
+
bail! "sanity check on rm -r did not pass for #{filepath}"
|
262
|
+
end
|
263
|
+
if File.directory?(filepath)
|
264
|
+
begin
|
265
|
+
FileUtils.rm_r(filepath)
|
266
|
+
log :removed, filepath
|
267
|
+
rescue Exception => exc
|
268
|
+
bail! do
|
269
|
+
log :failed, "to remove directory #{filepath}"
|
270
|
+
log "error message: " + exc.to_s
|
271
|
+
end
|
272
|
+
end
|
273
|
+
else
|
274
|
+
log :failed, "to remove '#{filepath}', it is not a directory"
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def write_file!(filepath, contents)
|
279
|
+
filepath = Path.named_path(filepath)
|
280
|
+
ensure_dir File.dirname(filepath)
|
281
|
+
existed = File.exists?(filepath)
|
282
|
+
if existed
|
283
|
+
if file_content_equals?(filepath, contents)
|
284
|
+
log :nochange, filepath, 2
|
285
|
+
return
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
File.open(filepath, 'w', 0600) do |f|
|
290
|
+
f.write contents
|
291
|
+
end
|
292
|
+
|
293
|
+
if existed
|
294
|
+
log :updated, filepath
|
295
|
+
else
|
296
|
+
log :created, filepath
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def rename_file!(oldpath, newpath)
|
301
|
+
oldpath = Path.named_path(oldpath)
|
302
|
+
newpath = Path.named_path(newpath)
|
303
|
+
if File.exists? newpath
|
304
|
+
log :skipping, "#{Path.relative_path(newpath)}, file already exists"
|
305
|
+
return
|
306
|
+
end
|
307
|
+
if !File.exists? oldpath
|
308
|
+
log :skipping, "#{Path.relative_path(oldpath)}, file is missing"
|
309
|
+
return
|
310
|
+
end
|
311
|
+
FileUtils.mv oldpath, newpath
|
312
|
+
log :moved, "#{Path.relative_path(oldpath)} to #{Path.relative_path(newpath)}"
|
313
|
+
end
|
314
|
+
|
315
|
+
def cmd_exists?(cmd)
|
316
|
+
`which #{cmd}`.strip.chars.any?
|
317
|
+
end
|
318
|
+
|
319
|
+
#
|
320
|
+
# creates a relative symlink from absolute paths, removing prior symlink if necessary
|
321
|
+
#
|
322
|
+
# symlink 'new' is created, pointing to 'old'
|
323
|
+
#
|
324
|
+
def relative_symlink(old, new)
|
325
|
+
relative_path = Pathname.new(old).relative_path_from(Pathname.new(new))
|
326
|
+
if File.symlink?(new)
|
327
|
+
if File.readlink(new) != relative_path.to_s
|
328
|
+
File.unlink(new)
|
329
|
+
log :updated, 'symlink %s' % Path.relative_path(new)
|
330
|
+
end
|
331
|
+
else
|
332
|
+
log :created, 'symlink %s' % Path.relative_path(new)
|
333
|
+
end
|
334
|
+
FileUtils.ln_s(relative_path, new)
|
335
|
+
end
|
336
|
+
|
337
|
+
#
|
338
|
+
# compares md5 fingerprints to see if the contents of a file match the string we have in memory
|
339
|
+
#
|
340
|
+
def file_content_equals?(filepath, contents)
|
341
|
+
filepath = Path.named_path(filepath)
|
342
|
+
output = `md5sum '#{filepath}'`.strip
|
343
|
+
if $?.to_i == 0
|
344
|
+
return output.split(" ").first == Digest::MD5.hexdigest(contents).to_s
|
345
|
+
else
|
346
|
+
return false
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
##
|
351
|
+
## PROCESSES
|
352
|
+
##
|
353
|
+
|
354
|
+
#
|
355
|
+
# run a long running block of code in a separate process and display marching ants as time goes by.
|
356
|
+
# if the user hits ctrl-c, the program exits.
|
357
|
+
#
|
358
|
+
def long_running(&block)
|
359
|
+
pid = fork
|
360
|
+
if pid == nil
|
361
|
+
yield
|
362
|
+
exit!
|
363
|
+
end
|
364
|
+
Signal.trap("SIGINT") do
|
365
|
+
Process.kill("KILL", pid)
|
366
|
+
Process.wait(pid)
|
367
|
+
bail!
|
368
|
+
end
|
369
|
+
while true
|
370
|
+
sleep 0.2
|
371
|
+
STDOUT.print '.'
|
372
|
+
STDOUT.flush
|
373
|
+
break if Process.wait(pid, Process::WNOHANG)
|
374
|
+
end
|
375
|
+
STDOUT.puts
|
376
|
+
end
|
377
|
+
|
378
|
+
#
|
379
|
+
# runs a command in a pseudo terminal
|
380
|
+
#
|
381
|
+
def pty_run(cmd)
|
382
|
+
PTY.spawn(cmd) do |output, input, pid|
|
383
|
+
begin
|
384
|
+
while line = output.gets do
|
385
|
+
puts line
|
386
|
+
end
|
387
|
+
rescue Errno::EIO
|
388
|
+
end
|
389
|
+
end
|
390
|
+
rescue PTY::ChildExited
|
391
|
+
end
|
392
|
+
|
393
|
+
##
|
394
|
+
## ERB
|
395
|
+
##
|
396
|
+
|
397
|
+
def erb_eval(string, binding=nil)
|
398
|
+
ERB.new(string, nil, '%<>-').result(binding)
|
399
|
+
end
|
400
|
+
|
401
|
+
##
|
402
|
+
## GIT
|
403
|
+
##
|
404
|
+
|
405
|
+
def is_git_directory?(dir)
|
406
|
+
Dir.chdir(dir) do
|
407
|
+
`which git && git rev-parse 2>/dev/null`
|
408
|
+
return $? == 0
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
def current_git_branch(dir)
|
413
|
+
Dir.chdir(dir) do
|
414
|
+
branch = `git symbolic-ref HEAD 2>/dev/null`.strip
|
415
|
+
if branch.chars.any?
|
416
|
+
branch.sub /^refs\/heads\//, ''
|
417
|
+
else
|
418
|
+
nil
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
def current_git_commit(dir)
|
424
|
+
Dir.chdir(dir) do
|
425
|
+
`git rev-parse HEAD 2>/dev/null`.strip
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module LeapCli
|
2
|
+
unless defined?(LeapCli::VERSION)
|
3
|
+
VERSION = '1.2.5'
|
4
|
+
COMPATIBLE_PLATFORM_VERSION = '0.2.4'..'1.99'
|
5
|
+
SUMMARY = 'Command line interface to the LEAP platform'
|
6
|
+
DESCRIPTION = 'The command "leap" can be used to manage a bevy of servers running the LEAP platform from the comfort of your own home.'
|
7
|
+
LOAD_PATHS = ['lib', 'vendor/certificate_authority/lib', 'vendor/rsync_command/lib']
|
8
|
+
end
|
9
|
+
end
|
data/lib/leap_cli.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module LeapCli; end
|
2
|
+
|
3
|
+
$ruby_version = RUBY_VERSION.split('.').collect{ |i| i.to_i }.extend(Comparable)
|
4
|
+
|
5
|
+
require 'leap/platform.rb'
|
6
|
+
|
7
|
+
require 'leap_cli/version.rb'
|
8
|
+
require 'leap_cli/constants.rb'
|
9
|
+
require 'leap_cli/requirements.rb'
|
10
|
+
|
11
|
+
require 'leap_cli/leapfile.rb'
|
12
|
+
require 'core_ext/hash'
|
13
|
+
require 'core_ext/boolean'
|
14
|
+
require 'core_ext/nil'
|
15
|
+
require 'core_ext/string'
|
16
|
+
require 'core_ext/json'
|
17
|
+
|
18
|
+
require 'leap_cli/log'
|
19
|
+
require 'leap_cli/path'
|
20
|
+
require 'leap_cli/util'
|
21
|
+
require 'leap_cli/util/secret'
|
22
|
+
require 'leap_cli/util/remote_command'
|
23
|
+
require 'leap_cli/util/x509'
|
24
|
+
require 'leap_cli/logger'
|
25
|
+
|
26
|
+
require 'leap_cli/ssh_key'
|
27
|
+
require 'leap_cli/config/object'
|
28
|
+
require 'leap_cli/config/node'
|
29
|
+
require 'leap_cli/config/tag'
|
30
|
+
require 'leap_cli/config/secrets'
|
31
|
+
require 'leap_cli/config/object_list'
|
32
|
+
require 'leap_cli/config/manager'
|
33
|
+
|
34
|
+
require 'leap_cli/markdown_document_listener'
|
35
|
+
|
36
|
+
module LeapCli::Commands; end
|
37
|
+
|
38
|
+
#
|
39
|
+
# allow everyone easy access to log() command.
|
40
|
+
#
|
41
|
+
module LeapCli
|
42
|
+
Util.send(:extend, LeapCli::Log)
|
43
|
+
Commands.send(:extend, LeapCli::Log)
|
44
|
+
Config::Manager.send(:include, LeapCli::Log)
|
45
|
+
extend LeapCli::Log
|
46
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Capistrano
|
2
|
+
class Configuration
|
3
|
+
module Connections
|
4
|
+
def failed!(server)
|
5
|
+
@failure_callback.call(server) if @failure_callback
|
6
|
+
Thread.current[:failed_sessions] << server
|
7
|
+
end
|
8
|
+
|
9
|
+
def call_on_failure(&block)
|
10
|
+
@failure_callback = block
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
data/lib/lib_ext/gli.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#
|
2
|
+
# print subcommands indented in the main global help screen
|
3
|
+
#
|
4
|
+
|
5
|
+
module GLI
|
6
|
+
module Commands
|
7
|
+
module HelpModules
|
8
|
+
class GlobalHelpFormat
|
9
|
+
SUB_CMD_INDENT = " "
|
10
|
+
def format
|
11
|
+
program_desc = @app.program_desc
|
12
|
+
program_long_desc = @app.program_long_desc
|
13
|
+
if program_long_desc
|
14
|
+
wrapper = @wrapper_class.new(Terminal.instance.size[0],4)
|
15
|
+
program_long_desc = "\n #{wrapper.wrap(program_long_desc)}\n\n" if program_long_desc
|
16
|
+
else
|
17
|
+
program_long_desc = "\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
# build a list of commands, sort them so the commands with subcommands are at the bottom
|
21
|
+
commands = @sorter.call(@app.commands_declaration_order.reject(&:nodoc)).sort do |a,b|
|
22
|
+
if a.commands.any? && b.commands.any?; a.name.to_s <=> b.name.to_s
|
23
|
+
elsif a.commands.any?; 1
|
24
|
+
elsif b.commands.any?; -1
|
25
|
+
else; a.name.to_s <=> b.name.to_s
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# build a list of command info ([name, description]), including subcommands if appropriate
|
30
|
+
command_info_list = []
|
31
|
+
commands.each do |command|
|
32
|
+
name = [command.name, Array(command.aliases)].flatten.join(', ')
|
33
|
+
command_info_list << [name, command.description]
|
34
|
+
if command.commands.any?
|
35
|
+
@sorter.call(command.commands_declaration_order).each do |cmd|
|
36
|
+
command_info_list << [SUB_CMD_INDENT + command.name.to_s + " " + cmd.names, cmd.description + (command.get_default_command == cmd.name ? " (default)" : "")]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# display
|
42
|
+
command_formatter = ListFormatter.new(command_info_list, @wrapper_class)
|
43
|
+
stringio = StringIO.new
|
44
|
+
command_formatter.output(stringio)
|
45
|
+
commands = stringio.string
|
46
|
+
global_option_descriptions = OptionsFormatter.new(global_flags_and_switches, @sorter, @wrapper_class).format
|
47
|
+
GLOBAL_HELP.result(binding)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'gli/commands/help_modules/arg_name_formatter'
|
3
|
+
|
4
|
+
#
|
5
|
+
# adaption of RdocDocumentListener to use Markdown
|
6
|
+
# see http://rtomayko.github.com/ronn/ronn-format.7
|
7
|
+
#
|
8
|
+
|
9
|
+
module GLI
|
10
|
+
module Commands
|
11
|
+
class MarkdownDocumentListener
|
12
|
+
|
13
|
+
def initialize(global_options,options,arguments)
|
14
|
+
@io = STDOUT #File.new(File.basename($0) + ".rdoc",'w')
|
15
|
+
@nest = ''
|
16
|
+
@arg_name_formatter = GLI::Commands::HelpModules::ArgNameFormatter.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def beginning
|
20
|
+
end
|
21
|
+
|
22
|
+
# Called when processing has completed
|
23
|
+
def ending
|
24
|
+
#@io.close
|
25
|
+
end
|
26
|
+
|
27
|
+
# Gives you the program description
|
28
|
+
def program_desc(desc)
|
29
|
+
@io.puts "== #{File.basename($0)} - #{desc}"
|
30
|
+
@io.puts
|
31
|
+
end
|
32
|
+
|
33
|
+
def program_long_desc(desc)
|
34
|
+
@io.puts desc
|
35
|
+
@io.puts
|
36
|
+
end
|
37
|
+
|
38
|
+
# Gives you the program version
|
39
|
+
def version(version)
|
40
|
+
@io.puts "v#{version}"
|
41
|
+
@io.puts
|
42
|
+
end
|
43
|
+
|
44
|
+
def options
|
45
|
+
if @nest.size == 0
|
46
|
+
@io.puts "=== Global Options"
|
47
|
+
else
|
48
|
+
@io.puts "#{@nest}=== Options"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Gives you a flag in the current context
|
53
|
+
def flag(name,aliases,desc,long_desc,default_value,arg_name,must_match,type)
|
54
|
+
invocations = ([name] + Array(aliases)).map { |_| add_dashes(_) }.join('|')
|
55
|
+
usage = "#{invocations} #{arg_name || 'arg'}"
|
56
|
+
@io.puts "#{@nest}=== #{usage}"
|
57
|
+
@io.puts
|
58
|
+
@io.puts String(desc).strip
|
59
|
+
@io.puts
|
60
|
+
@io.puts "[Default Value] #{default_value || 'None'}"
|
61
|
+
@io.puts "[Must Match] #{must_match.to_s}" unless must_match.nil?
|
62
|
+
@io.puts String(long_desc).strip
|
63
|
+
@io.puts
|
64
|
+
end
|
65
|
+
|
66
|
+
# Gives you a switch in the current context
|
67
|
+
def switch(name,aliases,desc,long_desc,negetable)
|
68
|
+
if negetable
|
69
|
+
name = "[no-]#{name}" if name.to_s.length > 1
|
70
|
+
aliases = aliases.map { |_| _.to_s.length > 1 ? "[no-]#{_}" : _ }
|
71
|
+
end
|
72
|
+
invocations = ([name] + aliases).map { |_| add_dashes(_) }.join('|')
|
73
|
+
@io.puts "#{@nest}=== #{invocations}"
|
74
|
+
@io.puts String(desc).strip
|
75
|
+
@io.puts
|
76
|
+
@io.puts String(long_desc).strip
|
77
|
+
@io.puts
|
78
|
+
end
|
79
|
+
|
80
|
+
def end_options
|
81
|
+
end
|
82
|
+
|
83
|
+
def commands
|
84
|
+
@io.puts "#{@nest}=== Commands"
|
85
|
+
@nest = "#{@nest}="
|
86
|
+
end
|
87
|
+
|
88
|
+
# Gives you a command in the current context and creates a new context of this command
|
89
|
+
def command(name,aliases,desc,long_desc,arg_name,arg_options)
|
90
|
+
@io.puts "#{@nest}=== Command: <tt>#{([name] + aliases).join('|')} #{@arg_name_formatter.format(arg_name,arg_options)}</tt>"
|
91
|
+
@io.puts String(desc).strip
|
92
|
+
@io.puts
|
93
|
+
@io.puts String(long_desc).strip
|
94
|
+
@nest = "#{@nest}="
|
95
|
+
end
|
96
|
+
|
97
|
+
# Ends a command, and "pops" you back up one context
|
98
|
+
def end_command(name)
|
99
|
+
@nest.gsub!(/=$/,'')
|
100
|
+
end
|
101
|
+
|
102
|
+
# Gives you the name of the current command in the current context
|
103
|
+
def default_command(name)
|
104
|
+
@io.puts "[Default Command] #{name}" unless name.nil?
|
105
|
+
end
|
106
|
+
|
107
|
+
def end_commands
|
108
|
+
@nest.gsub!(/=$/,'')
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def add_dashes(name)
|
114
|
+
name = "-#{name}"
|
115
|
+
name = "-#{name}" if name.length > 2
|
116
|
+
name
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|