shipit-engine 0.21.0 → 0.22.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 +5 -106
- data/app/assets/images/timedout.svg +14 -0
- data/app/assets/stylesheets/_pages/_commits.scss +11 -2
- data/app/controllers/shipit/stacks_controller.rb +1 -7
- data/app/controllers/shipit/webhooks_controller.rb +102 -66
- data/app/helpers/shipit/github_url_helper.rb +2 -2
- data/app/helpers/shipit/shipit_helper.rb +3 -31
- data/app/jobs/shipit/destroy_job.rb +9 -0
- data/app/jobs/shipit/github_sync_job.rb +1 -1
- data/app/jobs/shipit/setup_github_hook_job.rb +1 -3
- data/app/models/shipit/anonymous_user.rb +4 -1
- data/app/models/shipit/commit.rb +8 -8
- data/app/models/shipit/commit_deployment.rb +3 -3
- data/app/models/shipit/commit_deployment_status.rb +2 -2
- data/app/models/shipit/deploy.rb +3 -3
- data/app/models/shipit/deploy_spec/file_system.rb +3 -3
- data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +10 -2
- data/app/models/shipit/github_hook.rb +2 -99
- data/app/models/shipit/github_status.rb +1 -1
- data/app/models/shipit/hook.rb +1 -1
- data/app/models/shipit/pull_request.rb +10 -10
- data/app/models/shipit/rollback.rb +1 -1
- data/app/models/shipit/stack.rb +27 -26
- data/app/models/shipit/task.rb +2 -2
- data/app/models/shipit/team.rb +4 -17
- data/app/models/shipit/user.rb +3 -3
- data/app/serializers/shipit/task_serializer.rb +2 -2
- data/app/serializers/shipit/user_serializer.rb +1 -1
- data/app/views/shipit/missing_settings.html.erb +5 -36
- data/app/views/shipit/stacks/new.html.erb +1 -1
- data/app/views/shipit/stacks/settings.html.erb +0 -4
- data/config/routes.rb +3 -13
- data/config/secrets.development.shopify.yml +10 -15
- data/config/secrets.development.yml +1 -1
- data/db/migrate/20180417130436_remove_all_github_hooks.rb +11 -0
- data/lib/shipit.rb +13 -56
- data/lib/shipit/command.rb +1 -1
- data/lib/shipit/engine.rb +2 -8
- data/lib/shipit/github_app.rb +122 -0
- data/lib/shipit/octokit_bot_users_patch.rb +25 -0
- data/lib/shipit/octokit_iterator.rb +2 -2
- data/lib/shipit/version.rb +1 -1
- data/lib/tasks/teams.rake +8 -24
- data/test/controllers/stacks_controller_test.rb +3 -29
- data/test/controllers/webhooks_controller_test.rb +29 -46
- data/test/dummy/config/secrets.yml +40 -10
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/schema.rb +1 -1
- data/test/dummy/db/seeds.rb +0 -1
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/fixtures/payloads/push_master.json +7 -6
- data/test/fixtures/payloads/push_not_master.json +7 -6
- data/test/fixtures/shipit/users.yml +2 -2
- data/test/helpers/hooks_helper.rb +1 -1
- data/test/helpers/payloads_helper.rb +1 -2
- data/test/jobs/destroy_stack_job_test.rb +1 -1
- data/test/models/commits_test.rb +5 -5
- data/test/models/deploy_spec_test.rb +17 -5
- data/test/models/github_hook_test.rb +1 -40
- data/test/models/pull_request_test.rb +11 -11
- data/test/models/stacks_test.rb +4 -10
- data/test/models/team_test.rb +3 -3
- data/test/models/users_test.rb +7 -7
- data/test/test_helper.rb +1 -1
- data/test/unit/github_app_test.rb +44 -0
- data/test/unit/shipit_test.rb +2 -49
- metadata +9 -3
- data/lib/tasks/webhook.rake +0 -6
@@ -15,7 +15,7 @@ module Shipit
|
|
15
15
|
new_commits, shared_parent = fetch_missing_commits { @stack.github_commits }
|
16
16
|
|
17
17
|
@stack.transaction do
|
18
|
-
shared_parent
|
18
|
+
shared_parent&.detach_children!
|
19
19
|
appended_commits = new_commits.map do |gh_commit|
|
20
20
|
append_commit(gh_commit)
|
21
21
|
end
|
data/app/models/shipit/commit.rb
CHANGED
@@ -6,7 +6,7 @@ module Shipit
|
|
6
6
|
|
7
7
|
belongs_to :stack
|
8
8
|
has_many :deploys
|
9
|
-
has_many :statuses, -> { order(created_at: :desc) }, dependent: :destroy
|
9
|
+
has_many :statuses, -> { order(created_at: :desc) }, dependent: :destroy, inverse_of: :commit
|
10
10
|
has_many :commit_deployments, dependent: :destroy
|
11
11
|
belongs_to :pull_request, inverse_of: :merge_commit, optional: true
|
12
12
|
|
@@ -71,8 +71,8 @@ module Shipit
|
|
71
71
|
committer: User.find_or_create_from_github(commit.committer || commit.commit.committer),
|
72
72
|
committed_at: commit.commit.committer.date,
|
73
73
|
authored_at: commit.commit.author.date,
|
74
|
-
additions: commit.stats
|
75
|
-
deletions: commit.stats
|
74
|
+
additions: commit.stats&.additions,
|
75
|
+
deletions: commit.stats&.deletions,
|
76
76
|
)
|
77
77
|
end
|
78
78
|
|
@@ -92,7 +92,7 @@ module Shipit
|
|
92
92
|
end
|
93
93
|
|
94
94
|
def refresh_statuses!
|
95
|
-
github_statuses = stack.handle_github_redirections { Shipit.
|
95
|
+
github_statuses = stack.handle_github_redirections { Shipit.github.api.statuses(github_repo_name, sha) }
|
96
96
|
github_statuses.each do |status|
|
97
97
|
create_status_from_github!(status)
|
98
98
|
end
|
@@ -170,7 +170,7 @@ module Shipit
|
|
170
170
|
end
|
171
171
|
|
172
172
|
def github_commit
|
173
|
-
@github_commit ||= Shipit.
|
173
|
+
@github_commit ||= Shipit.github.api.commit(github_repo_name, sha)
|
174
174
|
end
|
175
175
|
|
176
176
|
def schedule_fetch_stats!
|
@@ -179,8 +179,8 @@ module Shipit
|
|
179
179
|
|
180
180
|
def fetch_stats!
|
181
181
|
update!(
|
182
|
-
additions: github_commit.stats
|
183
|
-
deletions: github_commit.stats
|
182
|
+
additions: github_commit.stats&.additions,
|
183
|
+
deletions: github_commit.stats&.deletions,
|
184
184
|
)
|
185
185
|
end
|
186
186
|
|
@@ -210,7 +210,7 @@ module Shipit
|
|
210
210
|
end
|
211
211
|
|
212
212
|
def deploy_requested_at
|
213
|
-
if pull_request
|
213
|
+
if pull_request&.merged?
|
214
214
|
pull_request.merge_requested_at
|
215
215
|
else
|
216
216
|
created_at
|
@@ -23,19 +23,19 @@ module Shipit
|
|
23
23
|
response = begin
|
24
24
|
create_deployment_on_github(author.github_api)
|
25
25
|
rescue Octokit::ClientError
|
26
|
-
raise if Shipit.
|
26
|
+
raise if Shipit.github.api == author.github_api
|
27
27
|
# If the deploy author didn't gave us the permission to create the deployment we falback the the main shipit
|
28
28
|
# user.
|
29
29
|
#
|
30
30
|
# Octokit currently raise NotFound, but I'm convinced it should be Forbidden if the user can see the repository.
|
31
31
|
# So to be future proof I catch boths.
|
32
|
-
create_deployment_on_github(Shipit.
|
32
|
+
create_deployment_on_github(Shipit.github.api)
|
33
33
|
end
|
34
34
|
update!(github_id: response.id, api_url: response.url)
|
35
35
|
end
|
36
36
|
|
37
37
|
def pull_request_head
|
38
|
-
pull_request = Shipit.
|
38
|
+
pull_request = Shipit.github.api.pull_request(stack.github_repo_name, commit.pull_request_number)
|
39
39
|
pull_request.head.sha
|
40
40
|
end
|
41
41
|
|
@@ -11,13 +11,13 @@ module Shipit
|
|
11
11
|
response = begin
|
12
12
|
create_status_on_github(author.github_api)
|
13
13
|
rescue Octokit::ClientError
|
14
|
-
raise if Shipit.
|
14
|
+
raise if Shipit.github.api == author.github_api
|
15
15
|
# If the deploy author didn't gave us the permission to create the deployment we falback the the main shipit
|
16
16
|
# user.
|
17
17
|
#
|
18
18
|
# Octokit currently raise NotFound, but I'm convinced it should be Forbidden if the user can see the repository.
|
19
19
|
# So to be future proof I catch boths.
|
20
|
-
create_status_on_github(Shipit.
|
20
|
+
create_status_on_github(Shipit.github.api)
|
21
21
|
end
|
22
22
|
update!(github_id: response.id, api_url: response.url)
|
23
23
|
end
|
data/app/models/shipit/deploy.rb
CHANGED
@@ -39,12 +39,12 @@ module Shipit
|
|
39
39
|
|
40
40
|
def build_rollback(user = nil, env: nil, force: false)
|
41
41
|
Rollback.new(
|
42
|
-
user_id: user
|
42
|
+
user_id: user&.id,
|
43
43
|
stack_id: stack_id,
|
44
44
|
parent_id: id,
|
45
45
|
since_commit: stack.last_deployed_commit,
|
46
46
|
until_commit: until_commit,
|
47
|
-
env: env
|
47
|
+
env: env&.to_h || {},
|
48
48
|
allow_concurrency: force,
|
49
49
|
ignored_safeties: force,
|
50
50
|
)
|
@@ -159,7 +159,7 @@ module Shipit
|
|
159
159
|
|
160
160
|
def default_since_commit_id
|
161
161
|
return unless stack
|
162
|
-
@default_since_commit_id ||= last_successful_deploy
|
162
|
+
@default_since_commit_id ||= last_successful_deploy&.until_commit_id
|
163
163
|
end
|
164
164
|
|
165
165
|
def denormalize_commit_stats
|
@@ -33,10 +33,10 @@ module Shipit
|
|
33
33
|
'merge' => {
|
34
34
|
'require' => pull_request_required_statuses,
|
35
35
|
'ignore' => pull_request_ignored_statuses,
|
36
|
-
'revalidate_after' => revalidate_pull_requests_after
|
36
|
+
'revalidate_after' => revalidate_pull_requests_after&.to_i,
|
37
37
|
'max_divergence' => {
|
38
|
-
'commits' => max_divergence_commits
|
39
|
-
'age' => max_divergence_age
|
38
|
+
'commits' => max_divergence_commits&.to_i,
|
39
|
+
'age' => max_divergence_age&.to_i,
|
40
40
|
},
|
41
41
|
},
|
42
42
|
'ci' => {
|
@@ -25,10 +25,16 @@ module Shipit
|
|
25
25
|
|
26
26
|
private
|
27
27
|
|
28
|
+
def timeout_duration
|
29
|
+
duration = kube_config.fetch('timeout', '900s')
|
30
|
+
Duration.parse(duration).to_i if duration.present?
|
31
|
+
end
|
32
|
+
|
28
33
|
def discover_kubernetes
|
29
34
|
return if kube_config.blank?
|
30
35
|
|
31
36
|
cmd = ["kubernetes-deploy"]
|
37
|
+
cmd += ["--max-watch-seconds", timeout_duration] if timeout_duration
|
32
38
|
if kube_config['template_dir']
|
33
39
|
cmd << '--template-dir'
|
34
40
|
cmd << kube_config['template_dir']
|
@@ -45,11 +51,13 @@ module Shipit
|
|
45
51
|
end
|
46
52
|
|
47
53
|
def kubernetes_restart_cmd
|
48
|
-
|
54
|
+
cmd = [
|
49
55
|
"kubernetes-restart",
|
50
56
|
kube_config.fetch('namespace'),
|
51
57
|
kube_config.fetch('context'),
|
52
|
-
]
|
58
|
+
]
|
59
|
+
cmd += ["--max-watch-seconds", timeout_duration] if timeout_duration
|
60
|
+
Shellwords.join(cmd)
|
53
61
|
end
|
54
62
|
end
|
55
63
|
end
|
@@ -1,28 +1,11 @@
|
|
1
1
|
module Shipit
|
2
2
|
class GithubHook < ActiveRecord::Base
|
3
|
-
|
4
|
-
|
3
|
+
# TODO: app-migration, delete class
|
5
4
|
belongs_to :stack, required: false # Required for fixtures
|
6
5
|
|
7
|
-
before_create :generate_secret
|
8
6
|
before_destroy :teardown!
|
9
7
|
|
10
|
-
def verify_signature(signature, message)
|
11
|
-
algorithm, signature = signature.split("=", 2)
|
12
|
-
return false unless algorithm == 'sha1'
|
13
|
-
|
14
|
-
secure_compare(signature, OpenSSL::HMAC.hexdigest(algorithm, secret, message))
|
15
|
-
end
|
16
|
-
|
17
8
|
delegate :github_repo_name, to: :stack
|
18
|
-
def setup!
|
19
|
-
hook = already_setup? ? update_hook! : create_hook!
|
20
|
-
update!(github_id: hook.id, api_url: hook.rels[:self].href)
|
21
|
-
end
|
22
|
-
|
23
|
-
def schedule_setup!
|
24
|
-
SetupGithubHookJob.perform_later(self)
|
25
|
-
end
|
26
9
|
|
27
10
|
def teardown!
|
28
11
|
destroy_hook! if already_setup?
|
@@ -37,36 +20,8 @@ module Shipit
|
|
37
20
|
github_id?
|
38
21
|
end
|
39
22
|
|
40
|
-
private
|
41
|
-
|
42
|
-
def update_hook!
|
43
|
-
edit_hook!
|
44
|
-
rescue Octokit::NotFound
|
45
|
-
create_hook!
|
46
|
-
end
|
47
|
-
|
48
|
-
def endpoint_url
|
49
|
-
raise NotImplementedError.new('Subclasses must implement a `endpoint_url` method')
|
50
|
-
end
|
51
|
-
|
52
|
-
def hook_properties
|
53
|
-
{url: endpoint_url, content_type: 'json', secret: secret}
|
54
|
-
end
|
55
|
-
|
56
|
-
def generate_secret
|
57
|
-
self.secret = SecureRandom.hex
|
58
|
-
end
|
59
|
-
|
60
|
-
def url_helpers
|
61
|
-
Shipit::Engine.routes.url_helpers
|
62
|
-
end
|
63
|
-
|
64
|
-
def host
|
65
|
-
Shipit.host
|
66
|
-
end
|
67
|
-
|
68
23
|
def api
|
69
|
-
Shipit.
|
24
|
+
Shipit.legacy_github_api
|
70
25
|
end
|
71
26
|
|
72
27
|
class Repo < GithubHook
|
@@ -74,37 +29,10 @@ module Shipit
|
|
74
29
|
|
75
30
|
private
|
76
31
|
|
77
|
-
def create_hook!
|
78
|
-
api.create_hook(github_repo_name, 'web', properties, events: [event], active: true)
|
79
|
-
end
|
80
|
-
|
81
|
-
def edit_hook!
|
82
|
-
api.edit_hook(github_repo_name, github_id, 'web', properties, events: [event], active: true)
|
83
|
-
end
|
84
|
-
|
85
32
|
def destroy_hook!
|
86
33
|
api.remove_hook(github_repo_name, github_id)
|
87
34
|
rescue Octokit::NotFound
|
88
35
|
end
|
89
|
-
|
90
|
-
def properties
|
91
|
-
{
|
92
|
-
url: endpoint_url,
|
93
|
-
content_type: 'json',
|
94
|
-
secret: secret,
|
95
|
-
}
|
96
|
-
end
|
97
|
-
|
98
|
-
def endpoint_url
|
99
|
-
case event
|
100
|
-
when 'push'
|
101
|
-
url_helpers.push_stack_webhooks_url(stack_id, host: host)
|
102
|
-
when 'status'
|
103
|
-
url_helpers.state_stack_webhooks_url(stack_id, host: host)
|
104
|
-
else
|
105
|
-
raise ArgumentError, "Unknown GithubHook::Repo event: `#{event.inspect}`"
|
106
|
-
end
|
107
|
-
end
|
108
36
|
end
|
109
37
|
|
110
38
|
class Organization < GithubHook
|
@@ -112,35 +40,10 @@ module Shipit
|
|
112
40
|
|
113
41
|
private
|
114
42
|
|
115
|
-
def create_hook!
|
116
|
-
api.create_org_hook(organization, properties, events: [event], active: true)
|
117
|
-
end
|
118
|
-
|
119
|
-
def edit_hook!
|
120
|
-
api.edit_org_hook(organization, github_id, properties, events: [event], active: true)
|
121
|
-
end
|
122
|
-
|
123
43
|
def destroy_hook!
|
124
44
|
api.remove_org_hook(organization, github_id)
|
125
45
|
rescue Octokit::NotFound
|
126
46
|
end
|
127
|
-
|
128
|
-
def properties
|
129
|
-
{
|
130
|
-
url: endpoint_url,
|
131
|
-
content_type: 'json',
|
132
|
-
secret: secret,
|
133
|
-
}
|
134
|
-
end
|
135
|
-
|
136
|
-
def endpoint_url
|
137
|
-
case event
|
138
|
-
when 'membership'
|
139
|
-
url_helpers.membership_webhooks_url(host: host)
|
140
|
-
else
|
141
|
-
raise ArgumentError, "Unknown GithubHook::Organization event: `#{event.inspect}`"
|
142
|
-
end
|
143
|
-
end
|
144
47
|
end
|
145
48
|
end
|
146
49
|
end
|
data/app/models/shipit/hook.rb
CHANGED
@@ -39,7 +39,7 @@ module Shipit
|
|
39
39
|
raise "#{event} is not declared in Shipit::Hook::EVENTS" unless EVENTS.include?(event.to_s)
|
40
40
|
Shipit::EmitEventJob.perform_later(
|
41
41
|
event: event.to_s,
|
42
|
-
stack_id: stack
|
42
|
+
stack_id: stack&.id,
|
43
43
|
payload: coerce_payload(payload),
|
44
44
|
)
|
45
45
|
end
|
@@ -27,11 +27,11 @@ module Shipit
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def ignored_statuses
|
30
|
-
deploy_spec
|
30
|
+
deploy_spec&.pull_request_ignored_statuses || []
|
31
31
|
end
|
32
32
|
|
33
33
|
def required_statuses
|
34
|
-
deploy_spec
|
34
|
+
deploy_spec&.pull_request_required_statuses || []
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -110,7 +110,7 @@ module Shipit
|
|
110
110
|
case number_or_url
|
111
111
|
when /\A#?(\d+)\z/
|
112
112
|
$1.to_i
|
113
|
-
when %r{\Ahttps://#{Regexp.escape(Shipit.
|
113
|
+
when %r{\Ahttps://#{Regexp.escape(Shipit.github.domain)}/([^/]+)/([^/]+)/pull/(\d+)}
|
114
114
|
return unless $1.downcase == stack.repo_owner.downcase
|
115
115
|
return unless $2.downcase == stack.repo_name.downcase
|
116
116
|
$3.to_i
|
@@ -157,7 +157,7 @@ module Shipit
|
|
157
157
|
|
158
158
|
raise NotReady if not_mergeable_yet?
|
159
159
|
|
160
|
-
Shipit.
|
160
|
+
Shipit.github.api.merge_pull_request(
|
161
161
|
stack.github_repo_name,
|
162
162
|
number,
|
163
163
|
merge_message,
|
@@ -166,8 +166,8 @@ module Shipit
|
|
166
166
|
merge_method: 'merge',
|
167
167
|
)
|
168
168
|
begin
|
169
|
-
if Shipit.
|
170
|
-
Shipit.
|
169
|
+
if Shipit.github.api.pull_requests(stack.github_repo_name, base: branch).empty?
|
170
|
+
Shipit.github.api.delete_branch(stack.github_repo_name, branch)
|
171
171
|
end
|
172
172
|
rescue Octokit::UnprocessableEntity
|
173
173
|
# branch was already deleted somehow
|
@@ -196,7 +196,7 @@ module Shipit
|
|
196
196
|
end
|
197
197
|
|
198
198
|
def need_revalidation?
|
199
|
-
timeout = stack.cached_deploy_spec
|
199
|
+
timeout = stack.cached_deploy_spec&.revalidate_pull_requests_after
|
200
200
|
return false unless timeout
|
201
201
|
(revalidated_at + timeout).past?
|
202
202
|
end
|
@@ -222,7 +222,7 @@ module Shipit
|
|
222
222
|
end
|
223
223
|
|
224
224
|
def refresh!
|
225
|
-
update!(github_pull_request: Shipit.
|
225
|
+
update!(github_pull_request: Shipit.github.api.pull_request(stack.github_repo_name, number))
|
226
226
|
head.refresh_statuses!
|
227
227
|
fetched! if fetching?
|
228
228
|
@comparison = nil
|
@@ -261,7 +261,7 @@ module Shipit
|
|
261
261
|
end
|
262
262
|
|
263
263
|
def comparison
|
264
|
-
@comparison ||= Shipit.
|
264
|
+
@comparison ||= Shipit.github.api.compare(
|
265
265
|
stack.github_repo_name,
|
266
266
|
base_ref,
|
267
267
|
head.sha,
|
@@ -284,7 +284,7 @@ module Shipit
|
|
284
284
|
if commit = stack.commits.by_sha(sha)
|
285
285
|
return commit
|
286
286
|
else
|
287
|
-
github_commit = Shipit.
|
287
|
+
github_commit = Shipit.github.api.commit(stack.github_repo_name, sha)
|
288
288
|
stack.commits.create_from_github!(github_commit, attributes)
|
289
289
|
end
|
290
290
|
rescue ActiveRecord::RecordNotUnique
|