summaryrefslogtreecommitdiff
path: root/app/models/ci/build.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/ci/build.rb')
-rw-r--r--app/models/ci/build.rb132
1 files changed, 100 insertions, 32 deletions
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 46fc87a6ea8..fdfffd9b0cd 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -11,6 +11,7 @@ module Ci
include Importable
include Ci::HasRef
include IgnorableColumns
+ include TaggableQueries
BuildArchivedError = Class.new(StandardError)
@@ -37,6 +38,8 @@ module Ci
has_one :deployment, as: :deployable, class_name: 'Deployment'
has_one :pending_state, class_name: 'Ci::BuildPendingState', inverse_of: :build
+ has_one :queuing_entry, class_name: 'Ci::PendingBuild', foreign_key: :build_id
+ has_one :runtime_metadata, class_name: 'Ci::RunningBuild', foreign_key: :build_id
has_many :trace_sections, class_name: 'Ci::BuildTraceSection'
has_many :trace_chunks, class_name: 'Ci::BuildTraceChunk', foreign_key: :build_id, inverse_of: :build
has_many :report_results, class_name: 'Ci::BuildReportResult', inverse_of: :build
@@ -88,16 +91,6 @@ 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
@@ -212,6 +205,8 @@ module Ci
end
scope :with_coverage, -> { where.not(coverage: nil) }
+ scope :without_coverage, -> { where(coverage: nil) }
+ scope :with_coverage_regex, -> { where.not(coverage_regex: nil) }
scope :for_project, -> (project_id) { where(project_id: project_id) }
@@ -222,6 +217,8 @@ module Ci
before_save :ensure_token
before_destroy { unscoped_project }
+ after_save :stick_build_if_status_changed
+
after_create unless: :importing? do |build|
run_after_commit { BuildHooksWorker.perform_async(build.id) }
end
@@ -304,12 +301,35 @@ module Ci
end
end
- after_transition any => [:pending] do |build|
+ # rubocop:disable CodeReuse/ServiceClass
+ after_transition any => [:pending] do |build, transition|
+ Ci::UpdateBuildQueueService.new.push(build, transition)
+
build.run_after_commit do
BuildQueueWorker.perform_async(id)
end
end
+ after_transition pending: any do |build, transition|
+ Ci::UpdateBuildQueueService.new.pop(build, transition)
+ end
+
+ after_transition any => [:running] do |build, transition|
+ Ci::UpdateBuildQueueService.new.track(build, transition)
+ end
+
+ after_transition running: any do |build, transition|
+ Ci::UpdateBuildQueueService.new.untrack(build, transition)
+
+ Ci::BuildRunnerSession.where(build: build).delete_all
+ end
+
+ # rubocop:enable CodeReuse/ServiceClass
+ #
+ after_transition pending: :running do |build|
+ build.ensure_metadata.update_timeout_state
+ end
+
after_transition pending: :running do |build|
build.deployment&.run
@@ -362,14 +382,6 @@ module Ci
end
end
- after_transition pending: :running do |build|
- build.ensure_metadata.update_timeout_state
- end
-
- after_transition running: any do |build|
- Ci::BuildRunnerSession.where(build: build).delete_all
- end
-
after_transition any => [:skipped, :canceled] do |build, transition|
if transition.to_name == :skipped
build.deployment&.skip
@@ -379,6 +391,33 @@ module Ci
end
end
+ def self.build_matchers(project)
+ unique_params = [
+ :protected,
+ Arel.sql("(#{arel_tag_names_array.to_sql})")
+ ]
+
+ group(*unique_params).pluck('array_agg(id)', *unique_params).map do |values|
+ Gitlab::Ci::Matching::BuildMatcher.new({
+ build_ids: values[0],
+ protected: values[1],
+ tag_list: values[2],
+ project: project
+ })
+ end
+ end
+
+ def build_matcher
+ strong_memoize(:build_matcher) do
+ Gitlab::Ci::Matching::BuildMatcher.new({
+ protected: protected?,
+ tag_list: tag_list,
+ build_ids: [id],
+ project: project
+ })
+ end
+ end
+
def auto_retry_allowed?
auto_retry.allowed?
end
@@ -442,7 +481,13 @@ module Ci
end
def retryable?
- !archived? && (success? || failed? || canceled?)
+ if Feature.enabled?(:prevent_retry_of_retried_jobs, project, default_enabled: :yaml)
+ return false if retried? || archived?
+
+ success? || failed? || canceled?
+ else
+ !archived? && (success? || failed? || canceled?)
+ end
end
def retries_count
@@ -560,6 +605,8 @@ module Ci
variables.concat(persisted_environment.predefined_variables)
+ variables.append(key: 'CI_ENVIRONMENT_ACTION', value: environment_action)
+
# Here we're passing unexpanded environment_url for runner to expand,
# and we need to make sure that CI_ENVIRONMENT_NAME and
# CI_ENVIRONMENT_SLUG so on are available for the URL be expanded.
@@ -716,22 +763,14 @@ module Ci
end
def any_runners_online?
- if Feature.enabled?(:runners_cached_states, project, default_enabled: :yaml)
- cache_for_online_runners do
- project.any_online_runners? { |runner| runner.match_build_if_online?(self) }
- end
- else
- project.any_active_runners? { |runner| runner.match_build_if_online?(self) }
+ cache_for_online_runners do
+ project.any_online_runners? { |runner| runner.match_build_if_online?(self) }
end
end
def any_runners_available?
- if Feature.enabled?(:runners_cached_states, project, default_enabled: :yaml)
- cache_for_available_runners do
- project.active_runners.exists?
- end
- else
- project.any_active_runners?
+ cache_for_available_runners do
+ project.active_runners.exists?
end
end
@@ -1039,6 +1078,28 @@ module Ci
options.dig(:allow_failure_criteria, :exit_codes).present?
end
+ def create_queuing_entry!
+ ::Ci::PendingBuild.upsert_from_build!(self)
+ end
+
+ ##
+ # We can have only one queuing entry or running build tracking entry,
+ # because there is a unique index on `build_id` in each table, but we need
+ # a relation to remove these entries more efficiently in a single statement
+ # without actually loading data.
+ #
+ def all_queuing_entries
+ ::Ci::PendingBuild.where(build_id: self.id)
+ end
+
+ def all_runtime_metadata
+ ::Ci::RunningBuild.where(build_id: self.id)
+ end
+
+ def shared_runner_build?
+ runner&.instance_type?
+ end
+
protected
def run_status_commit_hooks!
@@ -1049,6 +1110,13 @@ module Ci
private
+ def stick_build_if_status_changed
+ return unless saved_change_to_status?
+ return unless running?
+
+ ::Gitlab::Database::LoadBalancing::Sticking.stick(:build, id)
+ end
+
def status_commit_hooks
@status_commit_hooks ||= []
end