diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/ci/build.rb | 28 | ||||
-rw-r--r-- | app/models/ci/job_artifact.rb | 20 | ||||
-rw-r--r-- | app/models/ci/pipeline.rb | 12 | ||||
-rw-r--r-- | app/models/merge_request.rb | 42 |
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 |