git_reflow 0.3.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/git_reflow.rb CHANGED
@@ -7,67 +7,22 @@ require 'json/pure'
7
7
  require 'colorize'
8
8
 
9
9
  require 'git_reflow/version.rb' unless defined?(GitReflow::VERSION)
10
+ require 'git_reflow/config'
11
+ require 'git_reflow/git_server'
12
+ require 'git_reflow/git_server/git_hub'
13
+ require 'git_reflow/sandbox'
14
+ require 'git_reflow/git_helpers'
10
15
 
11
16
  module GitReflow
17
+ include Sandbox
18
+ include GitHelpers
19
+
12
20
  extend self
13
21
 
14
22
  LGTM = /lgtm|looks good to me|:\+1:|:thumbsup:|:shipit:/i
15
23
 
16
- def setup(options = {})
17
- project_only = options.delete(:project_only)
18
- using_enterprise = options.delete(:enterprise)
19
- gh_site_url = github_site_url
20
- gh_api_endpoint = github_api_endpoint
21
-
22
- if using_enterprise
23
- gh_site_url = ask("Please enter your Enterprise site URL (e.g. https://github.company.com):")
24
- gh_api_endpoint = ask("Please enter your Enterprise API endpoint (e.g. https://github.company.com/api/v3):")
25
- end
26
-
27
- if project_only
28
- set_github_site_url(gh_site_url, local: true)
29
- set_github_api_endpoint(gh_api_endpoint, local: true)
30
- else
31
- set_github_site_url(gh_site_url)
32
- set_github_api_endpoint(gh_api_endpoint)
33
- end
34
-
35
- gh_user = ask("Please enter your GitHub username: ")
36
- gh_password = ask("Please enter your GitHub password (we do NOT store this): ") { |q| q.echo = false }
37
-
38
- begin
39
-
40
- github = Github.new do |config|
41
- config.basic_auth = "#{gh_user}:#{gh_password}"
42
- config.endpoint = GitReflow.github_api_endpoint
43
- config.site = GitReflow.github_site_url
44
- config.adapter = :net_http
45
- config.ssl = {:verify => false}
46
- end
47
-
48
- previous_authorizations = github.oauth.all.select {|auth| auth.note == "git-reflow (#{`hostname`.strip})" }
49
- if previous_authorizations.any?
50
- authorization = previous_authorizations.last
51
- else
52
- authorization = github.oauth.create scopes: ['repo'], note: "git-reflow (#{`hostname`.strip})"
53
- end
54
-
55
- oauth_token = authorization.token
56
-
57
- if project_only
58
- set_oauth_token(oauth_token, local: true)
59
- else
60
- set_oauth_token(oauth_token)
61
- end
62
- rescue StandardError => e
63
- puts "\nInvalid username or password"
64
- else
65
- puts "\nYour GitHub account was successfully setup!"
66
- end
67
- end
68
-
69
24
  def status(destination_branch)
70
- pull_request = find_pull_request( :from => current_branch, :to => destination_branch )
25
+ pull_request = git_server.find_pull_request( :from => current_branch, :to => destination_branch )
71
26
 
72
27
  if pull_request.nil?
73
28
  puts "\n[notice] No pull request exists for #{current_branch} -> #{destination_branch}"
@@ -84,25 +39,25 @@ module GitReflow
84
39
  fetch_destination options['base']
85
40
 
86
41
  begin
87
- puts push_current_branch
88
- pull_request = github.pull_requests.create(remote_user, remote_repo_name,
89
- 'title' => options['title'],
90
- 'body' => options['body'],
91
- 'head' => "#{remote_user}:#{current_branch}",
92
- 'base' => options['base'])
42
+ push_current_branch
93
43
 
94
- puts "Successfully created pull request ##{pull_request.number}: #{pull_request.title}\nPull Request URL: #{pull_request.html_url}\n"
95
- ask_to_open_in_browser(pull_request.html_url)
96
- rescue Github::Error::UnprocessableEntity => e
97
- error_message = e.to_s
98
- if error_message =~ /request already exists/i
99
- existing_pull_request = find_pull_request( :from => current_branch, :to => options['base'] )
44
+ if existing_pull_request = git_server.find_pull_request( from: current_branch, to: options['base'] )
100
45
  puts "A pull request already exists for these branches:"
