git_reflow 0.8.6 → 0.8.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +348 -348
  4. data/Gemfile.lock +13 -15
  5. data/LICENSE +20 -20
  6. data/README.rdoc +461 -461
  7. data/Rakefile +8 -8
  8. data/bin/console +7 -7
  9. data/bin/setup +6 -6
  10. data/circle.yml +5 -5
  11. data/exe/git-reflow +36 -36
  12. data/git_reflow.gemspec +1 -1
  13. data/lib/git_reflow/commands/deliver.rb +10 -10
  14. data/lib/git_reflow/commands/refresh.rb +20 -20
  15. data/lib/git_reflow/commands/review.rb +13 -13
  16. data/lib/git_reflow/commands/setup.rb +11 -11
  17. data/lib/git_reflow/commands/stage.rb +9 -9
  18. data/lib/git_reflow/commands/start.rb +22 -22
  19. data/lib/git_reflow/commands/status.rb +7 -7
  20. data/lib/git_reflow/config.rb +9 -9
  21. data/lib/git_reflow/git_server/base.rb +68 -68
  22. data/lib/git_reflow/git_server/bit_bucket/pull_request.rb +84 -84
  23. data/lib/git_reflow/git_server/bit_bucket.rb +101 -101
  24. data/lib/git_reflow/git_server/git_hub/pull_request.rb +4 -1
  25. data/lib/git_reflow/git_server/pull_request.rb +11 -2
  26. data/lib/git_reflow/git_server.rb +63 -63
  27. data/lib/git_reflow/logger.rb +49 -0
  28. data/lib/git_reflow/merge_error.rb +9 -9
  29. data/lib/git_reflow/os_detector.rb +23 -23
  30. data/lib/git_reflow/rspec/command_line_helpers.rb +12 -8
  31. data/lib/git_reflow/rspec/stub_helpers.rb +13 -13
  32. data/lib/git_reflow/rspec.rb +2 -2
  33. data/lib/git_reflow/sandbox.rb +11 -6
  34. data/lib/git_reflow/version.rb +1 -1
  35. data/lib/git_reflow/workflow.rb +59 -59
  36. data/lib/git_reflow/workflows/core.rb +238 -238
  37. data/lib/git_reflow/workflows/flat_merge.rb +10 -10
  38. data/lib/git_reflow.rb +11 -0
  39. data/spec/fixtures/awesome_workflow.rb +7 -0
  40. data/spec/fixtures/git/git_config +7 -0
  41. data/spec/fixtures/issues/comment.json.erb +27 -0
  42. data/spec/fixtures/issues/comments.json +29 -0
  43. data/spec/fixtures/issues/comments.json.erb +15 -0
  44. data/spec/fixtures/pull_requests/comment.json.erb +45 -0
  45. data/spec/fixtures/pull_requests/comments.json +47 -0
  46. data/spec/fixtures/pull_requests/comments.json.erb +15 -0
  47. data/spec/fixtures/pull_requests/commits.json +29 -0
  48. data/spec/fixtures/pull_requests/external_pull_request.json +145 -0
  49. data/spec/fixtures/pull_requests/pull_request.json +142 -0
  50. data/spec/fixtures/pull_requests/pull_request.json.erb +142 -0
  51. data/spec/fixtures/pull_requests/pull_request_exists_error.json +32 -0
  52. data/spec/fixtures/pull_requests/pull_requests.json +136 -0
  53. data/spec/fixtures/repositories/commit.json +53 -0
  54. data/spec/fixtures/repositories/commit.json.erb +53 -0
  55. data/spec/fixtures/repositories/commits.json.erb +13 -0
  56. data/spec/fixtures/repositories/statuses.json +31 -0
  57. data/spec/fixtures/workflow_with_super.rb +8 -0
  58. data/spec/lib/git_reflow/config_spec.rb +74 -0
  59. data/spec/lib/git_reflow/git_helpers_spec.rb +182 -0
  60. data/spec/lib/git_reflow/git_server/bit_bucket_spec.rb +81 -0
  61. data/spec/lib/git_reflow/git_server/git_hub/pull_request_spec.rb +587 -0
  62. data/spec/lib/git_reflow/git_server/git_hub_spec.rb +221 -0
  63. data/spec/lib/git_reflow/git_server/pull_request_spec.rb +524 -0
  64. data/spec/lib/git_reflow/git_server_spec.rb +101 -0
  65. data/spec/lib/git_reflow/logger_spec.rb +18 -0
  66. data/spec/lib/git_reflow/sandbox_spec.rb +15 -0
  67. data/spec/lib/git_reflow/workflow_spec.rb +59 -0
  68. data/spec/lib/git_reflow/workflows/core_spec.rb +665 -0
  69. data/spec/lib/git_reflow/workflows/flat_merge_spec.rb +59 -0
  70. data/spec/lib/git_reflow_spec.rb +75 -0
  71. data/spec/spec_helper.rb +38 -0
  72. data/spec/support/fake_github.rb +128 -0
  73. data/spec/support/fixtures.rb +54 -0
  74. data/spec/support/github_helpers.rb +109 -0
  75. data/spec/support/mock_pull_request.rb +17 -0
  76. data/spec/support/web_mocks.rb +39 -0
  77. metadata +83 -6
