rbcli 0.1.7 → 0.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/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
|