Commit 41abc03f authored by Kamil Trzciński's avatar Kamil Trzciński

Remove GroupedStatuses

parent 7152a5ee
...@@ -24,9 +24,11 @@ module Ci ...@@ -24,9 +24,11 @@ module Ci
def status def status
strong_memoize(:status) do strong_memoize(:status) do
if Feature.enabled?(:ci_composite_status, default_enabled: true) if Feature.enabled?(:ci_composite_status, default_enabled: true)
Gitlab::Ci::Status::GroupedStatuses all_statuses = @jobs.pluck(:status, :allow_failure)
.new(@jobs)
.one[:status] Gitlab::Ci::Status::Composite
.new(all_statuses, status_key: 0, allow_failure_key: 1)
.status
else else
CommitStatus.where(id: @jobs).legacy_status CommitStatus.where(id: @jobs).legacy_status
end end
......
...@@ -406,16 +406,21 @@ module Ci ...@@ -406,16 +406,21 @@ module Ci
end end
def legacy_stages_using_composite_status def legacy_stages_using_composite_status
stages = Gitlab::Ci::Status::GroupedStatuses stages = statuses.latest
.new(statuses.latest, :stage, :stage_idx) .group_by(&:stage_idx)
.group(:stage, :stage_idx) .sort_by(&:first)
.sort_by { |stage| stage[:stage_idx] }
stages.map do |stage_idx, jobs|
stage_name = jobs.first.stage
jobs_statuses = jobs.pluck(:status, :allow_failure)
composite_status = Gitlab::Ci::Status::Composite
.new(jobs_statuses, status_key: 0, allow_failure_key: 1)
stages.map do |stage|
Ci::LegacyStage.new(self, Ci::LegacyStage.new(self,
name: stage[:stage], name: stage_name,
status: stage[:status], status: composite_status.status,
warnings: stage[:warnings]) warnings: composite_status.warnings?)
end end
end end
...@@ -423,7 +428,7 @@ module Ci ...@@ -423,7 +428,7 @@ module Ci
if Feature.enabled?(:ci_composite_status, default_enabled: true) if Feature.enabled?(:ci_composite_status, default_enabled: true)
legacy_stages_using_composite_status legacy_stages_using_composite_status
else else
legacy_status_using_sql legacy_stages_using_sql
end end
end end
......
...@@ -61,9 +61,14 @@ module HasStatus ...@@ -61,9 +61,14 @@ module HasStatus
def slow_composite_status def slow_composite_status
if Feature.enabled?(:ci_composite_status, default_enabled: true) if Feature.enabled?(:ci_composite_status, default_enabled: true)
Gitlab::Ci::Status::GroupedStatuses columns = [:status]
.new(all) columns << :allow_failure if column_names.include?('allow_failure')
.one[:status]
all_statuses = all.pluck(*columns)
Gitlab::Ci::Status::Composite
.new(all_statuses, status_key: 0, allow_failure_key: 1)
.status
else else
legacy_status legacy_status
end end
......
...@@ -3,13 +3,18 @@ ...@@ -3,13 +3,18 @@
module Gitlab module Gitlab
module Ci module Ci
module Status module Status
class CompositeStatus class Composite
attr_reader :warnings attr_reader :warnings
def initialize(all_statuses) # This class accepts an array of arrays or array of hashes
# The `status_key` and `allow_failure_key` define an index
# or key in each entry
def initialize(all_statuses, status_key:, allow_failure_key:)
@count = 0 @count = 0
@warnings = 0 @warnings = 0
@status_set = Set.new @status_set = Set.new
@status_key = status_key
@allow_failure_key = allow_failure_key
build_status_set(all_statuses) build_status_set(all_statuses)
end end
...@@ -19,29 +24,29 @@ module Gitlab ...@@ -19,29 +24,29 @@ module Gitlab
when @count.zero? when @count.zero?
nil nil
when none? || only_of?(:skipped) when none? || only_of?(:skipped)
warnings? ? :success : :skipped warnings? ? 'success' : 'skipped'
when only_of?(:success, :skipped) when only_of?(:success, :skipped)
:success 'success'
when only_of?(:created) when only_of?(:created)
:created 'created'
when only_of?(:preparing) when only_of?(:preparing)
:preparing 'preparing'
when only_of?(:success, :skipped, :canceled) when only_of?(:success, :skipped, :canceled)
:canceled 'canceled'
when only_of?(:created, :skipped, :pending) when only_of?(:created, :skipped, :pending)
:pending 'pending'
when any_of?(:running, :pending) when any_of?(:running, :pending)
:running 'running'
when any_of?(:manual) when any_of?(:manual)
:manual 'manual'
when any_of?(:scheduled) when any_of?(:scheduled)
:scheduled 'scheduled'
when any_of?(:preparing) when any_of?(:preparing)
:preparing 'preparing'
when any_of?(:created) when any_of?(:created)
:running 'running'
else else
:failed 'failed'
end end
end end
...@@ -67,19 +72,24 @@ module Gitlab ...@@ -67,19 +72,24 @@ module Gitlab
def build_status_set(all_statuses) def build_status_set(all_statuses)
all_statuses.each do |status| all_statuses.each do |status|
@count += 1 @count += 1
if status[:allow_failure] @warnings += 1 if count_as_warning?(status)
if HasStatus::PASSED_WITH_WARNINGS_STATUSES.include?(status[:status]) next if exclude_from_calculation?(status)
@warnings += 1
end
if HasStatus::EXCLUDE_IGNORED_STATUSES.include?(status[:status]) @status_set.add(status[@status_key].to_sym)
next
end
end
@status_set.add(status[:status].to_sym)
end end
end end
def count_as_warning?(status)
@allow_failure_key &&
status[@allow_failure_key] &&
HasStatus::PASSED_WITH_WARNINGS_STATUSES.include?(status[@status_key])
end
def exclude_from_calculation?(status)
@allow_failure_key &&
status[@allow_failure_key] &&
HasStatus::EXCLUDE_IGNORED_STATUSES.include?(status[@status_key])
end
end end
end end
end end
......
# frozen_string_literal: true
module Gitlab
module Ci
module Status
class GroupedStatuses
def initialize(subject, *keys)
@subject = subject
@keys = keys
end
def one(**query)
validate_keys!(query.keys)
if item_hash = find_one(data_hash, query)
status_for_key(query, item_hash)
else
{}
end
end
def group(*keys)
validate_keys!(keys)
grouped_statuses(data_hash, keys)
end
private
def validate_keys!(keys)
missing_keys = @keys - keys
if missing_keys.present?
raise ArgumentError, "the keys '#{missing_keys.join(',')} are not subset of #{@keys.join(',')}"
end
end
def data_hash
@data_hash ||= hash_from_relation(@subject, @keys)
end
def hash_from_relation(subject, keys)
columns = keys.dup
columns << :status
# we request allow_failure when
# we don't have column_names, or such column does exist
columns << :allow_failure if !subject.respond_to?(:column_names) || subject.column_names.include?('allow_failure')
subject
.pluck(*columns) # rubocop: disable CodeReuse/ActiveRecord
.map { |attrs| columns.zip(attrs).to_h }
end
def find_one(subject, query)
subject.select do |attrs|
query.all? do |key, value|
attrs[key] == value
end
end.presence
end
def grouped_statuses(subject, keys)
subject
.group_by { |attrs| attrs.slice(*keys) }
.map { |key, all_attrs| status_for_key(key, all_attrs) }
end
def status_for_key(key, all_attrs)
composite_status = Gitlab::Ci::Status::CompositeStatus.new(all_attrs)
key.merge(
status: composite_status.status.to_s,
warnings: composite_status.warnings?
)
end
end
end
end
end
require 'spec_helper' require 'spec_helper'
describe Gitlab::Ci::Status::CompositeStatus do describe Gitlab::Ci::Status::Composite do
set(:pipeline) { create(:ci_pipeline) } set(:pipeline) { create(:ci_pipeline) }
let(:composite_status) { described_class.new(all_statuses) } let(:composite_status) do
described_class.new(
all_statuses.pluck(:status, :allow_failure),
status_key: 0,
allow_failure_key: 0
)
end
before(:all) do before(:all) do
@statuses = HasStatus::STATUSES_ENUM.map do |status, idx| @statuses = HasStatus::STATUSES_ENUM.map do |status, idx|
...@@ -16,11 +22,11 @@ describe Gitlab::Ci::Status::CompositeStatus do ...@@ -16,11 +22,11 @@ describe Gitlab::Ci::Status::CompositeStatus do
end end
describe '#status' do describe '#status' do
subject { composite_status.status.to_s } subject { composite_status.status }
shared_examples 'compares composite with SQL status' do shared_examples 'compares composite with SQL status' do
it 'returns exactly the same result' do it 'returns exactly the same result' do
is_expected.to eq(Ci::Build.where(id: all_statuses).legacy_status.to_s) is_expected.to eq(Ci::Build.where(id: all_statuses).legacy_status)
end end
end end
......
...@@ -22,6 +22,32 @@ describe Ci::Group do ...@@ -22,6 +22,32 @@ describe Ci::Group do
end end
end end
describe '#status' do
let(:jobs) do
[create(:ci_build, :failed)]
end
context 'when ci_composite_status is enabled' do
before do
stub_feature_flags(ci_composite_status: true)
end
it 'returns a failed status' do
expect(subject.status).to eq('failed')
end
end
context 'when ci_composite_status is disabled' do
before do
stub_feature_flags(ci_composite_status: false)
end
it 'returns a failed status' do
expect(subject.status).to eq('failed')
end
end
end
describe '#detailed_status' do describe '#detailed_status' do
context 'when there is only one item in the group' do context 'when there is only one item in the group' do
it 'calls the status from the object itself' do it 'calls the status from the object itself' do
......
...@@ -1136,59 +1136,71 @@ describe Ci::Pipeline, :mailer do ...@@ -1136,59 +1136,71 @@ describe Ci::Pipeline, :mailer do
end end
describe '#legacy_stages' do describe '#legacy_stages' do
using RSpec::Parameterized::TableSyntax
subject { pipeline.legacy_stages } subject { pipeline.legacy_stages }
context 'stages list' do where(:ci_composite_status) do
it 'returns ordered list of stages' do [false, true]
expect(subject.map(&:name)).to eq(%w[build test deploy])
end
end end
context 'stages with statuses' do with_them do
let(:statuses) do before do
subject.map { |stage| [stage.name, stage.status] } stub_feature_flags(ci_composite_status: ci_composite_status)
end end
it 'returns list of stages with correct statuses' do context 'stages list' do
expect(statuses).to eq([%w(build failed), it 'returns ordered list of stages' do
%w(test success), expect(subject.map(&:name)).to eq(%w[build test deploy])
%w(deploy running)]) end
end end
context 'when commit status is retried' do context 'stages with statuses' do
before do let(:statuses) do
create(:commit_status, pipeline: pipeline, subject.map { |stage| [stage.name, stage.status] }
stage: 'build',
name: 'mac',
stage_idx: 0,
status: 'success')
pipeline.process!
end end
it 'ignores the previous state' do it 'returns list of stages with correct statuses' do
expect(statuses).to eq([%w(build success), expect(statuses).to eq([%w(build failed),
%w(test success), %w(test success),
%w(deploy running)]) %w(deploy running)])
end end
end
end
context 'when there is a stage with warnings' do context 'when commit status is retried' do
before do before do
create(:commit_status, pipeline: pipeline, create(:commit_status, pipeline: pipeline,
stage: 'deploy', stage: 'build',
name: 'prod:2', name: 'mac',
stage_idx: 2, stage_idx: 0,
status: 'failed', status: 'success')
allow_failure: true)
pipeline.process!
end
it 'ignores the previous state' do
expect(statuses).to eq([%w(build success),
%w(test success),
%w(deploy running)])
end
end
end end
it 'populates stage with correct number of warnings' do context 'when there is a stage with warnings' do
deploy_stage = pipeline.legacy_stages.third before do
create(:commit_status, pipeline: pipeline,
stage: 'deploy',
name: 'prod:2',
stage_idx: 2,
status: 'failed',
allow_failure: true)
end
expect(deploy_stage).not_to receive(:statuses) it 'populates stage with correct number of warnings' do
expect(deploy_stage).to have_warnings deploy_stage = pipeline.legacy_stages.third
expect(deploy_stage).not_to receive(:statuses)
expect(deploy_stage).to have_warnings
end
end end
end end
end end
......
...@@ -3,8 +3,10 @@ ...@@ -3,8 +3,10 @@
require 'spec_helper' require 'spec_helper'
describe HasStatus do describe HasStatus do
describe '.legacy_status' do describe '.slow_composite_status' do
subject { CommitStatus.legacy_status } using RSpec::Parameterized::TableSyntax
subject { CommitStatus.slow_composite_status }
shared_examples 'build status summary' do shared_examples 'build status summary' do
context 'all successful' do context 'all successful' do
...@@ -166,16 +168,26 @@ describe HasStatus do ...@@ -166,16 +168,26 @@ describe HasStatus do
end end
end end
context 'ci build statuses' do where(:ci_composite_status) do
let(:type) { :ci_build } [false, true]
it_behaves_like 'build status summary'
end end
context 'generic commit statuses' do with_them do
let(:type) { :generic_commit_status } before do
stub_feature_flags(ci_composite_status: ci_composite_status)
end
context 'ci build statuses' do
let(:type) { :ci_build }
it_behaves_like 'build status summary' it_behaves_like 'build status summary'
end
context 'generic commit statuses' do
let(:type) { :generic_commit_status }
it_behaves_like 'build status summary'
end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment