Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/ci/build.rb28
-rw-r--r--app/models/ci/job_artifact.rb20
-rw-r--r--app/models/ci/pipeline.rb12
-rw-r--r--app/models/merge_request.rb42
4 files changed, 102 insertions, 0 deletions
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 93bbee49c09..bf931a04592 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -69,6 +69,11 @@ module Ci
where('NOT EXISTS (?)', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').trace)
end
+ scope :with_test_reports, ->() do
+ includes(:job_artifacts_junit) # Prevent N+1 problem when iterating each ci_job_artifact row
+ .where('EXISTS (?)', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').test_reports)
+ end
+
scope :with_artifacts_stored_locally, -> { with_artifacts_archive.where(artifacts_file_store: [nil, LegacyArtifactUploader::Store::LOCAL]) }
scope :with_artifacts_not_expired, ->() { with_artifacts_archive.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
scope :with_expired_artifacts, ->() { with_artifacts_archive.where('artifacts_expire_at < ?', Time.now) }
@@ -627,8 +632,31 @@ module Ci
running? && runner_session_url.present?
end
+ def collect_test_reports!(test_reports)
+ raise ArgumentError, 'build does not have test reports' unless has_test_reports?
+
+ test_reports.get_suite(group_name).tap do |test_suite|
+ each_test_report do |file_type, blob|
+ parse_test_report!(test_suite, file_type, blob)
+ end
+ end
+ end
+
private
+ def each_test_report
+ Ci::JobArtifact::TEST_REPORT_FILE_TYPES.each do |file_type|
+ public_send("job_artifacts_#{file_type}").each_blob do |blob| # rubocop:disable GitlabSecurity/PublicSend
+ yield file_type, blob
+ end
+ end
+ end
+
+ def parse_test_report!(test_suite, file_type, blob)
+ "Gitlab::Ci::Parsers::#{file_type.capitalize}Parser".constantize
+ .new(blob).parse!(test_suite)
+ end
+
def update_artifacts_size
self.artifacts_size = legacy_artifacts_file&.size
end
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index 054b714f8ac..50f375f0804 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -4,6 +4,8 @@ module Ci
include ObjectStorage::BackgroundMove
extend Gitlab::Ci::Model
+ NotSupportedAdapterError = Class.new(StandardError)
+
TEST_REPORT_FILE_TYPES = %w[junit].freeze
DEFAULT_FILE_NAMES = { junit: 'junit.xml' }.freeze
TYPE_AND_FORMAT_PAIRS = { archive: :zip, metadata: :gzip, trace: :raw, junit: :gzip }.freeze
@@ -44,6 +46,10 @@ module Ci
gzip: 3
}
+ FILE_FORMAT_ADAPTERS = {
+ gzip: Gitlab::Ci::Build::Artifacts::GzipFileAdapter
+ }.freeze
+
def valid_file_format?
unless TYPE_AND_FORMAT_PAIRS[self.file_type&.to_sym] == self.file_format&.to_sym
errors.add(:file_format, 'Invalid file format with specified file type')
@@ -75,8 +81,22 @@ module Ci
end
end
+ def each_blob(&blk)
+ unless file_format_adapter_class
+ raise NotSupportedAdapterError, 'This file format requires a dedicated adapter'
+ end
+
+ file.open do |stream|
+ file_format_adapter_class.new(stream).each_blob(&blk)
+ end
+ end
+
private
+ def file_format_adapter_class
+ FILE_FORMAT_ADAPTERS[file_format.to_sym]
+ end
+
def set_size
self.size = file.size
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index e5caa3ffa41..f06a6416307 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -603,6 +603,18 @@ module Ci
@latest_builds_with_artifacts ||= builds.latest.with_artifacts_archive.to_a
end
+ def has_test_reports?
+ complete? && builds.with_test_reports.any?
+ end
+
+ def test_reports
+ Gitlab::Ci::Reports::TestReports.new.tap do |test_reports|
+ builds.with_test_reports.each do |build|
+ build.collect_test_reports!(test_reports)
+ end
+ end
+ end
+
private
def ci_yaml_from_repo
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 06642c15585..9f8ebd9b4ca 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -13,6 +13,11 @@ class MergeRequest < ActiveRecord::Base
include ThrottledTouch
include Gitlab::Utils::StrongMemoize
include LabelEventable
+ include ReactiveCaching
+
+ self.reactive_cache_key = ->(model) { [model.project.id, model.iid] }
+ self.reactive_cache_refresh_interval = 1.hour
+ self.reactive_cache_lifetime = 1.hour
ignore_column :locked_at,
:ref_fetched,
@@ -1012,6 +1017,37 @@ class MergeRequest < ActiveRecord::Base
.order(id: :desc)
end
+ def has_test_reports?
+ actual_head_pipeline&.has_test_reports?
+ end
+
+ def compare_test_reports
+ unless actual_head_pipeline && actual_head_pipeline.has_test_reports?
+ return { status: :error, status_reason: 'head pipeline does not have test reports' }
+ end
+
+ with_reactive_cache(base_pipeline&.iid, actual_head_pipeline.iid) { |data| data } || { status: :parsing }
+ end
+
+ def calculate_reactive_cache(base_pipeline_iid, head_pipeline_iid)
+ begin
+ base_pipeline = project.pipelines.find_by_iid(base_pipeline_iid)
+ head_pipeline = project.pipelines.find_by_iid(head_pipeline_iid)
+
+ comparer = Gitlab::Ci::Reports::TestReportsComparer
+ .new(base_pipeline&.test_reports, head_pipeline.test_reports)
+
+ {
+ status: :parsed,
+ data: TestReportsComparerSerializer
+ .new(project: project)
+ .represent(comparer).to_json
+ }
+ rescue => e
+ { status: :error, status_reason: e.message }
+ end
+ end
+
def all_commits
# MySQL doesn't support LIMIT in a subquery.
diffs_relation = if Gitlab::Database.postgresql?
@@ -1124,6 +1160,12 @@ class MergeRequest < ActiveRecord::Base
true
end
+ def base_pipeline
+ @base_pipeline ||= project.pipelines
+ .order(id: :desc)
+ .find_by(sha: diff_base_sha)
+ end
+
def discussions_rendered_on_frontend?
true
end