summaryrefslogtreecommitdiff
path: root/app/models/ci
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 15:44:42 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 15:44:42 +0000
commit4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch)
tree5423a1c7516cffe36384133ade12572cf709398d /app/models/ci
parente570267f2f6b326480d284e0164a6464ba4081bc (diff)
downloadgitlab-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.rb5
-rw-r--r--app/models/ci/build.rb19
-rw-r--r--app/models/ci/build_dependencies.rb51
-rw-r--r--app/models/ci/build_need.rb10
-rw-r--r--app/models/ci/build_runner_session.rb3
-rw-r--r--app/models/ci/build_trace_chunk.rb3
-rw-r--r--app/models/ci/commit_with_pipeline.rb18
-rw-r--r--app/models/ci/daily_build_group_report_result.rb2
-rw-r--r--app/models/ci/deleted_object.rb2
-rw-r--r--app/models/ci/job_artifact.rb18
-rw-r--r--app/models/ci/persistent_ref.rb6
-rw-r--r--app/models/ci/pipeline.rb34
-rw-r--r--app/models/ci/pipeline_artifact.rb4
-rw-r--r--app/models/ci/pipeline_schedule.rb30
-rw-r--r--app/models/ci/processable.rb4
-rw-r--r--app/models/ci/runner.rb9
-rw-r--r--app/models/ci/runner_namespace.rb5
-rw-r--r--app/models/ci/runner_project.rb5
-rw-r--r--app/models/ci/stage.rb2
-rw-r--r--app/models/ci/trigger.rb2
-rw-r--r--app/models/ci/unit_test.rb1
-rw-r--r--app/models/ci/unit_test_failure.rb2
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(