101
46
  display_pull_request_summary(existing_pull_request)
102
47
  ask_to_open_in_browser(existing_pull_request.html_url)
103
48
  else
104
- puts error_message
49
+ pull_request = git_server.create_pull_request(title: options['title'],
50
+ body: options['body'],
51
+ head: "#{remote_user}:#{current_branch}",
52
+ base: options['base'])
53
+
54
+ puts "Successfully created pull request ##{pull_request.number}: #{pull_request.title}\nPull Request URL: #{pull_request.html_url}\n"
55
+ ask_to_open_in_browser(pull_request.html_url)
105
56
  end
57
+ rescue Github::Error::UnprocessableEntity => e
58
+ puts "Github Error: #{e.to_s}"
59
+ rescue StandardError => e
60
+ puts "Error: #{e.inspect}"
106
61
  end
107
62
  end
108
63
 
@@ -114,30 +69,29 @@ module GitReflow
114
69
  update_destination(current_branch)
115
70
 
116
71
  begin
117
- existing_pull_request = find_pull_request( :from => current_branch, :to => options['base'] )
72
+ existing_pull_request = git_server.find_pull_request( :from => current_branch, :to => options['base'] )
118
73
 
119
74
  if existing_pull_request.nil?
120
75
  puts "Error: No pull request exists for #{remote_user}:#{current_branch}\nPlease submit your branch for review first with \`git reflow review\`"
121
76
  else
122
77
 
123
- open_comment_authors = find_authors_of_open_pull_request_comments(existing_pull_request)
124
- has_comments = has_pull_request_comments?(existing_pull_request)
125
- status = get_build_status existing_pull_request.head.sha
78
+ open_comment_authors = git_server.find_authors_of_open_pull_request_comments(existing_pull_request)
79
+ has_comments = git_server.has_pull_request_comments?(existing_pull_request)
80
+ status = git_server.get_build_status existing_pull_request.head.sha
126
81
 
127
82
  # if there any comment_authors left, then they haven't given a lgtm after the last commit
128
83
  if ((status.nil? or status.state == "success") and has_comments and open_comment_authors.empty?) or options['skip_lgtm']
129
- lgtm_authors = comment_authors_for_pull_request(existing_pull_request, :with => LGTM)
130
- commit_message = "#{(existing_pull_request[:body] || get_first_commit_message)}"
84
+ lgtm_authors = git_server.comment_authors_for_pull_request(existing_pull_request, :with => LGTM)
85
+ commit_message = ("#{existing_pull_request[:body]}".length > 0) ? existing_pull_request[:body] : "#{get_first_commit_message}"
131
86
  puts "Merging pull request ##{existing_pull_request.number}: '#{existing_pull_request.title}', from '#{existing_pull_request.head.label}' into '#{existing_pull_request.base.label}'"
132
87
 
133
88
  update_destination(options['base'])
134
- merge_feature_branch(:feature_branch => feature_branch,
135
- :destination_branch => options['base'],
89
+ merge_feature_branch(feature_branch,
90
+ :destination_branch => options['base'],
136
91
  :pull_request_number => existing_pull_request.number,
137
- :message => "\nCloses ##{existing_pull_request.number}\n\nLGTM given by: @#{lgtm_authors.join(', @')}\n")
138
- append_to_squashed_commit_message(commit_message)
139
- puts "git commit".colorize(:green)
140
- committed = system('git commit')
92
+ :lgtm_authors => lgtm_authors,
93
+ :message => commit_message)
94
+ committed = run_command_with_label 'git commit', with_system: true
141
95
 
142
96
  if committed
143
97
  puts "Merge complete!"
@@ -147,11 +101,14 @@ module GitReflow
147
101
  run_command_with_label "git push origin :#{feature_branch}"
148
102
  run_command_with_label "git branch -D #{feature_branch}"
149
103
  puts "Nice job buddy."
104
+ else
105
+ puts "Cleanup haulted. Local changes were not pushed to remote repo.".colorize(:red)
106
+ puts "To reset and go back to your branch run \`git reset --hard origin/master && git checkout new-feature\`"
150
107
  end
151
108
  else
152
109
  puts "There were problems commiting your feature... please check the errors above and try again."
153
110
  end
