watson-ruby 1.4.3 → 1.5.0

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.
@@ -31,9 +31,9 @@ module Watson::Formatters
31
31
  debug_print "#{ self } : #{ __method__ }\n"
32
32
 
33
33
  # Header
34
- cprint <<-MESSAGE.gsub(/^ {6}/, '')
34
+ cprint <<-MESSAGE.gsub(/^(\s+)/, '')
35
35
  #{BOLD}------------------------------#{RESET}
36
- #{BOLD}watson#{RESET} - #{RESET}#{BOLD}#{YELLOW}inline issue manager#{RESET}
36
+ #{BOLD}watson#{RESET} - #{RESET}#{BOLD}#{YELLOW}inline issue manager\n#{RESET}
37
37
 
38
38
  Run in: #{Dir.pwd}
39
39
  Run @ #{Time.now.asctime}
@@ -142,31 +142,36 @@ module Watson::Formatters
142
142
 
143
143
  # Go through each issue in tag
144
144
  entry[tag].each do |issue|
145
- cprint "#{WHITE} line #{issue[:line_number]} - #{RESET}#{BOLD}#{issue[:title]}#{RESET}"
146
-
147
- # Check to see if it has been resolved on GitHub/Bitbucket
148
- debug_print "Checking if issue has been resolved\n"
149
- @config.github_issues[:closed].each do | _closed |
150
- if _closed["body"].include?(issue[:md5])
151
- debug_print "Found in #{ _closed[:comment] }, not posting\n"
152
- cprint <<-MESSAGE.gsub(/^(\s+)/, '')
153
- #{BOLD} [#{RESET}#{GREEN}#{BOLD}Resolved on GitHub#{RESET}#{BOLD}]#{RESET}
145
+ cprint "#{WHITE} line #{issue[:line_number]} - #{RESET}#{BOLD}#{issue[:title]} #{RESET}"
146
+
147
+
148
+ # If there are any remote issues, print status and issue #
149
+ if _GH = @config.github_issues[issue[:md5]]
150
+ debug_print "Found #{ issue[:title]} in remote issues\n"
151
+
152
+ cprint <<-MESSAGE.gsub(/^(\s+)/, '').chomp
153
+ #{BOLD}[#{RESET}#{_GH[:state] != "closed" ? RED : GREEN}#{BOLD}GH##{_GH[:id]}#{RESET}#{BOLD}]#{RESET}
154
+ MESSAGE
155
+ end
156
+
157
+ if _BB = @config.bitbucket_issues[issue[:md5]]
158
+ debug_print "Found #{ issue[:title]} in remote issues\n"
159
+
160
+ cprint <<-MESSAGE.gsub(/^(\s+)/, '').chomp
161
+ #{BOLD}[#{RESET}#{_BB[:state] != "resolved" ? RED : GREEN}#{BOLD}BB##{_BB[:id]}#{RESET}#{BOLD}]#{RESET}
154
162
  MESSAGE
155
- end
156
- debug_print "Did not find in #{ _closed[:comment] }\n"
157
163
  end
158
164
 
159
- debug_print "Checking if issue has been resolved\n"
160
- @config.bitbucket_issues[:closed].each do |closed|
161
- if closed['content'].include?(issue[:md5])
162
- debug_print "Found in #{ closed["content"] }, not posting\n"
163
- cprint <<-MESSAGE.gsub(/^(\s+)/, '')
164
- #{BOLD} [#{RESET}#{GREEN}#{BOLD}Resolved on Bitbucket#{RESET}#{BOLD}]#{RESET}
165
+
166
+ if _GL = @config.gitlab_issues[issue[:md5]]
167
+ debug_print "Found #{ issue[:title]} in remote issues\n"
168
+
169
+ cprint <<-MESSAGE.gsub(/^(\s+)/, '').chomp
170
+ #{BOLD}[#{RESET}#{_GL[:state] != "closed" ? RED : GREEN}#{BOLD}GL##{_GL[:id]}#{RESET}#{BOLD}]#{RESET}
165
171
  MESSAGE
166
- end
167
- debug_print "Did not find in #{ closed["title"] }\n"
168
172
  end
169
173
 
174
+
170
175
  cprint "\n"
