summaryrefslogtreecommitdiff
path: root/app/models/ci
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/ci')
-rw-r--r--app/models/ci/bridge.rb14
-rw-r--r--app/models/ci/build.rb56
-rw-r--r--app/models/ci/build_need.rb2
-rw-r--r--app/models/ci/build_runner_session.rb4
-rw-r--r--app/models/ci/commit_with_pipeline.rb38
-rw-r--r--app/models/ci/group.rb24
-rw-r--r--app/models/ci/job_artifact.rb10
-rw-r--r--app/models/ci/pipeline.rb6
-rw-r--r--app/models/ci/ref.rb7
9 files changed, 132 insertions, 29 deletions
diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb
index 19a0d424e33..ef3891908f7 100644
--- a/app/models/ci/bridge.rb
+++ b/app/models/ci/bridge.rb
@@ -7,7 +7,6 @@ module Ci
include Importable
include AfterCommitQueue
include Ci::HasRef
- extend ::Gitlab::Utils::Override
InvalidBridgeTypeError = Class.new(StandardError)
InvalidTransitionError = Class.new(StandardError)
@@ -200,13 +199,6 @@ module Ci
end
end
- override :dependency_variables
- def dependency_variables
- return [] unless ::Feature.enabled?(:ci_bridge_dependency_variables, project, default_enabled: true)
-
- super
- end
-
def target_revision_ref
downstream_pipeline_params.dig(:target_revision, :ref)
end
@@ -218,7 +210,8 @@ module Ci
project: downstream_project,
source: :pipeline,
target_revision: {
- ref: target_ref || downstream_project.default_branch
+ ref: target_ref || downstream_project.default_branch,
+ variables_attributes: downstream_variables
},
execute_params: {
ignore_skip_ci: true,
@@ -238,7 +231,8 @@ module Ci
checkout_sha: parent_pipeline.sha,
before: parent_pipeline.before_sha,
source_sha: parent_pipeline.source_sha,
- target_sha: parent_pipeline.target_sha
+ target_sha: parent_pipeline.target_sha,
+ variables_attributes: downstream_variables
},
execute_params: {
ignore_skip_ci: true,
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 71939f070cb..5e3f42d7c2c 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -27,7 +27,8 @@ module Ci
upload_multiple_artifacts: -> (build) { build.publishes_artifacts_reports? },
refspecs: -> (build) { build.merge_request_ref? },
artifacts_exclude: -> (build) { build.supports_artifacts_exclude? },
- multi_build_steps: -> (build) { build.multi_build_steps? }
+ multi_build_steps: -> (build) { build.multi_build_steps? },
+ return_exit_code: -> (build) { build.exit_codes_defined? }
}.freeze
DEFAULT_RETRIES = {
@@ -146,6 +147,12 @@ module Ci
.includes(:metadata, :job_artifacts_metadata)
end
+ scope :with_project_and_metadata, -> do
+ if Feature.enabled?(:non_public_artifacts, type: :development)
+ joins(:metadata).includes(:project, :metadata)
+ end
+ end
+
scope :with_artifacts_not_expired, -> { with_downloadable_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.current) }
scope :with_expired_artifacts, -> { with_downloadable_artifacts.where('artifacts_expire_at < ?', Time.current) }
scope :last_month, -> { where('created_at > ?', Date.today - 1.month) }
@@ -382,12 +389,8 @@ module Ci
end
after_transition any => [:skipped, :canceled] do |build, transition|
- if Feature.enabled?(:cd_skipped_deployment_status, build.project)
- if transition.to_name == :skipped
- build.deployment&.skip
- else
- build.deployment&.cancel
- end
+ if transition.to_name == :skipped
+ build.deployment&.skip
else
build.deployment&.cancel
end
@@ -741,6 +744,16 @@ module Ci
artifacts_metadata?
end
+ def artifacts_public?
+ return true unless Feature.enabled?(:non_public_artifacts, type: :development)
+
+ artifacts_public = options.dig(:artifacts, :public)
+
+ return true if artifacts_public.nil? # Default artifacts:public to true
+
+ options.dig(:artifacts, :public)
+ end
+
def artifacts_metadata_entry(path, **options)
artifacts_metadata.open do |metadata_stream|
metadata = Gitlab::Ci::Build::Artifacts::Metadata.new(
@@ -1007,14 +1020,23 @@ module Ci
end
def debug_mode?
- return false unless Feature.enabled?(:restrict_access_to_build_debug_mode, default_enabled: true)
-
# TODO: Have `debug_mode?` check against data on sent back from runner
# to capture all the ways that variables can be set.
# See (https://gitlab.com/gitlab-org/gitlab/-/issues/290955)
variables.any? { |variable| variable[:key] == 'CI_DEBUG_TRACE' && variable[:value].casecmp('true') == 0 }
end
+ def drop_with_exit_code!(failure_reason, exit_code)
+ transaction do
+ conditionally_allow_failure!(exit_code)
+ drop!(failure_reason)
+ end
+ end
+
+ def exit_codes_defined?
+ options.dig(:allow_failure_criteria, :exit_codes).present?
+ end
+
protected
def run_status_commit_hooks!
@@ -1098,6 +1120,22 @@ module Ci
Gitlab::ErrorTracking.track_exception(e)
end
end
+
+ def conditionally_allow_failure!(exit_code)
+ return unless ::Gitlab::Ci::Features.allow_failure_with_exit_codes_enabled?
+ return unless exit_code
+
+ if allowed_to_fail_with_code?(exit_code)
+ update_columns(allow_failure: true)
+ end
+ end
+
+ def allowed_to_fail_with_code?(exit_code)
+ options
+ .dig(:allow_failure_criteria, :exit_codes)
+ .to_a
+ .include?(exit_code)
+ end
end
end
diff --git a/app/models/ci/build_need.rb b/app/models/ci/build_need.rb
index b977a5f4419..fac615f97b9 100644
--- a/app/models/ci/build_need.rb
+++ b/app/models/ci/build_need.rb
@@ -6,7 +6,7 @@ module Ci
include BulkInsertSafe
- belongs_to :build, class_name: "Ci::Build", foreign_key: :build_id, inverse_of: :needs
+ belongs_to :build, class_name: "Ci::Processable", foreign_key: :build_id, inverse_of: :needs
validates :build, presence: true
validates :name, presence: true, length: { maximum: 128 }
diff --git a/app/models/ci/build_runner_session.rb b/app/models/ci/build_runner_session.rb
index bc7f17f046c..b6196048ca1 100644
--- a/app/models/ci/build_runner_session.rb
+++ b/app/models/ci/build_runner_session.rb
@@ -7,8 +7,8 @@ module Ci
extend Gitlab::Ci::Model
TERMINAL_SUBPROTOCOL = 'terminal.gitlab.com'
- DEFAULT_SERVICE_NAME = 'build'.freeze
- DEFAULT_PORT_NAME = 'default_port'.freeze
+ DEFAULT_SERVICE_NAME = 'build'
+ DEFAULT_PORT_NAME = 'default_port'
self.table_name = 'ci_builds_runner_session'
diff --git a/app/models/ci/commit_with_pipeline.rb b/app/models/ci/commit_with_pipeline.rb
new file mode 100644
index 00000000000..7f952fb77a0
--- /dev/null
+++ b/app/models/ci/commit_with_pipeline.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+class Ci::CommitWithPipeline < SimpleDelegator
+ include Presentable
+
+ def initialize(commit)
+ @latest_pipelines = {}
+ super(commit)
+ end
+
+ def pipelines
+ project.ci_pipelines.where(sha: sha)
+ end
+
+ def last_pipeline
+ strong_memoize(:last_pipeline) do
+ pipelines.last
+ end
+ end
+
+ def latest_pipeline(ref = nil)
+ @latest_pipelines.fetch(ref) do |ref|
+ @latest_pipelines[ref] = latest_pipeline_for_project(ref, project)
+ end
+ end
+
+ def latest_pipeline_for_project(ref, pipeline_project)
+ pipeline_project.ci_pipelines.latest_pipeline_per_commit(id, ref)[id]
+ end
+
+ def set_latest_pipeline_for_ref(ref, pipeline)
+ @latest_pipelines[ref] = pipeline
+ end
+
+ def status(ref = nil)
+ latest_pipeline(ref)&.status
+ end
+end
diff --git a/app/models/ci/group.rb b/app/models/ci/group.rb
index f0c035635b9..c7c0ec61e62 100644
--- a/app/models/ci/group.rb
+++ b/app/models/ci/group.rb
@@ -24,9 +24,22 @@ module Ci
def status
strong_memoize(:status) do
+ status_struct.status
+ end
+ end
+
+ def success?
+ status.to_s == 'success'
+ end
+
+ def has_warnings?
+ status_struct.warnings?
+ end
+
+ def status_struct
+ strong_memoize(:status_struct) do
Gitlab::Ci::Status::Composite
.new(@jobs)
- .status
end
end
@@ -39,8 +52,13 @@ module Ci
end
end
- def self.fabricate(project, stage)
- stage.latest_statuses
+ # Construct a grouping of statuses for this stage.
+ # We allow the caller to pass in statuses for efficiency (avoiding N+1
+ # queries).
+ def self.fabricate(project, stage, statuses = nil)
+ statuses ||= stage.latest_statuses
+
+ statuses
.sort_by(&:sortable_name).group_by(&:group_name)
.map do |group_name, grouped_statuses|
self.new(project, stage, name: group_name, jobs: grouped_statuses)
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index c80d50ea131..f13be3b3c86 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -9,6 +9,7 @@ module Ci
include Sortable
include Artifactable
include FileStoreMounter
+ include EachBatch
extend Gitlab::Ci::Model
TEST_REPORT_FILE_TYPES = %w[junit].freeze
@@ -133,6 +134,12 @@ module Ci
scope :for_sha, ->(sha, project_id) { joins(job: :pipeline).where(ci_pipelines: { sha: sha, project_id: project_id }) }
scope :for_job_name, ->(name) { joins(:job).where(ci_builds: { name: name }) }
+ scope :with_job, -> do
+ if Feature.enabled?(:non_public_artifacts, type: :development)
+ joins(:job).includes(:job)
+ end
+ end
+
scope :with_file_types, -> (file_types) do
types = self.file_types.select { |file_type| file_types.include?(file_type) }.values
@@ -170,7 +177,8 @@ module Ci
end
scope :downloadable, -> { where(file_type: DOWNLOADABLE_TYPES) }
- scope :unlocked, -> { joins(job: :pipeline).merge(::Ci::Pipeline.unlocked).order(expire_at: :desc) }
+ scope :unlocked, -> { joins(job: :pipeline).merge(::Ci::Pipeline.unlocked) }
+ scope :order_expired_desc, -> { order(expire_at: :desc) }
scope :with_destroy_preloads, -> { includes(project: [:route, :statistics]) }
scope :scoped_project, -> { where('ci_job_artifacts.project_id = projects.id') }
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 5e5f51d776f..4a579892e3f 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -68,8 +68,8 @@ module Ci
has_many :variables, class_name: 'Ci::PipelineVariable'
has_many :deployments, through: :builds
has_many :environments, -> { distinct }, through: :deployments
- has_many :latest_builds, -> { latest }, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'Ci::Build'
- has_many :downloadable_artifacts, -> { not_expired.downloadable }, through: :latest_builds, source: :job_artifacts
+ 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 :messages, class_name: 'Ci::PipelineMessage', inverse_of: :pipeline
@@ -249,7 +249,7 @@ module Ci
after_transition any => ::Ci::Pipeline.completed_statuses do |pipeline|
pipeline.run_after_commit do
- ::Ci::Pipelines::CreateArtifactWorker.perform_async(pipeline.id)
+ ::Ci::PipelineArtifacts::CoverageReportWorker.perform_async(pipeline.id)
end
end
diff --git a/app/models/ci/ref.rb b/app/models/ci/ref.rb
index 6e9b8416c10..713a0bf9c45 100644
--- a/app/models/ci/ref.rb
+++ b/app/models/ci/ref.rb
@@ -33,6 +33,9 @@ module Ci
state :still_failing, value: 5
after_transition any => [:fixed, :success] do |ci_ref|
+ # Do not try to unlock if no artifacts are locked
+ next unless ci_ref.artifacts_locked?
+
ci_ref.run_after_commit do
Ci::PipelineSuccessUnlockArtifactsWorker.perform_async(ci_ref.last_finished_pipeline_id)
end
@@ -54,6 +57,10 @@ module Ci
Ci::Pipeline.last_finished_for_ref_id(self.id)&.id
end
+ def artifacts_locked?
+ self.pipelines.where(locked: :artifacts_locked).exists?
+ end
+
def update_status_by!(pipeline)
retry_lock(self) do
next unless last_finished_pipeline_id == pipeline.id