shipit-engine 0.15.0 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +34 -1
- data/app/assets/javascripts/shipit/page_updater.js.coffee +63 -0
- data/app/assets/javascripts/shipit/stacks.js.coffee +9 -21
- data/app/assets/stylesheets/_base/_base.scss +2 -2
- data/app/assets/stylesheets/_base/_colors.scss +0 -1
- data/app/assets/stylesheets/_base/_forms.scss +14 -0
- data/app/assets/stylesheets/_pages/_commits.scss +16 -6
- data/app/assets/stylesheets/_pages/_settings.scss +8 -0
- data/app/assets/stylesheets/_pages/_stacks.scss +1 -1
- data/app/controllers/shipit/api/base_controller.rb +7 -3
- data/app/controllers/shipit/api/ccmenu_controller.rb +33 -0
- data/app/controllers/shipit/api/pull_requests_controller.rb +36 -0
- data/app/controllers/shipit/api/stacks_controller.rb +1 -0
- data/app/controllers/shipit/ccmenu_url_controller.rb +22 -0
- data/app/controllers/shipit/pull_requests_controller.rb +30 -0
- data/app/controllers/shipit/stacks_controller.rb +7 -2
- data/app/controllers/shipit/webhooks_controller.rb +1 -2
- data/app/helpers/shipit/github_url_helper.rb +8 -2
- data/app/helpers/shipit/shipit_helper.rb +9 -0
- data/app/helpers/shipit/stacks_helper.rb +22 -7
- data/app/jobs/shipit/background_job/unique.rb +19 -1
- data/app/jobs/shipit/cache_deploy_spec_job.rb +1 -1
- data/app/jobs/shipit/merge_pull_requests_job.rb +26 -0
- data/app/jobs/shipit/perform_task_job.rb +1 -1
- data/app/jobs/shipit/refresh_pull_request_job.rb +8 -0
- data/app/models/concerns/shipit/deferred_touch.rb +6 -1
- data/app/models/shipit/anonymous_user.rb +4 -0
- data/app/models/shipit/application_record.rb +5 -0
- data/app/models/shipit/commit.rb +51 -49
- data/app/models/shipit/commit_message.rb +32 -0
- data/app/models/shipit/deploy.rb +5 -0
- data/app/models/shipit/deploy_spec.rb +26 -1
- data/app/models/shipit/deploy_spec/file_system.rb +6 -1
- data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +10 -13
- data/app/models/shipit/deploy_spec/npm_discovery.rb +2 -1
- data/app/models/shipit/duration.rb +3 -1
- data/app/models/shipit/hook.rb +1 -0
- data/app/models/shipit/pull_request.rb +252 -0
- data/app/models/shipit/stack.rb +33 -17
- data/app/models/shipit/status.rb +1 -16
- data/app/models/shipit/status/common.rb +45 -0
- data/app/models/shipit/status/group.rb +82 -0
- data/app/models/shipit/status/missing.rb +30 -0
- data/app/models/shipit/status/unknown.rb +33 -0
- data/app/models/shipit/unlimited_api_client.rb +10 -0
- data/app/serializers/shipit/commit_serializer.rb +1 -1
- data/app/serializers/shipit/pull_request_serializer.rb +20 -0
- data/app/serializers/shipit/stack_serializer.rb +6 -2
- data/app/views/layouts/shipit.html.erb +41 -39
- data/app/views/shipit/ccmenu/project.xml.builder +13 -0
- data/app/views/shipit/commits/_commit.html.erb +1 -1
- data/app/views/shipit/deploys/_deploy.html.erb +1 -1
- data/app/views/shipit/pull_requests/_pull_request.html.erb +29 -0
- data/app/views/shipit/pull_requests/index.html.erb +20 -0
- data/app/views/shipit/shared/_author.html.erb +7 -0
- data/app/views/shipit/stacks/_header.html.erb +5 -0
- data/app/views/shipit/stacks/settings.html.erb +13 -0
- data/app/views/shipit/stacks/show.html.erb +3 -2
- data/app/views/shipit/statuses/_group.html.erb +1 -1
- data/app/views/shipit/tasks/_task.html.erb +1 -1
- data/config/initializers/inflections.rb +3 -0
- data/config/locales/en.yml +1 -3
- data/config/routes.rb +8 -0
- data/db/migrate/20170130113633_create_shipit_pull_requests.rb +25 -0
- data/db/migrate/20170208143657_add_pull_request_number_and_title_to_commits.rb +7 -0
- data/db/migrate/20170208154609_backfill_merge_commits.rb +13 -0
- data/db/migrate/20170209160355_add_branch_to_pull_requests.rb +5 -0
- data/db/migrate/20170215123538_add_merge_queue_enabled_to_stacks.rb +5 -0
- data/db/migrate/20170220152410_improve_users_indexing.rb +6 -0
- data/db/migrate/20170221102128_improve_tasks_indexing.rb +8 -0
- data/db/migrate/20170221130336_add_last_revalidated_at_on_pull_requests.rb +10 -0
- data/lib/shipit.rb +2 -0
- data/lib/shipit/version.rb +1 -1
- data/lib/tasks/cron.rake +1 -0
- data/test/controllers/api/ccmenu_controller_test.rb +57 -0
- data/test/controllers/api/commits_controller_test.rb +1 -1
- data/test/controllers/api/pull_requests_controller_test.rb +59 -0
- data/test/controllers/ccmenu_controller_test.rb +33 -0
- data/test/controllers/pull_requests_controller_test.rb +31 -0
- data/test/controllers/webhooks_controller_test.rb +3 -4
- data/test/dummy/config/environments/development.rb +3 -1
- data/test/dummy/data/stacks/shopify/junk/production/git/README.md +8 -0
- data/test/dummy/data/stacks/shopify/junk/production/git/circle.yml +4 -0
- data/test/dummy/data/stacks/shopify/junk/production/git/shipit.yml +4 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/schema.rb +45 -11
- data/test/dummy/db/seeds.rb +33 -10
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/fixtures/shipit/commits.yml +14 -0
- data/test/fixtures/shipit/pull_requests.yml +56 -0
- data/test/fixtures/shipit/stacks.yml +5 -1
- data/test/fixtures/shipit/statuses.yml +8 -0
- data/test/helpers/json_helper.rb +16 -14
- data/test/jobs/merge_pull_requests_job_test.rb +59 -0
- data/test/models/commits_test.rb +104 -49
- data/test/{unit → models}/deploy_spec_test.rb +138 -12
- data/test/models/deploys_test.rb +10 -4
- data/test/models/pull_request_test.rb +197 -0
- data/test/models/stacks_test.rb +46 -53
- data/test/models/status/group_test.rb +44 -0
- data/test/models/status/missing_test.rb +23 -0
- data/test/models/status_test.rb +3 -6
- data/test/unit/csv_serializer_test.rb +10 -2
- metadata +57 -12
- data/app/models/shipit/missing_status.rb +0 -21
- data/app/models/shipit/status_group.rb +0 -35
- data/app/models/shipit/unknown_status.rb +0 -48
- data/app/views/shipit/commits/_commit_author.html.erb +0 -7
- data/test/models/missing_status_test.rb +0 -23
- data/test/models/status_group_test.rb +0 -26
data/test/models/commits_test.rb
CHANGED
@@ -30,11 +30,6 @@ module Shipit
|
|
30
30
|
assert_nil @commit.pull_request_title
|
31
31
|
end
|
32
32
|
|
33
|
-
test "#pull_request_url build the pull request url from the message" do
|
34
|
-
assert_equal 'https://github.com/shopify/shipit-engine/pull/31', @pr.pull_request_url
|
35
|
-
assert_nil @commit.pull_request_url
|
36
|
-
end
|
37
|
-
|
38
33
|
test "#newer_than(nil) returns all commits" do
|
39
34
|
assert_equal @stack.commits.all.to_a, @stack.commits.newer_than(nil).to_a
|
40
35
|
end
|
@@ -254,29 +249,19 @@ module Shipit
|
|
254
249
|
assert_equal 'pending', @commit.reload.state
|
255
250
|
end
|
256
251
|
|
257
|
-
test "#
|
258
|
-
assert_equal 4, shipit_commits(:second).statuses.count
|
259
|
-
assert_equal 2, shipit_commits(:second).last_statuses.count
|
260
|
-
end
|
261
|
-
|
262
|
-
test "#last_statuses returns [UnknownStatus] if the commit has no statuses" do
|
263
|
-
commit = shipit_commits(:second)
|
264
|
-
commit.statuses = []
|
265
|
-
assert_equal UnknownStatus.new(commit), commit.significant_status
|
266
|
-
end
|
267
|
-
|
268
|
-
test "#status returns UnknownStatus if the commit has no status" do
|
252
|
+
test "#status returns an unknown if the commit has no statuses" do
|
269
253
|
commit = shipit_commits(:second)
|
270
254
|
commit.statuses = []
|
271
|
-
|
255
|
+
assert_predicate commit.status, :unknown?
|
272
256
|
end
|
273
257
|
|
274
|
-
test "#
|
258
|
+
test "#status rejects the statuses that are specified in the deploy spec's `ci.hide`" do
|
275
259
|
commit = shipit_commits(:second)
|
276
|
-
|
260
|
+
assert_predicate commit.status, :group?
|
261
|
+
assert_equal 2, commit.status.size
|
277
262
|
commit.stack.update!(cached_deploy_spec: DeploySpec.new('ci' => {'hide' => 'metrics/coveralls'}))
|
278
263
|
commit.reload
|
279
|
-
|
264
|
+
refute_predicate commit.status, :group?
|
280
265
|
end
|
281
266
|
|
282
267
|
test "#deployable? is true if commit status is 'success'" do
|
@@ -343,8 +328,10 @@ module Shipit
|
|
343
328
|
end
|
344
329
|
|
345
330
|
test "#add_status does not fire webhooks for invisible statuses" do
|
331
|
+
@stack.deploys_and_rollbacks.destroy_all
|
346
332
|
commit = shipit_commits(:second)
|
347
333
|
assert commit.stack.hooks.where(events: ['commit_status']).size >= 1
|
334
|
+
refute_predicate commit, :deployed?
|
348
335
|
|
349
336
|
expect_no_hook(:deployable_status) do
|
350
337
|
github_status = OpenStruct.new(
|
@@ -358,8 +345,10 @@ module Shipit
|
|
358
345
|
end
|
359
346
|
|
360
347
|
test "#add_status does not fire webhooks for non-meaningful statuses" do
|
348
|
+
@stack.deploys_and_rollbacks.destroy_all
|
361
349
|
commit = shipit_commits(:second)
|
362
350
|
assert commit.stack.hooks.where(events: ['commit_status']).size >= 1
|
351
|
+
refute_predicate commit, :deployed?
|
363
352
|
|
364
353
|
expect_no_hook(:deployable_status) do
|
365
354
|
github_status = OpenStruct.new(
|
@@ -372,27 +361,38 @@ module Shipit
|
|
372
361
|
end
|
373
362
|
end
|
374
363
|
|
375
|
-
test "#
|
364
|
+
test "#add_status does not fire webhooks for already deployed commits" do
|
376
365
|
commit = shipit_commits(:second)
|
377
|
-
|
378
|
-
|
379
|
-
|
366
|
+
assert_predicate commit, :deployed?
|
367
|
+
|
368
|
+
expect_no_hook(:deployable_status) do
|
369
|
+
github_status = OpenStruct.new(
|
370
|
+
state: 'failure',
|
371
|
+
description: 'Sad',
|
372
|
+
context: 'ci/travis',
|
373
|
+
created_at: 1.day.ago.to_s(:db),
|
374
|
+
)
|
375
|
+
commit.create_status_from_github!(github_status)
|
376
|
+
end
|
380
377
|
end
|
381
378
|
|
382
|
-
test "#
|
379
|
+
test "#add_status schedule a MergePullRequests job if the commit transition to `pending` or `success`" do
|
383
380
|
commit = shipit_commits(:second)
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
381
|
+
github_status = OpenStruct.new(
|
382
|
+
state: 'success',
|
383
|
+
description: 'Cool',
|
384
|
+
context: 'metrics/coveralls',
|
385
|
+
created_at: 1.day.ago.to_s(:db),
|
386
|
+
)
|
388
387
|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
388
|
+
assert_equal 'failure', commit.state
|
389
|
+
assert_enqueued_with(job: MergePullRequestsJob, args: [@commit.stack]) do
|
390
|
+
commit.create_status_from_github!(github_status)
|
391
|
+
assert_equal 'success', commit.state
|
392
|
+
end
|
393
393
|
end
|
394
394
|
|
395
|
-
test "#
|
395
|
+
test "#status hierarchy uses failures and errors, then pending, then successes, then Status::Unknown" do
|
396
396
|
commit = shipit_commits(:first)
|
397
397
|
pending = commit.statuses.new(stack_id: @stack.id, state: 'pending', context: 'ci/pending')
|
398
398
|
failure = commit.statuses.new(stack_id: @stack.id, state: 'failure', context: 'ci/failure')
|
@@ -400,37 +400,92 @@ module Shipit
|
|
400
400
|
success = commit.statuses.new(stack_id: @stack.id, state: 'success', context: 'ci/success')
|
401
401
|
|
402
402
|
commit.reload.statuses = [pending, failure, success, error]
|
403
|
-
|
403
|
+
assert_equal 'error', commit.status.state
|
404
404
|
|
405
405
|
commit.reload.statuses = [pending, failure, success]
|
406
|
-
assert_equal failure, commit.
|
406
|
+
assert_equal 'failure', commit.status.state
|
407
407
|
|
408
408
|
commit.reload.statuses = [pending, error, success]
|
409
|
-
assert_equal error, commit.
|
409
|
+
assert_equal 'error', commit.status.state
|
410
410
|
|
411
411
|
commit.reload.statuses = [success, pending]
|
412
|
-
assert_equal pending, commit.
|
412
|
+
assert_equal 'pending', commit.status.state
|
413
413
|
|
414
414
|
commit.reload.statuses = [success]
|
415
|
-
assert_equal success, commit.
|
415
|
+
assert_equal 'success', commit.status.state
|
416
416
|
|
417
417
|
commit.reload.statuses = []
|
418
|
-
assert_equal
|
418
|
+
assert_equal 'unknown', commit.status.state
|
419
419
|
end
|
420
420
|
|
421
|
-
test "
|
422
|
-
commit =
|
423
|
-
|
424
|
-
|
421
|
+
test "merge commits are linked to the matching Pull Request if there is one" do
|
422
|
+
commit = @stack.commits.create!(
|
423
|
+
author: shipit_users(:shipit),
|
424
|
+
authored_at: Time.now,
|
425
|
+
committer: shipit_users(:shipit),
|
426
|
+
committed_at: Time.now,
|
427
|
+
sha: '5590fd8b5f2be05d1fedb763a3605ee461c39074',
|
428
|
+
message: "Merge pull request #62 from shipit-engine/yoloshipit\n\nyoloshipit!",
|
429
|
+
)
|
430
|
+
pull_request = shipit_pull_requests(:shipit_pending)
|
431
|
+
|
432
|
+
assert_predicate commit, :pull_request?
|
433
|
+
assert_equal 62, commit.pull_request_number
|
434
|
+
assert_equal pull_request.title, commit.pull_request_title
|
435
|
+
assert_equal pull_request, commit.pull_request
|
436
|
+
end
|
437
|
+
|
438
|
+
test "merge commits infer pull request number and title from the message if it's not a known pull request" do
|
439
|
+
commit = @stack.commits.create!(
|
440
|
+
author: shipit_users(:shipit),
|
441
|
+
authored_at: Time.now,
|
442
|
+
committer: shipit_users(:shipit),
|
443
|
+
committed_at: Time.now,
|
444
|
+
sha: '5590fd8b5f2be05d1fedb763a3605ee461c39074',
|
445
|
+
message: "Merge pull request #99 from shipit-engine/yoloshipit\n\nyoloshipit!",
|
446
|
+
)
|
447
|
+
|
448
|
+
assert_predicate commit, :pull_request?
|
449
|
+
assert_equal 99, commit.pull_request_number
|
450
|
+
assert_equal 'yoloshipit!', commit.pull_request_title
|
451
|
+
assert_nil commit.pull_request
|
452
|
+
end
|
453
|
+
|
454
|
+
test "the merge requester if known overrides the commit author" do
|
455
|
+
commit = @stack.commits.create!(
|
456
|
+
author: shipit_users(:shipit),
|
457
|
+
authored_at: Time.now,
|
458
|
+
committer: shipit_users(:shipit),
|
459
|
+
committed_at: Time.now,
|
460
|
+
sha: '5590fd8b5f2be05d1fedb763a3605ee461c39074',
|
461
|
+
message: "Merge pull request #62 from shipit-engine/yoloshipit\n\nyoloshipit!",
|
462
|
+
)
|
463
|
+
|
464
|
+
assert_equal shipit_users(:walrus), commit.author
|
465
|
+
end
|
466
|
+
|
467
|
+
test "#pull_request_number and #pull_request_title are nil if the message is not a merge commit message" do
|
468
|
+
commit = @stack.commits.create!(
|
469
|
+
author: shipit_users(:shipit),
|
470
|
+
authored_at: Time.now,
|
471
|
+
committer: shipit_users(:shipit),
|
472
|
+
committed_at: Time.now,
|
473
|
+
sha: '5590fd8b5f2be05d1fedb763a3605ee461c39074',
|
474
|
+
message: "Yoloshipit!",
|
475
|
+
)
|
476
|
+
|
477
|
+
refute_predicate commit, :pull_request?
|
478
|
+
assert_nil commit.pull_request_number
|
479
|
+
assert_nil commit.pull_request_title
|
480
|
+
assert_nil commit.pull_request
|
425
481
|
end
|
426
482
|
|
427
483
|
private
|
428
484
|
|
429
485
|
def expect_event(stack)
|
430
|
-
Pubsubstub
|
431
|
-
Pubsubstub
|
432
|
-
|
433
|
-
channel == "stack.#{stack.id}" && data['url'] == "/#{stack.to_param}"
|
486
|
+
Pubsubstub.expects(:publish).at_least_once
|
487
|
+
Pubsubstub.expects(:publish).with do |channel, _payload, _options = {}|
|
488
|
+
channel == "stack.#{stack.id}"
|
434
489
|
end
|
435
490
|
end
|
436
491
|
|
@@ -126,6 +126,29 @@ module Shipit
|
|
126
126
|
assert_equal ["kubernetes-deploy foo bar"], @spec.deploy_steps
|
127
127
|
end
|
128
128
|
|
129
|
+
test "#deploy_steps returns kubernetes-deploy command if both capfile and `kubernetes` are present" do
|
130
|
+
@spec.stubs(:bundler?).returns(true)
|
131
|
+
@spec.stubs(:capistrano?).returns(true)
|
132
|
+
@spec.stubs(:load_config).returns(
|
133
|
+
'kubernetes' => {
|
134
|
+
'namespace' => 'foo',
|
135
|
+
'context' => 'bar',
|
136
|
+
},
|
137
|
+
)
|
138
|
+
assert_equal ["kubernetes-deploy foo bar"], @spec.deploy_steps
|
139
|
+
end
|
140
|
+
|
141
|
+
test "#deploy_steps returns kubernetes command if `kubernetes` is present and template_dir is set" do
|
142
|
+
@spec.stubs(:load_config).returns(
|
143
|
+
'kubernetes' => {
|
144
|
+
'namespace' => 'foo',
|
145
|
+
'context' => 'bar',
|
146
|
+
'template_dir' => 'k8s_templates/',
|
147
|
+
},
|
148
|
+
)
|
149
|
+
assert_equal ["kubernetes-deploy --template-dir k8s_templates/ foo bar"], @spec.deploy_steps
|
150
|
+
end
|
151
|
+
|
129
152
|
test "#deploy_steps prepend and append pre and post steps" do
|
130
153
|
@spec.stubs(:load_config).returns('deploy' => {'pre' => ['before'], 'post' => ['after']})
|
131
154
|
@spec.expects(:bundler?).returns(true).at_least_once
|
@@ -168,20 +191,21 @@ module Shipit
|
|
168
191
|
assert_equal ["kubernetes-deploy foo bar"], @spec.rollback_steps
|
169
192
|
end
|
170
193
|
|
171
|
-
test
|
172
|
-
@spec.stubs(:
|
173
|
-
|
174
|
-
end
|
175
|
-
|
176
|
-
test '#discover_machine_env contains K8S_TEMPLATE_FOLDER if `kubernetes.template_dir` is present' do
|
194
|
+
test "#rollback_steps returns kubernetes-deploy command when both capfile and `kubernetes` are present" do
|
195
|
+
@spec.stubs(:bundler?).returns(true)
|
196
|
+
@spec.stubs(:capistrano?).returns(true)
|
177
197
|
@spec.stubs(:load_config).returns(
|
178
198
|
'kubernetes' => {
|
179
199
|
'namespace' => 'foo',
|
180
200
|
'context' => 'bar',
|
181
|
-
'template_dir' => '/egg/spam',
|
182
201
|
},
|
183
202
|
)
|
184
|
-
assert_equal
|
203
|
+
assert_equal ["kubernetes-deploy foo bar"], @spec.rollback_steps
|
204
|
+
end
|
205
|
+
|
206
|
+
test '#machine_env returns an environment hash' do
|
207
|
+
@spec.stubs(:load_config).returns('machine' => {'environment' => {'GLOBAL' => '1'}})
|
208
|
+
assert_equal({'GLOBAL' => '1'}, @spec.machine_env)
|
185
209
|
end
|
186
210
|
|
187
211
|
test '#load_config can grab the env-specific shipit.yml file' do
|
@@ -256,12 +280,21 @@ module Shipit
|
|
256
280
|
assert_instance_of DeploySpec::FileSystem, @spec
|
257
281
|
assert_instance_of DeploySpec, @spec.cacheable
|
258
282
|
config = {
|
259
|
-
'
|
283
|
+
'merge' => {
|
284
|
+
'require' => [],
|
285
|
+
'ignore' => [],
|
286
|
+
'revalidate_after' => nil,
|
287
|
+
},
|
288
|
+
'ci' => {
|
289
|
+
'hide' => [],
|
290
|
+
'allow_failures' => [],
|
291
|
+
'require' => [],
|
292
|
+
},
|
260
293
|
'machine' => {'environment' => {}, 'directory' => nil, 'cleanup' => true},
|
261
294
|
'review' => {'checklist' => [], 'monitoring' => [], 'checks' => []},
|
262
295
|
'dependencies' => {'override' => []},
|
263
296
|
'plugins' => {},
|
264
|
-
'deploy' => {'override' => nil, 'variables' => [], 'max_commits' =>
|
297
|
+
'deploy' => {'override' => nil, 'variables' => [], 'max_commits' => 8, 'interval' => 0},
|
265
298
|
'rollback' => {'override' => nil},
|
266
299
|
'fetch' => nil,
|
267
300
|
'tasks' => {},
|
@@ -380,6 +413,99 @@ module Shipit
|
|
380
413
|
assert_equal %w(ci/circleci ci/jenkins), @spec.hidden_statuses
|
381
414
|
end
|
382
415
|
|
416
|
+
test "pull_request_ignored_statuses defaults to the union of ci.hide and ci.allow_failures" do
|
417
|
+
@spec.expects(:load_config).returns(
|
418
|
+
'ci' => {
|
419
|
+
'hide' => %w(ci/circleci ci/jenkins),
|
420
|
+
'allow_failures' => %w(ci/circleci ci/travis),
|
421
|
+
},
|
422
|
+
)
|
423
|
+
assert_equal %w(ci/circleci ci/jenkins ci/travis).sort, @spec.pull_request_ignored_statuses.sort
|
424
|
+
end
|
425
|
+
|
426
|
+
test "pull_request_ignored_statuses defaults to empty if `merge.require` is present" do
|
427
|
+
@spec.expects(:load_config).returns(
|
428
|
+
'merge' => {
|
429
|
+
'require' => 'bar',
|
430
|
+
},
|
431
|
+
'ci' => {
|
432
|
+
'hide' => %w(ci/circleci ci/jenkins),
|
433
|
+
'allow_failures' => %w(ci/circleci ci/travis),
|
434
|
+
},
|
435
|
+
)
|
436
|
+
assert_equal [], @spec.pull_request_ignored_statuses
|
437
|
+
end
|
438
|
+
|
439
|
+
test "pull_request_ignored_statuses returns `merge.ignore` if present" do
|
440
|
+
@spec.expects(:load_config).returns(
|
441
|
+
'merge' => {
|
442
|
+
'ignore' => 'bar',
|
443
|
+
},
|
444
|
+
'ci' => {
|
445
|
+
'hide' => %w(ci/circleci ci/jenkins),
|
446
|
+
'allow_failures' => %w(ci/circleci ci/travis),
|
447
|
+
},
|
448
|
+
)
|
449
|
+
assert_equal ['bar'], @spec.pull_request_ignored_statuses
|
450
|
+
end
|
451
|
+
|
452
|
+
test "pull_request_required_statuses defaults to ci.require" do
|
453
|
+
@spec.expects(:load_config).returns(
|
454
|
+
'ci' => {
|
455
|
+
'require' => %w(ci/circleci ci/jenkins),
|
456
|
+
},
|
457
|
+
)
|
458
|
+
assert_equal %w(ci/circleci ci/jenkins), @spec.pull_request_required_statuses
|
459
|
+
end
|
460
|
+
|
461
|
+
test "pull_request_required_statuses defaults to empty if `merge.ignore` is present" do
|
462
|
+
@spec.expects(:load_config).returns(
|
463
|
+
'merge' => {
|
464
|
+
'ignore' => 'bar',
|
465
|
+
},
|
466
|
+
'ci' => {
|
467
|
+
'require' => %w(ci/circleci ci/jenkins),
|
468
|
+
},
|
469
|
+
)
|
470
|
+
assert_equal [], @spec.pull_request_required_statuses
|
471
|
+
end
|
472
|
+
|
473
|
+
test "pull_request_required_statuses returns `merge.require` if present" do
|
474
|
+
@spec.expects(:load_config).returns(
|
475
|
+
'merge' => {
|
476
|
+
'require' => 'bar',
|
477
|
+
},
|
478
|
+
'ci' => {
|
479
|
+
'hide' => %w(ci/circleci ci/jenkins),
|
480
|
+
'allow_failures' => %w(ci/circleci ci/travis),
|
481
|
+
},
|
482
|
+
)
|
483
|
+
assert_equal ['bar'], @spec.pull_request_required_statuses
|
484
|
+
end
|
485
|
+
|
486
|
+
test "revalidate_pull_requests_after defaults to `nil" do
|
487
|
+
@spec.expects(:load_config).returns({})
|
488
|
+
assert_nil @spec.revalidate_pull_requests_after
|
489
|
+
end
|
490
|
+
|
491
|
+
test "revalidate_pull_requests_after defaults to `nil` if `merge.timeout` cannot be parsed" do
|
492
|
+
@spec.expects(:load_config).returns(
|
493
|
+
'merge' => {
|
494
|
+
'revalidate_after' => 'ALSKhfjsdkf',
|
495
|
+
},
|
496
|
+
)
|
497
|
+
assert_nil @spec.revalidate_pull_requests_after
|
498
|
+
end
|
499
|
+
|
500
|
+
test "revalidate_after returns `merge.revalidate_after` if present" do
|
501
|
+
@spec.expects(:load_config).returns(
|
502
|
+
'merge' => {
|
503
|
+
'revalidate_after' => '5m30s',
|
504
|
+
},
|
505
|
+
)
|
506
|
+
assert_equal 330, @spec.revalidate_pull_requests_after.to_i
|
507
|
+
end
|
508
|
+
|
383
509
|
test "#file is impacted by `machine.directory`" do
|
384
510
|
subdir = '/foo/bar'
|
385
511
|
@spec.stubs(:load_config).returns('machine' => {'directory' => subdir})
|
@@ -495,9 +621,9 @@ module Shipit
|
|
495
621
|
assert_equal ['yarn install --no-progress'], @spec.dependencies_steps
|
496
622
|
end
|
497
623
|
|
498
|
-
test '#publish_yarn_package checks if version tag exists, and then invokes
|
624
|
+
test '#publish_yarn_package checks if version tag exists, and then invokes npm publish script' do
|
499
625
|
@spec.stubs(:yarn?).returns(true).at_least_once
|
500
|
-
assert_equal ['assert-npm-version-tag', '
|
626
|
+
assert_equal ['assert-npm-version-tag', 'npm publish'], @spec.deploy_steps
|
501
627
|
end
|
502
628
|
|
503
629
|
test 'yarn checklist takes precedence over npm checklist' do
|
data/test/models/deploys_test.rb
CHANGED
@@ -225,6 +225,13 @@ module Shipit
|
|
225
225
|
end
|
226
226
|
end
|
227
227
|
|
228
|
+
test "transitioning to success schedule a MergePullRequests job" do
|
229
|
+
@deploy = shipit_deploys(:shipit_running)
|
230
|
+
assert_enqueued_with(job: MergePullRequestsJob, args: [@deploy.stack]) do
|
231
|
+
@deploy.complete!
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
228
235
|
test "transitioning to success schedule a fetch of the deployed revision" do
|
229
236
|
@deploy = shipit_deploys(:shipit_running)
|
230
237
|
assert_enqueued_with(job: FetchDeployedRevisionJob, args: [@deploy.stack]) do
|
@@ -440,10 +447,9 @@ module Shipit
|
|
440
447
|
private
|
441
448
|
|
442
449
|
def expect_event(deploy)
|
443
|
-
Pubsubstub
|
444
|
-
Pubsubstub
|
445
|
-
|
446
|
-
channel == "stack.#{deploy.stack.id}" && data['url'] == "/#{deploy.stack.to_param}"
|
450
|
+
Pubsubstub.expects(:publish).at_least_once
|
451
|
+
Pubsubstub.expects(:publish).with do |channel, _payload, _options|
|
452
|
+
channel == "stack.#{deploy.stack.id}"
|
447
453
|
end
|
448
454
|
end
|
449
455
|
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Shipit
|
4
|
+
class PullRequestTest < ActiveSupport::TestCase
|
5
|
+
setup do
|
6
|
+
@stack = shipit_stacks(:shipit)
|
7
|
+
@pr = shipit_pull_requests(:shipit_pending)
|
8
|
+
@user = shipit_users(:walrus)
|
9
|
+
end
|
10
|
+
|
11
|
+
test ".request_merge! creates a record and schedule a refresh" do
|
12
|
+
pull_request = nil
|
13
|
+
assert_enqueued_with(job: RefreshPullRequestJob) do
|
14
|
+
pull_request = PullRequest.request_merge!(@stack, 64, @user)
|
15
|
+
end
|
16
|
+
assert_predicate pull_request, :persisted?
|
17
|
+
end
|
18
|
+
|
19
|
+
test ".request_merge! only track pull requests once" do
|
20
|
+
assert_difference -> { PullRequest.count }, +1 do
|
21
|
+
5.times { PullRequest.request_merge!(@stack, 65, @user) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
test ".request_merge! retry canceled pull requests" do
|
26
|
+
original_merge_requested_at = @pr.merge_requested_at
|
27
|
+
@pr.cancel!
|
28
|
+
assert_predicate @pr, :canceled?
|
29
|
+
PullRequest.request_merge!(@stack, @pr.number, @user)
|
30
|
+
assert_predicate @pr.reload, :pending?
|
31
|
+
assert_not_equal original_merge_requested_at, @pr.merge_requested_at
|
32
|
+
assert_in_delta Time.now.utc, @pr.merge_requested_at, 1
|
33
|
+
end
|
34
|
+
|
35
|
+
test ".request_merge! retry rejected pull requests" do
|
36
|
+
original_merge_requested_at = @pr.merge_requested_at
|
37
|
+
@pr.reject!('merge_conflict')
|
38
|
+
assert_predicate @pr, :rejected?
|
39
|
+
PullRequest.request_merge!(@stack, @pr.number, @user)
|
40
|
+
assert_predicate @pr.reload, :pending?
|
41
|
+
assert_not_equal original_merge_requested_at, @pr.merge_requested_at
|
42
|
+
assert_in_delta Time.now.utc, @pr.merge_requested_at, 1
|
43
|
+
assert_nil @pr.rejection_reason
|
44
|
+
end
|
45
|
+
|
46
|
+
test ".request_merge! retry revalidating pull requests but keep the original request time" do
|
47
|
+
original_merge_requested_at = @pr.merge_requested_at
|
48
|
+
@pr.revalidate!
|
49
|
+
assert_predicate @pr, :revalidating?
|
50
|
+
PullRequest.request_merge!(@stack, @pr.number, @user)
|
51
|
+
assert_predicate @pr.reload, :pending?
|
52
|
+
assert_equal original_merge_requested_at, @pr.merge_requested_at
|
53
|
+
end
|
54
|
+
|
55
|
+
test ".extract_number can get a pull request number from different formats" do
|
56
|
+
assert_equal 42, PullRequest.extract_number(@stack, '42')
|
57
|
+
assert_equal 42, PullRequest.extract_number(@stack, '#42')
|
58
|
+
assert_equal 42, PullRequest.extract_number(@stack, 'https://github.com/Shopify/shipit-engine/pull/42')
|
59
|
+
|
60
|
+
assert_nil PullRequest.extract_number(@stack, 'https://github.com/ACME/shipit-engine/pull/42')
|
61
|
+
|
62
|
+
Shipit.expects(:github_domain).returns('github.acme.com').at_least_once
|
63
|
+
assert_equal 42, PullRequest.extract_number(@stack, 'https://github.acme.com/Shopify/shipit-engine/pull/42')
|
64
|
+
assert_nil PullRequest.extract_number(@stack, 'https://github.com/Shopify/shipit-engine/pull/42')
|
65
|
+
end
|
66
|
+
|
67
|
+
test "refresh! pulls state from GitHub" do
|
68
|
+
pull_request = shipit_pull_requests(:shipit_fetching)
|
69
|
+
|
70
|
+
head_sha = '64b3833d39def7ec65b57b42f496eb27ab4980b6'
|
71
|
+
Shipit.github_api.expects(:pull_request).with(@stack.github_repo_name, pull_request.number).returns(
|
72
|
+
stub(
|
73
|
+
id: 4_857_578,
|
74
|
+
url: 'https://api.github.com/repos/Shopify/shipit-engine/pulls/64',
|
75
|
+
title: 'Great feature',
|
76
|
+
state: 'open',
|
77
|
+
mergeable: true,
|
78
|
+
additions: 24,
|
79
|
+
deletions: 5,
|
80
|
+
head: stub(
|
81
|
+
ref: 'super-branch',
|
82
|
+
sha: head_sha,
|
83
|
+
),
|
84
|
+
),
|
85
|
+
)
|
86
|
+
|
87
|
+
author = stub(
|
88
|
+
id: 1234,
|
89
|
+
login: 'bob',
|
90
|
+
name: 'Bob the Builder',
|
91
|
+
email: 'bob@bob.com',
|
92
|
+
)
|
93
|
+
Shipit.github_api.expects(:commit).with(@stack.github_repo_name, head_sha).returns(
|
94
|
+
stub(
|
95
|
+
sha: head_sha,
|
96
|
+
author: author,
|
97
|
+
committer: author,
|
98
|
+
commit: stub(
|
99
|
+
message: 'Great feature',
|
100
|
+
author: stub(date: 1.day.ago),
|
101
|
+
committer: stub(date: 1.day.ago),
|
102
|
+
),
|
103
|
+
stats: stub(
|
104
|
+
additions: 24,
|
105
|
+
deletions: 5,
|
106
|
+
),
|
107
|
+
),
|
108
|
+
)
|
109
|
+
|
110
|
+
Shipit.github_api.expects(:statuses).with(@stack.github_repo_name, head_sha).returns([stub(
|
111
|
+
state: 'success',
|
112
|
+
description: nil,
|
113
|
+
context: 'default',
|
114
|
+
target_url: 'http://example.com',
|
115
|
+
created_at: 1.day.ago,
|
116
|
+
)])
|
117
|
+
|
118
|
+
pull_request.refresh!
|
119
|
+
|
120
|
+
assert_predicate pull_request, :mergeable?
|
121
|
+
assert_predicate pull_request, :pending?
|
122
|
+
assert_equal 'super-branch', pull_request.branch
|
123
|
+
|
124
|
+
assert_not_nil pull_request.head
|
125
|
+
assert_predicate pull_request.head, :detached?
|
126
|
+
assert_predicate pull_request.head, :success?
|
127
|
+
end
|
128
|
+
|
129
|
+
test "#reject! records the reason" do
|
130
|
+
@pr.reject!('merge_conflict')
|
131
|
+
assert_equal 'merge_conflict', @pr.rejection_reason
|
132
|
+
end
|
133
|
+
|
134
|
+
test "transitionning from rejected to any other state clear the rejection reason" do
|
135
|
+
@pr.reject!('merge_conflict')
|
136
|
+
assert_equal 'merge_conflict', @pr.rejection_reason
|
137
|
+
@pr.retry!
|
138
|
+
assert_nil @pr.rejection_reason
|
139
|
+
assert_nil @pr.reload.rejection_reason
|
140
|
+
end
|
141
|
+
|
142
|
+
test "#reject_unless_mergeable! returns `false` if the PR is not yet mergeable" do
|
143
|
+
@pr.update!(mergeable: nil)
|
144
|
+
assert_predicate @pr, :not_mergeable_yet?
|
145
|
+
assert_equal false, @pr.reject_unless_mergeable!
|
146
|
+
assert_predicate @pr, :pending?
|
147
|
+
end
|
148
|
+
|
149
|
+
test "#reject_unless_mergeable! rejects the PR if it has a merge conflict" do
|
150
|
+
@pr.update!(mergeable: false)
|
151
|
+
|
152
|
+
assert_predicate @pr, :merge_conflict?
|
153
|
+
assert_equal true, @pr.reject_unless_mergeable!
|
154
|
+
assert_predicate @pr, :rejected?
|
155
|
+
assert_equal 'merge_conflict', @pr.rejection_reason
|
156
|
+
end
|
157
|
+
|
158
|
+
test "#reject_unless_mergeable! rejects the PR if it has a failing or pending CI status" do
|
159
|
+
@pr.head.statuses.create!(stack: @pr.stack, state: 'pending', context: 'ci/circle')
|
160
|
+
|
161
|
+
refute_predicate @pr, :all_status_checks_passed?
|
162
|
+
assert_equal true, @pr.reject_unless_mergeable!
|
163
|
+
assert_predicate @pr, :rejected?
|
164
|
+
assert_equal 'ci_failing', @pr.rejection_reason
|
165
|
+
end
|
166
|
+
|
167
|
+
test "#merge! revalidates the PR if it has been enqueued for too long" do
|
168
|
+
@pr.update!(revalidated_at: 5.hours.ago)
|
169
|
+
|
170
|
+
assert_predicate @pr, :need_revalidation?
|
171
|
+
assert_equal false, @pr.merge!
|
172
|
+
assert_predicate @pr, :revalidating?
|
173
|
+
end
|
174
|
+
|
175
|
+
test "#merge! raises a PullRequest::NotReady if the PR isn't mergeable yet" do
|
176
|
+
@pr.update!(mergeable: nil)
|
177
|
+
|
178
|
+
assert_predicate @pr, :not_mergeable_yet?
|
179
|
+
assert_raises PullRequest::NotReady do
|
180
|
+
@pr.merge!
|
181
|
+
end
|
182
|
+
@pr.reload
|
183
|
+
assert_predicate @pr, :pending?
|
184
|
+
end
|
185
|
+
|
186
|
+
test "status transitions emit hooks" do
|
187
|
+
job = assert_enqueued_with(job: EmitEventJob) do
|
188
|
+
@pr.reject!('merge_conflict')
|
189
|
+
end
|
190
|
+
params = job.arguments.first
|
191
|
+
assert_equal 'merge', params['event']
|
192
|
+
assert_json 'status', 'rejected', document: params['payload']
|
193
|
+
assert_json 'pull_request.rejection_reason', 'merge_conflict', document: params['payload']
|
194
|
+
assert_json 'pull_request.number', @pr.number, document: params['payload']
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|