154
- elsif status.state != "success"
111
+ elsif !status.nil? and status.state != "success"
155
112
  puts "[#{ 'deliver halted'.colorize(:red) }] #{status.description}: #{status.target_url}"
156
113
  elsif open_comment_authors.count > 0
157
114
  puts "[deliver halted] You still need a LGTM from: #{open_comment_authors.join(', ')}"
@@ -163,167 +120,12 @@ module GitReflow
163
120
  rescue Github::Error::UnprocessableEntity => e
164
121
  errors = JSON.parse(e.response_message[:body])
165
122
  error_messages = errors["errors"].collect {|error| "GitHub Error: #{error["message"].gsub(/^base\s/, '')}" unless error["message"].nil?}.compact.join("\n")
166
- puts error_messages
167
- end
168
- end
169
-
170
- def github
171
- @github ||= Github.new do |config|
172
- config.oauth_token = GitReflow.github_oauth_token
173
- config.endpoint = GitReflow.github_api_endpoint
174
- config.site = GitReflow.github_site_url
175
- end
176
- end
177
-
178
- def github_api_endpoint
179
- endpoint = `git config --get github.endpoint`.strip
180
- (endpoint.length > 0) ? endpoint : Github::Configuration::DEFAULT_ENDPOINT
181
- end
182
-
183
- def set_github_api_endpoint(api_endpoint, options = {local: false})
184
- if options[:local]
185
- `git config --replace-all github.endpoint #{api_endpoint}`
186
- else
187
- `git config --global --replace-all github.endpoint #{api_endpoint}`
188
- end
189
- end
190
-
191
- def github_site_url
192
- site_url = `git config --get github.site`.strip
193
- (site_url.length > 0) ? site_url : Github::Configuration::DEFAULT_SITE
194
- end
195
-
196
- def set_github_site_url(site_url, options = {local: false})
197
- if options[:local]
198
- `git config --replace-all github.site #{site_url}`
199
- else
200
- `git config --global --replace-all github.site #{site_url}`
123
+ puts "Github Error: #{error_messages}"
201
124
  end
202
125
  end
203
126
 
