shipit-engine 0.25.1 → 0.26.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/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"
|