diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 15:44:42 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 15:44:42 +0000 |
commit | 4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch) | |
tree | 5423a1c7516cffe36384133ade12572cf709398d /app/models/ci | |
parent | e570267f2f6b326480d284e0164a6464ba4081bc (diff) | |
download | gitlab-ce-4555e1b21c365ed8303ffb7a3325d773c9b8bf31.tar.gz |
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'app/models/ci')
-rw-r--r-- | app/models/ci/bridge.rb | 5 | ||||
-rw-r--r-- | app/models/ci/build.rb | 19 | ||||
-rw-r--r-- | app/models/ci/build_dependencies.rb | 51 | ||||
-rw-r--r-- | app/models/ci/build_need.rb | 10 | ||||
-rw-r--r-- | app/models/ci/build_runner_session.rb | 3 | ||||
-rw-r--r-- | app/models/ci/build_trace_chunk.rb | 3 | ||||
-rw-r--r-- | app/models/ci/commit_with_pipeline.rb | 18 | ||||
-rw-r--r-- | app/models/ci/daily_build_group_report_result.rb | 2 | ||||
-rw-r--r-- | app/models/ci/deleted_object.rb | 2 | ||||
-rw-r--r-- | app/models/ci/job_artifact.rb | 18 | ||||
-rw-r--r-- | app/models/ci/persistent_ref.rb | 6 | ||||
-rw-r--r-- | app/models/ci/pipeline.rb | 34 | ||||
-rw-r--r-- | app/models/ci/pipeline_artifact.rb | 4 | ||||
-rw-r--r-- | app/models/ci/pipeline_schedule.rb | 30 | ||||
-rw-r--r-- | app/models/ci/processable.rb | 4 | ||||
-rw-r--r-- | app/models/ci/runner.rb | 9 | ||||
-rw-r--r-- | app/models/ci/runner_namespace.rb | 5 | ||||
-rw-r--r-- | app/models/ci/runner_project.rb | 5 | ||||
-rw-r--r-- | app/models/ci/stage.rb | 2 | ||||
-rw-r--r-- | app/models/ci/trigger.rb | 2 | ||||
-rw-r--r-- | app/models/ci/unit_test.rb | 1 | ||||
-rw-r--r-- | app/models/ci/unit_test_failure.rb | 2 |
22 files changed, 157 insertions, 78 deletions
diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb index ca400cebe4e..352229c64da 100644 --- a/app/models/ci/bridge.rb +++ b/app/models/ci/bridge.rb @@ -163,6 +163,9 @@ module Ci def expanded_environment_name end + def instantized_environment + end + def execute_hooks raise NotImplementedError end @@ -248,4 +251,4 @@ module Ci end end -::Ci::Bridge.prepend_if_ee('::EE::Ci::Bridge') +::Ci::Bridge.prepend_mod_with('Ci::Bridge') diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 3d8e9f4c126..46fc87a6ea8 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -62,6 +62,9 @@ module Ci delegate :gitlab_deploy_token, to: :project delegate :trigger_short_token, to: :trigger_request, allow_nil: true + ignore_columns :id_convert_to_bigint, remove_with: '14.1', remove_after: '2021-07-22' + ignore_columns :stage_id_convert_to_bigint, remove_with: '14.1', remove_after: '2021-07-22' + ## # Since Gitlab 11.5, deployments records started being created right after # `ci_builds` creation. We can look up a relevant `environment` through @@ -85,6 +88,16 @@ module Ci end end + # Initializing an object instead of fetching `persisted_environment` for avoiding unnecessary queries. + # We're planning to introduce a direct relationship between build and environment + # in https://gitlab.com/gitlab-org/gitlab/-/issues/326445 to let us to preload + # in batch. + def instantized_environment + return unless has_environment? + + ::Environment.new(project: self.project, name: self.expanded_environment_name) + end + serialize :options # rubocop:disable Cop/ActiveRecordSerialize serialize :yaml_variables, Gitlab::Serializer::Ci::Variables # rubocop:disable Cop/ActiveRecordSerialize @@ -330,7 +343,7 @@ module Ci begin build.deployment.drop! - rescue => e + rescue StandardError => e Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e, build_id: build.id) end @@ -1047,7 +1060,7 @@ module Ci end def build_data - @build_data ||= Gitlab::DataBuilder::Build.build(self) + strong_memoize(:build_data) { Gitlab::DataBuilder::Build.build(self) } end def successful_deployment_status @@ -1141,4 +1154,4 @@ module Ci end end -Ci::Build.prepend_if_ee('EE::Ci::Build') +Ci::Build.prepend_mod_with('Ci::Build') diff --git a/app/models/ci/build_dependencies.rb b/app/models/ci/build_dependencies.rb index 8ae921f1416..716d919487d 100644 --- a/app/models/ci/build_dependencies.rb +++ b/app/models/ci/build_dependencies.rb @@ -14,14 +14,33 @@ module Ci (local + cross_pipeline + cross_project).uniq end + def invalid_local + local.reject(&:valid_dependency?) + end + + def valid? + valid_local? && valid_cross_pipeline? && valid_cross_project? + end + + private + + # Dependencies can only be of Ci::Build type because only builds + # can create artifacts + def model_class + ::Ci::Build + end + # Dependencies local to the given pipeline def local - return [] if no_local_dependencies_specified? - - deps = model_class.where(pipeline_id: processable.pipeline_id).latest - deps = from_previous_stages(deps) - deps = from_needs(deps) - from_dependencies(deps) + strong_memoize(:local) do + next [] if no_local_dependencies_specified? + next [] unless processable.pipeline_id # we don't have any dependency when creating the pipeline + + deps = model_class.where(pipeline_id: processable.pipeline_id).latest + deps = from_previous_stages(deps) + deps = from_needs(deps) + from_dependencies(deps).to_a + end end # Dependencies from the same parent-pipeline hierarchy excluding @@ -37,22 +56,6 @@ module Ci [] end - def invalid_local - local.reject(&:valid_dependency?) - end - - def valid? - valid_local? && valid_cross_pipeline? && valid_cross_project? - end - - private - - # Dependencies can only be of Ci::Build type because only builds - # can create artifacts - def model_class - ::Ci::Build - end - def fetch_dependencies_in_hierarchy deps_specifications = specified_cross_pipeline_dependencies return [] if deps_specifications.empty? @@ -102,8 +105,6 @@ module Ci end def valid_local? - return true unless Gitlab::Ci::Features.validate_build_dependencies?(project) - local.all?(&:valid_dependency?) end @@ -154,4 +155,4 @@ module Ci end end -Ci::BuildDependencies.prepend_if_ee('EE::Ci::BuildDependencies') +Ci::BuildDependencies.prepend_mod_with('Ci::BuildDependencies') diff --git a/app/models/ci/build_need.rb b/app/models/ci/build_need.rb index 7bc70f9f1e1..4a59c25cbb0 100644 --- a/app/models/ci/build_need.rb +++ b/app/models/ci/build_need.rb @@ -5,6 +5,9 @@ module Ci extend Gitlab::Ci::Model include BulkInsertSafe + include IgnorableColumns + + ignore_columns :build_id_convert_to_bigint, remove_with: '14.1', remove_after: '2021-07-22' belongs_to :build, class_name: "Ci::Processable", foreign_key: :build_id, inverse_of: :needs @@ -14,5 +17,12 @@ module Ci scope :scoped_build, -> { where('ci_builds.id=ci_build_needs.build_id') } scope :artifacts, -> { where(artifacts: true) } + + # TODO: Remove once build_id_convert_to_bigint is not an "ignored" column anymore (see .ignore_columns above) + # There is a database-side trigger to populate this column. This is unexpected in the context + # of cloning an instance, e.g. when retrying the job. Hence we exclude the ignored column explicitly here. + def attributes + super.except('build_id_convert_to_bigint') + end end end diff --git a/app/models/ci/build_runner_session.rb b/app/models/ci/build_runner_session.rb index b6196048ca1..2aa856dbc64 100644 --- a/app/models/ci/build_runner_session.rb +++ b/app/models/ci/build_runner_session.rb @@ -5,6 +5,9 @@ module Ci # Data will be removed after transitioning from running to any state. class BuildRunnerSession < ApplicationRecord extend Gitlab::Ci::Model + include IgnorableColumns + + ignore_columns :build_id_convert_to_bigint, remove_with: '14.1', remove_after: '2021-07-22' TERMINAL_SUBPROTOCOL = 'terminal.gitlab.com' DEFAULT_SERVICE_NAME = 'build' diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb index 7e03d709f24..719511bbb8a 100644 --- a/app/models/ci/build_trace_chunk.rb +++ b/app/models/ci/build_trace_chunk.rb @@ -8,6 +8,9 @@ module Ci include ::Checksummable include ::Gitlab::ExclusiveLeaseHelpers include ::Gitlab::OptimisticLocking + include IgnorableColumns + + ignore_columns :build_id_convert_to_bigint, remove_with: '14.1', remove_after: '2021-07-22' belongs_to :build, class_name: "Ci::Build", foreign_key: :build_id diff --git a/app/models/ci/commit_with_pipeline.rb b/app/models/ci/commit_with_pipeline.rb index 7f952fb77a0..dde4b534aaa 100644 --- a/app/models/ci/commit_with_pipeline.rb +++ b/app/models/ci/commit_with_pipeline.rb @@ -18,9 +18,25 @@ class Ci::CommitWithPipeline < SimpleDelegator end end + def lazy_latest_pipeline + BatchLoader.for(sha).batch do |shas, loader| + preload_pipelines = project.ci_pipelines.latest_pipeline_per_commit(shas.compact) + + shas.each do |sha| + pipeline = preload_pipelines[sha] + + loader.call(sha, pipeline) + end + end + end + def latest_pipeline(ref = nil) @latest_pipelines.fetch(ref) do |ref| - @latest_pipelines[ref] = latest_pipeline_for_project(ref, project) + @latest_pipelines[ref] = if ref + latest_pipeline_for_project(ref, project) + else + lazy_latest_pipeline&.itself + end end end diff --git a/app/models/ci/daily_build_group_report_result.rb b/app/models/ci/daily_build_group_report_result.rb index 5dcf575abd7..b46d32474c6 100644 --- a/app/models/ci/daily_build_group_report_result.rb +++ b/app/models/ci/daily_build_group_report_result.rb @@ -30,4 +30,4 @@ module Ci end end -Ci::DailyBuildGroupReportResult.prepend_if_ee('EE::Ci::DailyBuildGroupReportResult') +Ci::DailyBuildGroupReportResult.prepend_mod_with('Ci::DailyBuildGroupReportResult') diff --git a/app/models/ci/deleted_object.rb b/app/models/ci/deleted_object.rb index 2942a153e05..b2a949c9bb5 100644 --- a/app/models/ci/deleted_object.rb +++ b/app/models/ci/deleted_object.rb @@ -29,7 +29,7 @@ module Ci def delete_file_from_storage file.remove! true - rescue => exception + rescue StandardError => exception Gitlab::ErrorTracking.track_exception(exception) false end diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 50e21a1c323..5248a80f710 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -261,6 +261,22 @@ module Ci self.where(project: project).sum(:size) end + ## + # FastDestroyAll concerns + # rubocop: disable CodeReuse/ServiceClass + def self.begin_fast_destroy + service = ::Ci::JobArtifacts::DestroyAssociationsService.new(self) + service.destroy_records + service + end + # rubocop: enable CodeReuse/ServiceClass + + ## + # FastDestroyAll concerns + def self.finalize_fast_destroy(service) + service.update_statistics + end + def local_store? [nil, ::JobArtifactUploader::Store::LOCAL].include?(self.file_store) end @@ -331,4 +347,4 @@ module Ci end end -Ci::JobArtifact.prepend_if_ee('EE::Ci::JobArtifact') +Ci::JobArtifact.prepend_mod_with('Ci::JobArtifact') diff --git a/app/models/ci/persistent_ref.rb b/app/models/ci/persistent_ref.rb index 91163c85a9e..57aa1962bd2 100644 --- a/app/models/ci/persistent_ref.rb +++ b/app/models/ci/persistent_ref.rb @@ -15,13 +15,13 @@ module Ci def exist? ref_exists?(path) - rescue + rescue StandardError false end def create create_ref(sha, path) - rescue => e + rescue StandardError => e Gitlab::ErrorTracking .track_exception(e, pipeline_id: pipeline.id) end @@ -30,7 +30,7 @@ module Ci delete_refs(path) rescue Gitlab::Git::Repository::NoRepository # no-op - rescue => e + rescue StandardError => e Gitlab::ErrorTracking .track_exception(e, pipeline_id: pipeline.id) end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index c9ab69317e1..f0a2c074584 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -17,6 +17,7 @@ module Ci include FromUnion include UpdatedAtFilterable include EachBatch + include FastDestroyAll::Helpers MAX_OPEN_MERGE_REQUESTS_REFS = 4 @@ -70,7 +71,9 @@ module Ci has_many :deployments, through: :builds has_many :environments, -> { distinct }, through: :deployments has_many :latest_builds, -> { latest.with_project_and_metadata }, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'Ci::Build' - has_many :downloadable_artifacts, -> { not_expired.downloadable.with_job }, through: :latest_builds, source: :job_artifacts + has_many :downloadable_artifacts, -> do + not_expired.or(where_exists(::Ci::Pipeline.artifacts_locked.where('ci_pipelines.id = ci_builds.commit_id'))).downloadable.with_job + end, through: :latest_builds, source: :job_artifacts has_many :messages, class_name: 'Ci::PipelineMessage', inverse_of: :pipeline @@ -124,6 +127,8 @@ module Ci after_create :keep_around_commits, unless: :importing? + use_fast_destroy :job_artifacts + # We use `Enums::Ci::Pipeline.sources` here so that EE can more easily extend # this `Hash` with new values. enum_with_nil source: Enums::Ci::Pipeline.sources @@ -908,7 +913,7 @@ module Ci def same_family_pipeline_ids ::Gitlab::Ci::PipelineObjectHierarchy.new( - self.class.where(id: root_ancestor), options: { same_project: true } + self.class.default_scoped.where(id: root_ancestor), options: { same_project: true } ).base_and_descendants.select(:id) end @@ -1093,6 +1098,8 @@ module Ci merge_request.modified_paths elsif branch_updated? push_details.modified_paths + elsif external_pull_request? && ::Feature.enabled?(:ci_modified_paths_of_external_prs, project, default_enabled: :yaml) + external_pull_request.modified_paths end end end @@ -1117,6 +1124,10 @@ module Ci merge_request_id.present? end + def external_pull_request? + external_pull_request_id.present? + end + def detached_merge_request_pipeline? merge_request? && target_sha.nil? end @@ -1210,11 +1221,18 @@ module Ci # We need `base_and_ancestors` in a specific order to "break" when needed. # If we use `find_each`, then the order is broken. # rubocop:disable Rails/FindEach - def reset_ancestor_bridges! - base_and_ancestors.includes(:source_bridge).each do |pipeline| - break unless pipeline.bridge_waiting? + def reset_source_bridge!(current_user) + if ::Feature.enabled?(:ci_reset_bridge_with_subsequent_jobs, project, default_enabled: :yaml) + return unless bridge_waiting? - pipeline.source_bridge.pending! + source_bridge.pending! + Ci::AfterRequeueJobService.new(project, current_user).execute(source_bridge) # rubocop:disable CodeReuse/ServiceClass + else + base_and_ancestors.includes(:source_bridge).each do |pipeline| + break unless pipeline.bridge_waiting? + + pipeline.source_bridge.pending! + end end end # rubocop:enable Rails/FindEach @@ -1237,8 +1255,6 @@ module Ci private def add_message(severity, content) - return unless Gitlab::Ci::Features.store_pipeline_messages?(project) - messages.build(severity: severity, content: content) end @@ -1294,4 +1310,4 @@ module Ci end end -Ci::Pipeline.prepend_if_ee('EE::Ci::Pipeline') +Ci::Pipeline.prepend_mod_with('Ci::Pipeline') diff --git a/app/models/ci/pipeline_artifact.rb b/app/models/ci/pipeline_artifact.rb index 9dfe4252e95..889c5d094a7 100644 --- a/app/models/ci/pipeline_artifact.rb +++ b/app/models/ci/pipeline_artifact.rb @@ -40,6 +40,8 @@ module Ci code_quality_mr_diff: 2 } + scope :unlocked, -> { joins(:pipeline).merge(::Ci::Pipeline.unlocked) } + class << self def report_exists?(file_type) return false unless REPORT_TYPES.key?(file_type) @@ -58,4 +60,4 @@ module Ci end end -Ci::PipelineArtifact.prepend_ee_mod +Ci::PipelineArtifact.prepend_mod diff --git a/app/models/ci/pipeline_schedule.rb b/app/models/ci/pipeline_schedule.rb index 3c17246bc34..9e5d517c1fe 100644 --- a/app/models/ci/pipeline_schedule.rb +++ b/app/models/ci/pipeline_schedule.rb @@ -5,7 +5,7 @@ module Ci extend Gitlab::Ci::Model include Importable include StripAttribute - include Schedulable + include CronSchedulable include Limitable include EachBatch @@ -51,38 +51,16 @@ module Ci update_attribute(:active, false) end - ## - # The `next_run_at` column is set to the actual execution date of `PipelineScheduleWorker`. - # This way, a schedule like `*/1 * * * *` won't be triggered in a short interval - # when PipelineScheduleWorker runs irregularly by Sidekiq Memory Killer. - def set_next_run_at - now = Time.zone.now - ideal_next_run = ideal_next_run_from(now) - - self.next_run_at = if ideal_next_run == cron_worker_next_run_from(now) - ideal_next_run - else - cron_worker_next_run_from(ideal_next_run) - end - end - def job_variables variables&.map(&:to_runner_variable) || [] end private - def ideal_next_run_from(start_time) - Gitlab::Ci::CronParser.new(cron, cron_timezone) - .next_time_from(start_time) - end - - def cron_worker_next_run_from(start_time) - Gitlab::Ci::CronParser.new(Settings.cron_jobs['pipeline_schedule_worker']['cron'], - Time.zone.name) - .next_time_from(start_time) + def worker_cron_expression + Settings.cron_jobs['pipeline_schedule_worker']['cron'] end end end -Ci::PipelineSchedule.prepend_if_ee('EE::Ci::PipelineSchedule') +Ci::PipelineSchedule.prepend_mod_with('Ci::PipelineSchedule') diff --git a/app/models/ci/processable.rb b/app/models/ci/processable.rb index 3b61840805a..15c57550159 100644 --- a/app/models/ci/processable.rb +++ b/app/models/ci/processable.rb @@ -120,6 +120,10 @@ module Ci raise NotImplementedError end + def instantized_environment + raise NotImplementedError + end + override :all_met_to_become_pending? def all_met_to_become_pending? super && !with_resource_group? diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 05126853e0f..8c877c2b818 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -39,16 +39,16 @@ module Ci AVAILABLE_TYPES_LEGACY = %w[specific shared].freeze AVAILABLE_TYPES = runner_types.keys.freeze - AVAILABLE_STATUSES = %w[active paused online offline].freeze + AVAILABLE_STATUSES = %w[active paused online offline not_connected].freeze AVAILABLE_SCOPES = (AVAILABLE_TYPES_LEGACY + AVAILABLE_TYPES + AVAILABLE_STATUSES).freeze FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level maximum_timeout_human_readable].freeze MINUTES_COST_FACTOR_FIELDS = %i[public_projects_minutes_cost_factor private_projects_minutes_cost_factor].freeze has_many :builds - has_many :runner_projects, inverse_of: :runner, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :runner_projects, inverse_of: :runner, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :runner_projects - has_many :runner_namespaces, inverse_of: :runner + has_many :runner_namespaces, inverse_of: :runner, autosave: true has_many :groups, through: :runner_namespaces has_one :last_build, -> { order('id DESC') }, class_name: 'Ci::Build' @@ -65,6 +65,7 @@ module Ci # did `contacted_at <= ?` the query would effectively have to do a seq # scan. scope :offline, -> { where.not(id: online) } + scope :not_connected, -> { where(contacted_at: nil) } scope :ordered, -> { order(id: :desc) } scope :with_recent_runner_queue, -> { where('contacted_at > ?', recent_queue_deadline) } @@ -405,4 +406,4 @@ module Ci end end -Ci::Runner.prepend_if_ee('EE::Ci::Runner') +Ci::Runner.prepend_mod_with('Ci::Runner') diff --git a/app/models/ci/runner_namespace.rb b/app/models/ci/runner_namespace.rb index e6c1899c89d..f819dda207d 100644 --- a/app/models/ci/runner_namespace.rb +++ b/app/models/ci/runner_namespace.rb @@ -3,6 +3,11 @@ module Ci class RunnerNamespace < ApplicationRecord extend Gitlab::Ci::Model + include Limitable + + self.limit_name = 'ci_registered_group_runners' + self.limit_scope = :group + self.limit_feature_flag = :ci_runner_limits belongs_to :runner, inverse_of: :runner_namespaces belongs_to :namespace, inverse_of: :runner_namespaces, class_name: '::Namespace' diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb index f5bd50dc5a3..c26b8183b52 100644 --- a/app/models/ci/runner_project.rb +++ b/app/models/ci/runner_project.rb @@ -3,6 +3,11 @@ module Ci class RunnerProject < ApplicationRecord extend Gitlab::Ci::Model + include Limitable + + self.limit_name = 'ci_registered_project_runners' + self.limit_scope = :project + self.limit_feature_flag = :ci_runner_limits belongs_to :runner, inverse_of: :runner_projects belongs_to :project, inverse_of: :runner_projects diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index 5ae97dcd495..ef920b2d589 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -41,7 +41,7 @@ module Ci self.position = statuses.select(:stage_idx) .where.not(stage_idx: nil) .group(:stage_idx) - .order('COUNT(*) DESC') + .order('COUNT(id) DESC') .first&.stage_idx.to_i end diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index 85cb3f5b46a..6e27abb9f5b 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -37,4 +37,4 @@ module Ci end end -Ci::Trigger.prepend_if_ee('EE::Ci::Trigger') +Ci::Trigger.prepend_mod_with('Ci::Trigger') diff --git a/app/models/ci/unit_test.rb b/app/models/ci/unit_test.rb index 81623b4f6ad..9fddd9c6002 100644 --- a/app/models/ci/unit_test.rb +++ b/app/models/ci/unit_test.rb @@ -14,6 +14,7 @@ module Ci belongs_to :project scope :by_project_and_keys, -> (project, keys) { where(project_id: project.id, key_hash: keys) } + scope :deletable, -> { where('NOT EXISTS (?)', Ci::UnitTestFailure.select(1).where("#{Ci::UnitTestFailure.table_name}.unit_test_id = #{table_name}.id")) } class << self def find_or_create_by_batch(project, unit_test_attrs) diff --git a/app/models/ci/unit_test_failure.rb b/app/models/ci/unit_test_failure.rb index 653a56bd2b3..480f9cefb8e 100644 --- a/app/models/ci/unit_test_failure.rb +++ b/app/models/ci/unit_test_failure.rb @@ -11,6 +11,8 @@ module Ci belongs_to :unit_test, class_name: "Ci::UnitTest", foreign_key: :unit_test_id belongs_to :build, class_name: "Ci::Build", foreign_key: :build_id + scope :deletable, -> { where('failed_at < ?', REPORT_WINDOW.ago) } + def self.recent_failures_count(project:, unit_test_keys:, date_range: REPORT_WINDOW.ago..Time.current) joins(:unit_test) .where( |