watson-ruby 1.0.3 → 1.0.4

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 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