watson-ruby 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8c844168342cec7be1e4f91e7e13c875a43ba57e
4
- data.tar.gz: fa9e80a9cc46fe538a7cd7526a65eca20c072262
3
+ metadata.gz: 1e7a5e5e589587a830676cfecdbc585e2c3a3de3
4
+ data.tar.gz: 7fff249d9ac21e308bd4a512840fe9fdfffdec38
5
5
  SHA512:
6
- metadata.gz: a33eed2db55fe71083e6bf4f7bbf3a48d820b671d4830ea8cd08faa2804f5ad8b321340a937d6e70c14b47607dc672a97c92c4265b43e3e1dd11ab77f1b1cb3f
7
- data.tar.gz: 8c2ae5068a18f17685a42d752b444e51e6205fbafcf120315037543183237e7147281577c366c3cf95d5603d978d451e1c8b01965bfcf1c5179a03a48c52577e
6
+ metadata.gz: d9dc3bc1f36631e44e52ac94c2901ff496eb78344a99e349bc6ba17dd379afb19e1f25c0f0eb684a0bab7666d45159eb9443ebd5647ba55661c7d9b1521d252d
7
+ data.tar.gz: 0f192b53b5b45421850a1601dd822f6a5e8c0d143869cbeb1c9e3b579df5b0122fe25545067c903778aa2e7f5a5119448c50a8d5022ee8d85ca3547296cbd7d5
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ script:
5
+ - bundle exec rspec spec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- watson-ruby (1.0.1)
4
+ watson-ruby (1.0.4)
5
5
  json
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -19,9 +19,10 @@ gem install watson-ruby
19
19
 
20
20
  Or you can clone this repo and install with Rake
21
21
  ```
22
- clone https://github.com/nhmood/watson-ruby.git .
22
+ git clone https://github.com/nhmood/watson-ruby.git .
23
23
  cd watson-ruby
24
- rake
24
+ bundle install
25
+ bundle exec rake
25
26
  ```
26
27
 
27
28
  ## Usage
@@ -34,7 +35,7 @@ Usage: watson [OPTION]...
34
35
  Running watson with no arguments will parse with settings in RC file
35
36
  If no RC file exists, default RC file will be created
36
37
 
37
- -c, --context-lines number of lines of context to provide with posted issue
38
+ -c, --context-lines number of lines of context to provide with posted issue
38
39
  -d, --dirs list of directories to search in
39
40
  -f, --files list of files to search in
40
41
  -h, --help print help
@@ -42,7 +43,7 @@ If no RC file exists, default RC file will be created
42
43
  -p, --parse-depth depth to recursively parse directories
43
44
  -r, --remote list / create tokens for Bitbucket/Github
44
45
  -t, --tags list of tags to search for
45
- -u, --update update remote repos with current issues
46
+ -u, --update update remote repos with current issues
46
47
  -v, --version print watson version and info
47
48
 
48
49
  Any number of files, tags, dirs, and ignores can be listed after flag
