watson-ruby 1.4.3 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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