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
data/lib/leap_cli/log.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'paint'
|
2
|
+
|
3
|
+
##
|
4
|
+
## LOGGING
|
5
|
+
##
|
6
|
+
## Ugh. This class does not work well with multiple threads!
|
7
|
+
##
|
8
|
+
|
9
|
+
module LeapCli
|
10
|
+
extend self
|
11
|
+
|
12
|
+
# logging options
|
13
|
+
def log_level
|
14
|
+
@log_level ||= 1
|
15
|
+
end
|
16
|
+
def log_level=(value)
|
17
|
+
@log_level = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def indent_level
|
21
|
+
@indent_level ||= 0
|
22
|
+
end
|
23
|
+
def indent_level=(value)
|
24
|
+
@indent_level = value
|
25
|
+
end
|
26
|
+
|
27
|
+
def log_file
|
28
|
+
@log_file
|
29
|
+
end
|
30
|
+
def log_file=(value)
|
31
|
+
@log_file = value
|
32
|
+
if @log_file
|
33
|
+
if !File.directory?(File.dirname(@log_file))
|
34
|
+
Util.bail!('Invalid log file "%s", directory "%s" does not exist' % [@log_file, File.dirname(@log_file)])
|
35
|
+
end
|
36
|
+
@log_output_stream = File.open(@log_file, 'a')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def log_output_stream
|
41
|
+
@log_output_stream
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
module LeapCli
|
48
|
+
module Log
|
49
|
+
#
|
50
|
+
# these are log titles typically associated with files
|
51
|
+
#
|
52
|
+
FILE_TITLES = [:updated, :created, :removed, :missing, :nochange, :loading]
|
53
|
+
|
54
|
+
|
55
|
+
#
|
56
|
+
# master logging function.
|
57
|
+
#
|
58
|
+
# arguments can be a String, Integer, Symbol, or Hash, in any order.
|
59
|
+
#
|
60
|
+
# * String: treated as the message to log.
|
61
|
+
# * Integer: the log level (0, 1, 2)
|
62
|
+
# * Symbol: the prefix title to colorize. may be one of
|
63
|
+
# [:error, :warning, :info, :updated, :created, :removed, :no_change, :missing]
|
64
|
+
# * Hash: a hash of options. so far, only :indent is supported.
|
65
|
+
#
|
66
|
+
|
67
|
+
def log(*args)
|
68
|
+
level = args.grep(Integer).first || 1
|
69
|
+
title = args.grep(Symbol).first
|
70
|
+
message = args.grep(String).first
|
71
|
+
options = args.grep(Hash).first || {}
|
72
|
+
unless message && LeapCli.log_level >= level
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
# prefix
|
77
|
+
clear_prefix = colored_prefix = ""
|
78
|
+
if title
|
79
|
+
prefix_options = case title
|
80
|
+
when :error then ['error', :red, :bold]
|
81
|
+
when :warning then ['warning:', :yellow, :bold]
|
82
|
+
when :info then ['info', :cyan, :bold]
|
83
|
+
when :updated then ['updated', :cyan, :bold]
|
84
|
+
when :updating then ['updating', :cyan, :bold]
|
85
|
+
when :created then ['created', :green, :bold]
|
86
|
+
when :removed then ['removed', :red, :bold]
|
87
|
+
when :nochange then ['no change', :magenta]
|
88
|
+
when :loading then ['loading', :magenta]
|
89
|
+
when :missing then ['missing', :yellow, :bold]
|
90
|
+
when :skipping then ['skipping', :yellow, :bold]
|
91
|
+
when :run then ['run', :magenta]
|
92
|
+
when :failed then ['FAILED', :red, :bold]
|
93
|
+
when :completed then ['completed', :green, :bold]
|
94
|
+
when :ran then ['ran', :green, :bold]
|
95
|
+
when :bail then ['bailing out', :red, :bold]
|
96
|
+
when :invalid then ['invalid', :red, :bold]
|
97
|
+
else [title.to_s, :cyan, :bold]
|
98
|
+
end
|
99
|
+
if options[:host]
|
100
|
+
clear_prefix = "[%s] %s " % [options[:host], prefix_options[0]]
|
101
|
+
colored_prefix = "[%s] %s " % [Paint[options[:host], prefix_options[1], prefix_options[2]], prefix_options[0]]
|
102
|
+
else
|
103
|
+
clear_prefix = "%s " % prefix_options[0]
|
104
|
+
colored_prefix = "%s " % Paint[prefix_options[0], prefix_options[1], prefix_options[2]]
|
105
|
+
end
|
106
|
+
elsif options[:host]
|
107
|
+
clear_prefix = colored_prefix = "[%s] " % options[:host]
|
108
|
+
end
|
109
|
+
|
110
|
+
# transform absolute path names
|
111
|
+
if title && FILE_TITLES.include?(title) && message =~ /^\//
|
112
|
+
message = LeapCli::Path.relative_path(message)
|
113
|
+
end
|
114
|
+
|
115
|
+
log_raw(:log, nil) { [clear_prefix, message].join }
|
116
|
+
log_raw(:stdout, options[:indent]) { [colored_prefix, message].join }
|
117
|
+
|
118
|
+
# run block, if given
|
119
|
+
if block_given?
|
120
|
+
LeapCli.indent_level += 1
|
121
|
+
yield
|
122
|
+
LeapCli.indent_level -= 1
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Add a raw log entry, without any modifications (other than indent).
|
128
|
+
# Content to be logged is yielded by the block.
|
129
|
+
# Block may be either a string or array of strings.
|
130
|
+
#
|
131
|
+
# if mode == :stdout, output is sent to STDOUT.
|
132
|
+
# if mode == :log, output is sent to log file, if present.
|
133
|
+
#
|
134
|
+
def log_raw(mode, indent=nil, &block)
|
135
|
+
# NOTE: print message (using 'print' produces better results than 'puts' when multiple threads are logging)
|
136
|
+
if mode == :log
|
137
|
+
if LeapCli.log_output_stream
|
138
|
+
messages = [yield].compact.flatten
|
139
|
+
if messages.any?
|
140
|
+
timestamp = Time.now.strftime("%b %d %H:%M:%S")
|
141
|
+
messages.each do |message|
|
142
|
+
LeapCli.log_output_stream.print("#{timestamp} #{message}\n")
|
143
|
+
end
|
144
|
+
LeapCli.log_output_stream.flush
|
145
|
+
end
|
146
|
+
end
|
147
|
+
elsif mode == :stdout
|
148
|
+
messages = [yield].compact.flatten
|
149
|
+
if messages.any?
|
150
|
+
indent ||= LeapCli.indent_level
|
151
|
+
indent_str = ""
|
152
|
+
indent_str += " " * indent.to_i
|
153
|
+
if indent.to_i > 0
|
154
|
+
indent_str += ' - '
|
155
|
+
else
|
156
|
+
indent_str += ' = '
|
157
|
+
end
|
158
|
+
messages.each do |message|
|
159
|
+
STDOUT.print("#{indent_str}#{message}\n")
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
#
|
2
|
+
# A drop in replacement for Capistrano::Logger that integrates better with LEAP CLI.
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'capistrano/logger'
|
6
|
+
|
7
|
+
#
|
8
|
+
# from Capistrano::Logger
|
9
|
+
# =========================
|
10
|
+
#
|
11
|
+
# IMPORTANT = 0
|
12
|
+
# INFO = 1
|
13
|
+
# DEBUG = 2
|
14
|
+
# TRACE = 3
|
15
|
+
# MAX_LEVEL = 3
|
16
|
+
# COLORS = {
|
17
|
+
# :none => "0",
|
18
|
+
# :black => "30",
|
19
|
+
# :red => "31",
|
20
|
+
# :green => "32",
|
21
|
+
# :yellow => "33",
|
22
|
+
# :blue => "34",
|
23
|
+
# :magenta => "35",
|
24
|
+
# :cyan => "36",
|
25
|
+
# :white => "37"
|
26
|
+
# }
|
27
|
+
# STYLES = {
|
28
|
+
# :bright => 1,
|
29
|
+
# :dim => 2,
|
30
|
+
# :underscore => 4,
|
31
|
+
# :blink => 5,
|
32
|
+
# :reverse => 7,
|
33
|
+
# :hidden => 8
|
34
|
+
# }
|
35
|
+
#
|
36
|
+
|
37
|
+
module LeapCli
|
38
|
+
class Logger < Capistrano::Logger
|
39
|
+
|
40
|
+
def initialize(options={})
|
41
|
+
@options = options
|
42
|
+
@level = options[:level] || 0
|
43
|
+
@message_buffer = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def log(level, message, line_prefix=nil, options={})
|
47
|
+
if message !~ /\n$/ && level <= 2 && line_prefix.is_a?(String)
|
48
|
+
# in some cases, when the message doesn't end with a return, we buffer it and
|
49
|
+
# wait until we encounter the return before we log the message out.
|
50
|
+
@message_buffer ||= ""
|
51
|
+
@message_buffer += message
|
52
|
+
return
|
53
|
+
elsif @message_buffer
|
54
|
+
message = @message_buffer + message
|
55
|
+
@message_buffer = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
options[:level] ||= level
|
59
|
+
[:stdout, :log].each do |mode|
|
60
|
+
LeapCli::log_raw(mode) do
|
61
|
+
message_lines(mode, message, line_prefix, options)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def message_lines(mode, message, line_prefix, options)
|
69
|
+
formatted_message, formatted_prefix, message_options = apply_formatting(mode, message, line_prefix, options)
|
70
|
+
if message_options[:level] <= self.level && formatted_message && formatted_message.chars.any?
|
71
|
+
if formatted_prefix
|
72
|
+
formatted_message.lines.collect { |line|
|
73
|
+
"[#{formatted_prefix}] #{line.sub(/\s+$/, '')}"
|
74
|
+
}
|
75
|
+
else
|
76
|
+
formatted_message.lines.collect {|line| line.sub(/\s+$/, '')}
|
77
|
+
end
|
78
|
+
else
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
## FORMATTING
|
85
|
+
##
|
86
|
+
|
87
|
+
@formatters = [
|
88
|
+
# TRACE
|
89
|
+
{ :match => /command finished/, :color => :white, :style => :dim, :match_level => 3, :priority => -10 },
|
90
|
+
{ :match => /executing locally/, :color => :yellow, :match_level => 3, :priority => -20 },
|
91
|
+
|
92
|
+
# DEBUG
|
93
|
+
#{ :match => /executing .*/, :color => :green, :match_level => 2, :priority => -10, :timestamp => true },
|
94
|
+
#{ :match => /.*/, :color => :yellow, :match_level => 2, :priority => -30 },
|
95
|
+
{ :match => /^transaction:/, :level => 3 },
|
96
|
+
|
97
|
+
# INFO
|
98
|
+
{ :match => /.*out\] (fatal:|ERROR:).*/, :color => :red, :match_level => 1, :priority => -10 },
|
99
|
+
{ :match => /Permission denied/, :color => :red, :match_level => 1, :priority => -20 },
|
100
|
+
{ :match => /sh: .+: command not found/, :color => :magenta, :match_level => 1, :priority => -30 },
|
101
|
+
|
102
|
+
# IMPORTANT
|
103
|
+
{ :match => /^err ::/, :color => :red, :match_level => 0, :priority => -10 },
|
104
|
+
{ :match => /^ERROR:/, :color => :red, :match_level => 0, :priority => -10 },
|
105
|
+
{ :match => /.*/, :color => :blue, :match_level => 0, :priority => -20 },
|
106
|
+
|
107
|
+
# CLEANUP
|
108
|
+
{ :match => /\s+$/, :replace => '', :priority => 0},
|
109
|
+
|
110
|
+
# DEBIAN PACKAGES
|
111
|
+
{ :match => /^(Hit|Ign) /, :color => :green, :priority => -20},
|
112
|
+
{ :match => /^Err /, :color => :red, :priority => -20},
|
113
|
+
{ :match => /^W(ARNING)?: /, :color => :yellow, :priority => -20},
|
114
|
+
{ :match => /^E: /, :color => :red, :priority => -20},
|
115
|
+
{ :match => /already the newest version/, :color => :green, :priority => -20},
|
116
|
+
{ :match => /WARNING: The following packages cannot be authenticated!/, :color => :red, :level => 0, :priority => -10},
|
117
|
+
|
118
|
+
# PUPPET
|
119
|
+
{ :match => /^warning: .*is deprecated.*$/, :level => 2, :color => :yellow, :priority => -10},
|
120
|
+
{ :match => /^warning: Scope.*$/, :level => 2, :color => :yellow, :priority => -10},
|
121
|
+
{ :match => /^notice:/, :level => 1, :color => :cyan, :priority => -20},
|
122
|
+
{ :match => /^notice:.*executed successfully$/, :level => 2, :color => :cyan, :priority => -15},
|
123
|
+
{ :match => /^warning:/, :level => 0, :color => :yellow, :priority => -20},
|
124
|
+
{ :match => /^Duplicate declaration:/, :level => 0, :color => :red, :priority => -20},
|
125
|
+
{ :match => /Finished catalog run/, :level => 0, :color => :green, :priority => -10},
|
126
|
+
{ :match => /^Puppet apply complete \(changes made\)/, :level => 0, :color => :green, :priority => -10},
|
127
|
+
{ :match => /^Puppet apply complete \(no changes\)/, :level => 0, :color => :green, :priority => -10},
|
128
|
+
|
129
|
+
# PUPPET FATAL ERRORS
|
130
|
+
{ :match => /^err:/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
131
|
+
{ :match => /^Failed to parse template/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
132
|
+
{ :match => /^Parameter matches failed:/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
133
|
+
{ :match => /^Syntax error/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
134
|
+
{ :match => /^Cannot reassign variable/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
135
|
+
{ :match => /^Could not find template/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
136
|
+
{ :match => /^Puppet apply complete.*fail/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
137
|
+
|
138
|
+
# TESTS
|
139
|
+
{ :match => /^PASS: /, :color => :green, :priority => -20},
|
140
|
+
{ :match => /^(FAIL|ERROR): /, :color => :red, :priority => -20},
|
141
|
+
{ :match => /^SKIP: /, :color => :yellow, :priority => -20}
|
142
|
+
|
143
|
+
]
|
144
|
+
|
145
|
+
def self.sorted_formatters
|
146
|
+
# Sort matchers in reverse order so we can break if we found a match.
|
147
|
+
@sorted_formatters ||= @formatters.sort_by { |i| -(i[:priority] || i[:prio] || 0) }
|
148
|
+
end
|
149
|
+
|
150
|
+
@prefix_formatters = [
|
151
|
+
{ :match => /(err|out) :: /, :replace => '', :priority => 0},
|
152
|
+
{ :match => /\s+$/, :replace => '', :priority => 0}
|
153
|
+
]
|
154
|
+
def self.prefix_formatters; @prefix_formatters; end
|
155
|
+
|
156
|
+
def apply_formatting(mode, message, line_prefix = nil, options={})
|
157
|
+
message = message.dup
|
158
|
+
options = options.dup
|
159
|
+
if !line_prefix.nil?
|
160
|
+
if !line_prefix.is_a?(String)
|
161
|
+
line_prefix = line_prefix.to_s.dup
|
162
|
+
else
|
163
|
+
line_prefix = line_prefix.dup
|
164
|
+
end
|
165
|
+
end
|
166
|
+
color = options[:color] || :none
|
167
|
+
style = options[:style]
|
168
|
+
|
169
|
+
if line_prefix
|
170
|
+
self.class.prefix_formatters.each do |formatter|
|
171
|
+
if line_prefix =~ formatter[:match] && formatter[:replace]
|
172
|
+
line_prefix.gsub!(formatter[:match], formatter[:replace])
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
self.class.sorted_formatters.each do |formatter|
|
178
|
+
if (formatter[:match_level] == level || formatter[:match_level].nil?)
|
179
|
+
if message =~ formatter[:match]
|
180
|
+
options[:level] = formatter[:level] if formatter[:level]
|
181
|
+
color = formatter[:color] if formatter[:color]
|
182
|
+
style = formatter[:style] || formatter[:attribute] # (support original cap colors)
|
183
|
+
|
184
|
+
message.gsub!(formatter[:match], formatter[:replace]) if formatter[:replace]
|
185
|
+
message.replace(formatter[:prepend] + message) unless formatter[:prepend].nil?
|
186
|
+
message.replace(message + formatter[:append]) unless formatter[:append].nil?
|
187
|
+
message.replace(Time.now.strftime('%Y-%m-%d %T') + ' ' + message) if formatter[:timestamp]
|
188
|
+
|
189
|
+
if formatter[:exit]
|
190
|
+
LeapCli::Util.exit_status(formatter[:exit])
|
191
|
+
end
|
192
|
+
|
193
|
+
# stop formatting, unless formatter was just for string replacement
|
194
|
+
break unless formatter[:replace]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
if color == :hide
|
200
|
+
return nil
|
201
|
+
elsif mode == :log || (color == :none && style.nil?)
|
202
|
+
return [message, line_prefix, options]
|
203
|
+
else
|
204
|
+
term_color = COLORS[color]
|
205
|
+
term_style = STYLES[style]
|
206
|
+
if line_prefix.nil?
|
207
|
+
message.replace format(message, term_color, term_style)
|
208
|
+
else
|
209
|
+
line_prefix.replace format(line_prefix, term_color, term_style).strip # format() appends a \n
|
210
|
+
end
|
211
|
+
return [message, line_prefix, options]
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
#
|
2
|
+
# A class to generate a markdown file with all the information available with the
|
3
|
+
# help subcommand.
|
4
|
+
#
|
5
|
+
# This is adapted from GLI::Commands::RdocDocumentListener
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'stringio'
|
9
|
+
require 'gli/commands/help_modules/arg_name_formatter'
|
10
|
+
|
11
|
+
module LeapCli
|
12
|
+
class MarkdownDocumentListener
|
13
|
+
|
14
|
+
def initialize(global_options,options,arguments)
|
15
|
+
@io = File.new(File.basename($0) + ".md",'w')
|
16
|
+
@nest = ''
|
17
|
+
@commands = [File.basename($0)]
|
18
|
+
@arg_name_formatter = GLI::Commands::HelpModules::ArgNameFormatter.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def beginning
|
22
|
+
end
|
23
|
+
|
24
|
+
# Called when processing has completed
|
25
|
+
def ending
|
26
|
+
@io.close
|
27
|
+
end
|
28
|
+
|
29
|
+
# Gives you the program description
|
30
|
+
def program_desc(desc)
|
31
|
+
@io.puts "@title = 'Command Line Reference'"
|
32
|
+
#@io.puts "# #{File.basename($0)} - #{desc}"
|
33
|
+
@io.puts
|
34
|
+
end
|
35
|
+
|
36
|
+
def program_long_desc(desc)
|
37
|
+
@io.puts desc
|
38
|
+
@io.puts
|
39
|
+
end
|
40
|
+
|
41
|
+
# Gives you the program version
|
42
|
+
def version(version)
|
43
|
+
#@io.puts "v#{version}"
|
44
|
+
#@io.puts
|
45
|
+
end
|
46
|
+
|
47
|
+
def options
|
48
|
+
#@io.puts "<div class='options'>"
|
49
|
+
@io.puts
|
50
|
+
if @nest.size == 0
|
51
|
+
@io.puts "# Global Options"
|
52
|
+
else
|
53
|
+
#@io.puts "#{@nest}# Options"
|
54
|
+
@io.puts "**Options**"
|
55
|
+
end
|
56
|
+
@io.puts
|
57
|
+
end
|
58
|
+
|
59
|
+
# Gives you a flag in the current context
|
60
|
+
def flag(name,aliases,desc,long_desc,default_value,arg_name,must_match,type)
|
61
|
+
invocations = ([name] + Array(aliases)).map { |_| add_dashes(_) }.join('|')
|
62
|
+
usage = "#{invocations} #{arg_name || 'arg'}"
|
63
|
+
#@io.puts "#{@nest}## #{usage}"
|
64
|
+
@io.puts "* `#{usage}` "
|
65
|
+
@io.puts String(desc).strip + " "
|
66
|
+
@io.puts String(long_desc).strip + " " if long_desc
|
67
|
+
@io.puts "Default Value: #{default_value || 'None'} "
|
68
|
+
@io.puts "Must Match: #{must_match.to_s} " unless must_match.nil?
|
69
|
+
@io.puts
|
70
|
+
end
|
71
|
+
|
72
|
+
# Gives you a switch in the current context
|
73
|
+
def switch(name,aliases,desc,long_desc,negetable)
|
74
|
+
if negetable
|
75
|
+
name = "[no-]#{name}" if name.to_s.length > 1
|
76
|
+
aliases = aliases.map { |_| _.to_s.length > 1 ? "[no-]#{_}" : _ }
|
77
|
+
end
|
78
|
+
invocations = ([name] + aliases).map { |_| add_dashes(_) }.join('|')
|
79
|
+
#@io.puts "#{@nest}## #{invocations}"
|
80
|
+
@io.puts "* `#{invocations}` "
|
81
|
+
@io.puts String(desc).strip + " "
|
82
|
+
#@io.puts
|
83
|
+
#@io.puts String(long_desc).strip
|
84
|
+
@io.puts
|
85
|
+
end
|
86
|
+
|
87
|
+
def end_options
|
88
|
+
#@io.puts "</div>"
|
89
|
+
end
|
90
|
+
|
91
|
+
def commands
|
92
|
+
#@io.puts "#{@nest}## Commands"
|
93
|
+
#@nest = "#{@nest}#"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Gives you a command in the current context and creates a new context of this command
|
97
|
+
def command(name,aliases,desc,long_desc,arg_name,arg_options)
|
98
|
+
@commands.push(name)
|
99
|
+
#@io.puts "#{@nest}## Command: <tt>#{([name] + aliases).join('|')} #{@arg_name_formatter.format(arg_name,arg_options)}</tt>"
|
100
|
+
@io.puts
|
101
|
+
@io.puts "#{@nest}# #{@commands.join ' '} #{@arg_name_formatter.format(arg_name,arg_options)}"
|
102
|
+
@io.puts
|
103
|
+
@io.puts String(desc).strip
|
104
|
+
@io.puts
|
105
|
+
@io.puts String(long_desc).strip
|
106
|
+
@nest = "#{@nest}#"
|
107
|
+
end
|
108
|
+
|
109
|
+
# Ends a command, and "pops" you back up one context
|
110
|
+
def end_command(name)
|
111
|
+
@nest.gsub!(/\#$/,'')
|
112
|
+
@commands.pop
|
113
|
+
end
|
114
|
+
|
115
|
+
# Gives you the name of the current command in the current context
|
116
|
+
def default_command(name)
|
117
|
+
@io.puts "Default Command: #{name}" unless name.nil?
|
118
|
+
end
|
119
|
+
|
120
|
+
def end_commands
|
121
|
+
@nest.gsub!(/\#$/,'')
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def add_dashes(name)
|
127
|
+
name = "-#{name}"
|
128
|
+
name = "-#{name}" if name.length > 2
|
129
|
+
name
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module LeapCli; module Path
|
4
|
+
|
5
|
+
def self.platform
|
6
|
+
@platform
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.provider_base
|
10
|
+
"#{platform}/provider_base"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.provider_templates
|
14
|
+
"#{platform}/provider_templates"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.provider
|
18
|
+
@provider
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.set_provider_path(provider)
|
22
|
+
@provider = provider
|
23
|
+
end
|
24
|
+
def self.set_platform_path(platform)
|
25
|
+
@platform = platform
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# tries to find a file somewhere
|
30
|
+
#
|
31
|
+
def self.find_file(arg)
|
32
|
+
[Path.provider, Path.provider_base].each do |base|
|
33
|
+
file_path = named_path(arg, base)
|
34
|
+
return file_path if File.exists?(file_path)
|
35
|
+
if arg.is_a? String
|
36
|
+
file_path = base + '/files/' + arg
|
37
|
+
return file_path if File.exists?(file_path)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
return nil
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Three ways of calling:
|
45
|
+
#
|
46
|
+
# - named_path [:user_ssh, 'bob'] ==> 'users/bob/bob_ssh.pub'
|
47
|
+
# - named_path :known_hosts ==> 'files/ssh/known_hosts'
|
48
|
+
# - named_path '/tmp/x' ==> '/tmp/x'
|
49
|
+
#
|
50
|
+
def self.named_path(name, provider_dir=Path.provider)
|
51
|
+
if name.is_a? Array
|
52
|
+
if name.length > 2
|
53
|
+
arg = name[1..-1]
|
54
|
+
name = name[0]
|
55
|
+
else
|
56
|
+
name, arg = name
|
57
|
+
end
|
58
|
+
else
|
59
|
+
arg = nil
|
60
|
+
end
|
61
|
+
|
62
|
+
if name.is_a? Symbol
|
63
|
+
Util::assert!(Leap::Platform.paths[name], "Error, I don't know the path for :#{name} (with argument '#{arg}')")
|
64
|
+
filename = eval('"' + Leap::Platform.paths[name] + '"')
|
65
|
+
return provider_dir + '/' + filename
|
66
|
+
else
|
67
|
+
return name
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.exists?(name, provider_dir=nil)
|
72
|
+
File.exists?(named_path(name, provider_dir))
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.relative_path(path, provider_dir=Path.provider)
|
76
|
+
if provider_dir
|
77
|
+
path = named_path(path, provider_dir)
|
78
|
+
path.sub(/^#{Regexp.escape(provider_dir)}\//,'')
|
79
|
+
else
|
80
|
+
path
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end; end
|