shipit-engine 0.32.0 → 0.33.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/assets/images/magic-solid.svg +1 -0
- data/app/assets/javascripts/shipit/repositories_search.js.coffee +60 -0
- data/app/assets/javascripts/shipit/{search.js.coffee → stack_search.js.coffee} +0 -0
- data/app/assets/stylesheets/_pages/_repositories.scss +148 -0
- data/app/assets/stylesheets/_pages/_stacks.scss +19 -0
- data/app/assets/stylesheets/shipit.scss +1 -0
- data/app/controllers/shipit/api/{pull_requests_controller.rb → merge_requests_controller.rb} +8 -8
- data/app/controllers/shipit/api/stacks_controller.rb +14 -1
- data/app/controllers/shipit/deploys_controller.rb +2 -2
- data/app/controllers/shipit/merge_requests_controller.rb +31 -0
- data/app/controllers/shipit/merge_status_controller.rb +15 -15
- data/app/controllers/shipit/repositories_controller.rb +74 -0
- data/app/controllers/shipit/tasks_controller.rb +4 -4
- data/app/helpers/shipit/chunks_helper.rb +2 -2
- data/app/helpers/shipit/github_url_helper.rb +8 -0
- data/app/helpers/shipit/stacks_helper.rb +4 -0
- data/app/jobs/shipit/create_on_github_job.rb +1 -0
- data/app/jobs/shipit/destroy_repository_job.rb +24 -0
- data/app/jobs/shipit/destroy_stack_job.rb +2 -2
- data/app/jobs/shipit/perform_task_job.rb +4 -98
- data/app/jobs/shipit/process_merge_requests_job.rb +32 -0
- data/app/jobs/shipit/refresh_merge_request_job.rb +11 -0
- data/app/models/shipit/anonymous_user.rb +4 -0
- data/app/models/shipit/check_run.rb +2 -2
- data/app/models/shipit/command_line_user.rb +4 -0
- data/app/models/shipit/commit.rb +11 -11
- data/app/models/shipit/commit_checks.rb +1 -0
- data/app/models/shipit/deploy.rb +1 -0
- data/app/models/shipit/deploy_spec.rb +16 -4
- data/app/models/shipit/deploy_spec/file_system.rb +11 -5
- data/app/models/shipit/hook.rb +2 -0
- data/app/models/shipit/merge_request.rb +302 -0
- data/app/models/shipit/provisioning_handler.rb +32 -0
- data/app/models/shipit/provisioning_handler/base.rb +30 -0
- data/app/models/shipit/provisioning_handler/unregistered_provisioning_handler.rb +35 -0
- data/app/models/shipit/pull_request.rb +25 -264
- data/app/models/shipit/pull_request_assignment.rb +10 -0
- data/app/models/shipit/repository.rb +54 -0
- data/app/models/shipit/review_stack.rb +116 -0
- data/app/models/shipit/review_stack_provisioning_queue.rb +39 -0
- data/app/models/shipit/stack.rb +22 -8
- data/app/models/shipit/task.rb +56 -7
- data/app/models/shipit/task_execution_strategy/base.rb +20 -0
- data/app/models/shipit/task_execution_strategy/default.rb +110 -0
- data/app/models/shipit/user.rb +6 -1
- data/app/models/shipit/webhooks.rb +10 -0
- data/app/models/shipit/webhooks/handlers/pull_request/assigned_handler.rb +74 -0
- data/app/models/shipit/webhooks/handlers/pull_request/closed_handler.rb +68 -0
- data/app/models/shipit/webhooks/handlers/pull_request/edited_handler.rb +74 -0
- data/app/models/shipit/webhooks/handlers/pull_request/label_capturing_handler.rb +127 -0
- data/app/models/shipit/webhooks/handlers/pull_request/labeled_handler.rb +106 -0
- data/app/models/shipit/webhooks/handlers/pull_request/opened_handler.rb +83 -0
- data/app/models/shipit/webhooks/handlers/pull_request/reopened_handler.rb +88 -0
- data/app/models/shipit/webhooks/handlers/pull_request/review_stack_adapter.rb +103 -0
- data/app/models/shipit/webhooks/handlers/pull_request/unlabeled_handler.rb +107 -0
- data/app/serializers/shipit/deploy_serializer.rb +6 -0
- data/app/serializers/shipit/merge_request_serializer.rb +21 -0
- data/app/serializers/shipit/pull_request_serializer.rb +5 -8
- data/app/serializers/shipit/review_stack_serializer.rb +7 -0
- data/app/serializers/shipit/stack_serializer.rb +7 -6
- data/app/serializers/shipit/tail_task_serializer.rb +10 -2
- data/app/serializers/shipit/task_serializer.rb +1 -1
- data/app/views/shipit/merge_requests/_merge_request.html.erb +29 -0
- data/app/views/shipit/{pull_requests → merge_requests}/index.html.erb +2 -2
- data/app/views/shipit/merge_requests/merge_requests/_pull_request.html.erb +29 -0
- data/app/views/shipit/merge_requests/merge_requests/index.html.erb +20 -0
- data/app/views/shipit/merge_status/_merge_queue_button.html.erb +3 -3
- data/app/views/shipit/merge_status/backlogged.html.erb +1 -1
- data/app/views/shipit/merge_status/failure.html.erb +1 -1
- data/app/views/shipit/merge_status/locked.html.erb +1 -1
- data/app/views/shipit/merge_status/success.html.erb +2 -2
- data/app/views/shipit/repositories/_header.html.erb +19 -0
- data/app/views/shipit/repositories/index.html.erb +31 -0
- data/app/views/shipit/repositories/new.html.erb +23 -0
- data/app/views/shipit/repositories/settings.html.erb +53 -0
- data/app/views/shipit/repositories/show.html.erb +30 -0
- data/app/views/shipit/stacks/_banners.html.erb +13 -0
- data/app/views/shipit/stacks/_header.html.erb +5 -2
- data/app/views/shipit/stacks/_stack.html.erb +8 -0
- data/app/views/shipit/stacks/index.html.erb +2 -1
- data/app/views/shipit/stacks/settings.html.erb +5 -5
- data/app/views/shipit/stacks/show.html.erb +1 -1
- data/app/views/shipit/tasks/_task_output.html.erb +1 -1
- data/config/routes.rb +15 -5
- data/db/migrate/20200706145406_add_review_stacks.rb +12 -0
- data/db/migrate/20200804144639_rename_pull_request_to_merge_request.rb +7 -0
- data/db/migrate/20200804161512_rename_commits_pull_request_id_to_merge_request_id.rb +5 -0
- data/db/migrate/20200813134712_recreate_shipit_pull_requests.rb +22 -0
- data/db/migrate/20200813194056_create_pull_request_assignments.rb +8 -0
- data/db/migrate/20201001125502_add_provision_pr_stacks_flag_to_repositories.rb +7 -0
- data/db/migrate/20201008145809_add_retry_attempt_to_tasks.rb +5 -0
- data/db/migrate/20201008152744_add_max_retries_to_tasks.rb +5 -0
- data/lib/shipit.rb +11 -1
- data/lib/shipit/github_app.rb +1 -1
- data/lib/shipit/review_stack_commands.rb +8 -0
- data/lib/shipit/stack_commands.rb +6 -1
- data/lib/shipit/task_commands.rb +1 -0
- data/lib/shipit/version.rb +1 -1
- data/lib/tasks/cron.rake +11 -2
- data/test/controllers/api/{pull_requests_controller_test.rb → merge_requests_controller_test.rb} +12 -12
- data/test/controllers/api/outputs_controller_test.rb +1 -0
- data/test/controllers/api/rollback_controller_test.rb +1 -1
- data/test/controllers/api/stacks_controller_test.rb +21 -1
- data/test/controllers/{pull_requests_controller_test.rb → merge_requests_controller_test.rb} +6 -6
- data/test/controllers/repositories_controller_test.rb +71 -0
- data/test/controllers/stacks_controller_test.rb +9 -1
- data/test/controllers/tasks_controller_test.rb +14 -2
- data/test/controllers/webhooks_controller_test.rb +1 -1
- data/test/dummy/config/application.rb +6 -1
- data/test/dummy/config/environments/development.rb +0 -3
- data/test/dummy/config/environments/test.rb +0 -5
- data/test/dummy/db/schema.rb +52 -14
- data/test/dummy/db/seeds.rb +1 -1
- data/test/fixtures/payloads/check_suite_master.json +2 -2
- data/test/fixtures/payloads/invalid_pull_request.json +117 -0
- data/test/fixtures/payloads/provision_disabled_pull_request.json +454 -0
- data/test/fixtures/payloads/pull_request_assigned.json +480 -0
- data/test/fixtures/payloads/pull_request_closed.json +454 -0
- data/test/fixtures/payloads/pull_request_labeled.json +461 -0
- data/test/fixtures/payloads/pull_request_opened.json +454 -0
- data/test/fixtures/payloads/pull_request_reopened.json +454 -0
- data/test/fixtures/payloads/pull_request_unlabeled.json +454 -0
- data/test/fixtures/payloads/pull_request_with_no_repo.json +454 -0
- data/test/fixtures/shipit/commits.yml +15 -2
- data/test/fixtures/shipit/merge_requests.yml +141 -0
- data/test/fixtures/shipit/pull_request_assignments.yml +3 -0
- data/test/fixtures/shipit/pull_requests.yml +10 -131
- data/test/fixtures/shipit/repositories.yml +1 -0
- data/test/fixtures/shipit/stacks.yml +145 -0
- data/test/fixtures/shipit/statuses.yml +9 -0
- data/test/fixtures/shipit/tasks.yml +3 -0
- data/test/fixtures/shipit/users.yml +7 -0
- data/test/helpers/payloads_helper.rb +4 -0
- data/test/jobs/chunk_rollup_job_test.rb +15 -1
- data/test/jobs/destroy_repository_job_test.rb +27 -0
- data/test/jobs/perform_task_job_test.rb +8 -8
- data/test/jobs/{merge_pull_requests_job_test.rb → process_merge_requests_job_test.rb} +18 -18
- data/test/lib/shipit/deploy_commands_test.rb +16 -0
- data/test/lib/shipit/task_commands_test.rb +17 -0
- data/test/models/commits_test.rb +22 -13
- data/test/models/deploy_spec_test.rb +57 -24
- data/test/models/deploys_test.rb +148 -14
- data/test/models/{pull_request_test.rb → merge_request_test.rb} +30 -30
- data/test/models/pull_request_assignment_test.rb +16 -0
- data/test/models/shipit/provisioning_handler/base_test.rb +33 -0
- data/test/models/shipit/provisioning_handler/unregistered_provisioning_handler_test.rb +49 -0
- data/test/models/shipit/provisioning_handler_test.rb +64 -0
- data/test/models/shipit/pull_request_test.rb +52 -0
- data/test/models/shipit/repository_test.rb +5 -1
- data/test/models/shipit/review_stack_provision_status_test.rb +77 -0
- data/test/models/shipit/review_stack_provisioning_queue_test.rb +63 -0
- data/test/models/shipit/review_stack_test.rb +59 -0
- data/test/models/{stacks_test.rb → shipit/stacks_test.rb} +10 -4
- data/test/models/shipit/webhooks/handlers/pull_request/assigned_handler_test.rb +45 -0
- data/test/models/shipit/webhooks/handlers/pull_request/closed_handler_test.rb +192 -0
- data/test/models/shipit/webhooks/handlers/pull_request/edited_handler_test.rb +47 -0
- data/test/models/shipit/webhooks/handlers/pull_request/label_capturing_handler_test.rb +209 -0
- data/test/models/shipit/webhooks/handlers/pull_request/labeled_handler_test.rb +332 -0
- data/test/models/shipit/webhooks/handlers/pull_request/opened_handler_test.rb +238 -0
- data/test/models/shipit/webhooks/handlers/pull_request/reopened_handler_test.rb +282 -0
- data/test/models/shipit/webhooks/handlers/pull_request/review_stack_adapter_test.rb +83 -0
- data/test/models/shipit/webhooks/handlers/pull_request/unlabeled_handler_test.rb +324 -0
- data/test/models/shipit/{wehbooks → webhooks}/handlers_test.rb +0 -0
- data/test/models/tasks_test.rb +44 -3
- data/test/serializers/shipit/pull_request_serializer_test.rb +29 -0
- data/test/unit/command_test.rb +3 -3
- data/test/unit/github_url_helper_test.rb +5 -0
- data/test/unit/shipit_task_execution_strategy_test.rb +47 -0
- metadata +260 -154
- data/app/controllers/shipit/pull_requests_controller.rb +0 -31
- data/app/jobs/shipit/merge_pull_requests_job.rb +0 -32
- data/app/jobs/shipit/refresh_pull_request_job.rb +0 -11
- data/app/views/shipit/pull_requests/_pull_request.html.erb +0 -29
- data/test/fixtures/shipit/output_chunks.yml +0 -47
- data/test/models/output_chunk_test.rb +0 -21
data/app/models/shipit/deploy.rb
CHANGED
|
@@ -104,6 +104,10 @@ module Shipit
|
|
|
104
104
|
Duration.parse(config('deploy', 'interval') { 0 })
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
+
def provisioning_handler_name
|
|
108
|
+
config('provision', 'handler_name')
|
|
109
|
+
end
|
|
110
|
+
|
|
107
111
|
def deploy_steps
|
|
108
112
|
around_steps('deploy') do
|
|
109
113
|
config('deploy', 'override') { discover_deploy_steps }
|
|
@@ -122,6 +126,10 @@ module Shipit
|
|
|
122
126
|
deploy_variables.map { |v| [v.name, v.default] }.to_h
|
|
123
127
|
end
|
|
124
128
|
|
|
129
|
+
def retries_on_deploy
|
|
130
|
+
config('deploy', 'retries') { nil }
|
|
131
|
+
end
|
|
132
|
+
|
|
125
133
|
def rollback_steps
|
|
126
134
|
around_steps('rollback') do
|
|
127
135
|
config('rollback', 'override') { discover_rollback_steps }
|
|
@@ -132,6 +140,10 @@ module Shipit
|
|
|
132
140
|
rollback_steps || cant_detect!(:rollback)
|
|
133
141
|
end
|
|
134
142
|
|
|
143
|
+
def retries_on_rollback
|
|
144
|
+
config('rollback', 'retries') { nil }
|
|
145
|
+
end
|
|
146
|
+
|
|
135
147
|
def fetch_deployed_revision_steps
|
|
136
148
|
config('fetch') || discover_fetch_deployed_revision_steps
|
|
137
149
|
end
|
|
@@ -179,12 +191,12 @@ module Shipit
|
|
|
179
191
|
Array.wrap(config('ci', 'blocking'))
|
|
180
192
|
end
|
|
181
193
|
|
|
182
|
-
def
|
|
194
|
+
def merge_request_merge_method
|
|
183
195
|
method = config('merge', 'method')
|
|
184
196
|
method if %w(merge rebase squash).include?(method)
|
|
185
197
|
end
|
|
186
198
|
|
|
187
|
-
def
|
|
199
|
+
def merge_request_required_statuses
|
|
188
200
|
if config('merge', 'require') || config('merge', 'ignore')
|
|
189
201
|
Array.wrap(config('merge', 'require'))
|
|
190
202
|
else
|
|
@@ -192,7 +204,7 @@ module Shipit
|
|
|
192
204
|
end
|
|
193
205
|
end
|
|
194
206
|
|
|
195
|
-
def
|
|
207
|
+
def merge_request_ignored_statuses
|
|
196
208
|
if config('merge', 'require') || config('merge', 'ignore')
|
|
197
209
|
Array.wrap(config('merge', 'ignore')) + [release_status_context].compact
|
|
198
210
|
else
|
|
@@ -200,7 +212,7 @@ module Shipit
|
|
|
200
212
|
end
|
|
201
213
|
end
|
|
202
214
|
|
|
203
|
-
def
|
|
215
|
+
def revalidate_merge_requests_after
|
|
204
216
|
if timeout = config('merge', 'revalidate_after')
|
|
205
217
|
begin
|
|
206
218
|
Duration.parse(timeout)
|
|
@@ -13,6 +13,7 @@ module Shipit
|
|
|
13
13
|
def initialize(app_dir, env)
|
|
14
14
|
@app_dir = Pathname(app_dir)
|
|
15
15
|
@env = env
|
|
16
|
+
super(nil)
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def cacheable
|
|
@@ -32,10 +33,10 @@ module Shipit
|
|
|
32
33
|
def cacheable_config
|
|
33
34
|
(config || {}).deep_merge(
|
|
34
35
|
'merge' => {
|
|
35
|
-
'require' =>
|
|
36
|
-
'ignore' =>
|
|
37
|
-
'revalidate_after' =>
|
|
38
|
-
'method' =>
|
|
36
|
+
'require' => merge_request_required_statuses,
|
|
37
|
+
'ignore' => merge_request_ignored_statuses,
|
|
38
|
+
'revalidate_after' => revalidate_merge_requests_after&.to_i,
|
|
39
|
+
'method' => merge_request_merge_method,
|
|
39
40
|
'max_divergence' => {
|
|
40
41
|
'commits' => max_divergence_commits&.to_i,
|
|
41
42
|
'age' => max_divergence_age&.to_i,
|
|
@@ -63,13 +64,18 @@ module Shipit
|
|
|
63
64
|
'delay' => release_status_delay,
|
|
64
65
|
},
|
|
65
66
|
'dependencies' => { 'override' => dependencies_steps },
|
|
67
|
+
'provision' => { 'handler_name' => provisioning_handler_name },
|
|
66
68
|
'deploy' => {
|
|
67
69
|
'override' => deploy_steps,
|
|
68
70
|
'variables' => deploy_variables.map(&:to_h),
|
|
69
71
|
'max_commits' => maximum_commits_per_deploy,
|
|
70
72
|
'interval' => pause_between_deploys,
|
|
73
|
+
'retries' => retries_on_deploy,
|
|
74
|
+
},
|
|
75
|
+
'rollback' => {
|
|
76
|
+
'override' => rollback_steps,
|
|
77
|
+
'retries' => retries_on_rollback,
|
|
71
78
|
},
|
|
72
|
-
'rollback' => { 'override' => rollback_steps },
|
|
73
79
|
'fetch' => fetch_deployed_revision_steps,
|
|
74
80
|
'tasks' => cacheable_tasks,
|
|
75
81
|
)
|
data/app/models/shipit/hook.rb
CHANGED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Shipit
|
|
3
|
+
class MergeRequest < ApplicationRecord
|
|
4
|
+
include DeferredTouch
|
|
5
|
+
|
|
6
|
+
MERGE_REQUEST_FIELD = 'Merge-Requested-By'
|
|
7
|
+
|
|
8
|
+
WAITING_STATUSES = %w(fetching pending).freeze
|
|
9
|
+
QUEUED_STATUSES = %w(pending revalidating).freeze
|
|
10
|
+
REJECTION_REASONS = %w(ci_missing ci_failing merge_conflict requires_rebase).freeze
|
|
11
|
+
InvalidTransition = Class.new(StandardError)
|
|
12
|
+
NotReady = Class.new(StandardError)
|
|
13
|
+
|
|
14
|
+
class StatusChecker < Status::Group
|
|
15
|
+
def initialize(commit, statuses, deploy_spec)
|
|
16
|
+
@deploy_spec = deploy_spec
|
|
17
|
+
super(commit, statuses)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
attr_reader :deploy_spec
|
|
23
|
+
|
|
24
|
+
def reject_hidden(statuses)
|
|
25
|
+
statuses.reject { |s| ignored_statuses.include?(s.context) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def reject_allowed_to_fail(statuses)
|
|
29
|
+
statuses.reject { |s| ignored_statuses.include?(s.context) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def ignored_statuses
|
|
33
|
+
deploy_spec&.merge_request_ignored_statuses || []
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def required_statuses
|
|
37
|
+
deploy_spec&.merge_request_required_statuses || []
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
belongs_to :stack
|
|
42
|
+
belongs_to :head, class_name: 'Shipit::Commit', optional: true
|
|
43
|
+
belongs_to :base_commit, class_name: 'Shipit::Commit', optional: true
|
|
44
|
+
belongs_to :merge_requested_by, class_name: 'Shipit::User', optional: true
|
|
45
|
+
has_one :merge_commit, class_name: 'Shipit::Commit'
|
|
46
|
+
|
|
47
|
+
deferred_touch stack: :updated_at
|
|
48
|
+
|
|
49
|
+
validates :number, presence: true, uniqueness: { scope: :stack_id }
|
|
50
|
+
|
|
51
|
+
scope :waiting, -> { where(merge_status: WAITING_STATUSES) }
|
|
52
|
+
scope :pending, -> { where(merge_status: 'pending') }
|
|
53
|
+
scope :to_be_merged, -> { pending.order(merge_requested_at: :asc) }
|
|
54
|
+
scope :queued, -> { where(merge_status: QUEUED_STATUSES).order(merge_requested_at: :asc) }
|
|
55
|
+
|
|
56
|
+
after_save :record_merge_status_change
|
|
57
|
+
after_commit :emit_hooks
|
|
58
|
+
|
|
59
|
+
state_machine :merge_status, initial: :fetching do
|
|
60
|
+
state :fetching
|
|
61
|
+
state :pending
|
|
62
|
+
state :rejected
|
|
63
|
+
state :canceled
|
|
64
|
+
state :merged
|
|
65
|
+
state :revalidating
|
|
66
|
+
|
|
67
|
+
event :fetched do
|
|
68
|
+
transition fetching: :pending
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
event :reject do
|
|
72
|
+
transition pending: :rejected
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
event :revalidate do
|
|
76
|
+
transition pending: :revalidating
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
event :cancel do
|
|
80
|
+
transition any => :canceled
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
event :complete do
|
|
84
|
+
transition pending: :merged
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
event :retry do
|
|
88
|
+
transition %i(rejected canceled revalidating) => :pending
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
before_transition rejected: any do |pr|
|
|
92
|
+
pr.rejection_reason = nil
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
before_transition %i(fetching rejected canceled) => :pending do |pr|
|
|
96
|
+
pr.merge_requested_at = Time.now.utc
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
before_transition any => :pending do |pr|
|
|
100
|
+
pr.revalidated_at = Time.now.utc
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
before_transition %i(pending) => :merged do |pr|
|
|
104
|
+
Stack.increment_counter(:undeployed_commits_count, pr.stack_id)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def self.schedule_merges
|
|
109
|
+
Shipit::Stack.where(merge_queue_enabled: true).find_each(&:schedule_merges)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def self.extract_number(stack, number_or_url)
|
|
113
|
+
case number_or_url
|
|
114
|
+
when /\A#?(\d+)\z/
|
|
115
|
+
$1.to_i
|
|
116
|
+
when %r{\Ahttps://#{Regexp.escape(Shipit.github.domain)}/([^/]+)/([^/]+)/pull/(\d+)}
|
|
117
|
+
return unless $1.downcase == stack.repo_owner.downcase
|
|
118
|
+
return unless $2.downcase == stack.repo_name.downcase
|
|
119
|
+
$3.to_i
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def self.request_merge!(stack, number, user)
|
|
124
|
+
now = Time.now.utc
|
|
125
|
+
merge_request = begin
|
|
126
|
+
create_with(
|
|
127
|
+
merge_requested_at: now,
|
|
128
|
+
merge_requested_by: user.presence,
|
|
129
|
+
).find_or_create_by!(
|
|
130
|
+
stack: stack,
|
|
131
|
+
number: number,
|
|
132
|
+
)
|
|
133
|
+
rescue ActiveRecord::RecordNotUnique
|
|
134
|
+
retry
|
|
135
|
+
end
|
|
136
|
+
merge_request.update!(merge_requested_by: user.presence)
|
|
137
|
+
merge_request.retry! if merge_request.rejected? || merge_request.canceled? || merge_request.revalidating?
|
|
138
|
+
merge_request.schedule_refresh!
|
|
139
|
+
merge_request
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def reject!(reason)
|
|
143
|
+
unless REJECTION_REASONS.include?(reason)
|
|
144
|
+
raise ArgumentError, "invalid reason: #{reason.inspect}, must be one of: #{REJECTION_REASONS.inspect}"
|
|
145
|
+
end
|
|
146
|
+
self.rejection_reason = reason.presence
|
|
147
|
+
super()
|
|
148
|
+
true
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def reject_unless_mergeable!
|
|
152
|
+
return reject!('merge_conflict') if merge_conflict?
|
|
153
|
+
return reject!('ci_missing') if any_status_checks_missing?
|
|
154
|
+
return reject!('ci_failing') if any_status_checks_failed?
|
|
155
|
+
return reject!('requires_rebase') if stale?
|
|
156
|
+
false
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def merge!
|
|
160
|
+
raise InvalidTransition unless pending?
|
|
161
|
+
|
|
162
|
+
raise NotReady if not_mergeable_yet?
|
|
163
|
+
|
|
164
|
+
Shipit.github.api.merge_pull_request(
|
|
165
|
+
stack.github_repo_name,
|
|
166
|
+
number,
|
|
167
|
+
merge_message,
|
|
168
|
+
sha: head.sha,
|
|
169
|
+
commit_message: 'Merged by Shipit',
|
|
170
|
+
merge_method: stack.merge_method,
|
|
171
|
+
)
|
|
172
|
+
begin
|
|
173
|
+
if Shipit.github.api.pull_requests(stack.github_repo_name, base: branch).empty?
|
|
174
|
+
Shipit.github.api.delete_branch(stack.github_repo_name, branch)
|
|
175
|
+
end
|
|
176
|
+
rescue Octokit::UnprocessableEntity
|
|
177
|
+
# branch was already deleted somehow
|
|
178
|
+
end
|
|
179
|
+
complete!
|
|
180
|
+
true
|
|
181
|
+
rescue Octokit::MethodNotAllowed # merge conflict
|
|
182
|
+
reject!('merge_conflict')
|
|
183
|
+
false
|
|
184
|
+
rescue Octokit::Conflict # shas didn't match, PR was updated.
|
|
185
|
+
raise NotReady
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def all_status_checks_passed?
|
|
189
|
+
return false unless head
|
|
190
|
+
StatusChecker.new(head, head.statuses_and_check_runs, stack.cached_deploy_spec).success?
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def any_status_checks_failed?
|
|
194
|
+
status = StatusChecker.new(head, head.statuses_and_check_runs, stack.cached_deploy_spec)
|
|
195
|
+
status.failure? || status.error?
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def any_status_checks_missing?
|
|
199
|
+
StatusChecker.new(head, head.statuses_and_check_runs, stack.cached_deploy_spec).missing?
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def waiting?
|
|
203
|
+
WAITING_STATUSES.include?(merge_status)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def need_revalidation?
|
|
207
|
+
timeout = stack.cached_deploy_spec&.revalidate_merge_requests_after
|
|
208
|
+
return false unless timeout
|
|
209
|
+
(revalidated_at + timeout).past?
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def merge_conflict?
|
|
213
|
+
mergeable == false
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def not_mergeable_yet?
|
|
217
|
+
mergeable.nil?
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def schedule_refresh!
|
|
221
|
+
RefreshMergeRequestJob.perform_later(self)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def closed?
|
|
225
|
+
state == "closed"
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def merged_upstream?
|
|
229
|
+
closed? && merged_at
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def refresh!
|
|
233
|
+
update!(github_pull_request: Shipit.github.api.pull_request(stack.github_repo_name, number))
|
|
234
|
+
head.refresh_statuses!
|
|
235
|
+
fetched! if fetching?
|
|
236
|
+
@comparison = nil
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def github_pull_request=(github_pull_request)
|
|
240
|
+
self.github_id = github_pull_request.id
|
|
241
|
+
self.api_url = github_pull_request.url
|
|
242
|
+
self.title = github_pull_request.title
|
|
243
|
+
self.state = github_pull_request.state
|
|
244
|
+
self.mergeable = github_pull_request.mergeable
|
|
245
|
+
self.additions = github_pull_request.additions
|
|
246
|
+
self.deletions = github_pull_request.deletions
|
|
247
|
+
self.branch = github_pull_request.head.ref
|
|
248
|
+
self.head = find_or_create_commit_from_github_by_sha!(github_pull_request.head.sha, detached: true)
|
|
249
|
+
self.merged_at = github_pull_request.merged_at
|
|
250
|
+
self.base_ref = github_pull_request.base.ref
|
|
251
|
+
self.base_commit = find_or_create_commit_from_github_by_sha!(github_pull_request.base.sha, detached: true)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def merge_message
|
|
255
|
+
return title unless merge_requested_by
|
|
256
|
+
"#{title}\n\n#{MERGE_REQUEST_FIELD}: #{merge_requested_by.login}\n"
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def stale?
|
|
260
|
+
return false unless base_commit
|
|
261
|
+
spec = stack.cached_deploy_spec
|
|
262
|
+
if max_branch_age = spec.max_divergence_age
|
|
263
|
+
return true if Time.now.utc - head.committed_at > max_branch_age
|
|
264
|
+
end
|
|
265
|
+
if commit_count_limit = spec.max_divergence_commits
|
|
266
|
+
return true if comparison.behind_by > commit_count_limit
|
|
267
|
+
end
|
|
268
|
+
false
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def comparison
|
|
272
|
+
@comparison ||= Shipit.github.api.compare(
|
|
273
|
+
stack.github_repo_name,
|
|
274
|
+
base_ref,
|
|
275
|
+
head.sha,
|
|
276
|
+
)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
private
|
|
280
|
+
|
|
281
|
+
def record_merge_status_change
|
|
282
|
+
@merge_status_changed ||= saved_change_to_attribute?(:merge_status)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def emit_hooks
|
|
286
|
+
return unless @merge_status_changed
|
|
287
|
+
@merge_status_changed = nil
|
|
288
|
+
Hook.emit('merge', stack, merge_request: self, status: merge_status, stack: stack)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def find_or_create_commit_from_github_by_sha!(sha, attributes)
|
|
292
|
+
if commit = stack.commits.by_sha(sha)
|
|
293
|
+
commit
|
|
294
|
+
else
|
|
295
|
+
github_commit = Shipit.github.api.commit(stack.github_repo_name, sha)
|
|
296
|
+
stack.commits.create_from_github!(github_commit, attributes)
|
|
297
|
+
end
|
|
298
|
+
rescue ActiveRecord::RecordNotUnique
|
|
299
|
+
retry
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shipit
|
|
4
|
+
module ProvisioningHandler
|
|
5
|
+
class << self
|
|
6
|
+
def registry
|
|
7
|
+
@registry ||= reset_registry!
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def reset_registry!
|
|
11
|
+
@registry = {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def register(handler_class)
|
|
15
|
+
registry[handler_class.to_s] = handler_class
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def fetch(name)
|
|
19
|
+
return default if name.blank?
|
|
20
|
+
registry.fetch(name) { ProvisioningHandler::UnregisteredProvisioningHandler }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def default=(handler_class)
|
|
24
|
+
registry[:default] = handler_class
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def default
|
|
28
|
+
registry.fetch(:default) { ProvisioningHandler::Base }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|