shipit-engine 0.25.1 → 0.26.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/shipit/merge_status_controller.rb +1 -1
- data/app/controllers/shipit/stacks_controller.rb +2 -1
- data/app/controllers/shipit/webhooks_controller.rb +15 -0
- data/app/jobs/shipit/cache_deploy_spec_job.rb +1 -0
- data/app/jobs/shipit/perform_task_job.rb +4 -2
- data/app/jobs/shipit/refresh_check_runs_job.rb +14 -0
- data/app/models/shipit/check_run.rb +71 -0
- data/app/models/shipit/commit.rb +25 -2
- data/app/models/shipit/deploy_spec.rb +1 -1
- data/app/models/shipit/pull_request.rb +2 -2
- data/app/models/shipit/status.rb +1 -1
- data/app/views/shipit/commits/_commit.html.erb +1 -1
- data/db/migrate/20181010150947_create_shipit_check_runs.rb +17 -0
- data/lib/shipit.rb +1 -0
- data/lib/shipit/octokit_check_runs.rb +9 -0
- data/lib/shipit/stack_commands.rb +8 -3
- data/lib/shipit/task_commands.rb +2 -3
- data/lib/shipit/version.rb +1 -1
- data/test/controllers/merge_status_controller_test.rb +15 -0
- data/test/controllers/stacks_controller_test.rb +12 -2
- data/test/controllers/webhooks_controller_test.rb +9 -0
- data/test/dummy/db/schema.rb +17 -1
- data/test/fixtures/payloads/check_suite_master.json +194 -0
- data/test/fixtures/shipit/check_runs.yml +21 -0
- data/test/fixtures/shipit/commits.yml +13 -0
- data/test/fixtures/shipit/stacks.yml +8 -0
- data/test/jobs/perform_task_job_test.rb +10 -1
- data/test/models/commits_test.rb +29 -2
- data/test/models/deploy_spec_test.rb +40 -0
- data/test/models/shipit/check_run_test.rb +51 -0
- data/test/models/status/group_test.rb +3 -3
- data/test/unit/deploy_commands_test.rb +5 -1
- metadata +128 -118
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84e2ecaebc907d7522d77553a552bafefa65cc7f
|
4
|
+
data.tar.gz: 59cd29bbf69566d7a0c2fa82ed3ad0f0086d307b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa96c8e445ac42b2b3138d6fbc00b3f61a290f06d491547ee6e1a83c09bf5f322a716af4539cf138bbc586d31e19944fc43438338722fc144ec0ce4113c11ae3
|
7
|
+
data.tar.gz: dab317ea37e1211b7cc765a2e30521bc7ca2054880f3df11402570d86fcc0326baf1e832e7182d647dc9c2f7f56f52f85ddc1e1b317546170862e077b4ae0eaa
|
@@ -60,7 +60,7 @@ module Shipit
|
|
60
60
|
@stack ||= if params[:stack_id]
|
61
61
|
Stack.from_param!(params[:stack_id])
|
62
62
|
else
|
63
|
-
scope = Stack.order(id: :asc).where(
|
63
|
+
scope = Stack.order(merge_queue_enabled: :desc, id: :asc).where(
|
64
64
|
repo_owner: referrer_parser.repo_owner,
|
65
65
|
repo_name: referrer_parser.repo_name,
|
66
66
|
)
|
@@ -17,7 +17,7 @@ module Shipit
|
|
17
17
|
return if flash.empty? && !stale?(last_modified: @stack.updated_at)
|
18
18
|
|
19
19
|
@tasks = @stack.tasks.order(id: :desc).preload(:since_commit, :until_commit, :user).limit(10)
|
20
|
-
@commits = @stack.undeployed_commits { |scope| scope.preload(:author, :statuses) }
|
20
|
+
@commits = @stack.undeployed_commits { |scope| scope.preload(:author, :statuses, :check_runs) }
|
21
21
|
end
|
22
22
|
|
23
23
|
def lookup
|
@@ -43,6 +43,7 @@ module Shipit
|
|
43
43
|
|
44
44
|
def refresh
|
45
45
|
RefreshStatusesJob.perform_later(stack_id: @stack.id)
|
46
|
+
RefreshCheckRunsJob.perform_later(stack_id: @stack.id)
|
46
47
|
GithubSyncJob.perform_later(stack_id: @stack.id)
|
47
48
|
flash[:success] = 'Refresh scheduled'
|
48
49
|
redirect_to request.referer.presence || stack_path(@stack)
|
@@ -70,6 +70,20 @@ module Shipit
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
+
class CheckSuiteHandler < Handler
|
74
|
+
params do
|
75
|
+
requires :check_suite do
|
76
|
+
requires :head_sha, String
|
77
|
+
requires :head_branch, String
|
78
|
+
end
|
79
|
+
end
|
80
|
+
def process
|
81
|
+
stacks.where(branch: params.check_suite.head_branch).each do |stack|
|
82
|
+
stack.commits.where(sha: params.check_suite.head_sha).each(&:schedule_refresh_check_runs!)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
73
87
|
class MembershipHandler < Handler
|
74
88
|
params do
|
75
89
|
requires :action, String
|
@@ -114,6 +128,7 @@ module Shipit
|
|
114
128
|
'push' => PushHandler,
|
115
129
|
'status' => StatusHandler,
|
116
130
|
'membership' => MembershipHandler,
|
131
|
+
'check_suite' => CheckSuiteHandler,
|
117
132
|
}.freeze
|
118
133
|
|
119
134
|
def create
|
@@ -60,8 +60,10 @@ module Shipit
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def checkout_repository
|
63
|
-
@task.
|
64
|
-
|
63
|
+
unless @commands.fetched?(@task.until_commit).tap(&:run).success?
|
64
|
+
@task.acquire_git_cache_lock do
|
65
|
+
capture! @commands.fetch
|
66
|
+
end
|
65
67
|
end
|
66
68
|
capture_all! @commands.clone
|
67
69
|
capture! @commands.checkout(@task.until_commit)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Shipit
|
2
|
+
class RefreshCheckRunsJob < BackgroundJob
|
3
|
+
queue_as :default
|
4
|
+
|
5
|
+
def perform(params)
|
6
|
+
if params[:commit_id]
|
7
|
+
Commit.find(params[:commit_id]).refresh_check_runs!
|
8
|
+
else
|
9
|
+
stack = Stack.find(params[:stack_id])
|
10
|
+
stack.commits.order(id: :desc).limit(30).each(&:refresh_check_runs!)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Shipit
|
2
|
+
class CheckRun < ApplicationRecord
|
3
|
+
CONCLUSIONS = %w(success failure neutral cancelled timed_out action_required).freeze
|
4
|
+
include DeferredTouch
|
5
|
+
include Status::Common
|
6
|
+
|
7
|
+
belongs_to :stack, required: true
|
8
|
+
belongs_to :commit, required: true
|
9
|
+
|
10
|
+
deferred_touch commit: :updated_at
|
11
|
+
|
12
|
+
validates :conclusion, inclusion: {in: CONCLUSIONS, allow_nil: true}
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def create_or_update_by!(selector:, attributes: {})
|
16
|
+
create!(selector.merge(attributes))
|
17
|
+
rescue ActiveRecord::RecordNotUnique
|
18
|
+
record = find_by!(selector)
|
19
|
+
record.update!(attributes)
|
20
|
+
record
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_or_update_from_github!(stack_id, github_check_run)
|
24
|
+
create_or_update_by!(
|
25
|
+
selector: {
|
26
|
+
github_id: github_check_run.id,
|
27
|
+
},
|
28
|
+
attributes: {
|
29
|
+
stack_id: stack_id,
|
30
|
+
name: github_check_run.name,
|
31
|
+
conclusion: github_check_run.conclusion,
|
32
|
+
title: github_check_run.output.title.to_s.truncate(1_000),
|
33
|
+
details_url: github_check_run.details_url,
|
34
|
+
html_url: github_check_run.html_url,
|
35
|
+
},
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def state
|
41
|
+
case conclusion
|
42
|
+
when nil, 'action_required'
|
43
|
+
'pending'
|
44
|
+
when 'success', 'neutral'
|
45
|
+
'success'
|
46
|
+
when 'failure', 'cancelled'
|
47
|
+
'failure'
|
48
|
+
when 'timed_out'
|
49
|
+
'error'
|
50
|
+
else
|
51
|
+
'unknown'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def context
|
56
|
+
name
|
57
|
+
end
|
58
|
+
|
59
|
+
def target_url
|
60
|
+
html_url
|
61
|
+
end
|
62
|
+
|
63
|
+
def description
|
64
|
+
title
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_partial_path
|
68
|
+
'shipit/statuses/status'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/app/models/shipit/commit.rb
CHANGED
@@ -7,6 +7,7 @@ module Shipit
|
|
7
7
|
belongs_to :stack
|
8
8
|
has_many :deploys
|
9
9
|
has_many :statuses, -> { order(created_at: :desc) }, dependent: :destroy, inverse_of: :commit
|
10
|
+
has_many :check_runs, dependent: :destroy
|
10
11
|
has_many :commit_deployments, dependent: :destroy
|
11
12
|
has_many :release_statuses, dependent: :destroy
|
12
13
|
belongs_to :pull_request, inverse_of: :merge_commit, optional: true
|
@@ -17,7 +18,8 @@ module Shipit
|
|
17
18
|
after_commit { broadcast_update }
|
18
19
|
after_create { stack.update_undeployed_commits_count }
|
19
20
|
|
20
|
-
after_commit :schedule_refresh_statuses!, :
|
21
|
+
after_commit :schedule_refresh_statuses!, :schedule_refresh_check_runs!, :schedule_fetch_stats!,
|
22
|
+
:schedule_continuous_delivery, on: :create
|
21
23
|
|
22
24
|
belongs_to :author, class_name: 'User', inverse_of: :authored_commits
|
23
25
|
belongs_to :committer, class_name: 'User', inverse_of: :commits
|
@@ -101,10 +103,18 @@ module Shipit
|
|
101
103
|
record
|
102
104
|
end
|
103
105
|
|
106
|
+
def statuses_and_check_runs
|
107
|
+
statuses + check_runs
|
108
|
+
end
|
109
|
+
|
104
110
|
def schedule_refresh_statuses!
|
105
111
|
RefreshStatusesJob.perform_later(commit_id: id)
|
106
112
|
end
|
107
113
|
|
114
|
+
def schedule_refresh_check_runs!
|
115
|
+
RefreshCheckRunsJob.perform_later(commit_id: id)
|
116
|
+
end
|
117
|
+
|
108
118
|
def refresh_statuses!
|
109
119
|
github_statuses = stack.handle_github_redirections { Shipit.github.api.statuses(github_repo_name, sha) }
|
110
120
|
github_statuses.each do |status|
|
@@ -118,6 +128,19 @@ module Shipit
|
|
118
128
|
end
|
119
129
|
end
|
120
130
|
|
131
|
+
def refresh_check_runs!
|
132
|
+
response = stack.handle_github_redirections do
|
133
|
+
Shipit.github.api.check_runs(github_repo_name, sha)
|
134
|
+
end
|
135
|
+
response.check_runs.each do |check_run|
|
136
|
+
create_or_update_check_run_from_github!(check_run)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def create_or_update_check_run_from_github!(github_check_run)
|
141
|
+
check_runs.create_or_update_from_github!(stack_id, github_check_run)
|
142
|
+
end
|
143
|
+
|
121
144
|
def last_release_status
|
122
145
|
@last_release_status ||= release_statuses.last || Status::Unknown.new(self)
|
123
146
|
end
|
@@ -216,7 +239,7 @@ module Shipit
|
|
216
239
|
end
|
217
240
|
|
218
241
|
def status
|
219
|
-
@status ||= Status::Group.compact(self,
|
242
|
+
@status ||= Status::Group.compact(self, statuses_and_check_runs)
|
220
243
|
end
|
221
244
|
|
222
245
|
def deployed?
|
@@ -185,11 +185,11 @@ module Shipit
|
|
185
185
|
|
186
186
|
def all_status_checks_passed?
|
187
187
|
return false unless head
|
188
|
-
StatusChecker.new(head, head.
|
188
|
+
StatusChecker.new(head, head.statuses_and_check_runs, stack.cached_deploy_spec).success?
|
189
189
|
end
|
190
190
|
|
191
191
|
def any_status_checks_failed?
|
192
|
-
status = StatusChecker.new(head, head.
|
192
|
+
status = StatusChecker.new(head, head.statuses_and_check_runs, stack.cached_deploy_spec)
|
193
193
|
status.failure? || status.error? || status.missing?
|
194
194
|
end
|
195
195
|
|
data/app/models/shipit/status.rb
CHANGED
@@ -9,7 +9,7 @@ module Shipit
|
|
9
9
|
belongs_to :stack, required: true
|
10
10
|
belongs_to :commit, required: true
|
11
11
|
|
12
|
-
deferred_touch
|
12
|
+
deferred_touch commit: :updated_at
|
13
13
|
|
14
14
|
validates :state, inclusion: {in: STATES, allow_blank: true}, presence: true
|
15
15
|
|
@@ -9,7 +9,7 @@
|
|
9
9
|
<span class="commit-title"><%= render_commit_message_with_link commit %></span>
|
10
10
|
<p class="commit-meta">
|
11
11
|
<span class="sha"><%= render_commit_id_link(commit) %></span>
|
12
|
-
<% if commit.additions? && commit.deletions? %>
|
12
|
+
<% if commit.additions.present? && commit.deletions.present? %>
|
13
13
|
<span class="code-additions">+<%= commit.additions %></span>
|
14
14
|
<span class="code-deletions">-<%= commit.deletions %></span>
|
15
15
|
<% end %>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateShipitCheckRuns < ActiveRecord::Migration[5.1]
|
2
|
+
def change
|
3
|
+
create_table :check_runs do |t|
|
4
|
+
t.references :stack, foreign_key: false, null: false
|
5
|
+
t.references :commit, foreign_key: false, null: false
|
6
|
+
t.bigint :github_id, null: false
|
7
|
+
t.string :name, null: false
|
8
|
+
t.string :conclusion, limit: 20, null: true
|
9
|
+
t.string :title, limit: 1024
|
10
|
+
t.string :details_url
|
11
|
+
t.string :html_url
|
12
|
+
t.timestamps
|
13
|
+
|
14
|
+
t.index %i(github_id commit_id), unique: true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/shipit.rb
CHANGED
@@ -0,0 +1,9 @@
|
|
1
|
+
module OctokitCheckRuns
|
2
|
+
def check_runs(repo, sha, options = {})
|
3
|
+
paginate "#{Octokit::Repository.path repo}/commits/#{sha}/check-runs", options.reverse_merge(
|
4
|
+
accept: 'application/vnd.github.antiope-preview+json',
|
5
|
+
)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Octokit::Client.include(OctokitCheckRuns)
|
@@ -44,14 +44,19 @@ module Shipit
|
|
44
44
|
def with_temporary_working_directory(commit: nil)
|
45
45
|
commit ||= @stack.last_deployed_commit.presence || @stack.commits.last
|
46
46
|
|
47
|
-
|
48
|
-
|
47
|
+
if !commit || !fetched?(commit).tap(&:run).success?
|
48
|
+
@stack.acquire_git_cache_lock do
|
49
49
|
fetch.run!
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
53
|
Dir.mktmpdir do |dir|
|
54
|
-
git(
|
54
|
+
git(
|
55
|
+
'clone', @stack.git_path, @stack.repo_name,
|
56
|
+
'--origin', 'cache',
|
57
|
+
chdir: dir
|
58
|
+
).run!
|
59
|
+
|
55
60
|
git_dir = File.join(dir, @stack.repo_name)
|
56
61
|
git('checkout', commit.sha, chdir: git_dir).run! if commit
|
57
62
|
yield Pathname.new(git_dir)
|
data/lib/shipit/task_commands.rb
CHANGED
@@ -53,11 +53,10 @@ module Shipit
|
|
53
53
|
git(
|
54
54
|
'clone',
|
55
55
|
'--local',
|
56
|
-
'--origin',
|
57
|
-
'cache',
|
56
|
+
'--origin', 'cache',
|
58
57
|
@stack.git_path,
|
59
58
|
@task.working_directory,
|
60
|
-
chdir: @stack.deploys_path
|
59
|
+
chdir: @stack.deploys_path
|
61
60
|
),
|
62
61
|
git('remote', 'add', 'origin', @stack.repo_git_url, chdir: @task.working_directory),
|
63
62
|
]
|
data/lib/shipit/version.rb
CHANGED
@@ -33,5 +33,20 @@ module Shipit
|
|
33
33
|
assert_response :ok
|
34
34
|
assert_predicate response.body, :blank?
|
35
35
|
end
|
36
|
+
|
37
|
+
test "GET show prefers stacks with merge_queue_enabled" do
|
38
|
+
existing = shipit_stacks(:shipit)
|
39
|
+
Shipit::Stack.create(
|
40
|
+
repo_owner: existing.repo_owner,
|
41
|
+
repo_name: existing.repo_name,
|
42
|
+
environment: 'foo',
|
43
|
+
branch: existing.branch,
|
44
|
+
merge_queue_enabled: true,
|
45
|
+
)
|
46
|
+
existing.update!(merge_queue_enabled: false)
|
47
|
+
get :show, params: {referrer: 'https://github.com/Shopify/shipit-engine/pull/42', branch: 'master'}
|
48
|
+
assert_response :ok
|
49
|
+
assert_includes response.body, 'shipit-engine/foo'
|
50
|
+
end
|
36
51
|
end
|
37
52
|
end
|
@@ -51,6 +51,14 @@ module Shipit
|
|
51
51
|
assert_response :ok
|
52
52
|
end
|
53
53
|
|
54
|
+
test "#show with a single CheckRun is successful" do
|
55
|
+
@stack = shipit_stacks(:check_runs)
|
56
|
+
assert_not_equal 0, CheckRun.where(stack_id: @stack.id).count
|
57
|
+
|
58
|
+
get :show, params: {id: @stack.to_param}
|
59
|
+
assert_response :ok
|
60
|
+
end
|
61
|
+
|
54
62
|
test "#show handles locked stacks without a lock_author" do
|
55
63
|
@stack.update!(lock_reason: "I am a lock with no author")
|
56
64
|
get :show, params: {id: @stack.to_param}
|
@@ -120,8 +128,10 @@ module Shipit
|
|
120
128
|
request.env['HTTP_REFERER'] = stack_settings_path(@stack)
|
121
129
|
|
122
130
|
assert_enqueued_with(job: RefreshStatusesJob, args: [stack_id: @stack.id]) do
|
123
|
-
assert_enqueued_with(job:
|
124
|
-
|
131
|
+
assert_enqueued_with(job: RefreshCheckRunsJob, args: [stack_id: @stack.id]) do
|
132
|
+
assert_enqueued_with(job: GithubSyncJob, args: [stack_id: @stack.id]) do
|
133
|
+
post :refresh, params: {id: @stack.to_param}
|
134
|
+
end
|
125
135
|
end
|
126
136
|
end
|
127
137
|
|
@@ -55,6 +55,15 @@ module Shipit
|
|
55
55
|
assert_response :ok
|
56
56
|
end
|
57
57
|
|
58
|
+
test ":check_suite with the target branch queues a RefreshCheckRunsJob" do
|
59
|
+
request.headers['X-Github-Event'] = 'check_suite'
|
60
|
+
|
61
|
+
assert_enqueued_with(job: RefreshCheckRunsJob) do
|
62
|
+
post :create, body: payload(:check_suite_master), as: :json
|
63
|
+
assert_response :ok
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
58
67
|
test "returns head :ok if request is ping" do
|
59
68
|
@request.headers['X-Github-Event'] = 'ping'
|
60
69
|
|
data/test/dummy/db/schema.rb
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
#
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
|
-
ActiveRecord::Schema.define(version:
|
13
|
+
ActiveRecord::Schema.define(version: 20181010150947) do
|
14
14
|
|
15
15
|
create_table "api_clients", force: :cascade do |t|
|
16
16
|
t.text "permissions", limit: 65535
|
@@ -22,6 +22,22 @@ ActiveRecord::Schema.define(version: 20180906083930) do
|
|
22
22
|
t.index ["creator_id"], name: "index_api_clients_on_creator_id"
|
23
23
|
end
|
24
24
|
|
25
|
+
create_table "check_runs", force: :cascade do |t|
|
26
|
+
t.integer "stack_id", null: false
|
27
|
+
t.integer "commit_id", null: false
|
28
|
+
t.bigint "github_id", null: false
|
29
|
+
t.string "name", null: false
|
30
|
+
t.string "conclusion", limit: 20
|
31
|
+
t.string "title", limit: 1024
|
32
|
+
t.string "details_url"
|
33
|
+
t.string "html_url"
|
34
|
+
t.datetime "created_at", null: false
|
35
|
+
t.datetime "updated_at", null: false
|
36
|
+
t.index ["commit_id"], name: "index_check_runs_on_commit_id"
|
37
|
+
t.index ["github_id", "commit_id"], name: "index_check_runs_on_github_id_and_commit_id", unique: true
|
38
|
+
t.index ["stack_id"], name: "index_check_runs_on_stack_id"
|
39
|
+
end
|
40
|
+
|
25
41
|
create_table "commit_deployment_statuses", force: :cascade do |t|
|
26
42
|
t.integer "commit_deployment_id"
|
27
43
|
t.string "status"
|