git_reflow 0.2

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.
@@ -0,0 +1,123 @@
1
+ {
2
+ "url": "https://api.github.com/octocat/Hello-World/pulls/1",
3
+ "html_url": "https://github.com/octocat/Hello-World/pulls/1",
4
+ "diff_url": "https://github.com/octocat/Hello-World/pulls/1.diff",
5
+ "patch_url": "https://github.com/octocat/Hello-World/pulls/1.patch",
6
+ "issue_url": "https://github.com/octocat/Hello-World/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/octocat/Hello-World/pulls/1"
18
+ },
19
+ "html": {
20
+ "href": "https://github.com/octocat/Hello-World/pull/1"
21
+ },
22
+ "comments": {
23
+ "href": "https://api.github.com/octocat/Hello-World/issues/1/comments"
24
+ },
25
+ "review_comments": {
26
+ "href": "https://api.github.com/octocat/Hello-World/pulls/1/comments"
27
+ }
28
+ },
29
+ "merged": false,
30
+ "mergeable": true,
31
+ "merged_by": {
32
+ "login": "octocat",
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/octocat"
37
+ },
38
+ "comments": 10,
39
+ "commits": 3,
40
+ "additions": 100,
41
+ "deletions": 3,
42
+ "changed_files": 5,
43
+ "head": {
44
+ "label": "new-topic",
45
+ "ref": "new-topic",
46
+ "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
47
+ "user": {
48
+ "login": "octocat",
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/octocat"
53
+ },
54
+ "repo": {
55
+ "url": "https://api.github.com/repos/octocat/Hello-World",
56
+ "html_url": "https://github.com/octocat/Hello-World",
57
+ "clone_url": "https://github.com/octocat/Hello-World.git",
58
+ "git_url": "git://github.com/octocat/Hello-World.git",
59
+ "ssh_url": "git@github.com:octocat/Hello-World.git",
60
+ "svn_url": "https://svn.github.com/octocat/Hello-World",
61
+ "owner": {
62
+ "login": "octocat",
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/octocat"
67
+ },
68
+ "name": "Hello-World",
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": "octocat",
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/octocat"
93
+ },
94
+ "repo": {
95
+ "url": "https://api.github.com/repos/octocat/Hello-World",
96
+ "html_url": "https://github.com/octocat/Hello-World",
97
+ "clone_url": "https://github.com/octocat/Hello-World.git",
98
+ "git_url": "git://github.com/octocat/Hello-World.git",
99
+ "ssh_url": "git@github.com:octocat/Hello-World.git",
100
+ "svn_url": "https://svn.github.com/octocat/Hello-World",
101
+ "owner": {
102
+ "login": "octocat",
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/octocat"
107
+ },
108
+ "name": "Hello-World",
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
+ }
@@ -0,0 +1,117 @@
1
+ [
2
+ {
3
+ "url": "https://api.github.com/reenhanced/repo/pulls/1",
4
+ "html_url": "https://github.com/reenhanced/repo/pulls/1",
5
+ "diff_url": "https://github.com/reenhanced/repo/pulls/1.diff",
6
+ "patch_url": "https://github.com/reenhanced/repo/pulls/1.patch",
7
+ "issue_url": "https://github.com/reenhanced/repo/issue/1",
8
+ "number": 1,
9
+ "state": "open",
10
+ "title": "new-feature",
11
+ "body": "Please pull these awesome changes",
12
+ "created_at": "2011-01-26T19:01:12Z",
13
+ "updated_at": "2011-01-26T19:01:12Z",
14
+ "closed_at": "2011-01-26T19:01:12Z",
15
+ "merged_at": "2011-01-26T19:01:12Z",
16
+ "head": {
17
+ "label": "reenhanced:new-feature",
18
+ "ref": "new-feature",
19
+ "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
20
+ "user": {
21
+ "login": "reenhanced",
22
+ "id": 1,
23
+ "avatar_url": "https://github.com/images/error/reenhanced_happy.gif",
24
+ "gravatar_id": "somehexcode",
25
+ "url": "https://api.github.com/users/reenhanced"
26
+ },
27
+ "repo": {
28
+ "url": "https://api.github.com/repos/reenhanced/repo",
29
+ "html_url": "https://github.com/reenhanced/repo",
30
+ "clone_url": "https://github.com/reenhanced/repo.git",
31
+ "git_url": "git://github.com/reenhanced/repo.git",
32
+ "ssh_url": "git@github.com:reenhanced/repo.git",
33
+ "svn_url": "https://svn.github.com/reenhanced/repo",
34
+ "mirror_url": "git://git.example.com/reenhanced/repo",
35
+ "id": 1296269,
36
+ "owner": {
37
+ "login": "reenhanced",
38
+ "id": 1,
39
+ "avatar_url": "https://github.com/images/error/reenhanced_happy.gif",
40
+ "gravatar_id": "somehexcode",
41
+ "url": "https://api.github.com/users/reenhanced"
42
+ },
43
+ "name": "repo",
44
+ "description": "This your first repo!",
45
+ "homepage": "https://github.com",
46
+ "language": null,
47
+ "private": false,
48
+ "fork": false,
49
+ "forks": 9,
50
+ "watchers": 80,
51
+ "size": 108,
52
+ "master_branch": "master",
53
+ "open_issues": 0,
54
+ "pushed_at": "2011-01-26T19:06:43Z",
55
+ "created_at": "2011-01-26T19:01:12Z",
56
+ "updated_at": "2011-01-26T19:14:43Z"
57
+ }
58
+ },
59
+ "base": {
60
+ "label": "reenhanced:master",
61
+ "ref": "master",
62
+ "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
63
+ "user": {
64
+ "login": "reenhanced",
65
+ "id": 1,
66
+ "avatar_url": "https://github.com/images/error/reenhanced_happy.gif",
67
+ "gravatar_id": "somehexcode",
68
+ "url": "https://api.github.com/users/reenhanced"
69
+ },
70
+ "repo": {
71
+ "url": "https://api.github.com/repos/reenhanced/repo",
72
+ "html_url": "https://github.com/reenhanced/repo",
73
+ "clone_url": "https://github.com/reenhanced/repo.git",
74
+ "git_url": "git://github.com/reenhanced/repo.git",
75
+ "ssh_url": "git@github.com:reenhanced/repo.git",
76
+ "svn_url": "https://svn.github.com/reenhanced/repo",
77
+ "mirror_url": "git://git.example.com/reenhanced/repo",
78
+ "id": 1296269,
79
+ "owner": {
80
+ "login": "reenhanced",
81
+ "id": 1,
82
+ "avatar_url": "https://github.com/images/error/reenhanced_happy.gif",
83
+ "gravatar_id": "somehexcode",
84
+ "url": "https://api.github.com/users/reenhanced"
85
+ },
86
+ "name": "repo",
87
+ "description": "This your first repo!",
88
+ "homepage": "https://github.com",
89
+ "language": null,
90
+ "private": false,
91
+ "fork": false,
92
+ "forks": 9,
93
+ "watchers": 80,
94
+ "size": 108,
95
+ "master_branch": "master",
96
+ "open_issues": 0,
97
+ "pushed_at": "2011-01-26T19:06:43Z",
98
+ "created_at": "2011-01-26T19:01:12Z",
99
+ "updated_at": "2011-01-26T19:14:43Z"
100
+ }
101
+ },
102
+ "_links": {
103
+ "self": {
104
+ "href": "https://api.github.com/reenhanced/repo/pulls/1"
105
+ },
106
+ "html": {
107
+ "href": "https://github.com/reenhanced/repo/pull/1"
108
+ },
109
+ "comments": {
110
+ "href": "https://api.github.com/reenhanced/repo/issues/1/comments"
111
+ },
112
+ "review_comments": {
113
+ "href": "https://api.github.com/reenhanced/repo/pulls/1/comments"
114
+ }
115
+ }
116
+ }
117
+ ]
@@ -0,0 +1,139 @@
1
+ require 'spec_helper'
2
+ require 'aruba/api'
3
+
4
+ describe :git_reflow do
5
+ include Aruba::Api
6
+ let(:github) { Github.new }
7
+ let(:user) { 'reenhanced' }
8
+ let(:repo) { 'repo' }
9
+
10
+ before do
11
+ Github.stub :new => github
12
+ end
13
+
14
+ after { reset_authentication_for github }
15
+
16
+ context :setup do
17
+
18
+ before do
19
+ github.oauth.stub(:create).and_return({:token => '12345'})
20
+ GitReflow.stub(:set_oauth_token)
21
+ end
22
+
23
+ it "creates a new authorization" do
24
+ STDIN.stub(:gets).and_return("user", "password")
25
+ Github.should_receive :new
26
+ github.oauth.should_receive(:create).with('scopes' => ['repo'])
27
+ GitReflow.should_receive(:set_oauth_token).with('12345')
28
+ GitReflow.setup
29
+ end
30
+ end
31
+
32
+ context :github do
33
+ before do
34
+ GitReflow.stub(:get_oauth_token).and_return('12345')
35
+ end
36
+
37
+ it "creates a new authorization from the stored oauth token" do
38
+ Github.should_receive(:new).with({:oauth_token => '12345'})
39
+ GitReflow.should_receive(:get_oauth_token)
40
+ GitReflow.github
41
+ end
42
+ end
43
+
44
+ # Github Response specs thanks to:
45
+ # https://github.com/peter-murach/github/blob/master/spec/github/pull_requests_spec.rb
46
+ context :review do
47
+ let(:inputs) {
48
+ {
49
+ "title" => "Amazing new feature",
50
+ "body" => "Please pull this in!",
51
+ "head" => "reenhanced:new-feature",
52
+ "base" => "master",
53
+ "state" => "open"
54
+ }
55
+ }
56
+
57
+ before do
58
+ GitReflow.stub(:push_current_branch).and_return(true)
59
+ GitReflow.stub(:github).and_return(github)
60
+ GitReflow.stub(:current_branch).and_return('new-feature')
61
+ GitReflow.stub(:remote_repo_name).and_return(repo)
62
+ GitReflow.stub(:fetch_destination).and_return(true)
63
+ github.pull_requests.stub(:create).with(user, repo, inputs.except('state')).and_return(Hashie::Mash.new(:number => '1', :title => inputs['title'], :html_url => "http://github.com/#{user}/#{repo}/pulls/1"))
64
+ stub_post("/repos/#{user}/#{repo}/pulls").
65
+ to_return(:body => fixture('pull_requests/pull_request.json'), :status => 201, :headers => {:content_type => "application/json; charset=utf-8"})
66
+ end
67
+
68
+ it "successfully creates a pull request if I do not provide one" do
69
+ github.pull_requests.should_receive(:create).with(user, repo, inputs.except('state'))
70
+ STDOUT.should_receive(:puts).with("Successfully created pull request #1: #{inputs['title']}\nPull Request URL: http://github.com/#{user}/#{repo}/pulls/1\n")
71
+ GitReflow.review inputs
72
+ end
73
+
74
+ it "reports any errors returned from github" do
75
+ github_error = Github::Error::UnprocessableEntity.new( eval(fixture('pull_requests/pull_request_exists_error.json').read) )
76
+ github.pull_requests.stub(:create).with(user, repo, inputs.except('state')).and_raise(github_error)
77
+ stub_post("/repos/#{user}/#{repo}/pulls").
78
+ to_return(:body => fixture('pull_requests/pull_request_exists_error.json'), :status => 422, :headers => {:content_type => "application/json; charset=utf-8"})
79
+
80
+ STDOUT.should_receive(:puts).with("GitHub Error: A pull request already exists for reenhanced:banana.")
81
+ GitReflow.review inputs
82
+ end
83
+
84
+ it "pushes the latest current branch to the origin repo" do
85
+ github.pull_requests.should_receive(:create)
86
+ GitReflow.should_receive(:push_current_branch)
87
+ GitReflow.should_receive(:current_branch)
88
+ GitReflow.review
89
+ end
90
+
91
+ it "fetches the latest changes to the destination branch" do
92
+ GitReflow.should_receive(:fetch_destination)
93
+ github.pull_requests.should_receive(:create)
94
+ GitReflow.review
95
+ end
96
+ end
97
+
98
+ context :deliver do
99
+ before do
100
+ GitReflow.stub(:github).and_return(github)
101
+ GitReflow.stub(:current_branch).and_return('new-feature')
102
+ GitReflow.stub(:remote_repo_name).and_return(repo)
103
+ GitReflow.stub(:fetch_destination).and_return(true)
104
+ GitReflow.stub(:update_destination).and_return(true)
105
+ stub_get("/repos/#{user}/#{repo}/pulls").with(:query => {'state' => 'open'}).
106
+ to_return(:body => fixture('pull_requests/pull_requests.json'), :status => 201, :headers => {:content_type => "application/json; charset=utf-8"})
107
+ end
108
+
109
+ it "successfully finds a pull request for the current feature branch" do
110
+ STDOUT.should_receive(:puts).with("Merging pull request #1: 'new-feature', from 'reenhanced:new-feature' into 'reenhanced:master'")
111
+ GitReflow.deliver
112
+ end
113
+
114
+ it "reports an errors if no open pull requests exist for the current feature branch" do
115
+ github.pull_requests.stub(:all).with(user, repo, :state => 'open')
116
+ stub_get("/repos/#{user}/#{repo}/pulls").
117
+ to_return(:body => "[]", :status => 201, :headers => {:content_type => "application/json; charset=utf-8"})
118
+
119
+ STDOUT.should_receive(:puts).with("Error: No pull request exists for reenhanced:new-feature\nPlease submit your branch for review first with \`git reflow review\`")
120
+ GitReflow.deliver
121
+ end
122
+
123
+ it "fetches the latest changes to the destination branch" do
124
+ GitReflow.should_receive(:fetch_destination)
125
+ github.pull_requests.should_receive(:all)
126
+ GitReflow.deliver
127
+ end
128
+
129
+ it "checks out the destination branch and updates any remote changes" do
130
+ GitReflow.should_receive(:update_destination)
131
+ GitReflow.deliver
132
+ end
133
+
134
+ it "merges and squashes the feature branch into the master branch" do
135
+ GitReflow.should_receive(:merge_feature_branch)
136
+ GitReflow.deliver
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+ require 'json'
4
+ require 'webmock/rspec'
5
+ require 'ruby-debug'
6
+
7
+ $LOAD_PATH << 'lib'
8
+ require 'git_reflow'
9
+ require 'github_api'
10
+
11
+ Dir[File.expand_path('../support/**/*.rb', __FILE__)].each {|f| require f}
12
+
13
+ RSpec.configure do |config|
14
+ config.include GithubHelpers
15
+ config.include WebMock::API
16
+ config.color_enabled = true
17
+ config.before(:each) do
18
+ WebMock.reset!
19
+ end
20
+ end
21
+
22
+ OAUTH_TOKEN = 'bafec72922f31fe86aacc8aca4261117f3bd62cf'
23
+
24
+ def reset_authentication_for(object)
25
+ [ 'user', 'repo', 'basic_auth', 'oauth_token', 'login', 'password' ].each do |item|
26
+ object.send("#{item}=", nil)
27
+ end
28
+ end
@@ -0,0 +1,8 @@
1
+ def fixture_path
2
+ File.expand_path("../../fixtures", __FILE__)
3
+ end
4
+
5
+ def fixture(file)
6
+ File.new(File.join(fixture_path, '/', file))
7
+ end
8
+
@@ -0,0 +1,45 @@
1
+ $LOAD_PATH << 'lib'
2
+ require 'git_reflow'
3
+ require 'github_api'
4
+ require File.expand_path('../web_mocks', __FILE__)
5
+ require File.expand_path('../fixtures', __FILE__)
6
+
7
+ module GithubHelpers
8
+ def stub_github_with(options = {})
9
+ github = Github.new
10
+ user = options[:user] || 'reenhanced'
11
+ repo = options[:repo] || 'repo'
12
+ branch = options[:branch] || 'new-feature'
13
+ pull = options[:pull]
14
+
15
+ Github.stub :new => github
16
+ GitReflow.stub(:push_current_branch).and_return(true)
17
+ GitReflow.stub(:github).and_return(github)
18
+ GitReflow.stub(:current_branch).and_return(branch)
19
+ GitReflow.stub(:remote_repo_name).and_return(repo)
20
+ GitReflow.stub(:remote_user).and_return(user)
21
+ GitReflow.stub(:fetch_destination).and_return(true)
22
+ GitReflow.stub(:update_destination).and_return(true)
23
+
24
+ if pull
25
+ # Stubbing review
26
+ github.pull_requests.stub(:create).with(user, repo, pull.except('state')).and_return(Hashie::Mash.new(:number => '1', :title => pull['title'], :html_url => "http://github.com/#{user}/#{repo}/pulls/1"))
27
+ stub_post("/repos/#{user}/#{repo}/pulls").
28
+ to_return(:body => fixture('pull_requests/pull_request.json'), :status => 201, :headers => {:content_type => "application/json\; charset=utf-8"})
29
+
30
+ # Stubbing pull request finder
31
+ stub_get("/repos/#{user}/#{repo}/pulls").with(:query => {'state' => 'open'}).
32
+ to_return(:body => fixture('pull_requests/pull_requests.json'), :status => 201, :headers => {:content_type => "application/json; charset=utf-8"})
33
+ end
34
+ end
35
+ end
36
+
37
+ # the github_api gem does some overrides to Hash so we have to make sure
38
+ # this still works here...
39
+ class Hash
40
+ def except(*keys)
41
+ cpy = self.dup
42
+ keys.each { |key| cpy.delete(key) }
43
+ cpy
44
+ end
45
+ end
@@ -0,0 +1,39 @@
1
+ def stub_get(path, endpoint = Github.endpoint.to_s)
2
+ stub_request(:get, endpoint + path)
3
+ end
4
+
5
+ def stub_post(path, endpoint = Github.endpoint.to_s)
6
+ stub_request(:post, endpoint + path)
7
+ end
8
+
9
+ def stub_patch(path, endpoint = Github.endpoint.to_s)
10
+ stub_request(:patch, endpoint + path)
11
+ end
12
+
13
+ def stub_put(path, endpoint = Github.endpoint.to_s)
14
+ stub_request(:put, endpoint + path)
15
+ end
16
+
17
+ def stub_delete(path, endpoint = Github.endpoint.to_s)
18
+ stub_request(:delete, endpoint + path)
19
+ end
20
+
21
+ def a_get(path, endpoint = Github.endpoint.to_s)
22
+ a_request(:get, endpoint + path)
23
+ end
24
+
25
+ def a_post(path, endpoint = Github.endpoint.to_s)
26
+ a_request(:post, endpoint + path)
27
+ end
28
+
29
+ def a_patch(path, endpoint = Github.endpoint.to_s)
30
+ a_request(:patch, endpoint + path)
31
+ end
32
+
33
+ def a_put(path, endpoint = Github.endpoint)
34
+ a_request(:put, endpoint + path)
35
+ end
36
+
37
+ def a_delete(path, endpoint = Github.endpoint)
38
+ a_request(:delete, endpoint + path)
39
+ end