watson-ruby 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +28 -0
- data/LICENSE +19 -0
- data/README.md +147 -0
- data/Rakefile +21 -0
- data/assets/defaultConf +21 -0
- data/assets/watson.svg +25 -0
- data/bin/watson +18 -0
- data/lib/watson.rb +69 -0
- data/lib/watson/bitbucket.rb +373 -0
- data/lib/watson/command.rb +470 -0
- data/lib/watson/config.rb +493 -0
- data/lib/watson/fs.rb +69 -0
- data/lib/watson/github.rb +369 -0
- data/lib/watson/parser.rb +405 -0
- data/lib/watson/printer.rb +293 -0
- data/lib/watson/remote.rb +120 -0
- data/lib/watson/version.rb +3 -0
- data/spec/config_spec.rb +119 -0
- data/spec/fs_spec.rb +56 -0
- data/spec/helper_spec.rb +32 -0
- data/spec/parser_spec.rb +128 -0
- data/watson.gemspec +33 -0
- metadata +116 -0
@@ -0,0 +1,470 @@
|
|
1
|
+
module Watson
|
2
|
+
# Command line parser class
|
3
|
+
# Controls program flow and parses options given by command line
|
4
|
+
class Command
|
5
|
+
|
6
|
+
# Debug printing for this class
|
7
|
+
DEBUG = false
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
# Include for debug_print
|
12
|
+
include Watson
|
13
|
+
|
14
|
+
|
15
|
+
###########################################################
|
16
|
+
# Command line controller
|
17
|
+
# Manages program flow from given command line arguments
|
18
|
+
def execute(*args)
|
19
|
+
# [review] - Should command line args append or overwrite config/RC parameters?
|
20
|
+
# Currently we overwrite, maybe add flag to overwrite or not?
|
21
|
+
|
22
|
+
# Identify method entry
|
23
|
+
debug_print "#{ self } : #{ __method__ }\n"
|
24
|
+
|
25
|
+
# List of possible flags, used later in parsing and for user reference
|
26
|
+
_flag_list = ["-c", "--context-depth",
|
27
|
+
"-d", "--dirs",
|
28
|
+
"-f", "--files",
|
29
|
+
"-h", "--help",
|
30
|
+
"-i", "--ignore",
|
31
|
+
"-p", "--parse-depth",
|
32
|
+
"-r", "--remote",
|
33
|
+
"-t", "--tags",
|
34
|
+
"-u", "--update",
|
35
|
+
"-v", "--version"
|
36
|
+
]
|
37
|
+
|
38
|
+
|
39
|
+
# If we get the version or help flag, ignore all other flags
|
40
|
+
# Just display these and exit
|
41
|
+
# Using .index instead of .include? to stay consistent with other checks
|
42
|
+
return help if args.index('-h') != nil || args.index('--help') != nil
|
43
|
+
return version if args.index('-v') != nil || args.index('--version') != nil
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
# If not one of the above then we are performing actual watson stuff
|
48
|
+
# Create all the necessary watson components so we can perform
|
49
|
+
# all actions associated with command line args
|
50
|
+
|
51
|
+
|
52
|
+
# Only create Config, don't call run method
|
53
|
+
# Populate Config parameters from CL THEN call run to fill in gaps from RC
|
54
|
+
# Saves some messy checking and sorting/organizing of parameters
|
55
|
+
@config = Watson::Config.new
|
56
|
+
@parser = Watson::Parser.new(@config)
|
57
|
+
@printer = Watson::Printer.new(@config)
|
58
|
+
|
59
|
+
# Capture Ctrl+C interrupt for clean exit
|
60
|
+
# [review] - Not sure this is the correct place to put the Ctrl+C capture
|
61
|
+
trap("INT") do
|
62
|
+
File.delete(@config.tmp_file) if File.exists?(@config.tmp_file)
|
63
|
+
exit 2
|
64
|
+
end
|
65
|
+
|
66
|
+
# Parse command line options
|
67
|
+
# Begin by slicing off until we reach a valid flag
|
68
|
+
|
69
|
+
# Always look at first array element in case and then slice off what we need
|
70
|
+
# Accept parameters to be added / overwritten if called twice
|
71
|
+
# Slice out from argument until next argument
|
72
|
+
|
73
|
+
# Clean up argument list by removing elements until the first valid flag
|
74
|
+
until _flag_list.include?(args[0]) || args.length == 0
|
75
|
+
# [review] - Make this non-debug print to user?
|
76
|
+
debug_print "Unrecognized flag #{ args[0] }\n"
|
77
|
+
args.slice!(0)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Parse command line options
|
81
|
+
# Grab flag (should be first arg) then slice off args until next flag
|
82
|
+
# Repeat until all args have been dealt with
|
83
|
+
|
84
|
+
until args.length == 0
|
85
|
+
# Set flag for calling function later
|
86
|
+
_flag = args.slice!(0)
|
87
|
+
|
88
|
+
debug_print "Current Flag: #{ _flag }\n"
|
89
|
+
|
90
|
+
# Go through args until we find the next valid flag or all args are parsed
|
91
|
+
_i = 0
|
92
|
+
until _flag_list.include?(args[_i]) || _i > (args.length - 1)
|
93
|
+
debug_print "Arg: #{ args[_i] }\n"
|
94
|
+
_i = _i + 1
|
95
|
+
end
|
96
|
+
|
97
|
+
# Slice off the args for the flag (inclusive) using index from above
|
98
|
+
# [review] - This is a bit messy (to slice by _i - 1) when we have control
|
99
|
+
# over the _i index above but I don't want to
|
100
|
+
# think about the logic right now so look at later
|
101
|
+
_flag_args = args.slice!(0..(_i-1))
|
102
|
+
|
103
|
+
case _flag
|
104
|
+
when "-c", "--context-depth"
|
105
|
+
debug_print "Found -c/--context-depth argument\n"
|
106
|
+
set_context(_flag_args)
|
107
|
+
|
108
|
+
when "-d", "--dirs"
|
109
|
+
debug_print "Found -d/--dirs argument\n"
|
110
|
+
set_dirs(_flag_args)
|
111
|
+
|
112
|
+
when "-f", "--files"
|
113
|
+
debug_print "Found -f/--files argument\n"
|
114
|
+
set_files(_flag_args)
|
115
|
+
|
116
|
+
when "-i", "--ignore"
|
117
|
+
debug_print "Found -i/--ignore argument\n"
|
118
|
+
set_ignores(_flag_args)
|
119
|
+
|
120
|
+
when "-p", "--parse-depth"
|
121
|
+
debug_print "Found -r/--parse-depth argument\n"
|
122
|
+
set_parse_depth(_flag_args)
|
123
|
+
|
124
|
+
when "-r", "--remote"
|
125
|
+
debug_print "Found -r/--remote argument\n"
|
126
|
+
# Run config to populate all the fields and such
|
127
|
+
# [review] - Not a fan of running these here but want to avoid getting all issues when running remote (which @config.run does)
|
128
|
+
@config.check_conf
|
129
|
+
@config.read_conf
|
130
|
+
setup_remote(_flag_args)
|
131
|
+
|
132
|
+
# If setting up remote, exit afterwards
|
133
|
+
exit true
|
134
|
+
|
135
|
+
when "-t", "--tags"
|
136
|
+
debug_print "Found -t/--tags argument\n"
|
137
|
+
set_tags(_flag_args)
|
138
|
+
|
139
|
+
when "-u", "--update"
|
140
|
+
debug_print "Found -u/--update argument\n"
|
141
|
+
@config.remote_valid = true
|
142
|
+
|
143
|
+
|
144
|
+
else
|
145
|
+
print "Unknown argument #{ _flag }\n"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
debug_print "Args length 0, running watson...\n"
|
150
|
+
@config.run
|
151
|
+
structure = @parser.run
|
152
|
+
@printer.run(structure)
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
###########################################################
|
157
|
+
# Print help for watson
|
158
|
+
def help
|
159
|
+
# [todo] - Add bold and colored printing
|
160
|
+
|
161
|
+
# Identify method entry
|
162
|
+
debug_print "#{ self } : #{ __method__ }\n"
|
163
|
+
|
164
|
+
print BOLD;
|
165
|
+
print "Usage: watson [OPTION]...\n"
|
166
|
+
print "Running watson with no arguments will parse with settings in RC file\n"
|
167
|
+
print "If no RC file exists, default RC file will be created\n"
|
168
|
+
|
169
|
+
print "\n"
|
170
|
+
print " -c, --context-depth number of lines of context to provide with posted issue\n"
|
171
|
+
print " -d, --dirs list of directories to search in\n"
|
172
|
+
print " -f, --files list of files to search in\n"
|
173
|
+
print " -h, --help print help\n"
|
174
|
+
print " -i, --ignore list of files, directories, or types to ignore\n"
|
175
|
+
print " -p, --parse-depth depth to recursively parse directories\n"
|
176
|
+
print " -r, --remote list / create tokens for Bitbucket/GitHub\n"
|
177
|
+
print " -t, --tags list of tags to search for\n"
|
178
|
+
print " -u, --update update remote repos with current issues\n"
|
179
|
+
print " -v, --version print watson version and info\n"
|
180
|
+
print "\n"
|
181
|
+
|
182
|
+
print "Any number of files, tags, dirs, and ignores can be listed after flag\n"
|
183
|
+
print "Ignored files should be space separated\n"
|
184
|
+
print "To use *.filetype identifier, encapsulate in \"\" to avoid shell substitutions \n"
|
185
|
+
print "\n"
|
186
|
+
|
187
|
+
print "Report bugs to: watson\@goosecode.com\n"
|
188
|
+
print "watson home page: <http://goosecode.com/projects/watson>\n"
|
189
|
+
print "[goosecode] labs | 2012-2013\n"
|
190
|
+
print RESET;
|
191
|
+
|
192
|
+
return true
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
###########################################################
|
198
|
+
# Print version information about watson
|
199
|
+
def version
|
200
|
+
|
201
|
+
# Identify method entry
|
202
|
+
debug_print "#{ self } : #{ __method__ }\n"
|
203
|
+
|
204
|
+
print "watson v1.0\n"
|
205
|
+
print "Copyright (c) 2012-2013 goosecode labs\n"
|
206
|
+
print "Licensed under MIT, see LICENSE for details\n"
|
207
|
+
print "\n"
|
208
|
+
|
209
|
+
print "Written by nhmood, see <http://goosecode.com/projects/watson>\n"
|
210
|
+
return true
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
###########################################################
|
215
|
+
# set_context
|
216
|
+
# Set context_depth parameter in config
|
217
|
+
def set_context(args)
|
218
|
+
|
219
|
+
# Identify method entry
|
220
|
+
debug_print "#{ self } : #{ __method__ }\n"
|
221
|
+
|
222
|
+
# Need at least one dir in args
|
223
|
+
if args.length <= 0
|
224
|
+
# [review] - Make this a non-debug print to user?
|
225
|
+
debug_print "No args passed, exiting\n"
|
226
|
+
return false
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
# For context_depth we do NOT append to RC, ALWAYS overwrite
|
231
|
+
# For each argument passed, make sure valid, then set @config.parse_depth
|
232
|
+
args.each do | _context_depth |
|
233
|
+
if _context_depth.match(/^(\d+)/)
|
234
|
+
debug_print "Setting #{ _context_depth } to config context_depth\n"
|
235
|
+
@config.context_depth = _context_depth.to_i
|
236
|
+
else
|
237
|
+
debug_print "#{ _context_depth } invalid depth, ignoring\n"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# Doesn't make much sense to set context_depth for each individual post
|
242
|
+
# When you use this command line arg, it writes the config parameter
|
243
|
+
@config.update_conf("context_depth")
|
244
|
+
|
245
|
+
debug_print "Updated context_depth: #{ @config.context_depth }\n"
|
246
|
+
return true
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
###########################################################
|
251
|
+
# set_dirs
|
252
|
+
# Set directories to be parsed by watson
|
253
|
+
def set_dirs(args)
|
254
|
+
|
255
|
+
# Identify method entry
|
256
|
+
debug_print "#{ self } : #{ __method__ }\n"
|
257
|
+
|
258
|
+
# Need at least one dir in args
|
259
|
+
if args.length <= 0
|
260
|
+
# [review] - Make this a non-debug print to user?
|
261
|
+
debug_print "No args passed, exiting\n"
|
262
|
+
return false
|
263
|
+
end
|
264
|
+
|
265
|
+
# Set config flag for CL entryset in config
|
266
|
+
@config.cl_entry_set = true
|
267
|
+
debug_print "Updated cl_entry_set flag: #{ @config.cl_entry_set }\n"
|
268
|
+
|
269
|
+
# [review] - Should we clean the dir before adding here?
|
270
|
+
# For each argument passed, make sure valid, then add to @config.dir_list
|
271
|
+
args.each do | _dir |
|
272
|
+
# Error check on input
|
273
|
+
if !Watson::FS.check_dir(_dir)
|
274
|
+
print "Unable to open #{ _dir }\n"
|
275
|
+
else
|
276
|
+
# Clean up directory path
|
277
|
+
_dir = _dir.match(/^((\w+)?\.?\/?)+/)[0].gsub(/(\/)+$/, "")
|
278
|
+
if !_dir.empty?
|
279
|
+
debug_print "Adding #{ _dir } to config dir_list\n"
|
280
|
+
@config.dir_list.push(_dir)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
debug_print "Updated dirs: #{ @config.dir_list }\n"
|
286
|
+
return true
|
287
|
+
end
|
288
|
+
|
289
|
+
|
290
|
+
###########################################################
|
291
|
+
# set_files
|
292
|
+
# Set files to be parsed by watson
|
293
|
+
def set_files(args)
|
294
|
+
|
295
|
+
# Identify method entry
|
296
|
+
debug_print "#{ self } : #{ __method__ }\n"
|
297
|
+
|
298
|
+
# Need at least one file in args
|
299
|
+
if args.length <= 0
|
300
|
+
debug_print "No args passed, exiting\n"
|
301
|
+
return false
|
302
|
+
end
|
303
|
+
|
304
|
+
# Set config flag for CL entryset in config
|
305
|
+
@config.cl_entry_set = true
|
306
|
+
debug_print "Updated cl_entry_set flag: #{ @config.cl_entry_set }\n"
|
307
|
+
|
308
|
+
# For each argument passed, make sure valid, then add to @config.file_list
|
309
|
+
args.each do | _file |
|
310
|
+
# Error check on input
|
311
|
+
if !Watson::FS.check_file(_file)
|
312
|
+
print "Unable to open #{ _file }\n"
|
313
|
+
else
|
314
|
+
debug_print "Adding #{ _file } to config file_list\n"
|
315
|
+
@config.file_list.push(_file)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
debug_print "Updated files: #{ @config.file_list }\n"
|
320
|
+
return true
|
321
|
+
end
|
322
|
+
|
323
|
+
|
324
|
+
###########################################################
|
325
|
+
# set_ignores
|
326
|
+
# Set files and dirs to be ignored when parsing by watson
|
327
|
+
def set_ignores(args)
|
328
|
+
|
329
|
+
# Identify method entry
|
330
|
+
debug_print "#{ self } : #{ __method__ }\n"
|
331
|
+
|
332
|
+
# Need at least one ignore in args
|
333
|
+
if args.length <= 0
|
334
|
+
debug_print "No args passed, exiting\n"
|
335
|
+
return false
|
336
|
+
end
|
337
|
+
|
338
|
+
# Set config flag for CL ignore set in config
|
339
|
+
@config.cl_ignore_set = true
|
340
|
+
debug_print "Updated cl_ignore_set flag: #{ @config.cl_ignore_set }\n"
|
341
|
+
|
342
|
+
|
343
|
+
# For ignores we do NOT overwrite RC, just append
|
344
|
+
# For each argument passed, add to @config.ignore_list
|
345
|
+
args.each do | _ignore |
|
346
|
+
debug_print "Adding #{ _ignore } to config ignore_list\n"
|
347
|
+
@config.ignore_list.push(_ignore)
|
348
|
+
end
|
349
|
+
|
350
|
+
debug_print "Updated ignores: #{ @config.ignore_list }\n"
|
351
|
+
return true
|
352
|
+
end
|
353
|
+
|
354
|
+
|
355
|
+
###########################################################
|
356
|
+
# set_parse_depth
|
357
|
+
# Set how deep to recursively parse directories
|
358
|
+
def set_parse_depth(args)
|
359
|
+
|
360
|
+
# Identify method entry
|
361
|
+
debug_print "#{ self } : #{ __method__ }\n"
|
362
|
+
|
363
|
+
# This should be a single, numeric, value
|
364
|
+
# If they pass more, just take the last valid value
|
365
|
+
if args.length <= 0
|
366
|
+
debug_print "No args passed, exiting\n"
|
367
|
+
return false
|
368
|
+
end
|
369
|
+
|
370
|
+
# For max_dpeth we do NOT append to RC, ALWAYS overwrite
|
371
|
+
# For each argument passed, make sure valid, then set @config.parse_depth
|
372
|
+
args.each do | _parse_depth |
|
373
|
+
if _parse_depth.match(/^(\d+)/)
|
374
|
+
debug_print "Setting #{ _parse_depth } to config parse_depth\n"
|
375
|
+
@config.parse_depth = _parse_depth
|
376
|
+
else
|
377
|
+
debug_print "#{ _parse_depth } invalid depth, ignoring\n"
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
debug_print "Updated parse_depth: #{ @config.parse_depth }\n"
|
382
|
+
return true
|
383
|
+
end
|
384
|
+
|
385
|
+
|
386
|
+
###########################################################
|
387
|
+
# set_tags
|
388
|
+
# Set tags to look for when parsing files and folders
|
389
|
+
def set_tags(args)
|
390
|
+
|
391
|
+
# Identify method entry
|
392
|
+
debug_print "#{ self } : #{ __method__ }\n"
|
393
|
+
|
394
|
+
# Need at least one tag in args
|
395
|
+
if args.length <= 0
|
396
|
+
debug_print "No args passed, exiting\n"
|
397
|
+
return false
|
398
|
+
end
|
399
|
+
|
400
|
+
# Set config flag for CL tag set in config
|
401
|
+
@config.cl_tag_set = true
|
402
|
+
debug_print "Updated cl_tag_set flag: #{ @config.cl_tag_set }\n"
|
403
|
+
|
404
|
+
# If set from CL, we overwrite the RC parameters
|
405
|
+
# For each argument passed, add to @config.tag_list
|
406
|
+
args.each do | _tag |
|
407
|
+
debug_print "Adding #{ _tag } to config tag_list\n"
|
408
|
+
@config.tag_list.push(_tag)
|
409
|
+
end
|
410
|
+
|
411
|
+
debug_print "Updated tags: #{ @config.tag_list }\n"
|
412
|
+
return true
|
413
|
+
end
|
414
|
+
|
415
|
+
|
416
|
+
###########################################################
|
417
|
+
# setup_remote
|
418
|
+
# Handle setup of remote issue posting for GitHub and Bitbucket
|
419
|
+
def setup_remote(args)
|
420
|
+
|
421
|
+
# Identify method entry
|
422
|
+
debug_print "#{ self } : #{ __method__ }\n"
|
423
|
+
|
424
|
+
Printer.print_header
|
425
|
+
|
426
|
+
print BOLD + "Existing Remotes:\n" + RESET
|
427
|
+
|
428
|
+
# Check the config for any remote entries (GitHub or Bitbucket) and print
|
429
|
+
# We *should* always have a repo + API together, but API should be enough
|
430
|
+
if @config.github_api.empty? && @config.bitbucket_api.empty?
|
431
|
+
Printer.print_status "!", YELLOW
|
432
|
+
print BOLD + "No remotes currently exist\n\n" + RESET
|
433
|
+
end
|
434
|
+
|
435
|
+
if !@config.github_api.empty?
|
436
|
+
print BOLD + "GitHub User : " + RESET + "#{ @config.github_api }\n"
|
437
|
+
print BOLD + "GitHub Repo : " + RESET + "#{ @config.github_repo }\n\n"
|
438
|
+
end
|
439
|
+
|
440
|
+
if !@config.bitbucket_api.empty?
|
441
|
+
print BOLD + "Bitbucket User : " + RESET + "#{ @config.bitbucket_api }\n" + RESET
|
442
|
+
print BOLD + "Bitbucket Repo : " + RESET + "#{ @config.bitbucket_repo }\n\n" + RESET
|
443
|
+
end
|
444
|
+
|
445
|
+
# If github or bitbucket passed, setup
|
446
|
+
# If just -r (0 args) do nothing and only have above printed
|
447
|
+
# If more than 1 arg is passed, unrecognized, warn user
|
448
|
+
if args.length == 1
|
449
|
+
case args[0].downcase
|
450
|
+
when "github"
|
451
|
+
debug_print "GitHub setup called from CL\n"
|
452
|
+
Watson::Remote::GitHub.setup(@config)
|
453
|
+
|
454
|
+
when "bitbucket"
|
455
|
+
debug_print "Bitbucket setup called from CL\n"
|
456
|
+
Watson::Remote::Bitbucket.setup(@config)
|
457
|
+
end
|
458
|
+
elsif args.length > 1
|
459
|
+
Printer.print_status "x", RED
|
460
|
+
print BOLD + "Incorrect arguments passed\n" + RESET
|
461
|
+
print " Please specify either Github or Bitbucket to setup remote\n"
|
462
|
+
print " Or pass without argument to see current remotes\n"
|
463
|
+
print " See help (-h/--help) for more details\n"
|
464
|
+
return false
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|