204
- def github_oauth_token
205
- `git config --get github.oauth-token`.strip
206
- end
207
-
208
- def current_branch
209
- `git branch --no-color | grep '^\* ' | grep -v 'no branch' | sed 's/^* //g'`.strip
210
- end
211
-
212
- def github_user
213
- `git config --get github.user`.strip
214
- end
215
-
216
- def remote_user
217
- gh_remote_user = `git config --get remote.origin.url`.strip
218
- gh_remote_user.slice!(/github\.com[\/:](\w|-|\.)+/i)[11..-1]
219
- end
220
-
221
- def remote_repo_name
222
- gh_repo = `git config --get remote.origin.url`.strip
223
- gh_repo.slice(/\/(\w|-|\.)+$/i)[1..-5]
224
- end
225
-
226
- def get_first_commit_message
227
- `git log --pretty=format:"%s" --no-merges -n 1`.strip
228
- end
229
-
230
- def set_oauth_token(oauth_token, options = {})
231
- if options.delete(:local)
232
- `git config --replace-all github.oauth-token #{oauth_token}`
233
- else
234
- `git config --global --replace-all github.oauth-token #{oauth_token}`
235
- end
236
- end
237
-
238
- def push_current_branch
239
- run_command_with_label "git push origin #{current_branch}"
240
- end
241
-
242
- def fetch_destination(destination_branch)
243
- run_command_with_label "git fetch origin #{destination_branch}"
244
- end
245
-
246
- def update_destination(destination_branch)
247
- origin_branch = current_branch
248
- run_command_with_label "git checkout #{destination_branch}"
249
- run_command_with_label "git pull origin #{destination_branch}"
250
- run_command_with_label "git checkout #{origin_branch}"
251
- end
252
-
253
- def merge_feature_branch(options = {})
254
- options[:destination_branch] ||= 'master'
255
- message = options[:message] || "\nCloses ##{options[:pull_request_number]}\n"
256
-
257
- run_command_with_label "git checkout #{options[:destination_branch]}"
258
- run_command_with_label "git merge --squash #{options[:feature_branch]}"
259
- # append pull request number to commit message
260
- append_to_squashed_commit_message(message)
261
- end
262
-
263
- def append_to_squashed_commit_message(message = '')
264
- `echo "#{message}" | cat - .git/SQUASH_MSG > ./tmp_squash_msg`
265
- `mv ./tmp_squash_msg .git/SQUASH_MSG`
266
- end
267
-
268
- def find_pull_request(options)
269
- existing_pull_request = nil
270
- github.pull_requests.all(remote_user, remote_repo_name, :state => 'open') do |pull_request|
271
- if pull_request.base.label == "#{remote_user}:#{options[:to]}" and
272
- pull_request.head.label == "#{remote_user}:#{options[:from]}"
273
- existing_pull_request = pull_request
274
- break
275
- end
276
- end
277
-
278
- existing_pull_request
279
- end
280
-
281
- def pull_request_comments(pull_request)
282
- comments = github.issues.comments.all remote_user, remote_repo_name, issue_id: pull_request.number
283
- review_comments = github.pull_requests.comments.all remote_user, remote_repo_name, request_id: pull_request.number
284
-
285
- review_comments.to_a + comments.to_a
286
- end
287
-
288
- def has_pull_request_comments?(pull_request)
289
- pull_request_comments(pull_request).count > 0
290
- end
291
-
292
- def get_build_status sha
293
- github.repos.statuses.all(remote_user, remote_repo_name, sha).first
294
- end
295
-
296
- def build_color status
297
- colorized_statuses = { pending: :yellow, success: :green, error: :red, failure: :red }
298
- colorized_statuses[status.state.to_sym]
299
- end
300
-
301
- def colorized_build_description status
302
- status.description.colorize( build_color status )
303
- end
304
-
305
- def find_authors_of_open_pull_request_comments(pull_request)
306
- # first we'll gather all the authors that have commented on the pull request
307
- pull_last_committed_at = get_commited_time(pull_request.head.sha)
308
- comment_authors = comment_authors_for_pull_request(pull_request)
309
- lgtm_authors = comment_authors_for_pull_request(pull_request, :with => LGTM, :after => pull_last_committed_at)
310
-
311
- comment_authors - lgtm_authors
312
- end
313
-
314
- def comment_authors_for_pull_request(pull_request, options = {})
315
- all_comments = pull_request_comments(pull_request)
316
- comment_authors = []
317
-
318
- all_comments.each do |comment|
319
- next if options[:after] and Time.parse(comment.created_at) < options[:after]
320
- if (options[:with].nil? or comment[:body] =~ options[:with])
321
- comment_authors |= [comment.user.login]
322
- end
323
- end
324
-
325
- # remove the current user from the list to check
326
- comment_authors -= [github_user]
127
+ def git_server
128
+ @git_server ||= GitServer.connect provider: 'GitHub', silent: true
327
129
  end
328
130
 
329
131
  def display_pull_request_summary(pull_request)
@@ -334,21 +136,21 @@ module GitReflow
334
136
  }
335
137
 
336
138
  notices = ""
337
- reviewed_by = comment_authors_for_pull_request(pull_request).map {|author| author.colorize(:red) }
139
+ reviewed_by = git_server.comment_authors_for_pull_request(pull_request).map {|author| author.colorize(:red) }
338
140
 
339
141
  # check for CI build status
340
- status = get_build_status pull_request.head.sha
142
+ status = git_server.get_build_status pull_request.head.sha
341
143
  if status
342
144
  notices << "[notice] Your build status is not successful: #{status.target_url}.\n" unless status.state == "success"
343
- summary_data.merge!( "Build status" => colorized_build_description(status) )
145
+ summary_data.merge!( "Build status" => git_server.colorized_build_description(status) )
344
146
  end
345
147
 
346
148
  # check for needed lgtm's
347
- pull_comments = pull_request_comments(pull_request)
348
- if pull_comments.reject {|comment| comment.user.login == github_user}.any?
349
- open_comment_authors = find_authors_of_open_pull_request_comments(pull_request)
350
- last_committed_at = get_commited_time(pull_request.head.sha)
351
- lgtm_authors = comment_authors_for_pull_request(pull_request, :with => LGTM, :after => last_committed_at)
149
+ pull_comments = git_server.pull_request_comments(pull_request)
150
+ if pull_comments.reject {|comment| comment.user.login == git_server.class.user}.any?
151
+ open_comment_authors = git_server.find_authors_of_open_pull_request_comments(pull_request)
152
+ last_committed_at = git_server.get_commited_time(pull_request.head.sha)
153
+ lgtm_authors = git_server.comment_authors_for_pull_request(pull_request, :with => LGTM, :after => last_committed_at)
352
154
 
