shipit-engine 0.21.0 → 0.22.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 +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
|