171
176
  end
172
177
 
data/lib/watson/github.rb CHANGED
@@ -67,6 +67,12 @@ module Watson
67
67
  print BOLD + "Input blank. Please enter your API endpoint!\n\n" + RESET
68
68
  return false
69
69
  end
70
+
71
+ # Make sure we have the http(s)://
72
+ if !_endpoint.match(/(http|https):\/\//)
73
+ _endpoint = "http://#{_endpoint}"
74
+ end
75
+
70
76
  else
71
77
  _endpoint = ''
72
78
  end
@@ -105,15 +111,16 @@ module Watson
105
111
  # Auth URL for GitHub + SSL
106
112
  # Repo scope + notes for watson
107
113
  # Basic auth with user input
108
- opts = {:url => "#{ _endpoint }/authorizations",
109
- :ssl => true,
110
- :method => "POST",
111
- :basic_auth => [_username, _password],
112
- :data => {"scopes" => ["repo"],
113
- "note" => "watson",
114
- "note_url" => "http://watson.goosecode.com/" },
115
- :verbose => false
116
- }
114
+ opts = {
115
+ :url => "#{ _endpoint }/authorizations",
116
+ :ssl => true,
117
+ :method => "POST",
118
+ :basic_auth => [_username, _password],
119
+ :data => {"scopes" => ["repo"],
120
+ "note" => "watson",
121
+ "note_url" => "http://watson.goosecode.com/" },
122
+ :verbose => false
123
+ }
117
124
 
118
125
  _json, _resp = Watson::Remote.http_call(opts)
119
126
 
@@ -169,14 +176,15 @@ module Watson
169
176
  # Label URL for GitHub + SSL
170
177
  #
171
178
  # Auth token
172
- opts = {:url => "#{ _endpoint }/repos/#{ _owner }/#{ _repo }/labels",
173
- :ssl => true,
174
- :method => "POST",
175
- :auth => config.github_api,
176
- :data => {"name" => "watson",
177
- "color" => "00AEEF" },
178
- :verbose => false
179
- }
179
+ opts = {
180
+ :url => "#{ _endpoint }/repos/#{ _owner }/#{ _repo }/labels",
181
+ :ssl => true,
182
+ :method => "POST",
183
+ :auth => config.github_api,
184
+ :data => {"name" => "watson",
185
+ "color" => "00AEEF" },
186
+ :verbose => false
187
+ }
180
188
 
181
189
  _json, _resp = Watson::Remote.http_call(opts)
182
190
 
@@ -254,15 +262,16 @@ module Watson
254
262
  end
255
263
 
256
264
 
257
- # Get all open tickets
265
+ # Get all issues
258
266
  # Create options hash to pass to Remote::http_call
259
267
  # Issues URL for GitHub + SSL
260
- opts = {:url => "#{ config.github_endpoint }/repos/#{ config.github_repo }/issues?labels=watson&state=open",
261
- :ssl => true,
262
- :method => "GET",
263
- :auth => config.github_api,
264
- :verbose => false
265
- }
268
+ opts = {
269
+ :url => "#{ config.github_endpoint }/repos/#{ config.github_repo }/issues?labels=watson",
270
+ :ssl => true,
271
+ :method => "GET",
272
+ :auth => config.github_api,
273
+ :verbose => false
274
+ }
266
275
 
267
276
  _json, _resp = Watson::Remote.http_call(opts)
268
277
 
@@ -279,37 +288,25 @@ module Watson
279
288
  return false
280
289
  end
281
290
 
282
- config.github_issues[:open] = _json.empty? ? Hash.new : _json
283
- config.github_valid = true
284
291
 
285
- # Get all closed tickets
286
- # Create option hash to pass to Remote::http_call
287
- # Issues URL for GitHub + SSL
288
- opts = {:url => "#{ config.github_endpoint }/repos/#{ config.github_repo }/issues?labels=watson&state=closed",
289
- :ssl => true,
290
- :method => "GET",
291
- :auth => config.github_api,
292
- :verbose => false
293
- }
292
+ # Create hash entry from each returned issue
293
+ # MD5 of issue serves as hash key
294
+ # Hash value is another hash of info we will use
295
+ _json.each do |issue|
294
296
 
295
- _json, _resp = Watson::Remote.http_call(opts)
297
+ # Skip this issue if it doesn't have watson md5 tag
298
+ next if (_md5 = issue["body"].match(/__md5__ : (\w+)/)).nil?
296
299
 
297
- # Check response to validate repo access
298
- # Shouldn't be necessary if we passed the last check but just to be safe
299
- if _resp.code != "200"
300
- formatter.print_status "x", RED
301
- print BOLD + "Unable to get closed issues.\n" + RESET
302
- print " Since the open issues were obtained, something is probably wrong and you should file a bug report or something...\n"
303
- print " Status: #{ _resp.code } - #{ _resp.message }\n"
304
300
 
305
- debug_print "GitHub invalid, setting config var\n"
306
- config.github_valid = false
307
- return false
301
+ # If it does, use md5 as hash key and populate values with our info
302
+ config.github_issues[_md5[1]] = {
303
+ :title => issue["title"],
304
+ :id => issue["number"],
305
+ :state => issue["state"]
306
+ }
308
307
  end
309
308
 
310
- config.github_issues[:closed] = _json.empty? ? Hash.new : _json
311
309
  config.github_valid = true
312
- return true
313
310
  end
314
311
 
315
312
 
@@ -334,52 +331,33 @@ module Watson
334
331
  return false
335
332
  end
336
333
 
337
- # Check that issue hasn't been posted already by comparing md5s
338
- # Go through all open issues, if there is a match in md5, return out of method
339
- # [todo] - Play with idea of making body of GitHub issue hash format to be exec'd
340
- # Store pieces in text as :md5 => "whatever" so when we get issues we can
341
- # call exec and turn it into a real hash for parsing in watson
342
- # Makes watson code cleaner but not as readable comment on GitHub...?
343
- debug_print "Checking open issues to see if already posted\n"
344
- config.github_issues[:open].each do | _open |
345
- if _open["body"].include?(issue[:md5])
346
- debug_print "Found in #{ _open["title"] }, not posting\n"
347
- return false
348
- end
349
- debug_print "Did not find in #{ _open["title"] }\n"
350
- end
351
-
352
334
 
353
- debug_print "Checking closed issues to see if already posted\n"
354
- config.github_issues[:closed].each do | _closed |
355
- if _closed["body"].include?(issue[:md5])
356
- debug_print "Found in #{ _closed["title"] }, not posting\n"
357
- return false
358
- end
359
- debug_print "Did not find in #{ _closed["title"] }\n"
360
- end
335
+ return false if config.github_issues.key?(issue[:md5])
336
+ debug_print "#{issue[:md5]} not found in remote issues, posting\n"
361
337
 
362
338
  # We didn't find the md5 for this issue in the open or closed issues, so safe to post
363
339
 
364
340
  # Create the body text for the issue here, too long to fit nicely into opts hash
365
341
  # [review] - Only give relative path for privacy when posted
366
- _body = "__filename__ : #{ issue[:path] }\n" +
367
- "__line #__ : #{ issue[:line_number] }\n" +
368
- "__tag__ : #{ issue[:tag] }\n" +
369
- "__md5__ : #{ issue[:md5] }\n\n" +
370
- "#{ issue[:context].join }\n"
342
+ _body =
343
+ "__filename__ : #{ issue[:path] }\n" +
344
+ "__line #__ : #{ issue[:line_number] }\n" +
345
+ "__tag__ : #{ issue[:tag] }\n" +
346
+ "__md5__ : #{ issue[:md5] }\n\n" +
347
+ "#{ issue[:context].join }\n"
371
348
 
372
349
  # Create option hash to pass to Remote::http_call
373
350
  # Issues URL for GitHub + SSL
374
- opts = {:url => "#{ config.github_endpoint }/repos/#{ config.github_repo }/issues",
375
- :ssl => true,
376
- :method => "POST",
377
- :auth => config.github_api,
378
- :data => { "title" => issue[:title] + " [#{ issue[:path] }]",
379
- "labels" => [issue[:tag], "watson"],
380
- "body" => _body },
381
- :verbose => false
382
- }
351
+ opts = {
352
+ :url => "#{ config.github_endpoint }/repos/#{ config.github_repo }/issues",
353
+ :ssl => true,
354
+ :method => "POST",
355
+ :auth => config.github_api,
356
+ :data => { "title" => issue[:title] + " [#{ issue[:path] }]",
357
+ "labels" => [issue[:tag], "watson"],
358
+ "body" => _body },
359
+ :verbose => false
360
+ }
383
361
 
384
362
  _json, _resp = Watson::Remote.http_call(opts)
385
363
 
@@ -394,6 +372,12 @@ module Watson
394
372
  return false
395
373
  end
396
374
 
375
+ # Parse response and append issue hash so we are up to date
376
+ config.github_issues[issue[:md5]] = {
377
+ :title => _json["title"],
378
+ :id => _json["number"],
379
+ :state => _json["state"]
380
+ }
397
381
  return true
398
382
  end
399
383
 
@@ -0,0 +1,293 @@
1
+ module Watson
2
+ class Remote
3
+ # GitLab remote access class
4
+ # Contains all necessary methods to obtain access to, get issue list,
5
+ # and post issues to GitLab
6
+ class GitLab
7
+
8
+ # Debug printing for this class
9
+ DEBUG = false
10
+
11
+ class << self
12
+
13
+ # [todo] - Keep asking for user data until valid instead of leaving app
14
+
15
+ # Include for debug_print
16
+ include Watson
17
+
18
+ #############################################################################
19
+ # Setup remote access to GitLab
20
+ # Get Username, Repo, and PW and perform necessary HTTP calls to check validity
21
+ def setup(config)
22
+
23
+ # Identify method entry
24
+ debug_print "#{ self.class } : #{ __method__ }\n"
25
+
26
+ formatter = Printer.new(config).build_formatter
27
+ formatter.print_status "+", GREEN
28
+ print BOLD + "Obtaining OAuth Token for GitLab...\n" + RESET
29
+
30
+ # Check config to make sure no previous API exists
31
+ unless config.gitlab_api.empty? && config.gitlab_repo.empty? && config.gitlab_endpoint.empty?
32
+ formatter.print_status "!", RED
33
+ print BOLD + "Previous GitLab 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\n"
40
+ formatter.print_status "x", RED
41
+ print BOLD + "Not overwriting current GitLab API + repo info\n" + RESET
42
+ return false
43
+ end
44
+ end
45
+
46
+
47
+ formatter.print_status "!", YELLOW
48
+ print BOLD + "Access to your GitLab account required to make/update issues\n" + RESET
49
+ print " See help or README for more details on GitHub/Bitbucket/GitLab access\n\n"
50
+
51
+ print BOLD + "GitLab API Endpoint: " + RESET
52
+ _endpoint = $stdin.gets.chomp.chomp('/')
53
+ if _endpoint.empty?
54
+ formatter.print_status "x", RED
55
+ print BOLD + "Input blank. Please enter your API endpoint!\n\n" + RESET
56
+ return false
57
+ end
58
+
59
+ # Make sure we have the http(s)://
60
+ if !_endpoint.match(/(http|https):\/\//)
61
+ _endpoint = "http://#{_endpoint}"
62
+ end
63
+
64
+ print "\n"
65
+
66
+ # GitLab only requires private token to ID, can't get it with basic auth though
67
+ print BOLD + "GitLab Private Token: " + RESET
68
+ _token = $stdin.gets.chomp
69
+ if _token.empty?
70
+ formatter.print_status "x", RED
71
+ print BOLD + "Input blank. Please enter your private token!\n\n" + RESET
72
+ return false
73
+ end
74
+
75
+
76
+ # Get project to be synced against
77
+ print BOLD + "GitLab Project (ID or Namespace/Name): " + RESET
78
+ _repo = $stdin.gets.chomp
79
+ if _repo.empty?
80
+ formatter.print_status "x", RED
81
+ print BOLD + "Input blank. Please enter project!\n\n" + RESET
82
+ return false
83
+ end
84
+
85
+ # Format project to GitLab specs (i.e. if Namespace/Name, need to URL encode /)
86
+ if !_repo.match(/\d+/)
87
+ _repo = URI.escape(_repo, "/")
88
+ end
89
+
90
+ # HTTP Request to make sure we have access to project
91
+ # GitLab API - http://api.gitlab.org/
92
+
93
+ # Create options hash to pass to Remote::http_call
94
+ # Project URL + SSL
95
+
96
+ opts = {
97
+ :url => "#{ _endpoint }/api/v3/projects/#{ _repo }",
98
+ :ssl => _endpoint.match(/^https/) ? true : false,
99
+ :method => "GET",
100
+ :headers => [ { :field => "PRIVATE-TOKEN", :value => _token } ],
101
+ :verbose => false
102
+ }
103
+
104
+ _json, _resp = Watson::Remote.http_call(opts)
105
+
106
+ # Check response to validate authorization
107
+ if _resp.code == "200"
108
+ formatter.print_status "o", GREEN
109
+ print BOLD + "Succsesfully accessed GitLab with specified settings\n\n" + RESET
110
+ else
111
+ formatter.print_status "x", RED
112
+ print BOLD + "Unable to access GitLab with specified settings\n" + RESET
113
+ print " Status: #{ _resp.code } - #{ _resp.message }\n\n"
114
+ return false
115
+ end
116
+
117
+ # Store endpoint and API key obtained from POST to @config.gitlab_api
118
+ config.gitlab_endpoint = _endpoint
119
+ config.gitlab_api = _token
120
+ config.gitlab_repo = URI.unescape(_repo) # Unescape for config readability
121
+ debug_print "Config GitLab API Endpoint updated to: #{ config.gitlab_endpoint }\n"
122
+ debug_print "Config GitLab API Key updated to: #{ config.gitlab_api }\n"
123
+ debug_print "Config GitLab Repo updated to: #{ config.gitlab_repo }\n"
124
+
125
+
126
+
127
+ # All setup has been completed, need to update RC
128
+ # Call config updater/writer from @config to write config
129
+ debug_print "Updating config with new GitLab info\n"
130
+ config.update_conf("gitlab_api", "gitlab_repo", "gitlab_endpoint")
131
+
132
+ # Give user some info
133
+ print "\n"
134
+ formatter.print_status "o", GREEN
135
+ print BOLD + "GitLab successfully setup\n" + RESET
136
+ print " Issues will now automatically be retrieved from GitLab by default\n"
137
+ print " Use -u, --update to post issues to GitLab\n"
138
+ print " See help or README for more details on GitHub/Bitbucket/GitLab access\n\n"
139
+
140
+ return true
141
+
142
+ end
143
+
144
+
145
+ ###########################################################
146
+ # Get all remote GitLab issues and store into Config container class
147
+ def get_issues(config)
148
+
149
+ # Identify method entry
150
+ debug_print "#{ self.class } : #{ __method__ }\n"
151
+
152
+ # Set up formatter for printing errors
153
+ # config.output_format should be set based on less status by now
154
+ formatter = Printer.new(config).build_formatter
155
+
156
+ # Only attempt to get issues if API is specified
157
+ if config.gitlab_api.empty?
158
+ debug_print "No API found, this shouldn't be called...\n"
159
+ return false
160
+ end
161
+
162
+
163
+ # GitLab API doesn't allow you to filter based on state (or anything for that matter...)
164
+ # Grab all the issues and then sort through them based on state
165
+
166
+ # Get all issues
167
+ # Create options hash to pass to Remote::http_call
168
+ # Use URI.escape so config file is readable
169
+ opts = {
170
+ :url => "#{ config.gitlab_endpoint }/api/v3/projects/#{ URI.escape(config.gitlab_repo, "/") }/issues",
171
+ :ssl => config.gitlab_endpoint.match(/^https/) ? true : false,
172
+ :method => "GET",
173
+ :headers => [ { :field => "PRIVATE-TOKEN", :value => config.gitlab_api } ],
174
+ :verbose => false
175
+ }
176
+
177
+ _json, _resp = Watson::Remote.http_call(opts)
178
+
179
+ # Check response to validate repo access
180
+ if _resp.code != "200"
181
+ formatter.print_status "x", RED
182
+ print BOLD + "Unable to access remote #{ config.gitlab_repo }, GitLab API may be invalid\n" + RESET
183
+ print " Consider running --remote (-r) option to regenerate key\n\n"
184
+ print " Status: #{ _resp.code } - #{ _resp.message }\n"
185
+
186
+ debug_print "GitLab invalid, setting config var\n"
187
+ config.gitlab_valid = false
188
+ return false
189
+ end
190
+
191
+
192
+
193
+ # Create hash entry from each returned issue
194
+ # MD5 of issue serves as hash key
195
+ # Hash value is another hash of info we will use
196
+ _json.each do |issue|
197
+
198
+ # Skip this issue if it doesn't have watson md5 tag
199
+ next if (_md5 = issue["description"].match(/__md5__ : (\w+)/)).nil?
200
+
201
+ # If it does, use md5 as hash key and populate values with our info
202
+ config.gitlab_issues[_md5[1]] = {
203
+ :title => issue["title"],
204
+ :id => issue["iid"],
205
+ :state => issue["state"]
206
+ }
207
+ end
208
+
209
+ config.gitlab_valid = true
210
+ end
211
+
212
+
213
+ ###########################################################
214
+ # Post given issue to remote GitLab repo
215
+ def post_issue(issue, config)
216
+ # [todo] - Better way to identify/compare remote->local issues than md5
217
+ # Current md5 based on some things that easily can change, need better ident
218
+
219
+ # Identify method entry
220
+ debug_print "#{ self.class } : #{ __method__ }\n"
221
+
222
+
223
+ # Set up formatter for printing errors
224
+ # config.output_format should be set based on less status by now
225
+ formatter = Printer.new(config).build_formatter
226
+
227
+
228
+ # Only attempt to get issues if API is specified
229
+ if config.gitlab_api.empty?
230
+ debug_print "No API found, this shouldn't be called...\n"
231
+ return false
232
+ end
233
+
234
+
235
+
236
+ return false if config.gitlab_issues.key?(issue[:md5])
237
+ debug_print "#{issue[:md5]} not found in remote issues, posting\n"
238
+
239
+
240
+ # We didn't find the md5 for this issue in the open or closed issues, so safe to post
241
+
242
+ # Create the body text for the issue here, too long to fit nicely into opts hash
243
+ # [review] - Only give relative path for privacy when posted
244
+ _body =
245
+ "__filename__ : #{ issue[:path] }\n" +
246
+ "__line #__ : #{ issue[:line_number] }\n" +
247
+ "__tag__ : #{ issue[:tag] }\n" +
248
+ "__md5__ : #{ issue[:md5] }\n\n" +
249
+ "#{ issue[:context].join }\n"
250
+
251
+
252
+
253
+ # Create option hash to pass to Remote::http_call
254
+ # Issues URL for GitLab
255
+ opts = {
256
+ :url => "#{ config.gitlab_endpoint }/api/v3/projects/#{ URI.escape(config.gitlab_repo, "/") }/issues",
257
+ :ssl => config.gitlab_endpoint.match(/^https/) ? true : false,
258
+ :method => "POST",
259
+ :headers => [ { :field => "PRIVATE-TOKEN", :value => config.gitlab_api } ],
260
+ :data => [{ "title" => issue[:title] + " [#{ issue[:path] }]",
261
+ "labels" => "#{issue[:tag]}, watson",
262
+ "description" => _body }],
263
+ :verbose => false
264
+ }
265
+
266
+ _json, _resp = Watson::Remote.http_call(opts)
267
+
268
+
269
+ # Check response to validate repo access
270
+ # Shouldn't be necessary if we passed the last check but just to be safe
271
+ if _resp.code != "201"
272
+ formatter.print_status "x", RED
273
+ print BOLD + "Post unsuccessful. \n" + RESET
274
+ print " Since the open issues were obtained earlier, something is probably wrong and you should let someone know...\n"
275
+ print " Status: #{ _resp.code } - #{ _resp.message }\n"
276
+ return false
277
+ end
278
+
279
+ # Parse response and append issue hash so we are up to date
280
+ config.gitlab_issues[issue[:md5]] = {
281
+ :title => _json["title"],
282
+ :id => _json["iid"],
283
+ :state => _json["state"]
284
+ }
285
+
286
+
287
+ return true
288
+ end
289
+
290
+ end
291
+ end
292
+ end
293
+ end