353
155
  summary_data.merge!("Last comment" => pull_comments.last[:body].inspect)
354
156
 
@@ -371,31 +173,4 @@ module GitReflow
371
173
 
372
174
  puts "\n#{notices}" unless notices.empty?
373
175
  end
374
-
375
- def get_commited_time(commit_sha)
376
- last_commit = github.repos.commits.find remote_user, remote_repo_name, commit_sha
377
- Time.parse last_commit.commit.author[:date]
378
- end
379
-
380
- # WARNING: this currently only supports OS X and UBUNTU
381
- def ask_to_open_in_browser(url)
382
- if RUBY_PLATFORM =~ /darwin|linux/i
383
- open_in_browser = ask "Would you like to open it in your browser? "
384
- if open_in_browser =~ /^y/i
385
- if RUBY_PLATFORM =~ /darwin/i
386
- # OS X
387
- `open #{url}`
388
- else
389
- # Ubuntu
390
- `xdg-open #{url}`
391
- end
392
- end
393
- end
394
- end
395
-
396
- def run_command_with_label(command, options = {})
397
- label_color = options.delete(:color) || :green
398
- puts command.colorize(label_color)
399
- puts `#{command}`
400
- end
401
176
  end
@@ -0,0 +1,7 @@
1
+ [user]
2
+ name = Reenhanced
3
+ email = dev@reenhanced.com
4
+ [github]
5
+ user = reenhanced
6
+ token = 123456
7
+ oauth-token = 123456
@@ -0,0 +1,29 @@
1
+ [
2
+ {
3
+ "id": 1,
4
+ "url": "https://api.github.com/repos/reenhanced/repo/issues/comments/1",
5
+ "html_url": "https://github.com/reenhanced/repo/issues/1#issuecomment-1",
6
+ "body": "Me too",
7
+ "user": {
8
+ "login": "reenhanced",
9
+ "id": 1,
10
+ "avatar_url": "https://github.com/images/error/octocat_happy.gif",
11
+ "gravatar_id": "somehexcode",
12
+ "url": "https://api.github.com/users/reenhanced",
13
+ "html_url": "https://github.com/reenhanced",
14
+ "followers_url": "https://api.github.com/users/reenhanced/followers",
15
+ "following_url": "https://api.github.com/users/reenhanced/following{/other_user}",
16
+ "gists_url": "https://api.github.com/users/reenhanced/gists{/gist_id}",
17
+ "starred_url": "https://api.github.com/users/reenhanced/starred{/owner}{/repo}",
18
+ "subscriptions_url": "https://api.github.com/users/reenhanced/subscriptions",
19
+ "organizations_url": "https://api.github.com/users/reenhanced/orgs",
20
+ "repos_url": "https://api.github.com/users/reenhanced/repos",
21
+ "events_url": "https://api.github.com/users/reenhanced/events{/privacy}",
22
+ "received_events_url": "https://api.github.com/users/reenhanced/received_events",
23
+ "type": "User",
24
+ "site_admin": false
25
+ },
26
+ "created_at": "2011-04-14T16:00:49Z",
27
+ "updated_at": "2011-04-14T16:00:49Z"
28
+ }
29
+ ]
@@ -0,0 +1,47 @@
1
+ [
2
+ {
3
+ "url": "https://api.github.com/repos/reenhanced/repo/pulls/comments/1",
4
+ "id": 1,
5
+ "diff_hunk": "@@ -16,33 +16,40 @@ public class Connection : IConnection...",
6
+ "path": "file1.txt",
7
+ "position": 1,
8
+ "original_position": 4,
9
+ "commit_id": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
10
+ "original_commit_id": "9c48853fa3dc5c1c3d6f1f1cd1f2743e72652840",
11
+ "user": {
12
+ "login": "reenhanced",
13
+ "id": 1,
14
+ "avatar_url": "https://github.com/images/error/octocat_happy.gif",
15
+ "gravatar_id": "somehexcode",
16
+ "url": "https://api.github.com/users/reenhanced",
17
+ "html_url": "https://github.com/reenhanced",
18
+ "followers_url": "https://api.github.com/users/reenhanced/followers",
19
+ "following_url": "https://api.github.com/users/reenhanced/following{/other_user}",
20
+ "gists_url": "https://api.github.com/users/reenhanced/gists{/gist_id}",
21
+ "starred_url": "https://api.github.com/users/reenhanced/starred{/owner}{/repo}",
22
+ "subscriptions_url": "https://api.github.com/users/reenhanced/subscriptions",
23
+ "organizations_url": "https://api.github.com/users/reenhanced/orgs",
24
+ "repos_url": "https://api.github.com/users/reenhanced/repos",
25
+ "events_url": "https://api.github.com/users/reenhanced/events{/privacy}",
26
+ "received_events_url": "https://api.github.com/users/reenhanced/received_events",
27
+ "type": "User",
28
+ "site_admin": false
29
+ },
30
+ "body": "Great stuff",
31
+ "created_at": "2011-04-14T16:00:49Z",
32
+ "updated_at": "2011-04-14T16:00:49Z",
33
+ "html_url": "https://github.com/reenhanced/repo/pull/1#discussion-diff-1",
34
+ "pull_request_url": "https://api.github.com/repos/reenhanced/repo/pulls/1",
35
+ "_links": {
36
+ "self": {
37
+ "href": "https://api.github.com/repos/reenhanced/repo/pulls/comments/1"
38
+ },
39
+ "html": {
40
+ "href": "https://github.com/reenhanced/repo/pull/1#discussion-diff-1"
41
+ },
42
+ "pull_request": {
43
+ "href": "https://api.github.com/repos/reenhanced/repo/pulls/1"
44
+ }
45
+ }
46
+ }
47
+ ]
@@ -0,0 +1,123 @@
1
+ {
2
+ "url": "https://api.github.com/reenhanced/repo/pulls/1",
3
+ "html_url": "https://github.com/reenhanced/repo/pulls/1",
4
+ "diff_url": "https://github.com/reenhanced/repo/pulls/1.diff",
5
+ "patch_url": "https://github.com/reenhanced/repo/pulls/1.patch",
6
+ "issue_url": "https://github.com/reenhanced/repo/issue/1",
7
+ "number": 1,
8
+ "state": "open",
9
+ "title": "new-feature",
10
+ "body": "Please pull these awesome changes",
11
+ "created_at": "2011-01-26T19:01:12Z",
12
+ "updated_at": "2011-01-26T19:01:12Z",
13
+ "closed_at": "2011-01-26T19:01:12Z",
14
+ "merged_at": "2011-01-26T19:01:12Z",
15
+ "_links": {
16
+ "self": {
17
+ "href": "https://api.github.com/reenhanced/repo/pulls/1"
18
+ },
19
+ "html": {
20
+ "href": "https://github.com/reenhanced/repo/pull/1"
21
+ },
22
+ "comments": {
23
+ "href": "https://api.github.com/reenhanced/repo/issues/1/comments"
24
+ },
25
+ "review_comments": {
26
+ "href": "https://api.github.com/reenhanced/repo/pulls/1/comments"
27
+ }
28
+ },
29
+ "merged": false,
30
+ "mergeable": true,
31
+ "merged_by": {
32
+ "login": "reenhanced",
33
+ "id": 1,
34
+ "avatar_url": "https://github.com/images/error/octocat_happy.gif",
35
+ "gravatar_id": "somehexcode",
36
+ "url": "https://api.github.com/users/reenhanced"
37
+ },
38
+ "comments": 10,
39
+ "commits": 3,
40
+ "additions": 100,
41
+ "deletions": 3,
42
+ "changed_files": 5,
43
+ "head": {
44
+ "label": "new-feature",
45
+ "ref": "new-feature",
46
+ "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
47
+ "user": {
48
+ "login": "reenhanced",
49
+ "id": 1,
50
+ "avatar_url": "https://github.com/images/error/octocat_happy.gif",
51
+ "gravatar_id": "somehexcode",
52
+ "url": "https://api.github.com/users/reenhanced"
53
+ },
54
+ "repo": {
55
+ "url": "https://api.github.com/repos/reenhanced/repo",
56
+ "html_url": "https://github.com/reenhanced/repo",
57
+ "clone_url": "https://github.com/reenhanced/repo.git",
58
+ "git_url": "git://github.com/reenhanced/repo.git",
59
+ "ssh_url": "git@github.com:reenhanced/repo.git",
60
+ "svn_url": "https://svn.github.com/reenhanced/repo",
61
+ "owner": {
62
+ "login": "reenhanced",
63
+ "id": 1,
64
+ "avatar_url": "https://github.com/images/error/octocat_happy.gif",
65
+ "gravatar_id": "somehexcode",
66
+ "url": "https://api.github.com/users/reenhanced"
67
+ },
68
+ "name": "repo",
69
+ "description": "This your first repo!",
70
+ "homepage": "https://github.com",
71
+ "language": null,
72
+ "private": false,
73
+ "fork": false,
74
+ "forks": 9,
75
+ "watchers": 80,
76
+ "size": 108,
77
+ "master_branch": "master",
78
+ "open_issues": 0,
79
+ "pushed_at": "2011-01-26T19:06:43Z",
80
+ "created_at": "2011-01-26T19:01:12Z"
81
+ }
82
+ },
83
+ "base": {
84
+ "label": "master",
85
+ "ref": "master",
86
+ "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
87
+ "user": {
88
+ "login": "reenhanced",
89
+ "id": 1,
90
+ "avatar_url": "https://github.com/images/error/octocat_happy.gif",
91
+ "gravatar_id": "somehexcode",
92
+ "url": "https://api.github.com/users/reenhanced"
93
+ },
94
+ "repo": {
95
+ "url": "https://api.github.com/repos/reenhanced/repo",
96
+ "html_url": "https://github.com/reenhanced/repo",
97
+ "clone_url": "https://github.com/reenhanced/repo.git",
98
+ "git_url": "git://github.com/reenhanced/repo.git",
99
+ "ssh_url": "git@github.com:reenhanced/repo.git",
100
+ "svn_url": "https://svn.github.com/reenhanced/repo",
101
+ "owner": {
102
+ "login": "reenhanced",
103
+ "id": 1,
104
+ "avatar_url": "https://github.com/images/error/octocat_happy.gif",
105
+ "gravatar_id": "somehexcode",
106
+ "url": "https://api.github.com/users/reenhanced"
107
+ },
108
+ "name": "repo",
109
+ "description": "This your first repo!",
110
+ "homepage": "https://github.com",
111
+ "language": null,
112
+ "private": false,
113
+ "fork": false,
114
+ "forks": 9,
115
+ "watchers": 80,
116
+ "size": 108,
117
+ "master_branch": "master",
118
+ "open_issues": 0,
119
+ "pushed_at": "2011-01-26T19:06:43Z",
120
+ "created_at": "2011-01-26T19:01:12Z"
121
+ }
122
+ }
123
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ :method => :post,
3
+ :body => "{
4
+ \"errors\" : [{
5
+ \"code\" : \"custom\",
6
+ \"field\" : \"base\",
7
+ \"message\" : \"base A pull request already exists for reenhanced:banana.\",
8
+ \"resource\" : \"PullRequest\" }],
9
+ \"message\" : \"Validation Failed\"}",
10
+ :url => "https://api.github.com/repos/reenhanced/gitreflow/pulls?access_token=12345",
11
+ :request_headers => {
12
+ "Content-Type" => "application/json",
13
+ "Authorization" => "Token token=\"12345\""
14
+ },
15
+ :parallel_manager => nil,
16
+ :request => {:proxy => nil},
17
+ :ssl => {},
18
+ :status => 422,
19
+ :response_headers => {
20
+ "server" => "nginx/1.0.13",
21
+ "date" => "Fri, 27 Apr 2012 13:02:49 GMT",
22
+ "content-type" => "application/json; charset=utf-8",
23
+ "connection" => "close",
24
+ "status" => "422 Unprocessable Entity",
25
+ "x-ratelimit-limit" => "5000",
26
+ "etag" => "\"ebdeb717fe19444c308e608728569d5a\"",
27
+ "x-oauth-scopes" => "repo",
28
+ "x-ratelimit-remaining" => "4996",
29
+ "x-accepted-oauth-scopes" => "repo",
30
+ "content-length" => "192"},
31
+ :response => ""
32
+ }