143 lines
4.9 KiB
Ruby
143 lines
4.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Gitlab
|
|
module Ci
|
|
module Status
|
|
class Composite
|
|
include Gitlab::Utils::StrongMemoize
|
|
|
|
# This class accepts an array of arrays/hashes/or objects
|
|
# `with_allow_failure` will be removed when deleting ci_remove_ensure_stage_service
|
|
def initialize(all_statuses, with_allow_failure: true, dag: false)
|
|
unless all_statuses.respond_to?(:pluck)
|
|
raise ArgumentError, "all_statuses needs to respond to `.pluck`"
|
|
end
|
|
|
|
@status_set = Set.new
|
|
@status_key = 0
|
|
@allow_failure_key = 1 if with_allow_failure
|
|
@dag = dag
|
|
|
|
consume_all_statuses(all_statuses)
|
|
end
|
|
|
|
# The status calculation is order dependent,
|
|
# 1. In some cases we assume that that status is exact
|
|
# if the we only have given statues,
|
|
# 2. In other cases we assume that status is of that type
|
|
# based on what statuses are no longer valid based on the
|
|
# data set that we have
|
|
#
|
|
# This method is used for two cases:
|
|
# 1. When it is called for a stage or a pipeline (with `all_statuses` from all jobs in a stage or a pipeline),
|
|
# then, the returned status is assigned to the stage or pipeline.
|
|
# 2. When it is called for a job (with `all_statuses` from all previous jobs or all needed jobs),
|
|
# then, the returned status is used to determine if the job is processed or not.
|
|
# rubocop: disable Metrics/CyclomaticComplexity
|
|
# rubocop: disable Metrics/PerceivedComplexity
|
|
def status
|
|
return if none?
|
|
|
|
strong_memoize(:status) do
|
|
if @dag && any_skipped_or_ignored?
|
|
# The DAG job is skipped if one of the needs does not run at all.
|
|
'skipped'
|
|
elsif @dag && !only_of?(:success, :failed, :canceled, :skipped, :success_with_warnings)
|
|
# DAG is blocked from executing if a dependent is not "complete"
|
|
'pending'
|
|
elsif only_of?(:skipped, :ignored)
|
|
'skipped'
|
|
elsif only_of?(:success, :skipped, :success_with_warnings, :ignored)
|
|
'success'
|
|
elsif only_of?(:created, :success_with_warnings, :ignored)
|
|
'created'
|
|
elsif only_of?(:preparing, :success_with_warnings, :ignored)
|
|
'preparing'
|
|
elsif only_of?(:canceled, :success, :skipped, :success_with_warnings, :ignored)
|
|
'canceled'
|
|
elsif only_of?(:pending, :created, :skipped, :success_with_warnings, :ignored)
|
|
'pending'
|
|
elsif any_of?(:running, :pending)
|
|
'running'
|
|
elsif any_of?(:waiting_for_resource)
|
|
'waiting_for_resource'
|
|
elsif any_of?(:manual)
|
|
'manual'
|
|
elsif any_of?(:scheduled)
|
|
'scheduled'
|
|
elsif any_of?(:preparing)
|
|
'preparing'
|
|
elsif any_of?(:created)
|
|
'running'
|
|
else
|
|
'failed'
|
|
end
|
|
end
|
|
end
|
|
# rubocop: enable Metrics/CyclomaticComplexity
|
|
# rubocop: enable Metrics/PerceivedComplexity
|
|
|
|
def warnings?
|
|
@status_set.include?(:success_with_warnings)
|
|
end
|
|
|
|
private
|
|
|
|
def none?
|
|
@status_set.empty?
|
|
end
|
|
|
|
def any_of?(*names)
|
|
names.any? { |name| @status_set.include?(name) }
|
|
end
|
|
|
|
def only_of?(*names)
|
|
matching = names.count { |name| @status_set.include?(name) }
|
|
matching > 0 &&
|
|
matching == @status_set.size
|
|
end
|
|
|
|
def any_skipped_or_ignored?
|
|
any_of?(:skipped) || any_of?(:ignored)
|
|
end
|
|
|
|
def consume_all_statuses(all_statuses)
|
|
columns = []
|
|
columns[@status_key] = :status
|
|
columns[@allow_failure_key] = :allow_failure if @allow_failure_key
|
|
|
|
all_statuses
|
|
.pluck(*columns) # rubocop: disable CodeReuse/ActiveRecord
|
|
.each do |status_attrs|
|
|
consume_status(Array.wrap(status_attrs))
|
|
end
|
|
end
|
|
|
|
def consume_status(status_attrs)
|
|
status_result =
|
|
if success_with_warnings?(status_attrs)
|
|
:success_with_warnings
|
|
elsif ignored_status?(status_attrs)
|
|
:ignored
|
|
else
|
|
status_attrs[@status_key].to_sym
|
|
end
|
|
|
|
@status_set.add(status_result)
|
|
end
|
|
|
|
def success_with_warnings?(status)
|
|
@allow_failure_key &&
|
|
status[@allow_failure_key] &&
|
|
::Ci::HasStatus::PASSED_WITH_WARNINGS_STATUSES.include?(status[@status_key])
|
|
end
|
|
|
|
def ignored_status?(status)
|
|
@allow_failure_key &&
|
|
status[@allow_failure_key] &&
|
|
::Ci::HasStatus::IGNORED_STATUSES.include?(status[@status_key])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|