shipit-engine 0.20.1 → 0.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +43 -6
- data/app/assets/stylesheets/_base/_base.scss +4 -0
- data/app/assets/stylesheets/_pages/_commits.scss +3 -1
- data/app/assets/stylesheets/_pages/_deploy.scss +4 -2
- data/app/controllers/concerns/shipit/authentication.rb +1 -1
- data/app/controllers/shipit/api/base_controller.rb +6 -1
- data/app/controllers/shipit/api/pull_requests_controller.rb +1 -1
- data/app/controllers/shipit/commit_checks_controller.rb +1 -1
- data/app/controllers/shipit/shipit_controller.rb +1 -5
- data/app/controllers/shipit/stacks_controller.rb +2 -0
- data/app/controllers/shipit/tasks_controller.rb +1 -1
- data/app/controllers/shipit/webhooks_controller.rb +2 -2
- data/app/helpers/shipit/deploys_helper.rb +9 -0
- data/app/helpers/shipit/shipit_helper.rb +17 -15
- data/app/helpers/shipit/stacks_helper.rb +6 -1
- data/app/jobs/shipit/destroy_stack_job.rb +4 -2
- data/app/jobs/shipit/fetch_deployed_revision_job.rb +1 -1
- data/app/jobs/shipit/github_sync_job.rb +1 -1
- data/app/jobs/shipit/merge_pull_requests_job.rb +3 -3
- data/app/jobs/shipit/perform_task_job.rb +3 -0
- data/app/jobs/shipit/purge_old_deliveries_job.rb +1 -0
- data/app/models/shipit/api_client.rb +1 -1
- data/app/models/shipit/commit.rb +29 -6
- data/app/models/shipit/commit_deployment.rb +1 -1
- data/app/models/shipit/commit_deployment_status.rb +1 -1
- data/app/models/shipit/deploy_spec.rb +19 -2
- data/app/models/shipit/deploy_spec/bundler_discovery.rb +1 -10
- data/app/models/shipit/deploy_spec/file_system.rb +6 -0
- data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +1 -1
- data/app/models/shipit/deploy_spec/lerna_discovery.rb +85 -0
- data/app/models/shipit/deploy_spec/npm_discovery.rb +103 -5
- data/app/models/shipit/deploy_spec/pypi_discovery.rb +4 -2
- data/app/models/shipit/deploy_spec/rubygems_discovery.rb +4 -2
- data/app/models/shipit/duration.rb +1 -1
- data/app/models/shipit/github_status.rb +1 -1
- data/app/models/shipit/hook.rb +4 -5
- data/app/models/shipit/output_chunk.rb +1 -1
- data/app/models/shipit/pull_request.rb +36 -15
- data/app/models/shipit/stack.rb +15 -9
- data/app/models/shipit/status/common.rb +4 -0
- data/app/models/shipit/status/group.rb +4 -0
- data/app/models/shipit/task.rb +20 -8
- data/app/models/shipit/task_definition.rb +2 -2
- data/app/models/shipit/undeployed_commit.rb +13 -2
- data/app/models/shipit/user.rb +1 -1
- data/app/serializers/shipit/pull_request_serializer.rb +1 -1
- data/app/serializers/shipit/tail_task_serializer.rb +1 -1
- data/app/views/shipit/ccmenu/project.xml.builder +9 -8
- data/app/views/shipit/deploys/_deploy.html.erb +3 -2
- data/app/views/shipit/stacks/_banners.html.erb +4 -1
- data/app/views/shipit/stacks/settings.html.erb +4 -0
- data/app/views/shipit/statuses/_group.html.erb +1 -1
- data/app/views/shipit/statuses/_status.html.erb +1 -1
- data/app/views/shipit/tasks/_task.html.erb +1 -2
- data/config/locales/en.yml +2 -0
- data/config/secrets.development.example.yml +0 -4
- data/config/secrets.development.shopify.yml +1 -5
- data/db/migrate/20170904103242_reindex_deliveries.rb +7 -0
- data/db/migrate/20171120161420_add_base_info_to_pull_request.rb +7 -0
- data/db/migrate/20180202220850_add_aborted_by_to_tasks.rb +5 -0
- data/lib/shipit.rb +15 -23
- data/lib/shipit/command.rb +11 -3
- data/lib/shipit/engine.rb +0 -4
- data/lib/shipit/stack_commands.rb +3 -1
- data/lib/shipit/version.rb +1 -1
- data/lib/snippets/assert-lerna-fixed-version-tag +21 -0
- data/lib/snippets/assert-lerna-independent-version-tags +28 -0
- data/lib/snippets/generate-local-npmrc +19 -0
- data/lib/snippets/misconfigured-npm-publish-config +8 -0
- data/lib/snippets/publish-lerna-independent-packages +39 -0
- data/lib/snippets/push-to-heroku +5 -5
- data/lib/tasks/cron.rake +1 -1
- data/lib/tasks/dev.rake +1 -1
- data/test/controllers/api/deploys_controller_test.rb +19 -0
- data/test/controllers/api/stacks_controller_test.rb +1 -1
- data/test/controllers/github_authentication_controller_test.rb +1 -1
- data/test/controllers/stacks_controller_test.rb +10 -0
- data/test/controllers/tasks_controller_test.rb +2 -0
- data/test/controllers/webhooks_controller_test.rb +0 -7
- data/test/dummy/config/secrets.yml +0 -2
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/schema.rb +5 -3
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/fixtures/shipit/commits.yml +53 -0
- data/test/fixtures/shipit/pull_requests.yml +52 -0
- data/test/fixtures/shipit/stacks.yml +35 -0
- data/test/fixtures/shipit/statuses.yml +27 -0
- data/test/fixtures/shipit/tasks.yml +14 -0
- data/test/helpers/queries_helper.rb +1 -1
- data/test/jobs/merge_pull_requests_job_test.rb +19 -2
- data/test/jobs/perform_task_job_test.rb +26 -2
- data/test/models/commits_test.rb +55 -6
- data/test/models/deploy_spec_test.rb +288 -52
- data/test/models/deploys_test.rb +7 -7
- data/test/models/hook_test.rb +4 -3
- data/test/models/pull_request_test.rb +78 -24
- data/test/models/stacks_test.rb +21 -17
- data/test/models/status/group_test.rb +6 -0
- data/test/models/undeployed_commits_test.rb +9 -0
- data/test/models/users_test.rb +2 -2
- data/test/test_helper.rb +1 -1
- metadata +211 -222
- data/app/assets/javascripts/shipit_bs.js.coffee +0 -2
- data/app/assets/stylesheets/shipit_bs.scss +0 -22
- data/app/views/bootstrap/shipit/missing_settings.html.erb +0 -97
- data/app/views/bootstrap/shipit/stacks/new.html.erb +0 -44
- data/app/views/layouts/shipit_bootstrap.html.erb +0 -44
- data/lib/shipit/template_renderer_extension.rb +0 -16
@@ -87,3 +87,55 @@ shipit_pending_merged:
|
|
87
87
|
mergeable: null
|
88
88
|
additions: 23
|
89
89
|
deletions: 43
|
90
|
+
|
91
|
+
shipit_mergeable_pending_ci:
|
92
|
+
stack: shipit
|
93
|
+
number: 67
|
94
|
+
title: Super duper nice feature
|
95
|
+
merge_status: pending
|
96
|
+
merge_requested_at: <%= 5.minute.ago.to_s(:db) %>
|
97
|
+
revalidated_at: <%= 5.minute.ago.to_s(:db) %>
|
98
|
+
merge_requested_by: walrus
|
99
|
+
github_id: 424244242424241
|
100
|
+
api_url: https://api.github.com/repos/shopify/shipit-engine/pulls/67
|
101
|
+
state: open
|
102
|
+
branch: feature-67
|
103
|
+
head_id: 4
|
104
|
+
mergeable: true
|
105
|
+
additions: 23
|
106
|
+
deletions: 43
|
107
|
+
|
108
|
+
shipit_pending_expired:
|
109
|
+
stack: shipit
|
110
|
+
number: 68
|
111
|
+
title: Noice !
|
112
|
+
merge_status: pending
|
113
|
+
merge_requested_at: <%= 50.minute.ago.to_s(:db) %>
|
114
|
+
revalidated_at: <%= 25.minute.ago.to_s(:db) %>
|
115
|
+
merge_requested_by: walrus
|
116
|
+
github_id: 48484848484848
|
117
|
+
api_url: https://api.github.com/repos/shopify/shipit-engine/pulls/68
|
118
|
+
state: open
|
119
|
+
branch: feature-68
|
120
|
+
head_id: 8
|
121
|
+
mergeable: true
|
122
|
+
additions: 23
|
123
|
+
deletions: 43
|
124
|
+
|
125
|
+
cyclimse_pending_merged:
|
126
|
+
id: 99
|
127
|
+
stack: cyclimse
|
128
|
+
number: 66
|
129
|
+
merge_status: merged
|
130
|
+
merge_requested_by: walrus
|
131
|
+
merge_requested_at: <%= 3.minute.ago.to_s(:db) %>
|
132
|
+
merged_at: <%= 2.minute.ago.to_s(:db) %>
|
133
|
+
revalidated_at: <%= 3.minute.ago.to_s(:db) %>
|
134
|
+
github_id: 43243243243232
|
135
|
+
api_url: https://api.github.com/repos/shopify/cyclimse/pulls/66
|
136
|
+
state: closed
|
137
|
+
branch: feature-64
|
138
|
+
head_id: 8
|
139
|
+
mergeable: null
|
140
|
+
additions: 23
|
141
|
+
deletions: 43
|
@@ -123,3 +123,38 @@ undeployed_stack:
|
|
123
123
|
}
|
124
124
|
}
|
125
125
|
updated_at: <%= 8.days.ago.to_s(:db) %>
|
126
|
+
|
127
|
+
soc:
|
128
|
+
repo_owner: "shopify"
|
129
|
+
repo_name: "soc"
|
130
|
+
environment: "production"
|
131
|
+
branch: master
|
132
|
+
tasks_count: 0
|
133
|
+
undeployed_commits_count: 2
|
134
|
+
cached_deploy_spec: >
|
135
|
+
{
|
136
|
+
"machine": {"environment": {}},
|
137
|
+
"review": {
|
138
|
+
"checklist": ["foo", "bar", "baz"],
|
139
|
+
"monitoring": [
|
140
|
+
{"image": "https://example.com/monitor.png", "width": 200, "height": 300}
|
141
|
+
]
|
142
|
+
},
|
143
|
+
"dependencies": {"override": []},
|
144
|
+
"deploy": {"override": null},
|
145
|
+
"rollback": {"override": ["echo 'Rollback!'"]},
|
146
|
+
"fetch": ["echo '42'"],
|
147
|
+
"tasks": {
|
148
|
+
"restart": {
|
149
|
+
"action": "Restart application",
|
150
|
+
"description": "Restart app and job servers",
|
151
|
+
"steps": [
|
152
|
+
"cap $ENVIRONMENT deploy:restart"
|
153
|
+
]
|
154
|
+
}
|
155
|
+
},
|
156
|
+
"ci": {
|
157
|
+
"blocking": ["soc/compliance"]
|
158
|
+
}
|
159
|
+
}
|
160
|
+
updated_at: <%= 8.days.ago.to_s(:db) %>
|
@@ -98,3 +98,30 @@ shipit_pending_pr_success_travis:
|
|
98
98
|
created_at: <%= 9.days.ago.to_s(:db) %>
|
99
99
|
state: success
|
100
100
|
target_url: "http://www.example.com"
|
101
|
+
|
102
|
+
soc_first:
|
103
|
+
stack: soc
|
104
|
+
commit_id: 101 # soc_first
|
105
|
+
description: Woops
|
106
|
+
context: soc/compliance
|
107
|
+
created_at: <%= 9.days.ago.to_s(:db) %>
|
108
|
+
state: failure
|
109
|
+
target_url: "http://www.example.com"
|
110
|
+
|
111
|
+
soc_second:
|
112
|
+
stack: soc
|
113
|
+
commit_id: 102 # soc_second
|
114
|
+
description: All good
|
115
|
+
context: soc/compliance
|
116
|
+
created_at: <%= 7.days.ago.to_s(:db) %>
|
117
|
+
state: success
|
118
|
+
target_url: "http://www.example.com"
|
119
|
+
|
120
|
+
soc_third:
|
121
|
+
stack: soc
|
122
|
+
commit_id: 103 # soc_third
|
123
|
+
description: All good
|
124
|
+
context: soc/compliance
|
125
|
+
created_at: <%= 7.days.ago.to_s(:db) %>
|
126
|
+
state: success
|
127
|
+
target_url: "http://www.example.com"
|
@@ -120,3 +120,17 @@ shipit_rollback:
|
|
120
120
|
created_at: <%= (60 - 8).minutes.ago.to_s(:db) %>
|
121
121
|
started_at: <%= (60 - 8).minutes.ago.to_s(:db) %>
|
122
122
|
ended_at: <%= (60 - 7).minutes.ago.to_s(:db) %>
|
123
|
+
|
124
|
+
soc_deploy:
|
125
|
+
id: 9
|
126
|
+
user: walrus
|
127
|
+
since_commit_id: 101 # soc_first
|
128
|
+
until_commit_id: 101 # soc_first
|
129
|
+
type: Shipit::Deploy
|
130
|
+
stack: soc
|
131
|
+
status: success
|
132
|
+
additions: 1
|
133
|
+
deletions: 1
|
134
|
+
created_at: <%= (60 - 1).minutes.ago.to_s(:db) %>
|
135
|
+
started_at: <%= (60 - 1).minutes.ago.to_s(:db) %>
|
136
|
+
ended_at: <%= (60 - 3).minutes.ago.to_s(:db) %>
|
@@ -50,7 +50,7 @@ module QueriesHelper
|
|
50
50
|
|
51
51
|
# FIXME: this seems bad. we should probably have a better way to indicate
|
52
52
|
# the query was cached
|
53
|
-
return if
|
53
|
+
return if values[:name] == 'CACHE' || ignore.any? { |x| x =~ sql }
|
54
54
|
log << sql
|
55
55
|
end
|
56
56
|
end
|
@@ -11,6 +11,8 @@ module Shipit
|
|
11
11
|
@not_ready_pr = shipit_pull_requests(:shipit_pending_not_mergeable_yet)
|
12
12
|
@closed_pr = shipit_pull_requests(:shipit_pending_closed)
|
13
13
|
@merged_pr = shipit_pull_requests(:shipit_pending_merged)
|
14
|
+
@expired_pr = shipit_pull_requests(:shipit_pending_expired)
|
15
|
+
@mergable_pending_ci = shipit_pull_requests(:shipit_mergeable_pending_ci)
|
14
16
|
end
|
15
17
|
|
16
18
|
test "#perform rejects unmergeable PRs and merge the others" do
|
@@ -50,6 +52,13 @@ module Shipit
|
|
50
52
|
assert_predicate @pending_pr.reload, :pending?
|
51
53
|
end
|
52
54
|
|
55
|
+
test "#perform revalidate PRs but do not attempt to merge any if the stack doesn't allow merges" do
|
56
|
+
PullRequest.any_instance.stubs(:refresh!)
|
57
|
+
@stack.update!(lock_reason: 'Maintenance')
|
58
|
+
@job.perform(@stack)
|
59
|
+
assert_predicate @expired_pr.reload, :revalidating?
|
60
|
+
end
|
61
|
+
|
53
62
|
test "#perform schedules a new job if the first PR in the queue is not mergeable yet" do
|
54
63
|
PullRequest.any_instance.stubs(:refresh!)
|
55
64
|
|
@@ -67,11 +76,19 @@ module Shipit
|
|
67
76
|
assert_predicate @closed_pr.reload, :canceled?
|
68
77
|
end
|
69
78
|
|
70
|
-
test "#perform
|
79
|
+
test "#perform cancels merge requests for manually merged PRs" do
|
80
|
+
@pending_pr.cancel!
|
81
|
+
PullRequest.any_instance.stubs(:refresh!)
|
82
|
+
@job.perform(@stack)
|
83
|
+
assert_predicate @merged_pr.reload, :canceled?
|
84
|
+
end
|
85
|
+
|
86
|
+
test "#perform does not reject pull requests with pending statuses" do
|
71
87
|
@pending_pr.cancel!
|
72
88
|
PullRequest.any_instance.stubs(:refresh!)
|
73
89
|
@job.perform(@stack)
|
74
|
-
|
90
|
+
refute_predicate @mergable_pending_ci.reload, :rejected?
|
91
|
+
refute_predicate @mergable_pending_ci.reload, :merged?
|
75
92
|
end
|
76
93
|
end
|
77
94
|
end
|
@@ -67,15 +67,39 @@ module Shipit
|
|
67
67
|
@job.perform(@deploy)
|
68
68
|
end
|
69
69
|
|
70
|
-
test "mark deploy as error
|
71
|
-
Command.any_instance.expects(:stream!).at_least_once.raises(Command::
|
70
|
+
test "mark deploy as error an unexpected exception is raised" do
|
71
|
+
Command.any_instance.expects(:stream!).at_least_once.raises(Command::Denied)
|
72
72
|
|
73
73
|
@job.perform(@deploy)
|
74
74
|
|
75
75
|
assert_equal 'failed', @deploy.reload.status
|
76
|
+
assert_includes @deploy.chunk_output, 'Denied'
|
77
|
+
end
|
78
|
+
|
79
|
+
test "mark deploy as timedout if a command timeout" do
|
80
|
+
Command.any_instance.expects(:stream!).at_least_once.raises(Command::TimedOut)
|
81
|
+
|
82
|
+
@job.perform(@deploy)
|
83
|
+
|
84
|
+
assert_equal 'timedout', @deploy.reload.status
|
76
85
|
assert_includes @deploy.chunk_output, 'TimedOut'
|
77
86
|
end
|
78
87
|
|
88
|
+
test "mark deploy as timedout if a command exit in one of the codes in Shipit.timeout_exit_codes" do
|
89
|
+
previous_exit_codes = Shipit.timeout_exit_codes
|
90
|
+
begin
|
91
|
+
Shipit.timeout_exit_codes = [70].freeze
|
92
|
+
|
93
|
+
Command.any_instance.expects(:stream!).at_least_once.raises(Command::Failed.new('Blah', 70))
|
94
|
+
|
95
|
+
@job.perform(@deploy)
|
96
|
+
|
97
|
+
assert_equal 'timedout', @deploy.reload.status
|
98
|
+
ensure
|
99
|
+
Shipit.timeout_exit_codes = previous_exit_codes
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
79
103
|
test "records stack support for rollbacks and fetching deployed revision" do
|
80
104
|
@job.stubs(:capture!)
|
81
105
|
@commands = stub(:commands)
|
data/test/models/commits_test.rb
CHANGED
@@ -148,8 +148,8 @@ module Shipit
|
|
148
148
|
author: walrus,
|
149
149
|
committer: walrus,
|
150
150
|
sha: "ab12",
|
151
|
-
authored_at:
|
152
|
-
committed_at:
|
151
|
+
authored_at: Time.now,
|
152
|
+
committed_at: Time.now,
|
153
153
|
message: "more fish!",
|
154
154
|
)
|
155
155
|
end
|
@@ -176,8 +176,8 @@ module Shipit
|
|
176
176
|
author: walrus,
|
177
177
|
committer: walrus,
|
178
178
|
sha: "ab12",
|
179
|
-
authored_at:
|
180
|
-
committed_at:
|
179
|
+
authored_at: Time.now,
|
180
|
+
committed_at: Time.now,
|
181
181
|
message: "more fish!",
|
182
182
|
)
|
183
183
|
@stack.reload
|
@@ -217,8 +217,8 @@ module Shipit
|
|
217
217
|
author: walrus,
|
218
218
|
committer: walrus,
|
219
219
|
sha: "ab12",
|
220
|
-
authored_at:
|
221
|
-
committed_at:
|
220
|
+
authored_at: Time.now,
|
221
|
+
committed_at: Time.now,
|
222
222
|
message: "more fish!",
|
223
223
|
)
|
224
224
|
stack.reload
|
@@ -306,6 +306,37 @@ module Shipit
|
|
306
306
|
refute_predicate commit, :deployable?
|
307
307
|
end
|
308
308
|
|
309
|
+
test "#deployable? is false if a blocking status is missing on a previous undeployed commit" do
|
310
|
+
blocking_commit = shipit_commits(:soc_second)
|
311
|
+
blocking_commit.statuses.delete_all
|
312
|
+
|
313
|
+
assert_predicate blocking_commit, :pending?
|
314
|
+
assert_predicate blocking_commit, :blocking?
|
315
|
+
|
316
|
+
commit = shipit_commits(:soc_third)
|
317
|
+
refute_predicate commit, :deployable?
|
318
|
+
end
|
319
|
+
|
320
|
+
test "#deployable? is false if a blocking status is failing on a previous undeployed commit" do
|
321
|
+
blocking_commit = shipit_commits(:soc_second)
|
322
|
+
blocking_commit.statuses.update_all(state: 'failure')
|
323
|
+
|
324
|
+
assert_predicate blocking_commit, :failure?
|
325
|
+
assert_predicate blocking_commit, :blocking?
|
326
|
+
|
327
|
+
commit = shipit_commits(:soc_third)
|
328
|
+
refute_predicate commit, :deployable?
|
329
|
+
end
|
330
|
+
|
331
|
+
test "#deployable? is true if no blocking status is failing or missing on a previous undeployed commit" do
|
332
|
+
blocking_commit = shipit_commits(:soc_second)
|
333
|
+
assert_predicate blocking_commit, :success?
|
334
|
+
refute_predicate blocking_commit, :blocking?
|
335
|
+
|
336
|
+
commit = shipit_commits(:soc_third)
|
337
|
+
assert_predicate commit, :deployable?
|
338
|
+
end
|
339
|
+
|
309
340
|
expected_webhook_transitions = { # we expect deployable_status to fire on these transitions, and not on any others
|
310
341
|
'unknown' => %w(pending success failure error),
|
311
342
|
'pending' => %w(success failure error),
|
@@ -551,6 +582,24 @@ module Shipit
|
|
551
582
|
assert revert.revert_of?(commit)
|
552
583
|
end
|
553
584
|
|
585
|
+
test "deploy_requested_at defaults to commit created_at" do
|
586
|
+
assert_equal @commit.deploy_requested_at, @commit.created_at
|
587
|
+
end
|
588
|
+
|
589
|
+
test "when merged via the queue, deploy_requested_at is merge_requested_at" do
|
590
|
+
commit = shipit_commits(:cyclimse_merged)
|
591
|
+
assert_predicate commit, :pull_request?
|
592
|
+
assert_equal commit.pull_request, shipit_pull_requests(:cyclimse_pending_merged)
|
593
|
+
assert_equal commit.deploy_requested_at, commit.pull_request.merge_requested_at
|
594
|
+
end
|
595
|
+
|
596
|
+
test "when merged manually after being queued, deploy_requested_at is created_at" do
|
597
|
+
pr = shipit_pull_requests(:cyclimse_pending_merged)
|
598
|
+
pr.cancel!
|
599
|
+
commit = shipit_commits(:cyclimse_merged)
|
600
|
+
assert_equal commit.deploy_requested_at, commit.created_at
|
601
|
+
end
|
602
|
+
|
554
603
|
private
|
555
604
|
|
556
605
|
def expect_event(stack)
|
@@ -284,11 +284,16 @@ module Shipit
|
|
284
284
|
'require' => [],
|
285
285
|
'ignore' => [],
|
286
286
|
'revalidate_after' => nil,
|
287
|
+
'max_divergence' => {
|
288
|
+
'commits' => nil,
|
289
|
+
'age' => nil,
|
290
|
+
},
|
287
291
|
},
|
288
292
|
'ci' => {
|
289
293
|
'hide' => [],
|
290
294
|
'allow_failures' => [],
|
291
295
|
'require' => [],
|
296
|
+
'blocking' => [],
|
292
297
|
},
|
293
298
|
'machine' => {'environment' => {}, 'directory' => nil, 'cleanup' => true},
|
294
299
|
'review' => {'checklist' => [], 'monitoring' => [], 'checks' => []},
|
@@ -318,59 +323,19 @@ module Shipit
|
|
318
323
|
assert_equal 'SAFETY_DISABLED', variable_definition.name
|
319
324
|
end
|
320
325
|
|
321
|
-
test "task definitions prepend bundle exec by default" do
|
322
|
-
@spec.expects(:load_config).returns('tasks' => {'restart' => {'steps' => %w(foo)}})
|
323
|
-
@spec.expects(:bundler?).returns(true).at_least_once
|
324
|
-
assert_deprecated(/Automatically prepending `bundle exec`/) do
|
325
|
-
definition = @spec.find_task_definition('restart')
|
326
|
-
assert_equal ['bundle exec foo'], definition.steps
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
|
-
test "task definitions prepend bundle exec if enabled" do
|
331
|
-
Shipit.expects(:automatically_prepend_bundle_exec).returns(true).at_least_once
|
326
|
+
test "task definitions don't prepend bundle exec by default" do
|
332
327
|
@spec.expects(:load_config).returns('tasks' => {'restart' => {'steps' => %w(foo)}})
|
333
|
-
@spec.expects(:bundler?).returns(true).at_least_once
|
334
328
|
definition = @spec.find_task_definition('restart')
|
335
|
-
|
336
|
-
assert_equal ['bundle exec foo'], definition.steps
|
337
|
-
end
|
338
|
-
|
339
|
-
test "task definitions do not prepend bundle exec if disabled" do
|
340
|
-
Shipit.expects(:automatically_prepend_bundle_exec).returns(false).at_least_once
|
341
|
-
@spec.expects(:load_config).returns('tasks' => {'restart' => {'steps' => %w(foo)}})
|
342
|
-
definition = @spec.find_task_definition('restart')
|
343
|
-
|
344
329
|
assert_equal ['foo'], definition.steps
|
345
330
|
end
|
346
331
|
|
347
|
-
test "task definitions
|
348
|
-
Shipit.expects(:automatically_prepend_bundle_exec).returns(true).at_least_once
|
349
|
-
@spec.expects(:load_config).returns('tasks' => {'restart' => {'steps' => ['bundle exec foo']}})
|
350
|
-
@spec.stubs(:bundler?).returns(true)
|
351
|
-
definition = @spec.find_task_definition('restart')
|
352
|
-
|
353
|
-
assert_equal ['bundle exec foo'], definition.steps
|
354
|
-
end
|
355
|
-
|
356
|
-
test "task definitions do not prepend bundle exec if depedency step is overridden" do
|
357
|
-
@spec.expects(:load_config).returns(
|
358
|
-
'dependencies' => {'override' => []},
|
359
|
-
'tasks' => {'restart' => {'steps' => %w(foo)}},
|
360
|
-
)
|
361
|
-
@spec.expects(:bundler?).returns(true).at_least_once
|
362
|
-
definition = @spec.find_task_definition('restart')
|
363
|
-
|
364
|
-
assert_equal ['foo'], definition.steps
|
365
|
-
end
|
366
|
-
|
367
|
-
test "task definitions prepend bundle exec before serialization" do
|
332
|
+
test "task definitions don't bundle exec before serialization" do
|
368
333
|
@spec.expects(:discover_task_definitions).returns('restart' => {'steps' => %w(foo)})
|
369
334
|
@spec.expects(:bundler?).returns(true).at_least_once
|
370
335
|
|
371
336
|
cached_spec = DeploySpec.load(DeploySpec.dump(@spec))
|
372
337
|
definition = cached_spec.find_task_definition('restart')
|
373
|
-
assert_equal ['
|
338
|
+
assert_equal ['foo'], definition.steps
|
374
339
|
end
|
375
340
|
|
376
341
|
test "#task_definitions returns kubernetes commands as well as comands from the config" do
|
@@ -455,6 +420,16 @@ module Shipit
|
|
455
420
|
assert_equal %w(ci/circleci ci/jenkins), @spec.hidden_statuses
|
456
421
|
end
|
457
422
|
|
423
|
+
test "#required_statuses automatically includes #blocking_statuses" do
|
424
|
+
@spec.expects(:load_config).returns(
|
425
|
+
'ci' => {
|
426
|
+
'require' => %w(ci/circleci),
|
427
|
+
'blocking' => %w(soc/compliance),
|
428
|
+
},
|
429
|
+
)
|
430
|
+
assert_equal %w(ci/circleci soc/compliance), @spec.required_statuses
|
431
|
+
end
|
432
|
+
|
458
433
|
test "pull_request_ignored_statuses defaults to the union of ci.hide and ci.allow_failures" do
|
459
434
|
@spec.expects(:load_config).returns(
|
460
435
|
'ci' => {
|
@@ -573,26 +548,43 @@ module Shipit
|
|
573
548
|
assert_equal ['fake gem task'], @spec.deploy_steps
|
574
549
|
end
|
575
550
|
|
551
|
+
test 'lerna monorepos take priority over solo npm deploys' do
|
552
|
+
@spec.expects(:discover_npm_package).never
|
553
|
+
@spec.stubs(:discover_lerna_packages).returns(['fake monorepo task']).once
|
554
|
+
|
555
|
+
assert_equal ['fake monorepo task'], @spec.deploy_steps
|
556
|
+
end
|
557
|
+
|
558
|
+
test '#lerna? is false if there is no lerna.json' do
|
559
|
+
@spec.expects(:lerna_json).returns(Shipit::Engine.root.join("tmp-#{SecureRandom.hex}"))
|
560
|
+
refute @spec.lerna?
|
561
|
+
end
|
562
|
+
|
576
563
|
test '#npm? is false if there is no package.json' do
|
577
564
|
@spec.expects(:package_json).returns(Shipit::Engine.root.join("tmp-#{SecureRandom.hex}"))
|
578
565
|
refute @spec.npm?
|
579
566
|
end
|
580
567
|
|
581
|
-
test '#npm? is
|
568
|
+
test '#npm? is true if npm package is public' do
|
582
569
|
file = Pathname.new('/tmp/fake_package.json')
|
583
|
-
file.write('{"private":
|
570
|
+
file.write('{"private": false}')
|
584
571
|
|
585
572
|
@spec.expects(:package_json).returns(file)
|
586
|
-
|
573
|
+
|
574
|
+
assert @spec.npm?
|
587
575
|
end
|
588
576
|
|
589
|
-
test '#npm? is
|
577
|
+
test '#npm? is false if npm package is private' do
|
590
578
|
file = Pathname.new('/tmp/fake_package.json')
|
591
|
-
file.write('{"private":
|
579
|
+
file.write('{"private": true}')
|
592
580
|
|
593
581
|
@spec.expects(:package_json).returns(file)
|
582
|
+
refute @spec.npm?
|
583
|
+
end
|
594
584
|
|
595
|
-
|
585
|
+
test 'lerna monorepos have a checklist' do
|
586
|
+
@spec.stubs(:lerna?).returns(true).at_least_once
|
587
|
+
assert_match(/lerna publish --skip-npm/, @spec.review_checklist[0])
|
596
588
|
end
|
597
589
|
|
598
590
|
test 'npm packages have a checklist' do
|
@@ -600,14 +592,223 @@ module Shipit
|
|
600
592
|
assert_match(/npm version/, @spec.review_checklist[0])
|
601
593
|
end
|
602
594
|
|
595
|
+
test '#lerna_version returns the monorepo root version number' do
|
596
|
+
file = Pathname.new('/tmp/fake_lerna.json')
|
597
|
+
file.write('{"version": "1.0.0-beta.1"}')
|
598
|
+
|
599
|
+
@spec.expects(:lerna_json).returns(file)
|
600
|
+
assert_equal '1.0.0-beta.1', @spec.lerna_version
|
601
|
+
end
|
602
|
+
|
603
|
+
test '#package_version returns the version number' do
|
604
|
+
file = Pathname.new('/tmp/fake_package.json')
|
605
|
+
file.write('{"version": "1.0.0-beta.1"}')
|
606
|
+
|
607
|
+
@spec.expects(:package_json).returns(file)
|
608
|
+
assert_equal '1.0.0-beta.1', @spec.package_version
|
609
|
+
end
|
610
|
+
|
611
|
+
test '#dist_tag returns "latest" if the version does not contains a standard pre-release tag' do
|
612
|
+
assert_equal 'latest', @spec.dist_tag('1.0.0')
|
613
|
+
assert_equal 'latest', @spec.dist_tag('1.0.0-shopifyv4')
|
614
|
+
end
|
615
|
+
|
616
|
+
test '#dist_tag returns "next" if the version contains a pre-release tag' do
|
617
|
+
assert_equal 'next', @spec.dist_tag('1.0.0-alpha.1')
|
618
|
+
assert_equal 'next', @spec.dist_tag('1.0.0-beta')
|
619
|
+
assert_equal 'next', @spec.dist_tag('1.0.0-rc.3')
|
620
|
+
assert_equal 'next', @spec.dist_tag('1.0.0-next')
|
621
|
+
end
|
622
|
+
|
623
|
+
test '#dependencies_steps returns lerna setup if a `lerna.json` is present' do
|
624
|
+
@spec.expects(:lerna?).returns(true).at_least_once
|
625
|
+
assert_equal ['npm install --no-progress', 'node_modules/.bin/lerna bootstrap'], @spec.dependencies_steps
|
626
|
+
end
|
627
|
+
|
603
628
|
test '#dependencies_steps returns `npm install` if a `package.json` is present' do
|
604
629
|
@spec.expects(:npm?).returns(true).at_least_once
|
605
630
|
assert_equal ['npm install --no-progress'], @spec.dependencies_steps
|
606
631
|
end
|
607
632
|
|
633
|
+
test '#publish_lerna_packages checks if independent version tags exist, and then invokes lerna deploy script' do
|
634
|
+
@spec.stubs(:lerna?).returns(true)
|
635
|
+
@spec.stubs(:lerna_version).returns('independent')
|
636
|
+
assert_equal 'assert-lerna-independent-version-tags', @spec.deploy_steps[0]
|
637
|
+
assert_equal 'publish-lerna-independent-packages', @spec.deploy_steps[1]
|
638
|
+
end
|
639
|
+
|
640
|
+
test '#publish_lerna_packages checks if fixed version tag exists, and then invokes lerna deploy script' do
|
641
|
+
@spec.stubs(:lerna?).returns(true)
|
642
|
+
@spec.stubs(:lerna_version).returns('1.0.0')
|
643
|
+
assert_equal 'assert-lerna-fixed-version-tag', @spec.deploy_steps[0]
|
644
|
+
assert_equal 'node_modules/.bin/lerna publish --yes --skip-git --repo-version 1.0.0 --force-publish=* --npm-tag latest', @spec.deploy_steps[1]
|
645
|
+
end
|
646
|
+
|
647
|
+
test '#enforce_publish_config? is false when Shipit.enforce_publish_config is nil' do
|
648
|
+
Shipit.stubs(:enforce_publish_config).returns(nil)
|
649
|
+
refute @spec.enforce_publish_config?
|
650
|
+
end
|
651
|
+
|
652
|
+
test '#enforce_publish_config? is false when Shipit.enforce_publish_config is 0' do
|
653
|
+
Shipit.stubs(:enforce_publish_config).returns('0')
|
654
|
+
refute @spec.enforce_publish_config?
|
655
|
+
end
|
656
|
+
|
657
|
+
test '#enforce_publish_config? is true when Shipit.enforce_publish_config is 1' do
|
658
|
+
Shipit.stubs(:enforce_publish_config).returns('1')
|
659
|
+
assert @spec.enforce_publish_config?
|
660
|
+
end
|
661
|
+
|
662
|
+
test '#valid_publish_config? is false when enforce_publish_config? is true and publishConfig is missing from package.json' do
|
663
|
+
Shipit.stubs(:private_npm_registry).returns('some_private_registry')
|
664
|
+
@spec.stubs(:enforce_publish_config?).returns(true)
|
665
|
+
@spec.stubs(:publish_config_access).returns('restricted')
|
666
|
+
|
667
|
+
package_json = Pathname.new('/tmp/fake_package.json')
|
668
|
+
package_json.write('{"name": "foo"}')
|
669
|
+
|
670
|
+
@spec.expects(:package_json).returns(package_json)
|
671
|
+
refute @spec.valid_publish_config?
|
672
|
+
end
|
673
|
+
|
674
|
+
test '#valid_publish_config? is true when enforce_publish_config? is true and publishConfig.access is public' do
|
675
|
+
Shipit.stubs(:private_npm_registry).returns('some_private_registry')
|
676
|
+
@spec.stubs(:enforce_publish_config?).returns(true)
|
677
|
+
@spec.stubs(:publish_config_access).returns('public')
|
678
|
+
@spec.stubs(:publish_config).returns('something')
|
679
|
+
|
680
|
+
assert @spec.valid_publish_config?
|
681
|
+
end
|
682
|
+
|
683
|
+
test '#valid_publish_config? is true when shipit does not enforce a publishConfig' do
|
684
|
+
@spec.stubs(:lerna_version).returns('1.0.0')
|
685
|
+
assert @spec.valid_publish_config?
|
686
|
+
end
|
687
|
+
|
688
|
+
test '#publish_config returns publishConfig from package.json' do
|
689
|
+
package_json = Pathname.new('/tmp/fake_package.json')
|
690
|
+
package_json.write('{"publishConfig": "foo"}')
|
691
|
+
|
692
|
+
@spec.expects(:package_json).returns(package_json)
|
693
|
+
assert_equal "foo", @spec.publish_config
|
694
|
+
end
|
695
|
+
|
696
|
+
test '#valid_publish_config_access? is false when publishConfig.access is invalid' do
|
697
|
+
@spec.stubs(:publish_config_access).returns('foo')
|
698
|
+
refute @spec.valid_publish_config_access?
|
699
|
+
end
|
700
|
+
|
701
|
+
test '#valid_publish_config_access? is true when publishConfig.access is public or restricted' do
|
702
|
+
@spec.stubs(:publish_config_access).returns('public')
|
703
|
+
assert @spec.valid_publish_config_access?
|
704
|
+
|
705
|
+
@spec.stubs(:publish_config_access).returns('restricted')
|
706
|
+
assert @spec.valid_publish_config_access?
|
707
|
+
end
|
708
|
+
|
709
|
+
test '#publish_config_access is restricted when enforce_publish_config? is true and publishConfig is missing' do
|
710
|
+
package_json = Pathname.new('/tmp/fake_package.json')
|
711
|
+
package_json.write('{"name": "@shopify/foo"}')
|
712
|
+
|
713
|
+
@spec.stubs(:enforce_publish_config?).returns(true)
|
714
|
+
@spec.expects(:package_json).returns(package_json)
|
715
|
+
assert_equal 'restricted', @spec.publish_config_access
|
716
|
+
end
|
717
|
+
|
718
|
+
test '#publish_config_access is public when enforce_publish_config? is false and publishConfig is missing' do
|
719
|
+
package_json = Pathname.new('/tmp/fake_package.json')
|
720
|
+
package_json.write('{"name": "@shopify/foo"}')
|
721
|
+
|
722
|
+
@spec.stubs(:enforce_publish_config?).returns(false)
|
723
|
+
@spec.expects(:package_json).returns(package_json)
|
724
|
+
assert_equal 'public', @spec.publish_config_access
|
725
|
+
end
|
726
|
+
|
727
|
+
test '#publish_config_access returns publishConfig.access from package.json when enforce_publish_config? is true' do
|
728
|
+
package_json = Pathname.new('/tmp/fake_package.json')
|
729
|
+
package_json.write('{
|
730
|
+
"name": "@shopify/foo",
|
731
|
+
"publishConfig": {
|
732
|
+
"access": "foo"
|
733
|
+
}
|
734
|
+
}')
|
735
|
+
|
736
|
+
@spec.stubs(:enforce_publish_config?).returns(false)
|
737
|
+
@spec.expects(:package_json).returns(package_json)
|
738
|
+
assert_equal 'foo', @spec.publish_config_access
|
739
|
+
end
|
740
|
+
|
741
|
+
test "#scoped_package? is false when Shipit.npm_org_scope is not set and the package is private" do
|
742
|
+
Shipit.stubs(:npm_org_scope).returns(nil)
|
743
|
+
@spec.stubs(:publish_config_access).returns('restricted')
|
744
|
+
refute @spec.scoped_package?
|
745
|
+
end
|
746
|
+
|
747
|
+
test "#scoped_package? is true when Shipit.npm_org_scope is set and package_name starts with scope and the package is private" do
|
748
|
+
Shipit.stubs(:npm_org_scope).returns('@shopify')
|
749
|
+
@spec.stubs(:publish_config_access).returns('restricted')
|
750
|
+
@spec.stubs(:package_name).returns('@shopify/polaris')
|
751
|
+
assert @spec.scoped_package?
|
752
|
+
end
|
753
|
+
|
754
|
+
test "#private_scoped_package? is false when private packages are not scoped" do
|
755
|
+
@spec.stubs(:scoped_package?).returns(false)
|
756
|
+
@spec.stubs(:publish_config_access).returns("restricted")
|
757
|
+
refute @spec.private_scoped_package?
|
758
|
+
end
|
759
|
+
|
760
|
+
test "#private_scoped_package? is true when private packages are scoped" do
|
761
|
+
@spec.stubs(:scoped_package?).returns(true)
|
762
|
+
@spec.stubs(:publish_config_access).returns("restricted")
|
763
|
+
assert @spec.private_scoped_package?
|
764
|
+
end
|
765
|
+
|
608
766
|
test '#publish_npm_package checks if version tag exists, and then invokes npm deploy script' do
|
609
767
|
@spec.stubs(:npm?).returns(true)
|
610
|
-
|
768
|
+
@spec.stubs(:package_version).returns('1.0.0')
|
769
|
+
@spec.stubs(:valid_publish_config?).returns(true)
|
770
|
+
@spec.stubs(:publish_config_access).returns('restricted')
|
771
|
+
@spec.stubs(:registry).returns("@private:registry=some_private_registry")
|
772
|
+
assert_equal ['assert-npm-version-tag', 'npm publish --tag latest --access restricted'], @spec.deploy_steps
|
773
|
+
end
|
774
|
+
|
775
|
+
test '#npmrc_contents returns a scoped private package configuration when the package is scoped and private' do
|
776
|
+
registry = "@shopify:registry=some_private_registry"
|
777
|
+
Shipit.stubs(:npm_org_scope).returns('@shopify')
|
778
|
+
Shipit.stubs(:private_npm_registry).returns('some_private_registry')
|
779
|
+
@spec.stubs(:scoped_package?).returns(true)
|
780
|
+
@spec.stubs(:publish_config_access).returns('restricted')
|
781
|
+
assert_equal registry, @spec.registry
|
782
|
+
end
|
783
|
+
|
784
|
+
test '#npmrc_contents returns a public scoped package configuration when the package is scoped and public' do
|
785
|
+
registry = "@shopify:registry=https://registry.npmjs.org/"
|
786
|
+
Shipit.stubs(:npm_org_scope).returns('@shopify')
|
787
|
+
@spec.stubs(:scoped_package?).returns(true)
|
788
|
+
@spec.stubs(:publish_config_access).returns('public')
|
789
|
+
assert_equal registry, @spec.registry
|
790
|
+
end
|
791
|
+
|
792
|
+
test '#npmrc_contents returns a public non-scoped package configuration when the package is not scoped and public' do
|
793
|
+
registry = "registry=https://registry.npmjs.org/"
|
794
|
+
@spec.stubs(:scoped_package?).returns(false)
|
795
|
+
@spec.stubs(:publish_config_access).returns('public')
|
796
|
+
assert_equal registry, @spec.registry
|
797
|
+
end
|
798
|
+
|
799
|
+
test '#publish_lerna_packages guesses npm tag' do
|
800
|
+
@spec.stubs(:lerna?).returns(true)
|
801
|
+
@spec.stubs(:lerna_version).returns('1.0.0-alpha.1')
|
802
|
+
assert_match(/--npm-tag next/, @spec.deploy_steps.last)
|
803
|
+
end
|
804
|
+
|
805
|
+
test '#publish_npm_package checks if version tag and a pre-release flag exist, and then invokes npm deploy script' do
|
806
|
+
@spec.stubs(:npm?).returns(true)
|
807
|
+
@spec.stubs(:package_version).returns('1.0.0-alpha.1')
|
808
|
+
@spec.stubs(:valid_publish_config?).returns(true)
|
809
|
+
@spec.stubs(:publish_config_access).returns('restricted')
|
810
|
+
@spec.stubs(:registry).returns("@private:registry=some_private_registry")
|
811
|
+
assert_equal ['assert-npm-version-tag', 'npm publish --tag next --access restricted'], @spec.deploy_steps
|
611
812
|
end
|
612
813
|
|
613
814
|
test 'bundler installs take priority over yarn installs' do
|
@@ -663,14 +864,49 @@ module Shipit
|
|
663
864
|
assert_equal ['yarn install --no-progress'], @spec.dependencies_steps
|
664
865
|
end
|
665
866
|
|
666
|
-
test '#
|
867
|
+
test '#publish_npm_package checks if version tag exists, and then invokes npm publish script' do
|
667
868
|
@spec.stubs(:yarn?).returns(true).at_least_once
|
668
|
-
|
869
|
+
@spec.stubs(:package_version).returns('1.0.0')
|
870
|
+
@spec.stubs(:valid_publish_config?).returns(true)
|
871
|
+
@spec.stubs(:publish_config_access).returns('restricted')
|
872
|
+
@spec.stubs(:registry).returns("@private:registry=some_private_registry")
|
873
|
+
assert_equal ['assert-npm-version-tag', 'npm publish --tag latest --access restricted'], @spec.deploy_steps
|
874
|
+
end
|
875
|
+
|
876
|
+
test '#publish_npm_package checks if version tag exists, generates npmrc, and then invokes npm publish script when enforce_publish_config? is true' do
|
877
|
+
@spec.stubs(:yarn?).returns(true).at_least_once
|
878
|
+
@spec.stubs(:package_version).returns('1.0.0')
|
879
|
+
@spec.stubs(:valid_publish_config?).returns(true)
|
880
|
+
|
881
|
+
@spec.stubs(:publish_config_access).returns('restricted')
|
882
|
+
@spec.stubs(:enforce_publish_config?).returns(true)
|
883
|
+
@spec.stubs(:registry).returns('fake')
|
884
|
+
|
885
|
+
generate_npmrc = 'generate-local-npmrc "fake"'
|
886
|
+
npm_publish = 'npm publish --tag latest --access restricted'
|
887
|
+
deploy_steps = ['assert-npm-version-tag', generate_npmrc, npm_publish]
|
888
|
+
assert_equal deploy_steps, @spec.deploy_steps
|
669
889
|
end
|
670
890
|
|
671
891
|
test 'yarn checklist takes precedence over npm checklist' do
|
672
892
|
@spec.stubs(:yarn?).returns(true).at_least_once
|
673
893
|
assert_match(/yarn version/, @spec.review_checklist[0])
|
674
894
|
end
|
895
|
+
|
896
|
+
test "max_divergence_commits defaults to `nil" do
|
897
|
+
@spec.expects(:load_config).returns({})
|
898
|
+
assert_nil @spec.max_divergence_commits
|
899
|
+
end
|
900
|
+
|
901
|
+
test "max_divergence_age defaults to `nil` if `merge.max_divergence.age` cannot be parsed" do
|
902
|
+
@spec.expects(:load_config).returns(
|
903
|
+
'merge' => {
|
904
|
+
'max_divergence' => {
|
905
|
+
'age' => 'badbadbad',
|
906
|
+
},
|
907
|
+
},
|
908
|
+
)
|
909
|
+
assert_nil @spec.max_divergence_age
|
910
|
+
end
|
675
911
|
end
|
676
912
|
end
|