rbcli 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -1
- data/README.md +50 -6
- data/exe/rbcli +81 -32
- data/lib/rbcli/configuration/config.rb +6 -6
- data/lib/rbcli/configuration/configurate.rb +1 -1
- data/lib/rbcli/engine/load_project.rb +19 -0
- data/lib/rbcli/engine/parser.rb +2 -2
- data/lib/rbcli/logging/logging.rb +7 -4
- data/lib/rbcli/stateful_systems/state_storage.rb +4 -0
- data/lib/rbcli/version.rb +1 -1
- data/lib/rbcli-tool/erb_renderer.rb +43 -0
- data/lib/rbcli-tool/mdless_fix.rb +386 -0
- data/lib/rbcli-tool/project.rb +87 -0
- data/lib/rbcli-tool.rb +16 -0
- data/lib/rbcli.rb +11 -10
- data/rbcli.gemspec +1 -0
- data/skeletons/micro/executable +123 -0
- data/skeletons/mini/executable +241 -0
- data/skeletons/project/.gitignore +11 -0
- data/skeletons/project/.rbcli +0 -0
- data/skeletons/project/.rspec +3 -0
- data/skeletons/project/CODE_OF_CONDUCT.md +74 -0
- data/skeletons/project/Gemfile +6 -0
- data/skeletons/project/README.md +31 -0
- data/skeletons/project/Rakefile +6 -0
- data/skeletons/project/application/commands/command.rb +31 -0
- data/skeletons/project/application/commands/scripts/script.sh +23 -0
- data/skeletons/project/application/options.rb +30 -0
- data/skeletons/project/config/autoupdate.rb +32 -0
- data/skeletons/project/config/logging.rb +19 -0
- data/skeletons/project/config/storage.rb +34 -0
- data/skeletons/project/config/userspace.rb +28 -0
- data/skeletons/project/config/version.rb +3 -0
- data/skeletons/project/default_user_configs/user_defaults.yml +6 -0
- data/skeletons/project/exe/executable +54 -0
- data/skeletons/project/hooks/default_action.rb +16 -0
- data/skeletons/project/hooks/first_run.rb +16 -0
- data/skeletons/project/hooks/post_execution.rb +14 -0
- data/skeletons/project/hooks/pre_execution.rb +14 -0
- data/skeletons/project/spec/spec_helper.rb +14 -0
- data/skeletons/project/spec/untitled_spec.rb +9 -0
- data/skeletons/project/untitled.gemspec +40 -0
- metadata +47 -2
@@ -0,0 +1,386 @@
|
|
1
|
+
require 'mdless'
|
2
|
+
|
3
|
+
def class_exists?(class_name)
|
4
|
+
klass = Module.const_get(class_name)
|
5
|
+
return klass.is_a?(Class)
|
6
|
+
rescue NameError
|
7
|
+
return false
|
8
|
+
end
|
9
|
+
|
10
|
+
if class_exists? 'Encoding'
|
11
|
+
Encoding.default_external = Encoding::UTF_8 if Encoding.respond_to?('default_external')
|
12
|
+
Encoding.default_internal = Encoding::UTF_8 if Encoding.respond_to?('default_internal')
|
13
|
+
end
|
14
|
+
|
15
|
+
module CLIMarkdown
|
16
|
+
class Converter
|
17
|
+
def convert_markdown(input)
|
18
|
+
@headers = get_headers(input)
|
19
|
+
# yaml/MMD headers
|
20
|
+
in_yaml = false
|
21
|
+
if input.split("\n")[0] =~ /(?i-m)^---[ \t]*?(\n|$)/
|
22
|
+
@log.info("Found YAML")
|
23
|
+
# YAML
|
24
|
+
in_yaml = true
|
25
|
+
input.sub!(/(?i-m)^---[ \t]*\n([\s\S]*?)\n[\-.]{3}[ \t]*\n/) do |yaml|
|
26
|
+
m = Regexp.last_match
|
27
|
+
|
28
|
+
@log.warn("Processing YAML Header")
|
29
|
+
m[0].split(/\n/).map {|line|
|
30
|
+
if line =~ /^[\-.]{3}\s*$/
|
31
|
+
line = c([:d,:black,:on_black]) + "% " + c([:d,:black,:on_black]) + line
|
32
|
+
else
|
33
|
+
line.sub!(/^(.*?:)[ \t]+(\S)/, '\1 \2')
|
34
|
+
line = c([:d,:black,:on_black]) + "% " + c([:d,:white]) + line
|
35
|
+
end
|
36
|
+
if @cols - line.uncolor.size > 0
|
37
|
+
line += " "*(@cols-line.uncolor.size)
|
38
|
+
end
|
39
|
+
}.join("\n") + "#{xc}\n"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
if !in_yaml && input.gsub(/\n/,' ') =~ /(?i-m)^\w.+:\s+\S+ /
|
44
|
+
@log.info("Found MMD Headers")
|
45
|
+
input.sub!(/(?i-m)^([\S ]+:[\s\S]*?)+(?=\n\n)/) do |mmd|
|
46
|
+
puts mmd
|
47
|
+
mmd.split(/\n/).map {|line|
|
48
|
+
line.sub!(/^(.*?:)[ \t]+(\S)/, '\1 \2')
|
49
|
+
line = c([:d,:black,:on_black]) + "% " + c([:d,:white,:on_black]) + line
|
50
|
+
if @cols - line.uncolor.size > 0
|
51
|
+
line += " "*(@cols - line.uncolor.size)
|
52
|
+
end
|
53
|
+
}.join("\n") + " "*@cols + "#{xc}\n"
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
# Gather reference links
|
60
|
+
input.gsub!(/^\s{,3}(?<![\e*])\[\b(.+)\b\]: +(.+)/) do |m|
|
61
|
+
match = Regexp.last_match
|
62
|
+
@ref_links[match[1]] = match[2]
|
63
|
+
''
|
64
|
+
end
|
65
|
+
|
66
|
+
# Gather footnotes (non-inline)
|
67
|
+
input.gsub!(/^ {,3}(?<!\*)(?:\e\[[\d;]+m)*\[(?:\e\[[\d;]+m)*\^(?:\e\[[\d;]+m)*\b(.+)\b(?:\e\[[\d;]+m)*\]: *(.*?)\n/) do |m|
|
68
|
+
match = Regexp.last_match
|
69
|
+
@footnotes[match[1].uncolor] = match[2].uncolor
|
70
|
+
''
|
71
|
+
end
|
72
|
+
|
73
|
+
if @options[:section]
|
74
|
+
in_section = false
|
75
|
+
top_level = 1
|
76
|
+
new_content = []
|
77
|
+
|
78
|
+
input.split(/\n/).each {|graf|
|
79
|
+
if graf =~ /^(#+) *(.*?)( *#+)?$/
|
80
|
+
level = $1.length
|
81
|
+
title = $2
|
82
|
+
|
83
|
+
if in_section
|
84
|
+
if level >= top_level
|
85
|
+
new_content.push(graf)
|
86
|
+
else
|
87
|
+
in_section = false
|
88
|
+
break
|
89
|
+
end
|
90
|
+
elsif title.downcase == "#{@headers[@options[:section] - 1][1].downcase}"
|
91
|
+
in_section = true
|
92
|
+
top_level = level + 1
|
93
|
+
new_content.push(graf)
|
94
|
+
else
|
95
|
+
next
|
96
|
+
end
|
97
|
+
elsif in_section
|
98
|
+
new_content.push(graf)
|
99
|
+
end
|
100
|
+
}
|
101
|
+
|
102
|
+
input = new_content.join("\n")
|
103
|
+
end
|
104
|
+
|
105
|
+
h_adjust = highest_header(input) - 1
|
106
|
+
input.gsub!(/^(#+)/) do |m|
|
107
|
+
match = Regexp.last_match
|
108
|
+
"#" * (match[1].length - h_adjust)
|
109
|
+
end
|
110
|
+
|
111
|
+
input.gsub!(/(?i-m)([`~]{3,})([\s\S]*?)\n([\s\S]*?)\1/ ) do |cb|
|
112
|
+
m = Regexp.last_match
|
113
|
+
leader = m[2] ? m[2].upcase + ":" : 'CODE:'
|
114
|
+
leader += xc
|
115
|
+
|
116
|
+
if exec_available('pygmentize')
|
117
|
+
lexer = m[2].nil? ? '-g' : "-l #{m[2]}"
|
118
|
+
begin
|
119
|
+
hilite, s = Open3.capture2(%Q{pygmentize #{lexer} 2> /dev/null}, :stdin_data=>m[3])
|
120
|
+
|
121
|
+
if s.success?
|
122
|
+
hilite = hilite.split(/\n/).map{|l| "#{c([:x,:black])}~ #{xc}" + l}.join("\n")
|
123
|
+
end
|
124
|
+
rescue => e
|
125
|
+
@log.error(e)
|
126
|
+
hilite = m[0]
|
127
|
+
end
|
128
|
+
|
129
|
+
else
|
130
|
+
|
131
|
+
hilite = m[3].split(/\n/).map{|l|
|
132
|
+
new_code_line = l.gsub(/\t/,' ')
|
133
|
+
orig_length = new_code_line.size + 3
|
134
|
+
new_code_line.gsub!(/ /,"#{c([:x,:white,:on_black])} ")
|
135
|
+
"#{c([:x,:black])}~ #{c([:x,:white,:on_black])} " + new_code_line + c([:x,:white,:on_black]) + xc
|
136
|
+
}.join("\n")
|
137
|
+
end
|
138
|
+
"#{c([:x,:magenta])}#{leader}\n#{hilite}#{xc}"
|
139
|
+
end
|
140
|
+
|
141
|
+
# remove empty links
|
142
|
+
input.gsub!(/\[(.*?)\]\(\s*?\)/, '\1')
|
143
|
+
input.gsub!(/\[(.*?)\]\[\]/, '[\1][\1]')
|
144
|
+
|
145
|
+
lines = input.split(/\n/)
|
146
|
+
|
147
|
+
# previous_indent = 0
|
148
|
+
|
149
|
+
lines.map!.with_index do |aLine, i|
|
150
|
+
line = aLine.dup
|
151
|
+
clean_line = line.dup.uncolor
|
152
|
+
|
153
|
+
|
154
|
+
if clean_line.uncolor =~ /(^[%~])/ # || clean_line.uncolor =~ /^( {4,}|\t+)/
|
155
|
+
## TODO: find indented code blocks and prevent highlighting
|
156
|
+
## Needs to miss block indented 1 level in lists
|
157
|
+
## Needs to catch lists in code
|
158
|
+
## Needs to avoid within fenced code blocks
|
159
|
+
# if line =~ /^([ \t]+)([^*-+]+)/
|
160
|
+
# indent = $1.gsub(/\t/, " ").size
|
161
|
+
# if indent >= previous_indent
|
162
|
+
# line = "~" + line
|
163
|
+
# end
|
164
|
+
# p [indent, previous_indent]
|
165
|
+
# previous_indent = indent
|
166
|
+
# end
|
167
|
+
else
|
168
|
+
# Headlines
|
169
|
+
line.gsub!(/^(#+) *(.*?)(\s*#+)?\s*$/) do |match|
|
170
|
+
m = Regexp.last_match
|
171
|
+
pad = ""
|
172
|
+
ansi = ''
|
173
|
+
case m[1].length
|
174
|
+
when 1
|
175
|
+
ansi = c([:b, :black, :on_intense_white])
|
176
|
+
pad = c([:b,:white])
|
177
|
+
pad += m[2].length + 2 > @cols ? "*"*m[2].length : "*"*(@cols - (m[2].length + 2))
|
178
|
+
when 2
|
179
|
+
ansi = c([:b, :green, :on_black])
|
180
|
+
pad = c([:b,:black])
|
181
|
+
pad += m[2].length + 2 > @cols ? "-"*m[2].length : "-"*(@cols - (m[2].length + 2))
|
182
|
+
when 3
|
183
|
+
ansi = c([:u, :b, :yellow])
|
184
|
+
when 4
|
185
|
+
ansi = c([:x, :u, :yellow])
|
186
|
+
else
|
187
|
+
ansi = c([:b, :white])
|
188
|
+
end
|
189
|
+
|
190
|
+
"\n#{xc}#{ansi}#{m[2]} #{pad}#{xc}\n"
|
191
|
+
end
|
192
|
+
|
193
|
+
# place footnotes under paragraphs that reference them
|
194
|
+
if line =~ /\[(?:\e\[[\d;]+m)*\^(?:\e\[[\d;]+m)*(\S+)(?:\e\[[\d;]+m)*\]/
|
195
|
+
key = $1.uncolor
|
196
|
+
if @footnotes.key? key
|
197
|
+
line += "\n\n#{c([:b,:black,:on_black])}[#{c([:b,:cyan,:on_black])}^#{c([:x,:yellow,:on_black])}#{key}#{c([:b,:black,:on_black])}]: #{c([:u,:white,:on_black])}#{@footnotes[key]}#{xc}"
|
198
|
+
@footnotes.delete(key)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# color footnote references
|
203
|
+
line.gsub!(/\[\^(\S+)\]/) do |m|
|
204
|
+
match = Regexp.last_match
|
205
|
+
last = find_color(match.pre_match, true)
|
206
|
+
counter = i
|
207
|
+
while last.nil? && counter > 0
|
208
|
+
counter -= 1
|
209
|
+
find_color(lines[counter])
|
210
|
+
end
|
211
|
+
"#{c([:b,:black])}[#{c([:b,:yellow])}^#{c([:x,:yellow])}#{match[1]}#{c([:b,:black])}]" + (last ? last : xc)
|
212
|
+
end
|
213
|
+
|
214
|
+
# blockquotes
|
215
|
+
line.gsub!(/^(\s*>)+( .*?)?$/) do |m|
|
216
|
+
match = Regexp.last_match
|
217
|
+
last = find_color(match.pre_match, true)
|
218
|
+
counter = i
|
219
|
+
while last.nil? && counter > 0
|
220
|
+
counter -= 1
|
221
|
+
find_color(lines[counter])
|
222
|
+
end
|
223
|
+
"#{c([:b,:black])}#{match[1]}#{c([:x,:magenta])} #{match[2]}" + (last ? last : xc)
|
224
|
+
end
|
225
|
+
|
226
|
+
# make reference links inline
|
227
|
+
line.gsub!(/(?<![\e*])\[(\b.*?\b)?\]\[(\b.+?\b)?\]/) do |m|
|
228
|
+
match = Regexp.last_match
|
229
|
+
title = match[2] || ''
|
230
|
+
text = match[1] || ''
|
231
|
+
if match[2] && @ref_links.key?(title.downcase)
|
232
|
+
"[#{text}](#{@ref_links[title]})"
|
233
|
+
elsif match[1] && @ref_links.key?(text.downcase)
|
234
|
+
"[#{text}](#{@ref_links[text]})"
|
235
|
+
else
|
236
|
+
if input.match(/^#+\s*#{Regexp.escape(text)}/i)
|
237
|
+
"[#{text}](##{text})"
|
238
|
+
else
|
239
|
+
match[1]
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# color inline links
|
245
|
+
line.gsub!(/(?<![\e*!])\[(\S.*?\S)\]\((\S.+?\S)\)/) do |m|
|
246
|
+
match = Regexp.last_match
|
247
|
+
color_link(match.pre_match, match[1], match[2])
|
248
|
+
end
|
249
|
+
|
250
|
+
|
251
|
+
|
252
|
+
# inline code
|
253
|
+
line.gsub!(/`(.*?)`/) do |m|
|
254
|
+
match = Regexp.last_match
|
255
|
+
last = find_color(match.pre_match, true)
|
256
|
+
"#{c([:b,:black])}`#{c([:b,:white])}#{match[1]}#{c([:b,:black])}`" + (last ? last : xc)
|
257
|
+
end
|
258
|
+
|
259
|
+
# horizontal rules
|
260
|
+
line.gsub!(/^ {,3}([\-*] ?){3,}$/) do |m|
|
261
|
+
c([:x,:black]) + '_'*@cols + xc
|
262
|
+
end
|
263
|
+
|
264
|
+
# bold, bold/italic
|
265
|
+
line.gsub!(/(^|\s)[\*_]{2,3}([^\*_\s][^\*_]+?[^\*_\s])[\*_]{2,3}/) do |m|
|
266
|
+
match = Regexp.last_match
|
267
|
+
last = find_color(match.pre_match, true)
|
268
|
+
counter = i
|
269
|
+
while last.nil? && counter > 0
|
270
|
+
counter -= 1
|
271
|
+
find_color(lines[counter])
|
272
|
+
end
|
273
|
+
"#{match[1]}#{c([:b])}#{match[2]}" + (last ? last : xc)
|
274
|
+
end
|
275
|
+
|
276
|
+
# italic
|
277
|
+
line.gsub!(/(^|\s)[\*_]([^\*_\s][^\*_]+?[^\*_\s])[\*_]/) do |m|
|
278
|
+
match = Regexp.last_match
|
279
|
+
last = find_color(match.pre_match, true)
|
280
|
+
counter = i
|
281
|
+
while last.nil? && counter > 0
|
282
|
+
counter -= 1
|
283
|
+
find_color(lines[counter])
|
284
|
+
end
|
285
|
+
"#{match[1]}#{c([:u])}#{match[2]}" + (last ? last : xc)
|
286
|
+
end
|
287
|
+
|
288
|
+
# equations
|
289
|
+
line.gsub!(/((\\\\\[)(.*?)(\\\\\])|(\\\\\()(.*?)(\\\\\)))/) do |m|
|
290
|
+
match = Regexp.last_match
|
291
|
+
last = find_color(match.pre_match)
|
292
|
+
if match[2]
|
293
|
+
brackets = [match[2], match[4]]
|
294
|
+
equat = match[3]
|
295
|
+
else
|
296
|
+
brackets = [match[5], match[7]]
|
297
|
+
equat = match[6]
|
298
|
+
end
|
299
|
+
"#{c([:b, :black])}#{brackets[0]}#{xc}#{c([:b,:blue])}#{equat}#{c([:b, :black])}#{brackets[1]}" + (last ? last : xc)
|
300
|
+
end
|
301
|
+
|
302
|
+
# list items
|
303
|
+
# TODO: Fix ordered list numbering, pad numbers based on total number of list items
|
304
|
+
line.gsub!(/^(\s*)([*\-+]|\d+\.) /) do |m|
|
305
|
+
match = Regexp.last_match
|
306
|
+
last = find_color(match.pre_match)
|
307
|
+
indent = match[1] || ''
|
308
|
+
"#{indent}#{c([:d, :red])}#{match[2]} " + (last ? last : xc)
|
309
|
+
end
|
310
|
+
|
311
|
+
# definition lists
|
312
|
+
line.gsub!(/^(:\s*)(.*?)/) do |m|
|
313
|
+
match = Regexp.last_match
|
314
|
+
"#{c([:d, :red])}#{match[1]} #{c([:b, :white])}#{match[2]}#{xc}"
|
315
|
+
end
|
316
|
+
|
317
|
+
# misc html
|
318
|
+
line.gsub!(/<br\/?>/, "\n")
|
319
|
+
line.gsub!(/(?i-m)((<\/?)(\w+[\s\S]*?)(>))/) do |tag|
|
320
|
+
match = Regexp.last_match
|
321
|
+
last = find_color(match.pre_match)
|
322
|
+
"#{c([:d,:black])}#{match[2]}#{c([:b,:black])}#{match[3]}#{c([:d,:black])}#{match[4]}" + (last ? last : xc)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
line
|
327
|
+
end
|
328
|
+
|
329
|
+
input = lines.join("\n")
|
330
|
+
|
331
|
+
# images
|
332
|
+
input.gsub!(/^(.*?)!\[(.*)?\]\((.*?\.(?:png|gif|jpg))( +.*)?\)/) do |m|
|
333
|
+
match = Regexp.last_match
|
334
|
+
if match[1].uncolor =~ /^( {4,}|\t)+/
|
335
|
+
match[0]
|
336
|
+
else
|
337
|
+
tail = match[4].nil? ? '' : " "+match[4].strip
|
338
|
+
result = nil
|
339
|
+
if exec_available('imgcat') && @options[:local_images]
|
340
|
+
if match[3]
|
341
|
+
img_path = match[3]
|
342
|
+
if img_path =~ /^http/ && @options[:remote_images]
|
343
|
+
begin
|
344
|
+
res, s = Open3.capture2(%Q{curl -sS "#{img_path}" 2> /dev/null | imgcat})
|
345
|
+
|
346
|
+
if s.success?
|
347
|
+
pre = match[2].size > 0 ? " #{c([:d,:blue])}[#{match[2].strip}]\n" : ''
|
348
|
+
post = tail.size > 0 ? "\n #{c([:b,:blue])}-- #{tail} --" : ''
|
349
|
+
result = pre + res + post
|
350
|
+
end
|
351
|
+
rescue => e
|
352
|
+
@log.error(e)
|
353
|
+
end
|
354
|
+
else
|
355
|
+
if img_path =~ /^[~\/]/
|
356
|
+
img_path = File.expand_path(img_path)
|
357
|
+
elsif @file
|
358
|
+
base = File.expand_path(File.dirname(@file))
|
359
|
+
img_path = File.join(base,img_path)
|
360
|
+
end
|
361
|
+
if File.exists?(img_path)
|
362
|
+
pre = match[2].size > 0 ? " #{c([:d,:blue])}[#{match[2].strip}]\n" : ''
|
363
|
+
post = tail.size > 0 ? "\n #{c([:b,:blue])}-- #{tail} --" : ''
|
364
|
+
img = %x{imgcat "#{img_path}"}
|
365
|
+
result = pre + img + post
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
if result.nil?
|
371
|
+
match[1] + color_image(match.pre_match, match[2], match[3] + tail) + xc
|
372
|
+
else
|
373
|
+
match[1] + result + xc
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
@footnotes.each {|t, v|
|
379
|
+
input += "\n\n#{c([:b,:black,:on_black])}[#{c([:b,:yellow,:on_black])}^#{c([:x,:yellow,:on_black])}#{t}#{c([:b,:black,:on_black])}]: #{c([:u,:white,:on_black])}#{v}#{xc}"
|
380
|
+
}
|
381
|
+
|
382
|
+
@output += input
|
383
|
+
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module RBCliTool
|
2
|
+
class Project
|
3
|
+
|
4
|
+
def initialize path, template_vars = {}
|
5
|
+
@skelpath = "#{File.dirname(__FILE__)}/../../skeletons/project"
|
6
|
+
@minipath = "#{File.dirname(__FILE__)}/../../skeletons/mini/executable"
|
7
|
+
@micropath = "#{File.dirname(__FILE__)}/../../skeletons/micro/executable"
|
8
|
+
|
9
|
+
@dest = path
|
10
|
+
@template_vars = template_vars
|
11
|
+
end
|
12
|
+
|
13
|
+
def create force = false
|
14
|
+
return false if project_exists? and not force
|
15
|
+
src = @skelpath
|
16
|
+
|
17
|
+
# Create Top Level Folder (TLF)
|
18
|
+
FileUtils.mkdir_p @dest
|
19
|
+
|
20
|
+
# Create project structure
|
21
|
+
%w(
|
22
|
+
application/commands
|
23
|
+
application/commands/scripts
|
24
|
+
config
|
25
|
+
default_user_configs
|
26
|
+
exe
|
27
|
+
hooks
|
28
|
+
spec
|
29
|
+
).each do |folder|
|
30
|
+
FileUtils.mkdir_p "#{@dest}/#{folder}"
|
31
|
+
FileUtils.touch "#{@dest}/#{folder}/.keep"
|
32
|
+
end
|
33
|
+
|
34
|
+
# Create executable
|
35
|
+
RBCliTool.cp_file "#{src}/exe/executable", "#{@dest}/exe/#{@template_vars[:cmdname]}", @template_vars
|
36
|
+
|
37
|
+
# Create files for Gem package
|
38
|
+
Dir.entries(src).each do |file|
|
39
|
+
next if File.directory? "#{src}/#{file}"
|
40
|
+
RBCliTool.cp_file "#{src}/#{file}", "#{@dest}/", @template_vars
|
41
|
+
end
|
42
|
+
|
43
|
+
# Create default config
|
44
|
+
Dir.glob "#{src}/config/*.rb" do |file|
|
45
|
+
RBCliTool.cp_file file, "#{@dest}/config/", @template_vars
|
46
|
+
end
|
47
|
+
|
48
|
+
# Create application options
|
49
|
+
RBCliTool.cp_file "#{src}/application/options.rb", "#{@dest}/application/options.rb", @template_vars
|
50
|
+
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_mini force = false
|
55
|
+
return false if project_exists? and not force
|
56
|
+
RBCliTool.cp_file @minipath, @dest, @template_vars
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_micro force = false
|
60
|
+
return false if project_exists? and not force
|
61
|
+
RBCliTool.cp_file @micropath, @dest, @template_vars
|
62
|
+
end
|
63
|
+
|
64
|
+
def exists?
|
65
|
+
project_exists?
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def project_exists?
|
71
|
+
# If the specified file already exists...
|
72
|
+
return true if File.exists? @dest
|
73
|
+
|
74
|
+
# Or if the .rbcli file exists anywhere in the tree...
|
75
|
+
searchpath = @dest
|
76
|
+
while !searchpath.empty?
|
77
|
+
return searchpath if File.directory? searchpath and File.exists? "#{searchpath}/.rbcli"
|
78
|
+
spath = searchpath.split('/')
|
79
|
+
searchpath = (spath.length == 2) ? '/' : spath[0..-2].join('/')
|
80
|
+
end
|
81
|
+
|
82
|
+
# Otherwise we say the project does not exist
|
83
|
+
false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
data/lib/rbcli-tool.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
###########
|
2
|
+
## RBCLI ##
|
3
|
+
###########
|
4
|
+
#
|
5
|
+
# This file loads the Rbcli CLI Tool components.
|
6
|
+
#
|
7
|
+
###########
|
8
|
+
|
9
|
+
lib = File.expand_path('../../lib', __FILE__)
|
10
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
11
|
+
|
12
|
+
module RBCliTool end
|
13
|
+
|
14
|
+
require 'rbcli-tool/mdless_fix'
|
15
|
+
require 'rbcli-tool/erb_renderer'
|
16
|
+
require 'rbcli-tool/project'
|
data/lib/rbcli.rb
CHANGED
@@ -12,7 +12,7 @@ lib = File.expand_path('../../lib', __FILE__)
|
|
12
12
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
13
13
|
|
14
14
|
module Rbcli end # Empty module declaration required to declare submodules freely
|
15
|
-
require
|
15
|
+
require 'rbcli/version'
|
16
16
|
|
17
17
|
# UTILS
|
18
18
|
require 'rbcli/util/hash_deep_symbolize'
|
@@ -26,22 +26,23 @@ require 'rbcli/logging/logging'
|
|
26
26
|
|
27
27
|
|
28
28
|
# STATE TOOLS
|
29
|
-
require
|
30
|
-
require
|
31
|
-
require
|
32
|
-
require
|
29
|
+
require 'rbcli/stateful_systems/configuratestorage'
|
30
|
+
require 'rbcli/stateful_systems/state_storage'
|
31
|
+
require 'rbcli/stateful_systems/storagetypes/localstate'
|
32
|
+
require 'rbcli/stateful_systems/storagetypes/remotestate_dynamodb'
|
33
33
|
# END STATE TOOLS
|
34
34
|
|
35
35
|
# AUTOUPDATE
|
36
|
-
require
|
36
|
+
require 'rbcli/autoupdate/autoupdate'
|
37
37
|
# END AUTOUPDATE
|
38
38
|
|
39
39
|
# SCRIPT WRAPPER
|
40
|
-
require
|
40
|
+
require 'rbcli/scriptwrapping/scriptwrapper'
|
41
41
|
# END SCRIPT WRAPPER
|
42
42
|
|
43
43
|
# CORE
|
44
|
-
require
|
45
|
-
require
|
46
|
-
require
|
44
|
+
require 'rbcli/configuration/configurate'
|
45
|
+
require 'rbcli/engine/load_project'
|
46
|
+
require 'rbcli/engine/command'
|
47
|
+
require 'rbcli/engine/parser'
|
47
48
|
# END CORE
|
data/rbcli.gemspec
CHANGED
@@ -44,6 +44,7 @@ Gem::Specification.new do |spec|
|
|
44
44
|
spec.add_dependency 'macaddr', '~> 1.7'
|
45
45
|
spec.add_dependency 'rufus-scheduler', '~> 3.5'
|
46
46
|
spec.add_dependency 'octokit', '~> 4.9'
|
47
|
+
spec.add_dependency 'mdless', '~> 0.0'
|
47
48
|
|
48
49
|
#spec.add_dependency 'trollop', '~> 2.1'
|
49
50
|
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#########################################################
|
4
|
+
### <%= @vars[:cmdname] %>
|
5
|
+
#######
|
6
|
+
## This is the main executable for <%= @vars[:cmdname] %>.
|
7
|
+
## It's job is to load and execute the application.
|
8
|
+
#########################################################
|
9
|
+
|
10
|
+
require 'rbcli'
|
11
|
+
|
12
|
+
#########################
|
13
|
+
## Configuration Block ##
|
14
|
+
#########################
|
15
|
+
Rbcli::Configurate.me do
|
16
|
+
## Script Name -- (Required) -- This line identifies the tool's executable on the command line.
|
17
|
+
scriptname __FILE__.split('/')[-1]
|
18
|
+
|
19
|
+
## Version Number -- (Required)
|
20
|
+
version '0.1.0'
|
21
|
+
|
22
|
+
## Description -- (Requierd) -- A description that will appear when the user looks at the help with -h. This can be as long as needed.
|
23
|
+
description %q{<%= @vars[:description] %>}
|
24
|
+
|
25
|
+
|
26
|
+
## Log Target -- (Optional) -- Set the target for logs.
|
27
|
+
log_target nil
|
28
|
+
|
29
|
+
## Log Level -- (Optional) -- Set the default log_level for users.
|
30
|
+
log_level nil
|
31
|
+
|
32
|
+
|
33
|
+
## Config Userfile -- (Optional) -- Set location of user's config file.
|
34
|
+
config_userfile '~/.<%= @vars[:cmdname] %>', merge_defaults: true, required: false
|
35
|
+
|
36
|
+
## Config Defaults -- (Optional, Multiple) -- Load a YAML file as part of the default config.
|
37
|
+
#config_defaults 'defaults.yml'
|
38
|
+
|
39
|
+
## Config Default -- (Optional, Multiple) -- Specify an individual configuration parameter and set a default value.
|
40
|
+
config_default :myopt, description: 'Testing this', default: true
|
41
|
+
|
42
|
+
|
43
|
+
## Autoupdate, Github -- (Optional) -- Check for updates to this application at a GitHub repo.
|
44
|
+
#autoupdate github_repo: '<your_user>/<your_repo>', access_token: nil, enterprise_hostname: nil, force_update: false
|
45
|
+
|
46
|
+
## Autoupdate, Rubygems.org -- (Optional) -- Check for updates to this application on Rubygems.org
|
47
|
+
#autoupdate gem: '<your_gem>', force_update: false
|
48
|
+
|
49
|
+
|
50
|
+
## Option -- (Optional, Multiple) -- Add a global CLI Option
|
51
|
+
option :name, 'Give me your name', type: :string, default: 'Foo', required: false, permitted: ['Jack', 'Jill']
|
52
|
+
|
53
|
+
|
54
|
+
## Default Action -- (Optional) -- The default code to execute when no subcommand is given.
|
55
|
+
default_action do |opts|
|
56
|
+
puts "Hello, #{opts[:name]}."
|
57
|
+
puts "To see the help, use -h"
|
58
|
+
end
|
59
|
+
|
60
|
+
## Pre-Execution Hook -- (Optional) -- Allows providing a block of code that runs _before_ all commands
|
61
|
+
# pre_hook do |opts|
|
62
|
+
# puts 'This is a pre-command hook. It executes before the command.'
|
63
|
+
# end
|
64
|
+
|
65
|
+
## Post-Execution Hook -- (Optional) -- Allows providing a block of code that runs _after_ all commands
|
66
|
+
# post_hook do |opts|
|
67
|
+
# puts 'This is a post-command hook. It executes after the command.'
|
68
|
+
# end
|
69
|
+
|
70
|
+
## First-Run Hook -- (Optional) -- Allows providing a block of code that executes the first time that the application is run on a given system.
|
71
|
+
# first_run halt_after_running: false do
|
72
|
+
# puts "This is the first time the mytool command is run! Don't forget to generate a config file with the `-g` option before continuing."
|
73
|
+
# end
|
74
|
+
end
|
75
|
+
|
76
|
+
###############################
|
77
|
+
## State Configuration Block ##
|
78
|
+
###############################
|
79
|
+
Rbcli::Configurate.storage do
|
80
|
+
###
|
81
|
+
# Local State Storage
|
82
|
+
###
|
83
|
+
|
84
|
+
## Local State -- (Optional) -- Creates a hash that is automatically saved to a file locally for state persistance. It is accessible to all commands at Rbcli.localstate[:yourkeyhere]
|
85
|
+
#local_state '/var/mytool/localstate', force_creation: true, halt_on_error: true
|
86
|
+
|
87
|
+
|
88
|
+
## Remote State -- (Optional) -- Creates a hash that is automatically saved to a DynamoDB table. It is recommended to keep halt_on_error=true when using a shared state. Locking can be one of (:manual :auto :none).
|
89
|
+
#remote_state_dynamodb table_name: 'mytable', region: 'us-east-1', force_creation: true, halt_on_error: true, locking: :auto
|
90
|
+
end
|
91
|
+
|
92
|
+
#########################
|
93
|
+
## Command Declaration ##
|
94
|
+
#########################
|
95
|
+
class Test < Rbcli::Command # Declare a new command by subclassing Rbcli::Command
|
96
|
+
description 'This is a short description.' # (Required) Short description for the global help
|
97
|
+
usage 'This is some really long usage text description!' # (Required) Long description for the command-specific help
|
98
|
+
parameter :force, 'Force testing', type: :boolean, default: false, required: false # (Optional, Multiple) Add a command-specific CLI parameter. Can be called multiple times
|
99
|
+
|
100
|
+
config_defaults 'defaults.yml' # (Optional, Multiple) Load a YAML file as part of the default config. This can be called multiple times, and the YAML files will be merged. User config is generated from these
|
101
|
+
config_default :myopt2, description: 'Testing this again', default: true # (Optional, Multiple) Specify an individual configuration parameter and set a default value. These will also be included in generated user config
|
102
|
+
|
103
|
+
extern path: 'env | grep "^__PARAMS\|^__ARGS\|^__GLOBAL\|^__CONFIG"', envvars: {MYVAR: 'some_value'} # (Required unless `action` defined) Runs a given application, with optional environment variables, when the user runs the command.
|
104
|
+
extern envvars: {MY_OTHER_VAR: 'another_value'} do |params, args, global_opts, config| # Alternate usage. Supplying a block instead of a path allows us to modify the command based on the arguments and configuration supplied by the user.
|
105
|
+
"echo #{params[:force].to_s}__YESSS!!!"
|
106
|
+
end
|
107
|
+
|
108
|
+
action do |params, args, global_opts, config| # (Required unless `extern` defined) Block to execute if the command is called.
|
109
|
+
Rbcli::log.info { 'These logs can go to STDERR, STDOUT, or a file' } # Example log. Interface is identical to Ruby's logger
|
110
|
+
puts "\nArgs:\n#{args}" # Arguments that came after the command on the CLI (i.e.: `mytool test bar baz` will yield args=['bar', 'baz'])
|
111
|
+
puts "Params:\n#{params}" # Parameters, as described through the option statements above
|
112
|
+
puts "Global opts:\n#{global_opts}" # Global Parameters, as descirbed in the Configurate section
|
113
|
+
puts "Config:\n#{config}" # Config file values
|
114
|
+
puts "LocalState:\n#{Rbcli.local_state}" # Local persistent state storage (when available) -- if unsure use Rbcli.local_state.nil?
|
115
|
+
puts "RemoteState:\n#{Rbcli.remote_state}" # Remote persistent state storage (when available) -- if unsure use Rbcli.remote_state.nil?
|
116
|
+
puts "\nDone!!!"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
#####################
|
121
|
+
## Parse Statement ##
|
122
|
+
#####################
|
123
|
+
Rbcli.parse # Parse CLI and execute
|