shipit-engine 0.15.0 → 0.16.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 +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
|