@@ -139,9 +140,9 @@ Special thanks to [@crowell](http://github.com/crowell) for testing out watson-r
139
140
 
140
141
  ## FAQ
141
142
  - **Why Ruby?**
142
- I wanted to learn Ruby and this seemed like a pretty decent project.
143
+ I wanted to learn Ruby and this seemed like a pretty decent project.
143
144
 
144
145
  - **Why is the Ruby version different from the Perl version?**
145
- The Ruby version was developed after the Perl version was made. Because of this, it was a lot easier to add on features that were thought of while/after making the Perl version as the plumbing was still being setup.
146
- With a combination of wanting to finish watson-ruby as well as laziness, some of the improvements that were added to watson-ruby *have yet* to be pulled back into watson-perl.
147
- If you are interested in helping out or maintaining watson-perl let me know!
146
+ The Ruby version was developed after the Perl version was made. Because of this, it was a lot easier to add on features that were thought of while/after making the Perl version as the plumbing was still being setup.
147
+ With a combination of wanting to finish watson-ruby as well as laziness, some of the improvements that were added to watson-ruby *have yet* to be pulled back into watson-perl.
148
+ If you are interested in helping out or maintaining watson-perl let me know!
data/Rakefile CHANGED
@@ -14,8 +14,8 @@ RSpec::Core::RakeTask.new(:spec)
14
14
 
15
15
  desc 'Generate RDoc documentation'
16
16
  RDoc::Task.new(:rdoc) do | rdoc |
17
- rdoc.rdoc_dir = 'doc'
18
- rdoc.rdoc_files.include 'lib'
17
+ rdoc.rdoc_dir = 'doc'
18
+ rdoc.rdoc_files.include 'lib'
19
19
  end
20
20
 
21
21
 
data/lib/watson.rb CHANGED
@@ -8,62 +8,62 @@ require_relative 'watson/github'
8
8
  require_relative 'watson/bitbucket'
9
9
 
10
10
  module Watson
11
- # [todo] - Replace all regex parentheses() with brackets[] if not matching
12
- # Was using () to group things together for syntax instead of []
13
- # Replace so we can get cleaner matches and don't need to keep track of matches
14
-
15
- # [todo] - Change debug_print to provide its own \n
16
-
17
- # [todo] - Add ability to pass "IDENTIFY" to debug_print to auto print method entry info
18
-
19
- # [todo] - Make sure all methods have proper return at end
20
-
21
- # [review] - Method input arg always renamed from arg to _arg inside method, change this?
22
- # Not sure if I should just make input arg _arg or if explicit _ is useful
23
-
24
- # [todo] - Add option to save output to specified file
25
- # [todo] - Replace Identify line in each method with method_added call
26
- # http://ruby-doc.org/core-2.0.0/Module.html#method-i-method_added
11
+ # [todo] - Replace all regex parentheses() with brackets[] if not matching
12
+ # Was using () to group things together for syntax instead of []
13
+ # Replace so we can get cleaner matches and don't need to keep track of matches
14
+
15
+ # [todo] - Change debug_print to provide its own \n
16
+
17
+ # [todo] - Add ability to pass "IDENTIFY" to debug_print to auto print method entry info
18
+
19
+ # [todo] - Make sure all methods have proper return at end
20
+
21
+ # [review] - Method input arg always renamed from arg to _arg inside method, change this?
22
+ # Not sure if I should just make input arg _arg or if explicit _ is useful
23
+
24
+ # [todo] - Add option to save output to specified file
25
+ # [todo] - Replace Identify line in each method with method_added call
26
+ # http://ruby-doc.org/core-2.0.0/Module.html#method-i-method_added
27
27
 
28
- # Separate ON and OFF so we can force state and still let
29
- # individual classes have some control over their prints
28
+ # Separate ON and OFF so we can force state and still let
29
+ # individual classes have some control over their prints
30
30
 
31
- # Global flag to turn ON debugging across all files
32
- GLOBAL_DEBUG_ON = false
33
- # Gllobal flag to turn OFF debugging across all files
34
- GLOBAL_DEBUG_OFF = false
31
+ # Global flag to turn ON debugging across all files
32
+ GLOBAL_DEBUG_ON = false
33
+ # Gllobal flag to turn OFF debugging across all files
34
+ GLOBAL_DEBUG_OFF = false
35
35
 
36
- # [review] - Not sure if module_function is proper way to scope
37
- # I want to be able to call debug_print without having to use the scope
38
- # operator (Watson::Printer.debug_print) so it is defined here as a
39
- # module_function instead of having it in the Printer class
40
- # Gets included into every class individually
41
- module_function
42
-
43
- ###########################################################
44
- # Global debug print that prints based on local file DEBUG flag as well as GLOBAL debug flag
45
- def debug_print(msg)
46
- # [todo] - If input msg is a Hash, use pp to dump it
36
+ # [review] - Not sure if module_function is proper way to scope
37
+ # I want to be able to call debug_print without having to use the scope
38
+ # operator (Watson::Printer.debug_print) so it is defined here as a
39
+ # module_function instead of having it in the Printer class
40
+ # Gets included into every class individually
41
+ module_function
42
+
43
+ ###########################################################
44
+ # Global debug print that prints based on local file DEBUG flag as well as GLOBAL debug flag
45
+ def debug_print(msg)
46
+ # [todo] - If input msg is a Hash, use pp to dump it
47
47
 
48
- # Print only if DEBUG flag of calling class is true OR
49
- # GLOBAL_DEBUG_ON of Watson module (defined above) is true
50
- # AND GLOBAL_DEBUG_OFF of Watson module (Defined above) is false
48
+ # Print only if DEBUG flag of calling class is true OR
49
+ # GLOBAL_DEBUG_ON of Watson module (defined above) is true
50
+ # AND GLOBAL_DEBUG_OFF of Watson module (Defined above) is false
51
51
 
52
- # Sometimes we call debug_print from a static method (class << self)
53
- # and other times from a class method, and ::DEBUG is accessed differently
54
- # from a class vs object, so lets take care of that
55
- _DEBUG = (self.is_a? Class) ? self::DEBUG : self.class::DEBUG
52
+ # Sometimes we call debug_print from a static method (class << self)
53
+ # and other times from a class method, and ::DEBUG is accessed differently
54
+ # from a class vs object, so lets take care of that
55
+ _DEBUG = (self.is_a? Class) ? self::DEBUG : self.class::DEBUG
56
56
 
57
- print "=> #{msg}" if ( (_DEBUG == true || GLOBAL_DEBUG_ON == true) && (GLOBAL_DEBUG_OFF == false))
58
- end
57
+ print "=> #{msg}" if ( (_DEBUG == true || GLOBAL_DEBUG_ON == true) && (GLOBAL_DEBUG_OFF == false))
58
+ end
59
59
 
60
60
 
61
- ###########################################################
62
- # Perform system check to see if we are able to use unix less for printing
63
- def check_less
64
- # Check if system has less (so we can print out to it to allow scrolling)
65
- # [todo] - Implement this scrolling thing inside watson with ncurses
66
- return system("which less > /dev/null 2>&1")
67
- end
61
+ ###########################################################
62
+ # Perform system check to see if we are able to use unix less for printing
63
+ def check_less
64
+ # Check if system has less (so we can print out to it to allow scrolling)
65
+ # [todo] - Implement this scrolling thing inside watson with ncurses
66
+ return system("which less > /dev/null 2>&1")
67
+ end
68
68
 
69
69
  end
@@ -1,373 +1,372 @@
1
1
  module Watson
2
- class Remote
3
- # Bitbucket remote access class
4
- # Contains all necessary methods to obtain access to, get issue list,
5
- # and post issues to Bitbucket
6
- class Bitbucket
7
-
8
- # Debug printing for this class
9
- DEBUG = false
10
-
11
- class << self
12
-
13
- # [todo] - Allow closing of issues from watson? Don't like that idea but maybe
14
- # [todo] - Wrap Bitbucket password grabbing into separate method
15
-
16
- # Include for debug_print
17
- include Watson
18
-
19
- #############################################################################
20
- # Setup remote access to Bitbucket
21
- # Get Username, Repo, and PW and perform necessary HTTP calls to check validity
22
- def setup(config)
23
-
24
- # Identify method entry
25
- debug_print "#{ self.class } : #{ __method__ }\n"
26
-
27
- Printer.print_status "+", GREEN
28
- print BOLD + "Attempting to access Bitbucket...\n" + RESET
29
-
30
- # Check config to make sure no previous repo info exists
31
- unless config.bitbucket_api.empty? && config.bitbucket_repo.empty?
32
- Printer.print_status "!", RED
33
- print BOLD + "Previous Bitbucket API + Repo is in RC, are you sure you want to overwrite?\n" + RESET
34
- print " (Y)es/(N)o: "
35
-
36
- # Get user input
37
- _overwrite = $stdin.gets.chomp
38
- if ["no", "n"].include?(_overwrite.downcase)
39
- print "\n"
40
- Printer.print_status "x", RED
41
- print BOLD + "Not overwriting current Bitbucket API + repo info\n" + RESET
42
- return false
43
- end
44
- end
45
-
46
-
47
- Printer.print_status "!", YELLOW
48
- print BOLD + "Access to your Bitbucket account required to make/update issues\n" + RESET
49
- print " See help or README for more details on GitHub/Bitbucket access\n\n"
50
-
51
-
52
- # [todo] - Bitbucket OAuth not implemented yet so warn user about HTTP Auth
53
- # Bitbucket doesn't have nonOAuth flow that GitHub does :(
54
- # Even if I use OAuth lib, still need to validate from webview which is lame
55
- Printer.print_status "!", RED
56
- print BOLD + "Bitbucket OAuth not implemented yet.\n" + RESET;
57
- print " Basic HTTP Auth in use, will request PW entry every time.\n\n"
58
-
59
-
60
- # [todo] - Don't just check for blank password but invalid as well
61
- # Poor mans username/password grabbing
62
- print BOLD + "Username: " + RESET
63
- _username = $stdin.gets.chomp
64
- if _username.empty?
65
- Printer.print_status "x", RED
66
- print BOLD + "Input blank. Please enter your username!\n\n" + RESET
67
- return false
68
- end
69
-
70
- print "\n"
71
-
72
- # Get repo information, if blank give error
73
- Printer.print_status "!", YELLOW
74
- print BOLD + "Repo information required\n" + RESET
75
- print " Please provide owner that repo is under followed by repo name\n"
76
- print " e.g. owner: nhmood, repo: watson (case sensitive)\n"
77
- print " See help or README for more details on GitHub access\n\n"
78
-
79
- print BOLD + "Owner: " + RESET
80
- _owner = $stdin.gets.chomp
81
- if _owner.empty?
82
- print "\n"
83
- Printer.print_status "x", RED
84
- print BOLD + "Input blank. Please enter the owner the repo is under!\n\n" + RESET
85
- return false
86
- end
87
-
88
- print BOLD + "Repo: " + RESET
89
- _repo = $stdin.gets.chomp
90
- if _repo.empty?
91
- print "\n"
92
- Printer.print_status "x", RED
93
- print BOLD + "Input blank. Please enter the repo name!\n\n" + RESET
94
- return false
95
- end
96
-
97
- print "\n"
98
-
99
- # [fix] - Crossplatform password block needed, not sure if current method is safe either
100
- # Block output to tty to prevent PW showing, Linux/Unix only :(
101
- print BOLD + "Password: " + RESET
102
- system "stty -echo"
103
- _password = $stdin.gets.chomp
104
- system "stty echo"
105
- print "\n"
106
- if _password.empty?
107
- Printer.print_status "x", RED
108
- print BOLD + "Input is blank. Please enter your password!\n\n" + RESET
109
- return false
110
- end
111
-
112
- # HTTP Request to check if Repo exists and user has access
113
- # http://confluence.atlassian.com/display/BITBUCKET/Use+the+Bitbucket+REST+APIs
114
-
115
- # Create options hash to pass to Remote::http_call
116
- # Endpoint for accessing Repo as User with SSL
117
- # Basic auth with user input
118
- opts = {:url => "https://bitbucket.org/api/1.0/repositories/#{_owner}/#{_repo}",
119
- :ssl => true,
120
- :method => "GET",
121
- :basic_auth => [_username, _password],
122
- :verbose => false
123
- }
124
-
125
- _json, _resp = Watson::Remote.http_call(opts)
126
-
127
- # Check response to validate authorization
128
- if _resp.code == "200"
129
- print "\n"
130
- Printer.print_status "o", GREEN
131
- print BOLD + "Successfully accessed remote repo with given credentials\n" + RESET
132
- else
133
- print "\n"
134
- Printer.print_status "x", RED
135
- print BOLD + "Unable to access /#{ _owner }/#{ _repo } with given credentials\n" + RESET
136
- print " Check that credentials are correct and repository exists under user\n"
137
- print " Status: #{ _resp.code } - #{ _resp.message }\n\n"
138
- return false
139
- end
140
-
141
-
142
- # No OAuth for Bitbucket yet so just store username in api for config
143
- # This will let us just prompt for PW
144
- config.bitbucket_api = _owner
145
- config.bitbucket_pw = _password # Never gets written to file
146
- config.bitbucket_repo = _repo
147
- debug_print " \n"
148
-
149
- # All setup has been completed, need to update RC
150
- # Call config updater/writer from @config to write config
151
- debug_print "Updating config with new Bitbucket info\n"
152
- config.update_conf("bitbucket_api", "bitbucket_repo")
153
-
154
- print "\n"
155
- Printer.print_status "o", GREEN
156
- print BOLD + "Bitbucket successfully setup\n" + RESET
157
- print " Issues will now automatically be retrieved from Bitbucket by default\n"
158
- print " Use -p, --push to post issues to GitHub\n"
159
- print " See help or README for more details on GitHub/Bitbucket access\n\n"
160
-
161
- return true
162
-
163
- end
164
-
165
-
166
- ###########################################################
167
- # Get all remote Bitbucket issues and store into Config container class
168
- def get_issues(config)
169
-
170
- # Identify method entry
171
- debug_print "#{ self.class } : #{ __method__ }\n"
172
-
173
- # Only attempt to get issues if API is specified
174
- if config.bitbucket_api.empty?
175
- debug_print "No API found, this shouldn't be called...\n"
176
- return false
177
- end
178
-
179
-
180
- # If we haven't obtained the pw from user yet, do it
181
- if config.bitbucket_pw.empty?
182
- # No OAuth for Bitbucket yet, gotta get user password in order to make calls :(
183
- Printer.print_status "!", YELLOW
184
- print BOLD + "Bitbucket password required for remote checking/posting.\n" + RESET
185
- print " Password: "
186
-
187
- # Block output to tty to prevent PW showing, Linux/Unix only :(
188
- system "stty -echo"
189
- _password = $stdin.gets.chomp
190
- system "stty echo"
191
- if _password.empty?
192
- print "Input is blank. Please enter your password!\n"
193
- return false
194
- else
195
- print "\n"
196
- end
197
-
198
- config.bitbucket_pw = _password
199
- end
200
-
201
-
202
- # Get all open tickets (anything but resolved)
203
- # Create options hash to pass to Remote::http_call
204
- # Issues URL for Bitbucket + SSL
205
- opts = {:url => "https://bitbucket.org/api/1.0/repositories/#{ config.bitbucket_api }/#{ config.bitbucket_repo }/issues?status=!resolved",
206
- :ssl => true,
207
- :method => "GET",
208
- :basic_auth => [config.bitbucket_api, config.bitbucket_pw],
209
- :verbose => false
210
- }
211
-
212
- _json, _resp = Watson::Remote.http_call(opts)
213
-
214
-
215
- # Check response to validate repo access
216
- if _resp.code != "200"
217
- Printer.print_status "x", RED
218
- print BOLD + "Unable to access remote #{ config.bitbucket_repo }, Bitbucket API may be invalid\n" + RESET
219
- print " Make sure you have created an issue tracker for your repository on the Bitbucket website\n"
220
- print " Consider running --remote (-r) option to regenerate/validate settings\n"
221
- print " Status: #{ _resp.code } - #{ _resp.message }\n\n"
222
-
223
- debug_print "Bitbucket invalid, setting config var\n"
224
- config.bitbucket_valid = false
225
- return false
226
- end
227
-
228
-
229
-
230
- config.bitbucket_issues[:open] = _json["issues"].empty? ? Hash.new : _json["issues"]
231
- config.bitbucket_valid = true
232
-
233
- # Get all closed tickets
234
- # Create options hash to pass to Remote::http_call
235
- # Issues URL for Bitbucket + SSL
236
- opts = {:url => "https://bitbucket.org/api/1.0/repositories/#{ config.bitbucket_api }/#{ config.bitbucket_repo }/issues?status=resolved",
237
- :ssl => true,
238
- :method => "GET",
239
- :basic_auth => [config.bitbucket_api, config.bitbucket_pw],
240
- :verbose => false
241
- }
242
-
243
- _json, _resp = Watson::Remote.http_call(opts)
244
-
245
- # Check response to validate repo access
246
- # Shouldn't be necessary if we passed the last check but just to be safe
247
- if _resp.code != "200"
248
- Printer.print_status "x", RED
249
- print BOLD + "Unable to get closed issues.\n" + RESET
250
- print " Since the open issues were obtained, something is probably wrong and you should file a bug report or something...\n"
251
- print " Status: #{ _resp.code } - #{ _resp.message }\n"
252
-
253
- debug_print "Bitbucket invalid, setting config var\n"
254
- config.bitbucket_valid = false
255
- return false
256
- end
257
-
258
- config.bitbucket_issues[:closed] = _json["issues"].empty? ? Hash.new : _json["issues"]
259
- config.bitbucket_valid = true
260
- return true
261
- end
262
-
263
-
264
- ###########################################################
265
- # Post given issue to remote Bitbucket repo
266
- def post_issue(issue, config)
267
- # [todo] - Better way to identify/compare remote->local issues than md5
268
- # Current md5 based on some things that easily can change, need better ident
269
-
270
- # Identify method entry
271
- debug_print "#{self.class} : #{__method__}\n"
272
-
273
-
274
- # Only attempt to get issues if API is specified
275
- if config.bitbucket_api.empty?
276
- debug_print "No API found, this shouldn't be called...\n"
277
- return false
278
- end
279
-
280
- # Check that issue hasn't been posted already by comparing md5s
281
- # Go through all open issues, if there is a match in md5, return out of method
282
- # [todo] - Play with idea of making body of GitHub issue hash format to be exec'd
283
- # Store pieces in text as :md5 => "whatever" so when we get issues we can
284
- # call exec and turn it into a real hash for parsing in watson
285
- # Makes watson code cleaner but not as readable comment on GitHub...?
286
- debug_print "Checking open issues to see if already posted\n"
287
- config.bitbucket_issues[:open].each do | _open |
288
- if _open["content"].include?(issue[:md5])
289
- debug_print "Found in #{ _open["title"] }, not posting\n"
290
- return false
291
- end
292
- debug_print "Did not find in #{_open["title"]}\n"
293
- end
294
-
295
- debug_print "Checking closed issues to see if already posted\n"
296
- config.bitbucket_issues[:closed].each do | _closed |
297
- if _closed["content"].include?(issue[:md5])
298
- debug_print "Found in #{ _closed["title"] }, not posting\n"
299
- return false
300
- end
301
- debug_print "Did not find in #{ _closed["title"] }\n"
302
- end
303
-
304
-
305
- # If we haven't obtained the pw from user yet, do it
306
- if config.bitbucket_pw.empty?
307
- # No OAuth for Bitbucket yet, gotta get user password in order to make calls :(
308
- Printer.print_status "!", YELLOW
309
- print BOLD + "Bitbucket password required for remote checking/posting.\n" + RESET
310
- print " Password: "
311
-
312
- # Block output to tty to prevent PW showing, Linux/Unix only :(
313
- print "Password: "
314
- system "stty -echo"
315
- _password = $stdin.gets.chomp
316
- system "stty echo"
317
- if _password.empty?
318
- print "Input is blank. Please enter your password!\n"
319
- return false
320
- else
321
- print "\n"
322
- end
323
-
324
- config.bitbucket_pw = _password
325
- end
326
-
327
-
328
-
329
-
330
- # We didn't find the md5 for this issue in the open or closed issues, so safe to post
331
-
332
- # Create the body text for the issue here, too long to fit nicely into opts hash
333
- # [review] - Only give relative path for privacy when posted
334
- _body = "__filename__ : #{ issue[:path] } \n" +
335
- "__line #__ : #{ issue[:line_number] } \n" +
336
- "__tag__ : #{ issue[:tag] } \n" +
337
- "__md5__ : #{ issue[:md5] } \n\n" +
338
- "#{ issue[:context].join }"
339
-
340
- # Create option hash to pass to Remote::http_call
341
- # Issues URL for GitHub + SSL
342
- # No tag or label concept in Bitbucket unfortunately :(
343
- opts = {:url => "https://bitbucket.org/api/1.0/repositories/#{ config.bitbucket_api }/#{ config.bitbucket_repo }/issues",
344
- :ssl => true,
345
- :method => "POST",
346
- :basic_auth => [config.bitbucket_api, config.bitbucket_pw],
347
- :data => [{"title" => issue[:title] + " [#{ issue[:path] }]",
348
- "content" => _body }],
349
- :verbose => false
350
- }
351
-
352
- _json, _resp = Watson::Remote.http_call(opts)
353
-
354
-
355
- # Check response to validate repo access
356
- # Shouldn't be necessary if we passed the last check but just to be safe
357
- if _resp.code != "200"
358
- Printer.print_status "x", RED
359
- print BOLD + "Post unsuccessful. \n" + RESET
360
- print " Since the open issues were obtained earlier, something is probably wrong and you should let someone know...\n"
361
- print " Status: #{ _resp.code } - #{ _resp.message }\n"
362
- return false
363
- end
364
-
365
- return true
366
- end
367
-
368
- end
369
-
370
-
371
- end
372
- end
2
+ class Remote
3
+ # Bitbucket remote access class
4
+ # Contains all necessary methods to obtain access to, get issue list,
5
+ # and post issues to Bitbucket
6
+ class Bitbucket
7
+
8
+ # Debug printing for this class
9
+ DEBUG = false
10
+
11
+ class << self
12
+
13
+ # [todo] - Allow closing of issues from watson? Don't like that idea but maybe
14
+ # [todo] - Wrap Bitbucket password grabbing into separate method
15
+
16
+ # Include for debug_print
17
+ include Watson
18
+
19
+ #############################################################################
20
+ # Setup remote access to Bitbucket
21
+ # Get Username, Repo, and PW and perform necessary HTTP calls to check validity
22
+ def setup(config)
23
+
24
+ # Identify method entry
25
+ debug_print "#{ self.class } : #{ __method__ }\n"
26
+
27
+ Printer.print_status "+", GREEN
28
+ print BOLD + "Attempting to access Bitbucket...\n" + RESET
29
+
30
+ # Check config to make sure no previous repo info exists
31
+ unless config.bitbucket_api.empty? && config.bitbucket_repo.empty?
32
+ Printer.print_status "!", RED
33
+ print BOLD + "Previous Bitbucket API + Repo is in RC, are you sure you want to overwrite?\n" + RESET
34
+ print " (Y)es/(N)o: "
35
+
36
+ # Get user input
37
+ _overwrite = $stdin.gets.chomp
38
+ if ["no", "n"].include?(_overwrite.downcase)
39
+ print "\n"
40
+ Printer.print_status "x", RED
41
+ print BOLD + "Not overwriting current Bitbucket API + repo info\n" + RESET
42
+ return false
43
+ end
44
+ end
45
+
46
+
47
+ Printer.print_status "!", YELLOW
48
+ print BOLD + "Access to your Bitbucket account required to make/update issues\n" + RESET
49
+ print " See help or README for more details on GitHub/Bitbucket access\n\n"
50
+
51
+
52
+ # [todo] - Bitbucket OAuth not implemented yet so warn user about HTTP Auth
53
+ # Bitbucket doesn't have nonOAuth flow that GitHub does :(
54
+ # Even if I use OAuth lib, still need to validate from webview which is lame
55
+ Printer.print_status "!", RED
56
+ print BOLD + "Bitbucket OAuth not implemented yet.\n" + RESET;
57
+ print " Basic HTTP Auth in use, will request PW entry every time.\n\n"
58
+
59
+
60
+ # [todo] - Don't just check for blank password but invalid as well
61
+ # Poor mans username/password grabbing
62
+ print BOLD + "Username: " + RESET
63
+ _username = $stdin.gets.chomp
64
+ if _username.empty?
65
+ Printer.print_status "x", RED
66
+ print BOLD + "Input blank. Please enter your username!\n\n" + RESET
67
+ return false
68
+ end
69
+
70
+ print "\n"
71
+
72
+ # Get repo information, if blank give error
73
+ Printer.print_status "!", YELLOW
74
+ print BOLD + "Repo information required\n" + RESET
75
+ print " Please provide owner that repo is under followed by repo name\n"
76
+ print " e.g. owner: nhmood, repo: watson (case sensitive)\n"
77
+ print " See help or README for more details on GitHub access\n\n"
78
+
79
+ print BOLD + "Owner: " + RESET
80
+ _owner = $stdin.gets.chomp
81
+ if _owner.empty?
82
+ print "\n"
83
+ Printer.print_status "x", RED
84
+ print BOLD + "Input blank. Please enter the owner the repo is under!\n\n" + RESET
85
+ return false
86
+ end
87
+
88
+ print BOLD + "Repo: " + RESET
89
+ _repo = $stdin.gets.chomp
90
+ if _repo.empty?
91
+ print "\n"
92
+ Printer.print_status "x", RED
93
+ print BOLD + "Input blank. Please enter the repo name!\n\n" + RESET
94
+ return false
95
+ end
96
+
97
+ print "\n"
98
+
99
+ # [fix] - Crossplatform password block needed, not sure if current method is safe either
100
+ # Block output to tty to prevent PW showing, Linux/Unix only :(
101
+ print BOLD + "Password: " + RESET
102
+ system "stty -echo"
103
+ _password = $stdin.gets.chomp
104
+ system "stty echo"
105
+ print "\n"
106
+ if _password.empty?
107
+ Printer.print_status "x", RED
108
+ print BOLD + "Input is blank. Please enter your password!\n\n" + RESET
109
+ return false
110
+ end
111
+
112
+ # HTTP Request to check if Repo exists and user has access
113
+ # http://confluence.atlassian.com/display/BITBUCKET/Use+the+Bitbucket+REST+APIs
114
+
115
+ # Create options hash to pass to Remote::http_call
116
+ # Endpoint for accessing Repo as User with SSL
117
+ # Basic auth with user input
118
+ opts = {:url => "https://bitbucket.org/api/1.0/repositories/#{_owner}/#{_repo}",
119
+ :ssl => true,
120
+ :method => "GET",
121
+ :basic_auth => [_username, _password],
122
+ :verbose => false
123
+ }
124
+
125
+ _json, _resp = Watson::Remote.http_call(opts)
126
+
127
+ # Check response to validate authorization
128
+ if _resp.code == "200"
129
+ print "\n"
130
+ Printer.print_status "o", GREEN
131
+ print BOLD + "Successfully accessed remote repo with given credentials\n" + RESET
132
+ else
133
+ print "\n"
134
+ Printer.print_status "x", RED
135
+ print BOLD + "Unable to access /#{ _owner }/#{ _repo } with given credentials\n" + RESET
136
+ print " Check that credentials are correct and repository exists under user\n"
137
+ print " Status: #{ _resp.code } - #{ _resp.message }\n\n"
138
+ return false
139
+ end
140
+
141
+
142
+ # No OAuth for Bitbucket yet so just store username in api for config
143
+ # This will let us just prompt for PW
144
+ config.bitbucket_api = _username
145
+ config.bitbucket_pw = _password # Never gets written to file
146
+ config.bitbucket_repo = "#{ _owner }/#{ _repo }"
147
+ debug_print " \n"
148
+
149
+ # All setup has been completed, need to update RC
150
+ # Call config updater/writer from @config to write config
151
+ debug_print "Updating config with new Bitbucket info\n"
152
+ config.update_conf("bitbucket_api", "bitbucket_repo")
153
+
154
+ print "\n"
155
+ Printer.print_status "o", GREEN
156
+ print BOLD + "Bitbucket successfully setup\n" + RESET
157
+ print " Issues will now automatically be retrieved from Bitbucket by default\n"
158
+ print " Use -p, --push to post issues to GitHub\n"
159
+ print " See help or README for more details on GitHub/Bitbucket access\n\n"
160
+
161
+ return true
162
+
163
+ end
164
+
165
+
166
+ ###########################################################
167
+ # Get all remote Bitbucket issues and store into Config container class
168
+ def get_issues(config)
169
+
170
+ # Identify method entry
171
+ debug_print "#{ self.class } : #{ __method__ }\n"
172
+
173
+ # Only attempt to get issues if API is specified
174
+ if config.bitbucket_api.empty?
175
+ debug_print "No API found, this shouldn't be called...\n"
176
+ return false
177
+ end
178
+
179
+ # If we haven't obtained the pw from user yet, do it
180
+ if config.bitbucket_pw.empty?
181
+ # No OAuth for Bitbucket yet, gotta get user password in order to make calls :(
182
+ Printer.print_status "!", YELLOW
183
+ print BOLD + "Bitbucket password required for remote checking/posting.\n" + RESET
184
+ print " Password: "
185
+
186
+ # Block output to tty to prevent PW showing, Linux/Unix only :(
187
+ system "stty -echo"
188
+ _password = $stdin.gets.chomp
189
+ system "stty echo"
190
+ if _password.empty?
191
+ print "Input is blank. Please enter your password!\n"
192
+ return false
193
+ else
194
+ print "\n"
195
+ end
196
+
197
+ config.bitbucket_pw = _password
198
+ end
199
+
200
+
201
+ # Get all open tickets (anything but resolved)
202
+ # Create options hash to pass to Remote::http_call
203
+ # Issues URL for Bitbucket + SSL
204
+ opts = {:url => "https://bitbucket.org/api/1.0/repositories/#{ config.bitbucket_repo }/issues?status=!resolved",
205
+ :ssl => true,
206
+ :method => "GET",
207
+ :basic_auth => [config.bitbucket_api, config.bitbucket_pw],
208
+ :verbose => false
209
+ }
210
+
211
+ _json, _resp = Watson::Remote.http_call(opts)
212
+
213
+
214
+ # Check response to validate repo access
215
+ if _resp.code != "200"
216
+ Printer.print_status "x", RED
217
+ print BOLD + "Unable to access remote #{ config.bitbucket_repo }, Bitbucket API may be invalid\n" + RESET
218
+ print " Make sure you have created an issue tracker for your repository on the Bitbucket website\n"
219
+ print " Consider running --remote (-r) option to regenerate/validate settings\n"
220
+ print " Status: #{ _resp.code } - #{ _resp.message }\n\n"
221
+
222
+ debug_print "Bitbucket invalid, setting config var\n"
223
+ config.bitbucket_valid = false
224
+ return false
225
+ end
226
+
227
+
228
+
229
+ config.bitbucket_issues[:open] = _json["issues"].empty? ? Hash.new : _json["issues"]
230
+ config.bitbucket_valid = true
231
+
232
+ # Get all closed tickets
233
+ # Create options hash to pass to Remote::http_call
234
+ # Issues URL for Bitbucket + SSL
235
+ opts = {:url => "https://bitbucket.org/api/1.0/repositories/#{ config.bitbucket_repo }/issues?status=resolved",
236
+ :ssl => true,
237
+ :method => "GET",
238
+ :basic_auth => [config.bitbucket_api, config.bitbucket_pw],
239
+ :verbose => false
240
+ }
241
+
242
+ _json, _resp = Watson::Remote.http_call(opts)
243
+
244
+ # Check response to validate repo access
245
+ # Shouldn't be necessary if we passed the last check but just to be safe
246
+ if _resp.code != "200"
247
+ Printer.print_status "x", RED
248
+ print BOLD + "Unable to get closed issues.\n" + RESET
249
+ print " Since the open issues were obtained, something is probably wrong and you should file a bug report or something...\n"
250
+ print " Status: #{ _resp.code } - #{ _resp.message }\n"
251
+
252
+ debug_print "Bitbucket invalid, setting config var\n"
253
+ config.bitbucket_valid = false
254
+ return false
255
+ end
256
+
257
+ config.bitbucket_issues[:closed] = _json["issues"].empty? ? Hash.new : _json["issues"]
258
+ config.bitbucket_valid = true
259
+ return true
260
+ end
261
+
262
+
263
+ ###########################################################
264
+ # Post given issue to remote Bitbucket repo
265
+ def post_issue(issue, config)
266
+ # [todo] - Better way to identify/compare remote->local issues than md5
267
+ # Current md5 based on some things that easily can change, need better ident
268
+
269
+ # Identify method entry
270
+ debug_print "#{self.class} : #{__method__}\n"
271
+
272
+
273
+ # Only attempt to get issues if API is specified
274
+ if config.bitbucket_api.empty?
275
+ debug_print "No API found, this shouldn't be called...\n"
276
+ return false
277
+ end
278
+
279
+ # Check that issue hasn't been posted already by comparing md5s
280
+ # Go through all open issues, if there is a match in md5, return out of method
281
+ # [todo] - Play with idea of making body of GitHub issue hash format to be exec'd
282
+ # Store pieces in text as :md5 => "whatever" so when we get issues we can
283
+ # call exec and turn it into a real hash for parsing in watson
284
+ # Makes watson code cleaner but not as readable comment on GitHub...?
285
+ debug_print "Checking open issues to see if already posted\n"
286
+ config.bitbucket_issues[:open].each do | _open |
287
+ if _open["content"].include?(issue[:md5])
288
+ debug_print "Found in #{ _open["title"] }, not posting\n"
289
+ return false
290
+ end
291
+ debug_print "Did not find in #{_open["title"]}\n"
292
+ end
293
+
294
+ debug_print "Checking closed issues to see if already posted\n"
295
+ config.bitbucket_issues[:closed].each do | _closed |
296
+ if _closed["content"].include?(issue[:md5])
297
+ debug_print "Found in #{ _closed["title"] }, not posting\n"
298
+ return false
299
+ end
300
+ debug_print "Did not find in #{ _closed["title"] }\n"
301
+ end
302
+
303
+
304
+ # If we haven't obtained the pw from user yet, do it
305
+ if config.bitbucket_pw.empty?
306
+ # No OAuth for Bitbucket yet, gotta get user password in order to make calls :(
307
+ Printer.print_status "!", YELLOW
308
+ print BOLD + "Bitbucket password required for remote checking/posting.\n" + RESET
309
+ print " Password: "
310
+
311
+ # Block output to tty to prevent PW showing, Linux/Unix only :(
312
+ print "Password: "
313
+ system "stty -echo"
314
+ _password = $stdin.gets.chomp
315
+ system "stty echo"
316
+ if _password.empty?
317
+ print "Input is blank. Please enter your password!\n"
318
+ return false
319
+ else
320
+ print "\n"
321
+ end
322
+
323
+ config.bitbucket_pw = _password
324
+ end
325
+
326
+
327
+
328
+
329
+ # We didn't find the md5 for this issue in the open or closed issues, so safe to post
330
+
331
+ # Create the body text for the issue here, too long to fit nicely into opts hash
332
+ # [review] - Only give relative path for privacy when posted
333
+ _body = "__filename__ : #{ issue[:path] } \n" +
334
+ "__line #__ : #{ issue[:line_number] } \n" +
335
+ "__tag__ : #{ issue[:tag] } \n" +
336
+ "__md5__ : #{ issue[:md5] } \n\n" +
337
+ "#{ issue[:context].join }"
338
+
339
+ # Create option hash to pass to Remote::http_call
340
+ # Issues URL for GitHub + SSL
341
+ # No tag or label concept in Bitbucket unfortunately :(
342
+ opts = {:url => "https://bitbucket.org/api/1.0/repositories/#{ config.bitbucket_repo }/issues",
343
+ :ssl => true,
344
+ :method => "POST",
345
+ :basic_auth => [config.bitbucket_api, config.bitbucket_pw],
346
+ :data => [{"title" => issue[:title] + " [#{ issue[:path] }]",
347
+ "content" => _body }],
348
+ :verbose => false
349
+ }
350
+
351
+ _json, _resp = Watson::Remote.http_call(opts)
352
+
353
+
354
+ # Check response to validate repo access
355
+ # Shouldn't be necessary if we passed the last check but just to be safe
356
+ if _resp.code != "200"
357
+ Printer.print_status "x", RED
358
+ print BOLD + "Post unsuccessful. \n" + RESET
359
+ print " Since the open issues were obtained earlier, something is probably wrong and you should let someone know...\n"
360
+ print " Status: #{ _resp.code } - #{ _resp.message }\n"
361
+ return false
362
+ end
363
+
364
+ return true
365
+ end
366
+
367
+ end
368
+
369
+
370
+ end
371
+ end
373
372
  end