git_reflow 0.2

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