2018-08-03 07:15:25 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-06-02 10:16:11 +00:00
|
|
|
module Ci
|
2019-03-28 13:17:42 +00:00
|
|
|
class Stage < ApplicationRecord
|
2017-09-06 11:13:52 +00:00
|
|
|
extend Gitlab::Ci::Model
|
2017-07-04 13:50:03 +00:00
|
|
|
include Importable
|
2020-06-22 15:09:27 +00:00
|
|
|
include Ci::HasStatus
|
2017-07-20 11:30:05 +00:00
|
|
|
include Gitlab::OptimisticLocking
|
2017-07-04 13:31:15 +00:00
|
|
|
|
2020-06-22 15:09:27 +00:00
|
|
|
enum status: Ci::HasStatus::STATUSES_ENUM
|
2017-06-02 10:16:11 +00:00
|
|
|
|
|
|
|
belongs_to :project
|
|
|
|
belongs_to :pipeline
|
|
|
|
|
2017-08-16 11:30:49 +00:00
|
|
|
has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id
|
2020-05-14 18:08:06 +00:00
|
|
|
has_many :latest_statuses, -> { ordered.latest }, class_name: 'CommitStatus', foreign_key: :stage_id
|
2020-01-17 15:08:37 +00:00
|
|
|
has_many :processables, class_name: 'Ci::Processable', foreign_key: :stage_id
|
2017-07-20 11:05:26 +00:00
|
|
|
has_many :builds, foreign_key: :stage_id
|
2019-01-23 10:15:14 +00:00
|
|
|
has_many :bridges, foreign_key: :stage_id
|
2017-07-04 13:50:03 +00:00
|
|
|
|
2020-01-17 15:08:37 +00:00
|
|
|
scope :ordered, -> { order(position: :asc) }
|
|
|
|
|
2018-04-17 10:56:04 +00:00
|
|
|
with_options unless: :importing? do
|
|
|
|
validates :project, presence: true
|
|
|
|
validates :pipeline, presence: true
|
|
|
|
validates :name, presence: true
|
2018-05-01 12:30:44 +00:00
|
|
|
validates :position, presence: true
|
2018-04-17 10:56:04 +00:00
|
|
|
end
|
2017-07-20 11:30:05 +00:00
|
|
|
|
2018-04-18 12:56:08 +00:00
|
|
|
after_initialize do
|
2017-08-22 12:04:34 +00:00
|
|
|
self.status = DEFAULT_STATUS if self.status.nil?
|
|
|
|
end
|
|
|
|
|
2018-04-24 09:18:12 +00:00
|
|
|
before_validation unless: :importing? do
|
2018-05-01 12:30:44 +00:00
|
|
|
next if position.present?
|
2018-04-24 09:18:12 +00:00
|
|
|
|
2018-05-01 12:30:44 +00:00
|
|
|
self.position = statuses.select(:stage_idx)
|
2018-04-24 09:18:12 +00:00
|
|
|
.where('stage_idx IS NOT NULL')
|
|
|
|
.group(:stage_idx)
|
|
|
|
.order('COUNT(*) DESC')
|
|
|
|
.first&.stage_idx.to_i
|
2018-04-18 12:56:08 +00:00
|
|
|
end
|
|
|
|
|
2017-07-20 11:30:05 +00:00
|
|
|
state_machine :status, initial: :created do
|
|
|
|
event :enqueue do
|
2020-05-08 12:09:37 +00:00
|
|
|
transition any - [:pending] => :pending
|
2017-07-20 11:30:05 +00:00
|
|
|
end
|
|
|
|
|
2019-12-24 12:08:01 +00:00
|
|
|
event :request_resource do
|
|
|
|
transition any - [:waiting_for_resource] => :waiting_for_resource
|
|
|
|
end
|
|
|
|
|
2019-02-27 02:13:06 +00:00
|
|
|
event :prepare do
|
|
|
|
transition any - [:preparing] => :preparing
|
|
|
|
end
|
|
|
|
|
2017-07-20 11:30:05 +00:00
|
|
|
event :run do
|
|
|
|
transition any - [:running] => :running
|
|
|
|
end
|
|
|
|
|
|
|
|
event :skip do
|
|
|
|
transition any - [:skipped] => :skipped
|
|
|
|
end
|
|
|
|
|
|
|
|
event :drop do
|
|
|
|
transition any - [:failed] => :failed
|
|
|
|
end
|
|
|
|
|
|
|
|
event :succeed do
|
|
|
|
transition any - [:success] => :success
|
|
|
|
end
|
|
|
|
|
|
|
|
event :cancel do
|
|
|
|
transition any - [:canceled] => :canceled
|
|
|
|
end
|
|
|
|
|
|
|
|
event :block do
|
|
|
|
transition any - [:manual] => :manual
|
|
|
|
end
|
2018-09-21 06:24:19 +00:00
|
|
|
|
2018-10-04 10:26:15 +00:00
|
|
|
event :delay do
|
2018-09-21 06:24:19 +00:00
|
|
|
transition any - [:scheduled] => :scheduled
|
|
|
|
end
|
2017-07-20 11:30:05 +00:00
|
|
|
end
|
|
|
|
|
2020-01-17 15:08:37 +00:00
|
|
|
def set_status(new_status)
|
2017-07-20 11:30:05 +00:00
|
|
|
retry_optimistic_lock(self) do
|
2019-10-01 12:05:59 +00:00
|
|
|
case new_status
|
2018-06-04 09:29:39 +00:00
|
|
|
when 'created' then nil
|
2019-12-24 12:08:01 +00:00
|
|
|
when 'waiting_for_resource' then request_resource
|
2019-02-27 02:13:06 +00:00
|
|
|
when 'preparing' then prepare
|
2017-07-20 11:30:05 +00:00
|
|
|
when 'pending' then enqueue
|
|
|
|
when 'running' then run
|
|
|
|
when 'success' then succeed
|
|
|
|
when 'failed' then drop
|
|
|
|
when 'canceled' then cancel
|
|
|
|
when 'manual' then block
|
2018-10-04 10:26:15 +00:00
|
|
|
when 'scheduled' then delay
|
2018-05-25 10:43:27 +00:00
|
|
|
when 'skipped', nil then skip
|
2018-06-04 09:29:39 +00:00
|
|
|
else
|
2020-06-22 15:09:27 +00:00
|
|
|
raise Ci::HasStatus::UnknownStatusError,
|
2019-10-01 12:05:59 +00:00
|
|
|
"Unknown status `#{new_status}`"
|
2017-07-20 11:30:05 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-05-21 09:39:46 +00:00
|
|
|
|
2020-01-17 15:08:37 +00:00
|
|
|
def update_legacy_status
|
|
|
|
set_status(latest_stage_status.to_s)
|
|
|
|
end
|
|
|
|
|
2018-05-21 09:39:46 +00:00
|
|
|
def groups
|
2020-04-08 12:09:42 +00:00
|
|
|
@groups ||= Ci::Group.fabricate(project, self)
|
2018-05-21 09:39:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def has_warnings?
|
2018-05-22 10:30:45 +00:00
|
|
|
number_of_warnings.positive?
|
|
|
|
end
|
|
|
|
|
|
|
|
def number_of_warnings
|
|
|
|
BatchLoader.for(id).batch(default_value: 0) do |stage_ids, loader|
|
|
|
|
::Ci::Build.where(stage_id: stage_ids)
|
|
|
|
.latest
|
|
|
|
.failed_but_allowed
|
|
|
|
.group(:stage_id)
|
|
|
|
.count
|
|
|
|
.each { |id, amount| loader.call(id, amount) }
|
|
|
|
end
|
2018-05-21 09:39:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def detailed_status(current_user)
|
|
|
|
Gitlab::Ci::Status::Stage::Factory
|
|
|
|
.new(self, current_user)
|
|
|
|
.fabricate!
|
|
|
|
end
|
2019-05-02 18:27:35 +00:00
|
|
|
|
|
|
|
def manual_playable?
|
|
|
|
blocked? || skipped?
|
|
|
|
end
|
2019-10-01 12:05:59 +00:00
|
|
|
|
|
|
|
def latest_stage_status
|
2020-04-02 18:08:11 +00:00
|
|
|
statuses.latest.slow_composite_status(project: project) || 'skipped'
|
2019-10-01 12:05:59 +00:00
|
|
|
end
|
2017-06-02 10:16:11 +00:00
|
|
|
end
|
|
|
|
end
|