@@ -0,0 +1,665 @@
1
+ require 'spec_helper'
2
+
3
+ describe GitReflow::Workflows::Core do
4
+ let(:feature_branch) { 'new-feature' }
5
+ let(:existing_pull_requests) { Fixture.new('pull_requests/pull_requests.json').to_json_hashie }
6
+ let(:existing_gh_pull_request) { GitReflow::GitServer::GitHub::PullRequest.new existing_pull_requests.first }
7
+ let(:pull_request_message_file) { "#{GitReflow.git_root_dir}/.git/GIT_REFLOW_PR_MSG" }
8
+
9
+ before do
10
+ allow(GitReflow).to receive(:current_branch).and_return(feature_branch)
11
+ allow_any_instance_of(HighLine).to receive(:choose)
12
+ end
13
+
14
+ describe ".setup" do
15
+ subject { GitReflow::Workflows::Core.setup }
16
+
17
+ before do
18
+ allow(File).to receive(:exist?).and_return(false)
19
+ stub_command_line_inputs({
20
+ 'Set the minimum number of approvals (leaving blank will require approval from all commenters): ' => ''
21
+ })
22
+ end
23
+
24
+ specify { expect { subject }.to have_run_command_silently "git config -f #{GitReflow::Config::CONFIG_FILE_PATH} --replace-all core.editor \"#{GitReflow.default_editor}\"", blocking: false }
25
+ specify { expect { subject }.to have_said "Updated git's editor (via git config key 'core.editor') to: #{GitReflow.default_editor}.", :notice }
26
+
27
+ context "core.editor git config has already been set" do
28
+ before do
29
+ allow(GitReflow::Config).to receive(:get) { "" }
30
+ allow(GitReflow::Config).to receive(:get).with('core.editor').and_return('emacs')
31
+ end
32
+
33
+ specify { expect { subject }.to_not have_run_command_silently "git config -f #{GitReflow::Config::CONFIG_FILE_PATH} --replace-all core.editor \"#{GitReflow.default_editor}\"", blocking: false }
34
+ end
35
+
36
+ context "git-reflow has not been setup before" do
37
+ it "notifies the user of global setup" do
38
+ expect { subject }.to have_said "We'll walk you through setting up git-reflow's defaults for all your projects.", :notice
39
+ expect { subject }.to have_said "In the future, you can run \`git-reflow setup\` from the root of any project you want to setup differently.", :notice
40
+ expect { subject }.to have_said "To adjust these settings globally, you can run \`git-reflow setup --global\`.", :notice
41
+ end
42
+
43
+ it "creates a .gitconfig.reflow file and includes it in the user's global git config" do
44
+ expect { subject }.to have_run_command "touch #{GitReflow::Config::CONFIG_FILE_PATH}"
45
+ expect { subject }.to have_run_command_silently "git config --global --add include.path \"#{GitReflow::Config::CONFIG_FILE_PATH}\"", blocking: false
46
+
47
+ expect { subject }.to have_said "Created #{GitReflow::Config::CONFIG_FILE_PATH} for git-reflow specific configurations.", :notice
48
+ expect { subject }.to have_said "Added #{GitReflow::Config::CONFIG_FILE_PATH} to include.path in $HOME/.gitconfig.", :notice
49
+ end
50
+
51
+ it "sets the default approval minimum and regex" do
52
+ expect { subject }.to have_run_command_silently "git config -f #{GitReflow::Config::CONFIG_FILE_PATH} --replace-all constants.minimumApprovals \"\"", blocking: false
53
+ expect { subject }.to have_run_command_silently "git config -f #{GitReflow::Config::CONFIG_FILE_PATH} --replace-all constants.approvalRegex \"#{GitReflow::GitServer::PullRequest::DEFAULT_APPROVAL_REGEX}\"", blocking: false
54
+ end
55
+
56
+ context "when setting a custom approval minimum" do
57
+ before do
58
+ stub_command_line_inputs({
59
+ 'Set the minimum number of approvals (leaving blank will require approval from all commenters): ' => '3'
60
+ })
61
+ end
62
+
63
+ specify { expect { subject }.to have_run_command_silently "git config -f #{GitReflow::Config::CONFIG_FILE_PATH} --replace-all constants.minimumApprovals \"3\"", blocking: false }
64
+ end
65
+ end
66
+
67
+ context "git-reflow has been setup before" do
68
+ before do
69
+ allow(File).to receive(:exist?).and_return(true)
70
+ end
71
+
72
+ it "doesn't create another .gitconfig.reflow file" do
73
+ expect { subject }.to_not have_run_command "touch #{GitReflow::Config::CONFIG_FILE_PATH}"
74
+ expect { subject }.to_not have_said "Created #{GitReflow::Config::CONFIG_FILE_PATH} for git-reflow specific configurations.", :notice
75
+ end
76
+
77
+ it "doesn't add the .gitconfig.reflow file to the git-config include path" do
78
+ expect { subject }.to_not have_run_command_silently "git config --global --add include.path \"#{GitReflow::Config::CONFIG_FILE_PATH}\"", blocking: false
79
+
80
+ expect { subject }.to_not have_said "Added #{GitReflow::Config::CONFIG_FILE_PATH} to include.path in $HOME/.gitconfig.", :notice
81
+ end
82
+ end
83
+ end
84
+
85
+ describe ".start" do
86
+ let(:feature_branch) { 'new_feature' }
87
+ subject { GitReflow::Workflows::Core.start feature_branch: feature_branch }
88
+
89
+ it "updates the local repo and starts creates a new branch" do
90
+ expect { subject }.to have_run_commands_in_order [
91
+ "git checkout master",
92
+ "git pull origin master",
93
+ "git push origin master:refs/heads/#{feature_branch}",
94
+ "git checkout --track -b #{feature_branch} origin/#{feature_branch}"
95
+ ]
96
+ end
97
+
98
+ context "but no branch name is provided" do
99
+ let(:feature_branch) { "" }
100
+
101
+ it "doesn't run any commands and returns usage" do
102
+ expect { subject }.to_not have_run_command "git checkout master"
103
+ expect { subject }.to_not have_run_command "git pull origin master"
104
+ expect { subject }.to_not have_run_command "git push origin master:refs/heads/#{feature_branch}"
105
+ expect { subject }.to_not have_run_command "git checkout --track -b #{feature_branch} origin/#{feature_branch}"
106
+ expect { subject }.to have_said "usage: git-reflow start [new-branch-name]", :error
107
+ end
108
+ end
109
+
110
+ it "starts from a different base branch when one is supplied" do
111
+ expect { GitReflow::Workflows::Core.start feature_branch: feature_branch, base: 'development' }.to have_run_commands_in_order [
112
+ "git checkout development",
113
+ "git pull origin development",
114
+ "git push origin development:refs/heads/#{feature_branch}",
115
+ "git checkout --track -b #{feature_branch} origin/#{feature_branch}"
116
+ ]
117
+ end
118
+ end
119
+
120
+ describe ".review" do
121
+ let(:feature_branch) { 'new-feature' }
122
+ let(:user) { 'reenhanced' }
123
+ let(:password) { 'shazam' }
124
+ let(:repo) { 'repo' }
125
+ let(:inputs) { {} }
126
+
127
+ before do
128
+ allow(GitReflow).to receive(:remote_user).and_return(user)
129
+ allow(GitReflow).to receive(:git_server).and_return(GitReflow::GitServer)
130
+ allow(GitReflow.git_server).to receive(:get_build_status).and_return(Struct.new(:state, :description, :url, :target_url).new)
131
+ allow(GitReflow.git_server).to receive(:find_open_pull_request).and_return(nil)
132
+ allow(GitReflow.git_server).to receive(:create_pull_request).and_return(existing_gh_pull_request)
133
+ allow(File).to receive(:open).with(pull_request_message_file, 'w')
134
+ allow(File).to receive(:read).with(pull_request_message_file).and_return("bingo")
135
+ allow(File).to receive(:delete)
136
+
137
+ stub_command_line_inputs({
138
+ "Submit pull request? (Y)" => ""
139
+ })
140
+ end
141
+
142
+ subject { GitReflow::Workflows::Core.review inputs }
143
+
144
+ it "fetches updates to the base branch" do
145
+ expect { subject }.to have_run_command "git fetch origin master"
146
+ end
147
+
148
+ it "pushes the current branch to the remote repo" do
149
+ expect { subject }.to have_run_command "git push origin #{feature_branch}"
150
+ end
151
+
152
+ it "uses the current branch name as the text for the PR" do
153
+ fake_file = double
154
+ expect(File).to receive(:open).with(pull_request_message_file, "w").and_yield(fake_file)
155
+ expect(fake_file).to receive(:write).with(GitReflow.current_branch)
156
+ subject
157
+ end
158
+
159
+ it "opens the pull request file for modification" do
160
+ allow(File).to receive(:open).with(pull_request_message_file, 'w')
161
+ expect { subject }.to have_run_command "#{GitReflow.git_editor_command} #{pull_request_message_file}"
162
+ end
163
+
164
+ it "reads the file then cleans up the temporary pull request message file" do
165
+ expect(File).to receive(:read).with(pull_request_message_file)
166
+ expect(File).to receive(:delete).with(pull_request_message_file)
167
+ subject
168
+ end
169
+
170
+ it "displays a review of the PR before submitting it" do
171
+ expect { subject }.to have_said "\nReview your PR:\n"
172
+ expect { subject }.to have_said "Title:\nbingo\n\n"
173
+ expect { subject }.to have_said "Body:\n\n\n"
174
+ end
175
+
176
+ context "and a PR template exists" do
177
+ let(:template_content) { "hey now" }
178
+ before do
179
+ allow(GitReflow).to receive(:pull_request_template).and_return(template_content)
180
+ end
181
+
182
+ it "uses the template's content for the PR" do
183
+ fake_file = double
184
+ expect(File).to receive(:open).with(pull_request_message_file, "w").and_yield(fake_file)
185
+ expect(fake_file).to receive(:write).with(template_content)
186
+ subject
187
+ end
188
+
189
+ end
190
+
191
+ context "providing a base branch" do
192
+ let(:inputs) {{ base: "development" }}
193
+
194
+ before do
195
+ stub_command_line_inputs({
196
+ 'Submit pull request? (Y)' => 'yes'
197
+ })
198
+ end
199
+
200
+ it "creates a pull request using the custom base branch" do
201
+ expect(GitReflow.git_server).to receive(:create_pull_request).with({
202
+ title: 'bingo',
203
+ body: "\n",
204
+ head: "#{user}:#{feature_branch}",
205
+ base: inputs[:base]
206
+ })
207
+ subject
208
+ end
209
+ end
210
+
211
+ context "providing only a title" do
212
+ let(:inputs) {{ title: "Amazing new feature" }}
213
+
214
+ before do
215
+ stub_command_line_inputs({
216
+ 'Submit pull request? (Y)' => 'yes'
217
+ })
218
+ end
219
+
220
+ it "creates a pull request with only the given title" do
221
+ expect(GitReflow.git_server).to receive(:create_pull_request).with({
222
+ title: inputs[:title],
223
+ body: nil,
224
+ head: "#{user}:#{feature_branch}",
225
+ base: 'master'
226
+ })
227
+ subject
228
+ end
229
+
230
+ it "does not bother opening any template file" do
231
+ expect(File).to_not receive(:open)
232
+ expect(File).to_not receive(:read)
233
+ expect(File).to_not receive(:delete)
234
+ subject
235
+ end
236
+ end
237
+
238
+ context "providing only a body" do
239
+ let(:inputs) {{ body: "Please pull this in!" }}
240
+
241
+ before do
242
+ stub_command_line_inputs({
243
+ 'Submit pull request? (Y)' => 'yes'
244
+ })
245
+ end
246
+
247
+ it "creates a pull request with the body as both title and body" do
248
+ expect(GitReflow.git_server).to receive(:create_pull_request).with({
249
+ title: inputs[:body],
250
+ body: inputs[:body],
251
+ head: "#{user}:#{feature_branch}",
252
+ base: 'master'
253
+ })
254
+ subject
255
+ end
256
+ end
257
+
258
+ context "providing both title and body" do
259
+ let(:inputs) {{ title: "Amazing new feature", body: "Please pull this in!" }}
260
+
261
+ before do
262
+ stub_command_line_inputs({
263
+ 'Submit pull request? (Y)' => 'yes'
264
+ })
265
+ end
266
+
267
+ it "creates a pull request with only the given title" do
268
+ expect(GitReflow.git_server).to receive(:create_pull_request).with({
269
+ title: inputs[:title],
270
+ body: inputs[:body],
271
+ head: "#{user}:#{feature_branch}",
272
+ base: 'master'
273
+ })
274
+ subject
275
+ end
276
+ end
277
+
278
+ context "with existing pull request" do
279
+ before do
280
+ expect(GitReflow.git_server).to receive(:find_open_pull_request).and_return(existing_gh_pull_request)
281
+ allow(existing_gh_pull_request).to receive(:display_pull_request_summary)
282
+ end
283
+
284
+ specify { expect{subject}.to have_said "A pull request already exists for these branches:", :notice }
285
+
286
+ it "displays a notice that an existing PR exists" do
287
+ expect(existing_gh_pull_request).to receive(:display_pull_request_summary)
288
+ subject
289
+ end
290
+ end
291
+
292
+ context "approving PR submission" do
293
+ before do
294
+ stub_command_line_inputs({
295
+ 'Submit pull request? (Y)' => 'yes'
296
+ })
297
+ end
298
+
299
+ it "creates a pull request" do
300
+ expect(GitReflow.git_server).to receive(:create_pull_request).with({
301
+ title: 'bingo',
302
+ body: "\n",
303
+ head: "#{user}:#{feature_branch}",
304
+ base: 'master'
305
+ })
306
+ subject
307
+ end
308
+
309
+ it "notifies the user that the pull request was created" do
310
+ expect(GitReflow.git_server).to receive(:create_pull_request).and_return(existing_gh_pull_request)
311
+ expect{ subject }.to have_said "Successfully created pull request ##{existing_gh_pull_request.number}: #{existing_gh_pull_request.title}\nPull Request URL: #{existing_gh_pull_request.html_url}\n", :success
312
+ end
313
+ end
314
+
315
+ context "aborting during PR template review" do
316
+ before do
317
+ stub_command_line_inputs({
318
+ 'Submit pull request? (Y)' => 'no'
319
+ })
320
+ end
321
+
322
+ it "does not create a pull request" do
323
+ expect(GitReflow.git_server).to_not receive(:create_pull_request)
324
+ subject
325
+ end
326
+
327
+ it "notifies the user that the review was aborted" do
328
+ expect { subject }.to have_said "Review aborted. No pull request has been created.", :review_halted
329
+ end
330
+ end
331
+ end
332
+
333
+ describe ".status" do
334
+ let(:feature_branch) { 'new-feature' }
335
+ let(:destination_branch) { nil }
336
+
337
+ subject { GitReflow::Workflows::Core.status destination_branch: destination_branch }
338
+
339
+ before do
340
+ allow(GitReflow).to receive(:git_server).and_return(GitReflow::GitServer)
341
+ allow(GitReflow.git_server).to receive(:get_build_status).and_return(Struct.new(:state, :description, :url, :target_url).new)
342
+ allow(GitReflow).to receive(:current_branch).and_return(feature_branch)
343
+ allow(existing_gh_pull_request).to receive(:display_pull_request_summary)
344
+ end
345
+
346
+ context "with no existing pull request" do
347
+ before { allow(GitReflow.git_server).to receive(:find_open_pull_request).with({from: feature_branch, to: 'master'}).and_return(nil) }
348
+ it { expect{ subject }.to have_said "No pull request exists for #{feature_branch} -> master", :notice }
349
+ it { expect{ subject }.to have_said "Run 'git reflow review master' to start the review process", :notice }
350
+ end
351
+
352
+ context "with an existing pull request" do
353
+ let(:destination_branch) { 'master' }
354
+ before do
355
+ allow(GitReflow.git_server).to receive(:find_open_pull_request).and_return(existing_gh_pull_request)
356
+ end
357
+
358
+ it "displays a summary of the pull request" do
359
+ expect(existing_gh_pull_request).to receive(:display_pull_request_summary)
360
+ expect{ subject }.to have_said "Here's the status of your review:"
361
+ end
362
+
363
+ context "with custom destination branch" do
364
+ let(:destination_branch) { 'develop' }
365
+
366
+ it "uses the custom destination branch to lookup the pull request" do
367
+ expect(GitReflow.git_server).to receive(:find_open_pull_request).with({from: feature_branch, to: destination_branch}).and_return(existing_gh_pull_request)
368
+ subject
369
+ end
370
+ end
371
+ end
372
+ end
373
+
374
+ describe ".deploy" do
375
+ let(:deploy_command) { "bundle exec cap #{destination} deploy" }
376
+ let(:destination) { nil }
377
+ subject { GitReflow::Workflows::Core.deploy(destination_server: destination) }
378
+
379
+ before do
380
+ stub_command_line_inputs({
381
+ "Enter the command you use to deploy to default (leaving blank will skip deployment)" => "bundle exec cap deploy",
382
+ "Enter the command you use to deploy to #{destination} (leaving blank will skip deployment)" => "bundle exec cap #{destination} deploy"
383
+ })
384
+ end
385
+
386
+ it "sets the local git-config for reflow.deploy-to-default-command" do
387
+ expect(GitReflow::Config).to receive(:set).with('reflow.deploy-to-default-command', deploy_command.squeeze, local: true)
388
+ subject
389
+ end
390
+
391
+ it "runs the staging deploy command" do
392
+ expect { subject }.to have_run_command(deploy_command.squeeze)
393
+ end
394
+
395
+ context "staging" do
396
+ let(:destination) { "staging" }
397
+
398
+ it "sets the local git-config for reflow.deploy-to-staging-command" do
399
+ expect(GitReflow::Config).to receive(:set).with('reflow.deploy-to-staging-command', deploy_command, local: true)
400
+ subject
401
+ end
402
+
403
+ it "runs the staging deploy command" do
404
+ expect { subject }.to have_run_command(deploy_command)
405
+ end
406
+ end
407
+ end
408
+
409
+ describe ".stage" do
410
+ let(:feature_branch) { 'new-feature' }
411
+
412
+ subject { GitReflow::Workflows::Core.stage }
413
+
414
+ before do
415
+ allow(GitReflow).to receive(:current_branch).and_return(feature_branch)
416
+ allow(GitReflow::Config).to receive(:get).and_call_original
417
+ allow(GitReflow::Workflows::Core).to receive(:deploy)
418
+ end
419
+
420
+ it "checks out and updates the staging branch" do
421
+ expect(GitReflow::Config).to receive(:get).with('reflow.staging-branch', local: true).and_return('staging')
422
+ expect { subject }.to have_run_commands_in_order([
423
+ "git checkout staging",
424
+ "git pull origin staging",
425
+ "git merge #{feature_branch}",
426
+ "git push origin staging"
427
+ ])
428
+ end
429
+
430
+ context "merge is not successful" do
431
+ before do
432
+ allow(GitReflow::Config).to receive(:get).with('reflow.staging-branch', local: true).and_return('staging')
433
+ allow(GitReflow).to receive(:run_command_with_label).and_call_original
434
+ expect(GitReflow).to receive(:run_command_with_label).with("git merge #{feature_branch}", with_system: true).and_return(false)
435
+ end
436
+
437
+ it "notifies the user of unsuccessful merge" do
438
+ expect { subject }.to have_said "There were issues merging your feature branch to staging.", :error
439
+ end
440
+
441
+ it "does not push any changes to the remote repo" do
442
+ expect { subject }.to_not have_run_command "git push origin staging"
443
+ end
444
+
445
+ it "does not deploy to staging" do
446
+ expect(GitReflow::Workflows::Core).to_not receive(:deploy)
447
+ subject
448
+ end
449
+ end
450
+
451
+ context "merge is successful" do
452
+ before do
453
+ allow(GitReflow::Config).to receive(:get).with('reflow.staging-branch', local: true).and_return('staging')
454
+ allow(GitReflow).to receive(:run_command_with_label).and_call_original
455
+ expect(GitReflow).to receive(:run_command_with_label).with("git merge #{feature_branch}", with_system: true).and_return(true).and_call_original
456
+ end
457
+
458
+ specify { expect{ subject }.to have_run_command "git push origin staging" }
459
+
460
+ context "and deployment is successful" do
461
+ before { expect(GitReflow::Workflows::Core).to receive(:deploy).with(destination_server: :staging).and_return(true) }
462
+ specify { expect{ subject }.to have_said "Deployed to Staging.", :success }
463
+ end
464
+
465
+ context "but deployment is not successful" do
466
+ before { allow(GitReflow::Workflows::Core).to receive(:deploy).with(destination_server: :staging).and_return(false) }
467
+ specify { expect{ subject }.to have_said "There were issues deploying to staging.", :error }
468
+ end
469
+ end
470
+
471
+ context "no staging branch has been setup" do
472
+ before do
473
+ allow(GitReflow::Config).to receive(:get).with('reflow.staging-branch', local: true).and_return('')
474
+ stub_command_line_inputs({
475
+ "What's the name of your staging branch? (default: 'staging') " => "bobby"
476
+ })
477
+ end
478
+
479
+ it "sets the reflow.staging-branch git config to 'staging'" do
480
+ expect(GitReflow::Config).to receive(:set).with("reflow.staging-branch", "bobby", local: true)
481
+ subject
482
+ end
483
+
484
+ it "checks out and updates the staging branch" do
485
+ allow(GitReflow::Config).to receive(:set).with("reflow.staging-branch", "bobby", local: true)
486
+ expect { subject }.to have_run_commands_in_order([
487
+ "git checkout bobby",
488
+ "git pull origin bobby",
489
+ "git merge #{feature_branch}",
490
+ "git push origin bobby"
491
+ ])
492
+ end
493
+
494
+ context "and I don't enter one in" do
495
+ before do
496
+ stub_command_line_inputs({
497
+ "What's the name of your staging branch? (default: 'staging') " => ""
498
+ })
499
+ end
500
+
501
+ it "sets the reflow.staging-branch git config to 'staging'" do
502
+ expect { subject }.to have_run_command_silently "git config --replace-all reflow.staging-branch \"staging\"", blocking: false
503
+ end
504
+
505
+ it "checks out and updates the staging branch" do
506
+ expect { subject }.to have_run_commands_in_order([
507
+ "git checkout staging",
508
+ "git pull origin staging",
509
+ "git merge #{feature_branch}",
510
+ "git push origin staging"
511
+ ])
512
+ end
513
+ end
514
+ end
515
+ end
516
+
517
+ describe ".deliver" do
518
+ let(:feature_branch) { 'new-feature' }
519
+ let(:user) { 'reenhanced' }
520
+ let(:repo) { 'repo' }
521
+
522
+ subject { GitReflow::Workflows::Core.deliver }
523
+
524
+ before do
525
+ allow(GitReflow).to receive(:git_server).and_return(GitReflow::GitServer)
526
+ allow(GitReflow).to receive(:remote_user).and_return(user)
527
+ allow(GitReflow).to receive(:current_branch).and_return(feature_branch)
528
+ allow(GitReflow.git_server).to receive(:get_build_status).and_return(Struct.new(:state, :description, :url, :target_url).new)
529
+ end
530
+
531
+ context "pull request does not exist" do
532
+ before { allow(GitReflow.git_server).to receive(:find_open_pull_request).with( from: feature_branch, to: 'master').and_return(nil) }
533
+ specify { expect{ subject }.to have_said "No pull request exists for #{user}:#{feature_branch}\nPlease submit your branch for review first with \`git reflow review\`", :deliver_halted }
534
+ end
535
+
536
+ context "pull request exists" do
537
+ before do
538
+ allow(GitReflow.git_server).to receive(:find_open_pull_request).with( from: feature_branch, to: 'master').and_return(existing_gh_pull_request)
539
+ allow(GitReflow::Workflows::Core).to receive(:status)
540
+ end
541
+
542
+ context "and PR passes all QA checks" do
543
+ before { allow(existing_gh_pull_request).to receive(:good_to_merge?).and_return(true) }
544
+
545
+ it "displays the status of the PR" do
546
+ allow(existing_gh_pull_request).to receive(:merge!)
547
+ expect(GitReflow::Workflows::Core).to receive(:status).with(destination_branch: 'master')
548
+ subject
549
+ end
550
+
551
+ it "merges the feature branch" do
552
+ expect(existing_gh_pull_request).to receive(:merge!)
553
+ subject
554
+ end
555
+
556
+ context "but there is an error from the git server" do
557
+ let(:github_error) { Github::Error::UnprocessableEntity.new(eval(Fixture.new('pull_requests/pull_request_exists_error.json').to_s)) }
558
+ before do
559
+ allow(existing_gh_pull_request).to receive(:merge!).and_raise github_error
560
+ end
561
+
562
+ it "notifies the user of the error" do
563
+ expect { subject }.to have_said "Github Error: #{github_error.inspect}", :error
564
+ end
565
+ end
566
+ end
567
+
568
+ context "and PR fails some QA checks" do
569
+ before do
570
+ allow(existing_gh_pull_request).to receive(:good_to_merge?).and_return(false)
571
+ allow(existing_gh_pull_request).to receive(:rejection_message).and_return("I think you need a hug.")
572
+ end
573
+
574
+ it "does not merge the feature branch" do
575
+ expect(existing_gh_pull_request).to_not receive(:merge!)
576
+ subject
577
+ end
578
+
579
+ it "does not display the status of the PR" do
580
+ expect(GitReflow::Workflows::Core).to_not receive(:status).with(destination_branch: 'master')
581
+ subject
582
+ end
583
+
584
+ it "notifies the user of the reason the merge is unsafe" do
585
+ expect { subject }.to have_said "I think you need a hug.", :deliver_halted
586
+ end
587
+
588
+ context "but forcing the deliver" do
589
+ subject { GitReflow::Workflows::Core.deliver force: true }
590
+
591
+ before do
592
+ allow(existing_gh_pull_request).to receive(:good_to_merge?).with(force: true).and_return(true)
593
+ allow(existing_gh_pull_request).to receive(:merge!).with(force: true, base: 'master', skip_lgtm: true)
594
+ end
595
+
596
+ it "displays the status of the PR" do
597
+ expect(GitReflow::Workflows::Core).to receive(:status).with(destination_branch: 'master')
598
+ subject
599
+ end
600
+
601
+ it "merges the feature branch anyway" do
602
+ expect(existing_gh_pull_request).to receive(:merge!).with(force: true, base: 'master', skip_lgtm: true)
603
+ subject
604
+ end
605
+ end
606
+ end
607
+
608
+ context "and using a custom base branch" do
609
+ subject { GitReflow::Workflows::Core.deliver base: 'development' }
610
+ before do
611
+ expect(GitReflow.git_server).to receive(:find_open_pull_request).with( from: feature_branch, to: 'development').and_return(existing_gh_pull_request)
612
+ allow(existing_gh_pull_request).to receive(:good_to_merge?).and_return(true)
613
+ end
614
+
615
+
616
+ it "displays the status of the PR" do
617
+ allow(existing_gh_pull_request).to receive(:merge!).with(base: 'development')
618
+ expect(GitReflow::Workflows::Core).to receive(:status).with(destination_branch: 'development')
619
+ subject
620
+ end
621
+
622
+ it "merges the feature branch" do
623
+ expect(existing_gh_pull_request).to receive(:merge!).with(base: 'development')
624
+ subject
625
+ end
626
+ end
627
+ end
628
+ end
629
+
630
+ describe ".refresh" do
631
+ subject { GitReflow::Workflows::Core.refresh }
632
+
633
+ it "updates the feature branch with default remote repo and base branch" do
634
+ expect(GitReflow).to receive(:update_feature_branch).with(remote: 'origin', base: 'master')
635
+ subject
636
+ end
637
+
638
+ context "providing a custom base branch" do
639
+ subject { GitReflow::Workflows::Core.refresh base: 'development' }
640
+
641
+ it "updates the feature branch with default remote repo and base branch" do
642
+ expect(GitReflow).to receive(:update_feature_branch).with(remote: 'origin', base: 'development')
643
+ subject
644
+ end
645
+ end
646
+
647
+ context "provding a custom remote repo" do
648
+ subject { GitReflow::Workflows::Core.refresh remote: 'upstream' }
649
+
650
+ it "updates the feature branch with default remote repo and base branch" do
651
+ expect(GitReflow).to receive(:update_feature_branch).with(remote: 'upstream', base: 'master')
652
+ subject
653
+ end
654
+ end
655
+
656
+ context "providing a custom base branch and remote repo" do
657
+ subject { GitReflow::Workflows::Core.refresh remote: 'upstream', base: 'development' }
658
+
659
+ it "updates the feature branch with default remote repo and base branch" do
660
+ expect(GitReflow).to receive(:update_feature_branch).with(remote: 'upstream', base: 'development')
661
+ subject
662
+ end
663
+ end
664
+ end
665
+ end