git_reflow 0.3.5 → 0.4.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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile.lock +67 -35
- data/Rakefile +8 -0
- data/git_reflow.gemspec +5 -4
- data/lib/git_reflow/commands/setup.rb +8 -1
- data/lib/git_reflow/commands/start.rb +2 -2
- data/lib/git_reflow/config.rb +23 -0
- data/lib/git_reflow/git_helpers.rb +65 -0
- data/lib/git_reflow/git_server/base.rb +88 -0
- data/lib/git_reflow/git_server/git_hub.rb +184 -0
- data/lib/git_reflow/git_server.rb +52 -0
- data/lib/git_reflow/sandbox.rb +39 -0
- data/lib/git_reflow/version.rb +1 -1
- data/lib/git_reflow.rb +48 -273
- data/spec/fixtures/git/git_config +7 -0
- data/spec/fixtures/issues/comments.json +29 -0
- data/spec/fixtures/pull_requests/comments.json +47 -0
- data/spec/fixtures/pull_requests/pull_request.json +123 -0
- data/spec/fixtures/pull_requests/pull_request_exists_error.json +32 -0
- data/spec/fixtures/pull_requests/pull_requests.json +117 -0
- data/spec/fixtures/repositories/statuses.json +31 -0
- data/spec/git_reflow_spec.rb +386 -0
- data/spec/lib/git_reflow/config_spec.rb +18 -0
- data/spec/lib/git_reflow/git_helpers_spec.rb +146 -0
- data/spec/lib/git_reflow/git_server_spec.rb +94 -0
- data/spec/lib/git_server/git_hub_spec.rb +236 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/support/command_line_helpers.rb +99 -0
- data/spec/support/fixtures.rb +8 -0
- data/spec/support/github_helpers.rb +98 -0
- data/spec/support/rspec_stub_helpers.rb +7 -0
- data/spec/support/web_mocks.rb +39 -0
- metadata +104 -62
@@ -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": "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": "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,31 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"created_at": "2012-07-20T01:19:13Z",
|
4
|
+
"updated_at": "2012-07-20T01:19:13Z",
|
5
|
+
"state": "success",
|
6
|
+
"target_url": "https://ci.example.com/1000/output",
|
7
|
+
"description": "Build has completed successfully",
|
8
|
+
"id": 1,
|
9
|
+
"url": "https://api.github.com/repos/reenhanced/example/statuses/1",
|
10
|
+
"context": "continuous-integration/jenkins",
|
11
|
+
"creator": {
|
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
|
+
}
|
31
|
+
]
|
@@ -0,0 +1,386 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GitReflow do
|
4
|
+
let(:git_server) { GitReflow::GitServer::GitHub.new {} }
|
5
|
+
let(:github) { Github.new basic_auth: "#{user}:#{password}" }
|
6
|
+
let(:user) { 'reenhanced' }
|
7
|
+
let(:password) { 'shazam' }
|
8
|
+
let(:oauth_token_hash) { Hashie::Mash.new({ token: 'a1b2c3d4e5f6g7h8i9j0', note: 'hostname.local git-reflow'}) }
|
9
|
+
let(:repo) { 'repo' }
|
10
|
+
let(:base_branch) { 'master' }
|
11
|
+
let(:feature_branch) { 'new-feature' }
|
12
|
+
let(:enterprise_site) { 'https://github.reenhanced.com' }
|
13
|
+
let(:enterprise_api) { 'https://github.reenhanced.com' }
|
14
|
+
let(:hostname) { 'hostname.local' }
|
15
|
+
|
16
|
+
let(:github_authorizations) { Github::Client::Authorizations.new }
|
17
|
+
let(:existing_pull_requests) { JSON.parse(fixture('pull_requests/pull_requests.json').read).collect { |pull| Hashie::Mash.new(pull)} }
|
18
|
+
let(:existing_pull_request) { existing_pull_requests.first }
|
19
|
+
|
20
|
+
before do
|
21
|
+
HighLine.any_instance.stub(:ask) do |terminal, question|
|
22
|
+
values = {
|
23
|
+
"Please enter your GitHub username: " => user,
|
24
|
+
"Please enter your GitHub password (we do NOT store this): " => password,
|
25
|
+
"Please enter your Enterprise site URL (e.g. https://github.company.com):" => enterprise_site,
|
26
|
+
"Please enter your Enterprise API endpoint (e.g. https://github.company.com/api/v3):" => enterprise_api,
|
27
|
+
"Would you like to push this branch to your remote repo and cleanup your feature branch? " => 'yes',
|
28
|
+
"Would you like to open it in your browser?" => 'n'
|
29
|
+
}
|
30
|
+
return_value = values[question] || values[terminal]
|
31
|
+
question = ""
|
32
|
+
return_value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context :status do
|
37
|
+
subject { GitReflow.status(base_branch) }
|
38
|
+
|
39
|
+
before do
|
40
|
+
GitReflow.stub(:current_branch).and_return(feature_branch)
|
41
|
+
GitReflow.stub(:destination_branch).and_return(base_branch)
|
42
|
+
|
43
|
+
Github.stub(:new).and_return(github)
|
44
|
+
GitReflow.stub(:git_server).and_return(git_server)
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with no existing pull request' do
|
48
|
+
before { git_server.stub(:find_pull_request).with({from: feature_branch, to: base_branch}).and_return(nil) }
|
49
|
+
it { expect{ subject }.to have_output "\n[notice] No pull request exists for #{feature_branch} -> #{base_branch}" }
|
50
|
+
it { expect{ subject }.to have_output "[notice] Run 'git reflow review #{base_branch}' to start the review process" }
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'with an existing pull request' do
|
54
|
+
before { git_server.stub(:find_pull_request).with({from: feature_branch, to: base_branch}).and_return(existing_pull_request) }
|
55
|
+
|
56
|
+
it 'displays a summary of the pull request and asks to open it in the browser' do
|
57
|
+
GitReflow.should_receive(:display_pull_request_summary).with(existing_pull_request)
|
58
|
+
GitReflow.should_receive(:ask_to_open_in_browser).with(existing_pull_request.html_url)
|
59
|
+
subject
|
60
|
+
$output.should include "Here's the status of your review:"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Github Response specs thanks to:
|
66
|
+
# https://github.com/peter-murach/github/blob/master/spec/github/pull_requests_spec.rb
|
67
|
+
context :review do
|
68
|
+
let(:branch) { 'new-feature' }
|
69
|
+
let(:inputs) {
|
70
|
+
{
|
71
|
+
"title" => "Amazing new feature",
|
72
|
+
"body" => "Please pull this in!",
|
73
|
+
"head" => "reenhanced:new-feature",
|
74
|
+
"base" => "master",
|
75
|
+
"state" => "open"
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
let(:github) do
|
80
|
+
stub_github_with({
|
81
|
+
:user => user,
|
82
|
+
:password => password,
|
83
|
+
:repo => repo,
|
84
|
+
:branch => branch,
|
85
|
+
:pull => inputs
|
86
|
+
})
|
87
|
+
end
|
88
|
+
|
89
|
+
subject { GitReflow.review inputs }
|
90
|
+
|
91
|
+
it "fetches the latest changes to the destination branch" do
|
92
|
+
GitReflow.should_receive(:fetch_destination).with(inputs['base'])
|
93
|
+
github.should_receive(:find_pull_request).and_return(nil)
|
94
|
+
github.stub(:create_pull_request).and_return(existing_pull_request)
|
95
|
+
subject
|
96
|
+
end
|
97
|
+
|
98
|
+
it "pushes the latest current branch to the origin repo" do
|
99
|
+
GitReflow.should_receive(:push_current_branch)
|
100
|
+
github.should_receive(:find_pull_request).and_return(nil)
|
101
|
+
github.stub(:create_pull_request).and_return(existing_pull_request)
|
102
|
+
subject
|
103
|
+
end
|
104
|
+
|
105
|
+
context "pull request doesn't exist" do
|
106
|
+
before { github.stub(:find_pull_request).and_return(nil) }
|
107
|
+
|
108
|
+
it "successfully creates a pull request if I do not provide one" do
|
109
|
+
existing_pull_request.stub(:title).and_return(inputs['title'])
|
110
|
+
github.should_receive(:create_pull_request).with(inputs.except('state').symbolize_keys).and_return(existing_pull_request)
|
111
|
+
expect { subject }.to have_output "Successfully created pull request #1: #{inputs['title']}\nPull Request URL: https://github.com/#{user}/#{repo}/pulls/1\n"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context "pull request exists" do
|
116
|
+
before do
|
117
|
+
GitReflow.stub(:push_current_branch)
|
118
|
+
github_error = Github::Error::UnprocessableEntity.new( eval(fixture('pull_requests/pull_request_exists_error.json').read) )
|
119
|
+
github.stub(:create_pull_request).with(inputs.except('state')).and_raise(github_error)
|
120
|
+
GitReflow.stub(:display_pull_request_summary).with(existing_pull_request)
|
121
|
+
end
|
122
|
+
|
123
|
+
subject { GitReflow.review inputs }
|
124
|
+
|
125
|
+
it "displays a pull request summary for the existing pull request" do
|
126
|
+
GitReflow.should_receive(:display_pull_request_summary).with(existing_pull_request)
|
127
|
+
subject
|
128
|
+
end
|
129
|
+
|
130
|
+
it "asks to open the pull request in the browser" do
|
131
|
+
GitReflow.should_receive(:ask_to_open_in_browser).with(existing_pull_request.html_url)
|
132
|
+
subject
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context :deliver do
|
138
|
+
let(:branch) { 'new-feature' }
|
139
|
+
let(:inputs) { {} }
|
140
|
+
let!(:github) do
|
141
|
+
stub_github_with({
|
142
|
+
:user => user,
|
143
|
+
:password => password,
|
144
|
+
:repo => repo,
|
145
|
+
:branch => branch,
|
146
|
+
:pull => existing_pull_request
|
147
|
+
})
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
before do
|
152
|
+
module Kernel
|
153
|
+
def system(cmd)
|
154
|
+
"call #{cmd}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
subject { GitReflow.deliver inputs }
|
160
|
+
|
161
|
+
it "fetches the latest changes to the destination branch" do
|
162
|
+
GitReflow.should_receive(:fetch_destination).with('master')
|
163
|
+
subject
|
164
|
+
end
|
165
|
+
|
166
|
+
it "looks for a pull request matching the feature branch and destination branch" do
|
167
|
+
github.should_receive(:find_pull_request).with(from: branch, to: 'master')
|
168
|
+
subject
|
169
|
+
end
|
170
|
+
|
171
|
+
context "and pull request exists for the feature branch to the destination branch" do
|
172
|
+
before do
|
173
|
+
github.stub(:get_build_status).and_return(build_status)
|
174
|
+
github.stub(:has_pull_request_comments?).and_return(true)
|
175
|
+
github.stub(:find_authors_of_open_pull_request_comments).and_return([])
|
176
|
+
github.stub(:comment_authors_for_pull_request).and_return(['codenamev'])
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'and build status is not "success"' do
|
180
|
+
let(:build_status) { Hashie::Mash.new({ state: 'failure', description: 'Build resulted in failed test(s)' }) }
|
181
|
+
|
182
|
+
before do
|
183
|
+
# just stubbing these in a locked state as the test is specific to this scenario
|
184
|
+
GitReflow.stub(:find_authors_of_open_pull_request_comments).and_return([])
|
185
|
+
GitReflow.stub(:has_pull_request_comments?).and_return(true)
|
186
|
+
end
|
187
|
+
|
188
|
+
it "halts delivery and notifies user of a failed build" do
|
189
|
+
expect { subject }.to have_output "[#{ 'deliver halted'.colorize(:red) }] #{build_status.description}: #{build_status.target_url}"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'and build status is nil' do
|
194
|
+
let(:build_status) { nil }
|
195
|
+
let(:inputs) {{ skip_lgtm: true }}
|
196
|
+
|
197
|
+
before do
|
198
|
+
# stubbing unrelated results so we can just test that it made it insdide the conditional block
|
199
|
+
GitReflow.stub(:find_authors_of_open_pull_request_comments).and_return([])
|
200
|
+
GitReflow.stub(:has_pull_request_comments?).and_return(true)
|
201
|
+
GitReflow.stub(:comment_authors_for_pull_request).and_return([])
|
202
|
+
GitReflow.stub(:update_destination).and_return(true)
|
203
|
+
GitReflow.stub(:merge_feature_branch).and_return(true)
|
204
|
+
GitReflow.stub(:append_to_squashed_commit_message).and_return(true)
|
205
|
+
end
|
206
|
+
|
207
|
+
it "ignores build status when not setup" do
|
208
|
+
expect { subject }.to have_output "Merge complete!"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context 'and build status is "success"' do
|
213
|
+
let(:build_status) { Hashie::Mash.new({ state: 'success' }) }
|
214
|
+
|
215
|
+
context 'and has comments' do
|
216
|
+
before do
|
217
|
+
GitReflow.stub(:has_pull_request_comments?).and_return(true)
|
218
|
+
GitReflow.stub(:find_authors_of_open_pull_request_comments).and_return([])
|
219
|
+
end
|
220
|
+
|
221
|
+
context 'but there is a LGTM' do
|
222
|
+
let(:lgtm_comment_authors) { ['nhance'] }
|
223
|
+
before { stub_with_fallback(github, :comment_authors_for_pull_request).with(existing_pull_request, with: GitReflow::LGTM).and_return(lgtm_comment_authors) }
|
224
|
+
|
225
|
+
it "includes the pull request body in the commit message" do
|
226
|
+
squash_message = "#{existing_pull_request.body}\nCloses ##{existing_pull_request.number}\n\nLGTM given by: @nhance\n"
|
227
|
+
GitReflow.should_receive(:append_to_squashed_commit_message).with(squash_message)
|
228
|
+
subject
|
229
|
+
end
|
230
|
+
|
231
|
+
context "and the pull request has no body" do
|
232
|
+
let(:first_commit_message) { "We'll do it live." }
|
233
|
+
|
234
|
+
before do
|
235
|
+
existing_pull_request[:body] = ''
|
236
|
+
github.stub(:find_pull_request).and_return(existing_pull_request)
|
237
|
+
GitReflow.stub(:get_first_commit_message).and_return(first_commit_message)
|
238
|
+
github.stub(:comment_authors_for_pull_request).and_return(lgtm_comment_authors)
|
239
|
+
end
|
240
|
+
|
241
|
+
it "includes the first commit message for the new branch in the commit message of the merge" do
|
242
|
+
squash_message = "#{first_commit_message}\nCloses ##{existing_pull_request.number}\n\nLGTM given by: @nhance\n"
|
243
|
+
GitReflow.should_receive(:append_to_squashed_commit_message).with(squash_message)
|
244
|
+
subject
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
it "notifies user of the merge and performs it" do
|
249
|
+
GitReflow.should_receive(:merge_feature_branch).with('new-feature', {
|
250
|
+
destination_branch: 'master',
|
251
|
+
pull_request_number: existing_pull_request.number,
|
252
|
+
lgtm_authors: ['nhance'],
|
253
|
+
message: existing_pull_request.body
|
254
|
+
})
|
255
|
+
|
256
|
+
expect { subject }.to have_output "Merging pull request ##{existing_pull_request.number}: '#{existing_pull_request.title}', from '#{existing_pull_request.head.label}' into '#{existing_pull_request.base.label}'"
|
257
|
+
end
|
258
|
+
|
259
|
+
it "updates the destination brnach" do
|
260
|
+
GitReflow.should_receive(:update_destination).with('master')
|
261
|
+
subject
|
262
|
+
end
|
263
|
+
|
264
|
+
it "commits the changes for the squash merge" do
|
265
|
+
subject
|
266
|
+
$output.should include 'Merge complete!'
|
267
|
+
end
|
268
|
+
|
269
|
+
context "and cleaning up feature branch" do
|
270
|
+
before do
|
271
|
+
HighLine.any_instance.stub(:ask) do |terminal, question|
|
272
|
+
values = {
|
273
|
+
"Please enter your GitHub username: " => user,
|
274
|
+
"Please enter your GitHub password (we do NOT store this): " => password,
|
275
|
+
"Please enter your Enterprise site URL (e.g. https://github.company.com):" => enterprise_site,
|
276
|
+
"Please enter your Enterprise API endpoint (e.g. https://github.company.com/api/v3):" => enterprise_api,
|
277
|
+
"Would you like to push this branch to your remote repo and cleanup your feature branch? " => 'yes',
|
278
|
+
"Would you like to open it in your browser?" => 'no'
|
279
|
+
}
|
280
|
+
return_value = values[question] || values[terminal]
|
281
|
+
question = ""
|
282
|
+
return_value
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
it "pushes local squash merged base branch to remote repo" do
|
287
|
+
expect { subject }.to have_run_command("git push origin master")
|
288
|
+
end
|
289
|
+
|
290
|
+
it "deletes the remote feature branch" do
|
291
|
+
expect { subject }.to have_run_command("git push origin :new-feature")
|
292
|
+
end
|
293
|
+
|
294
|
+
it "deletes the local feature branch" do
|
295
|
+
expect { subject }.to have_run_command("git branch -D new-feature")
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
context "and not cleaning up feature branch" do
|
300
|
+
before do
|
301
|
+
HighLine.any_instance.stub(:ask) do |terminal, question|
|
302
|
+
values = {
|
303
|
+
"Please enter your GitHub username: " => user,
|
304
|
+
"Please enter your GitHub password (we do NOT store this): " => password,
|
305
|
+
"Please enter your Enterprise site URL (e.g. https://github.company.com):" => enterprise_site,
|
306
|
+
"Please enter your Enterprise API endpoint (e.g. https://github.company.com/api/v3):" => enterprise_api,
|
307
|
+
"Would you like to push this branch to your remote repo and cleanup your feature branch? " => 'no',
|
308
|
+
"Would you like to open it in your browser?" => 'no'
|
309
|
+
}
|
310
|
+
return_value = values[question] || values[terminal]
|
311
|
+
question = ""
|
312
|
+
return_value
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
it "doesn't update the remote repo with the new squash merge" do
|
317
|
+
expect { subject }.to_not have_run_command('git push origin master')
|
318
|
+
end
|
319
|
+
|
320
|
+
it "doesn't delete the feature branch on the remote repo" do
|
321
|
+
expect { subject }.to_not have_run_command('git push origin :new-feature')
|
322
|
+
end
|
323
|
+
|
324
|
+
it "doesn't delete the local feature branch" do
|
325
|
+
expect { subject }.to_not have_run_command('git branch -D new-feature')
|
326
|
+
end
|
327
|
+
|
328
|
+
it "provides instructions to undo the steps taken" do
|
329
|
+
expect { subject }.to have_output("To reset and go back to your branch run \`git reset --hard origin/master && git checkout new-feature\`")
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
context "and there were issues commiting the squash merge to the base branch" do
|
334
|
+
before { stub_with_fallback(GitReflow, :run_command_with_label).with('git commit', {with_system: true}).and_return false }
|
335
|
+
it "notifies user of issues commiting the squash merge of the feature branch" do
|
336
|
+
expect { subject }.to have_output("There were problems commiting your feature... please check the errors above and try again.")
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
end
|
341
|
+
|
342
|
+
context 'but there are still unaddressed comments' do
|
343
|
+
let(:open_comment_authors) { ['nhance', 'codenamev'] }
|
344
|
+
before { github.stub(:find_authors_of_open_pull_request_comments).and_return(open_comment_authors) }
|
345
|
+
it "notifies the user to get their code reviewed" do
|
346
|
+
expect { subject }.to have_output "[deliver halted] You still need a LGTM from: #{open_comment_authors.join(', ')}"
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
context 'but has no comments' do
|
352
|
+
before do
|
353
|
+
github.stub(:has_pull_request_comments?).and_return(false)
|
354
|
+
github.stub(:find_authors_of_open_pull_request_comments).and_return([])
|
355
|
+
end
|
356
|
+
|
357
|
+
it "notifies the user to get their code reviewed" do
|
358
|
+
expect { subject }.to have_output "[deliver halted] Your code has not been reviewed yet."
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
it "successfully finds a pull request for the current feature branch" do
|
363
|
+
expect { subject }.to have_output "Merging pull request #1: 'new-feature', from 'new-feature' into 'master'"
|
364
|
+
end
|
365
|
+
|
366
|
+
it "checks out the destination branch and updates any remote changes" do
|
367
|
+
GitReflow.should_receive(:update_destination)
|
368
|
+
subject
|
369
|
+
end
|
370
|
+
|
371
|
+
it "merges and squashes the feature branch into the master branch" do
|
372
|
+
GitReflow.should_receive(:merge_feature_branch)
|
373
|
+
subject
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
context "and no pull request exists for the feature branch to the destination branch" do
|
379
|
+
before { github.stub(:find_pull_request).and_return(nil) }
|
380
|
+
|
381
|
+
it "notifies the user of a missing pull request" do
|
382
|
+
expect { subject }.to have_output "Error: No pull request exists for #{user}:#{branch}\nPlease submit your branch for review first with \`git reflow review\`"
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GitReflow::Config do
|
4
|
+
describe ".get(key)" do
|
5
|
+
subject { GitReflow::Config.get('chucknorris.roundhouse') }
|
6
|
+
it { expect{ subject }.to have_run_command_silently 'git config --get chucknorris.roundhouse' }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe ".set(key)" do
|
10
|
+
subject { GitReflow::Config.set('chucknorris.roundhouse', 'to the face') }
|
11
|
+
it { expect{ subject }.to have_run_command_silently 'git config --global --replace-all chucknorris.roundhouse "to the face"' }
|
12
|
+
|
13
|
+
context "for current project only" do
|
14
|
+
subject { GitReflow::Config.set('chucknorris.roundhouse', 'to the face', local: true) }
|
15
|
+
it { expect{ subject }.to have_run_command_silently 'git config --replace-all chucknorris.roundhouse "to the face"' }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|