Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/ci')
-rw-r--r--app/models/ci/bridge.rb4
-rw-r--r--app/models/ci/build.rb67
-rw-r--r--app/models/ci/daily_build_group_report_result.rb20
-rw-r--r--app/models/ci/daily_report_result.rb22
-rw-r--r--app/models/ci/freeze_period.rb18
-rw-r--r--app/models/ci/freeze_period_status.rb47
-rw-r--r--app/models/ci/group.rb2
-rw-r--r--app/models/ci/instance_variable.rb76
-rw-r--r--app/models/ci/job_artifact.rb61
-rw-r--r--app/models/ci/legacy_stage.rb4
-rw-r--r--app/models/ci/persistent_ref.rb12
-rw-r--r--app/models/ci/pipeline.rb55
-rw-r--r--app/models/ci/pipeline_schedule.rb4
-rw-r--r--app/models/ci/processable.rb12
-rw-r--r--app/models/ci/stage.rb4
15 files changed, 342 insertions, 66 deletions
diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb
index 76882dfcb0d..1e92a47ab49 100644
--- a/app/models/ci/bridge.rb
+++ b/app/models/ci/bridge.rb
@@ -166,6 +166,10 @@ module Ci
end
end
+ def dependency_variables
+ []
+ end
+
private
def cross_project_params
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index e515447e394..7f64ea7dd97 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -25,13 +25,16 @@ module Ci
RUNNER_FEATURES = {
upload_multiple_artifacts: -> (build) { build.publishes_artifacts_reports? },
- refspecs: -> (build) { build.merge_request_ref? }
+ refspecs: -> (build) { build.merge_request_ref? },
+ artifacts_exclude: -> (build) { build.supports_artifacts_exclude? }
}.freeze
DEFAULT_RETRIES = {
scheduler_failure: 2
}.freeze
+ DEGRADATION_THRESHOLD_VARIABLE_NAME = 'DEGRADATION_THRESHOLD'
+
has_one :deployment, as: :deployable, class_name: 'Deployment'
has_one :resource, class_name: 'Ci::Resource', inverse_of: :build
has_many :trace_sections, class_name: 'Ci::BuildTraceSection'
@@ -87,8 +90,12 @@ module Ci
scope :unstarted, ->() { where(runner_id: nil) }
scope :ignore_failures, ->() { where(allow_failure: false) }
- scope :with_artifacts_archive, ->() do
- where('EXISTS (?)', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').archive)
+ scope :with_downloadable_artifacts, ->() do
+ where('EXISTS (?)',
+ Ci::JobArtifact.select(1)
+ .where('ci_builds.id = ci_job_artifacts.job_id')
+ .where(file_type: Ci::JobArtifact::DOWNLOADABLE_TYPES)
+ )
end
scope :with_existing_job_artifacts, ->(query) do
@@ -130,8 +137,8 @@ module Ci
.includes(:metadata, :job_artifacts_metadata)
end
- 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) }
+ scope :with_artifacts_not_expired, ->() { with_downloadable_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
+ scope :with_expired_artifacts, ->() { with_downloadable_artifacts.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
scope :manual_actions, ->() { where(when: :manual, status: COMPLETED_STATUSES + %i[manual]) }
scope :scheduled_actions, ->() { where(when: :delayed, status: COMPLETED_STATUSES + %i[scheduled]) }
@@ -486,8 +493,7 @@ module Ci
end
def requires_resource?
- Feature.enabled?(:ci_resource_group, project, default_enabled: true) &&
- self.resource_group_id.present?
+ self.resource_group_id.present?
end
def has_environment?
@@ -530,6 +536,7 @@ module Ci
.concat(job_variables)
.concat(environment_changed_page_variables)
.concat(persisted_environment_variables)
+ .concat(deploy_freeze_variables)
.to_runner_variables
end
end
@@ -585,6 +592,26 @@ module Ci
end
end
+ def deploy_freeze_variables
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ break variables unless freeze_period?
+
+ variables.append(key: 'CI_DEPLOY_FREEZE', value: 'true')
+ end
+ end
+
+ def freeze_period?
+ Ci::FreezePeriodStatus.new(project: project).execute
+ end
+
+ def dependency_variables
+ return [] if all_dependencies.empty?
+
+ Gitlab::Ci::Variables::Collection.new.concat(
+ Ci::JobVariable.where(job: all_dependencies).dotenv_source
+ )
+ end
+
def features
{ trace_sections: true }
end
@@ -870,6 +897,14 @@ module Ci
end
end
+ def collect_accessibility_reports!(accessibility_report)
+ each_report(Ci::JobArtifact::ACCESSIBILITY_REPORT_FILE_TYPES) do |file_type, blob|
+ Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, accessibility_report)
+ end
+
+ accessibility_report
+ end
+
def collect_coverage_reports!(coverage_report)
each_report(Ci::JobArtifact::COVERAGE_REPORT_FILE_TYPES) do |file_type, blob|
Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, coverage_report)
@@ -878,6 +913,14 @@ module Ci
coverage_report
end
+ def collect_terraform_reports!(terraform_reports)
+ each_report(::Ci::JobArtifact::TERRAFORM_REPORT_FILE_TYPES) do |file_type, blob, report_artifact|
+ ::Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, terraform_reports, artifact: report_artifact)
+ end
+
+ terraform_reports
+ end
+
def report_artifacts
job_artifacts.with_reports
end
@@ -902,6 +945,16 @@ module Ci
failure_reason: :data_integrity_failure)
end
+ def supports_artifacts_exclude?
+ options&.dig(:artifacts, :exclude)&.any? &&
+ Gitlab::Ci::Features.artifacts_exclude_enabled?
+ end
+
+ def degradation_threshold
+ var = yaml_variables.find { |v| v[:key] == DEGRADATION_THRESHOLD_VARIABLE_NAME }
+ var[:value]&.to_i if var
+ end
+
private
def dependencies
diff --git a/app/models/ci/daily_build_group_report_result.rb b/app/models/ci/daily_build_group_report_result.rb
new file mode 100644
index 00000000000..3506b27e974
--- /dev/null
+++ b/app/models/ci/daily_build_group_report_result.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Ci
+ class DailyBuildGroupReportResult < ApplicationRecord
+ extend Gitlab::Ci::Model
+
+ PARAM_TYPES = %w[coverage].freeze
+
+ belongs_to :last_pipeline, class_name: 'Ci::Pipeline', foreign_key: :last_pipeline_id
+ belongs_to :project
+
+ def self.upsert_reports(data)
+ upsert_all(data, unique_by: :index_daily_build_group_report_results_unique_columns) if data.any?
+ end
+
+ def self.recent_results(attrs, limit: nil)
+ where(attrs).order(date: :desc, group_name: :asc).limit(limit)
+ end
+ end
+end
diff --git a/app/models/ci/daily_report_result.rb b/app/models/ci/daily_report_result.rb
deleted file mode 100644
index 3c1c5f11ed4..00000000000
--- a/app/models/ci/daily_report_result.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- class DailyReportResult < ApplicationRecord
- extend Gitlab::Ci::Model
-
- belongs_to :last_pipeline, class_name: 'Ci::Pipeline', foreign_key: :last_pipeline_id
- belongs_to :project
-
- # TODO: Refactor this out when BuildReportResult is implemented.
- # They both need to share the same enum values for param.
- REPORT_PARAMS = {
- coverage: 0
- }.freeze
-
- enum param_type: REPORT_PARAMS
-
- def self.upsert_reports(data)
- upsert_all(data, unique_by: :index_daily_report_results_unique_columns) if data.any?
- end
- end
-end
diff --git a/app/models/ci/freeze_period.rb b/app/models/ci/freeze_period.rb
new file mode 100644
index 00000000000..bf03b92259a
--- /dev/null
+++ b/app/models/ci/freeze_period.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Ci
+ class FreezePeriod < ApplicationRecord
+ include StripAttribute
+ self.table_name = 'ci_freeze_periods'
+
+ default_scope { order(created_at: :asc) }
+
+ belongs_to :project, inverse_of: :freeze_periods
+
+ strip_attributes :freeze_start, :freeze_end
+
+ validates :freeze_start, cron: true, presence: true
+ validates :freeze_end, cron: true, presence: true
+ validates :cron_timezone, cron_freeze_period_timezone: true, presence: true
+ end
+end
diff --git a/app/models/ci/freeze_period_status.rb b/app/models/ci/freeze_period_status.rb
new file mode 100644
index 00000000000..befa935e750
--- /dev/null
+++ b/app/models/ci/freeze_period_status.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module Ci
+ class FreezePeriodStatus
+ attr_reader :project
+
+ def initialize(project:)
+ @project = project
+ end
+
+ def execute
+ project.freeze_periods.any? { |period| within_freeze_period?(period) }
+ end
+
+ def within_freeze_period?(period)
+ # previous_freeze_end, ..., previous_freeze_start, ..., NOW, ..., next_freeze_end, ..., next_freeze_start
+ # Current time is within a freeze period if
+ # it falls between a previous freeze start and next freeze end
+ start_freeze = Gitlab::Ci::CronParser.new(period.freeze_start, period.cron_timezone)
+ end_freeze = Gitlab::Ci::CronParser.new(period.freeze_end, period.cron_timezone)
+
+ previous_freeze_start = previous_time(start_freeze)
+ previous_freeze_end = previous_time(end_freeze)
+ next_freeze_start = next_time(start_freeze)
+ next_freeze_end = next_time(end_freeze)
+
+ previous_freeze_end < previous_freeze_start &&
+ previous_freeze_start <= time_zone_now &&
+ time_zone_now <= next_freeze_end &&
+ next_freeze_end < next_freeze_start
+ end
+
+ private
+
+ def previous_time(cron_parser)
+ cron_parser.previous_time_from(time_zone_now)
+ end
+
+ def next_time(cron_parser)
+ cron_parser.next_time_from(time_zone_now)
+ end
+
+ def time_zone_now
+ @time_zone_now ||= Time.zone.now
+ end
+ end
+end
diff --git a/app/models/ci/group.rb b/app/models/ci/group.rb
index 15dc1ca8954..4b2081f2977 100644
--- a/app/models/ci/group.rb
+++ b/app/models/ci/group.rb
@@ -46,7 +46,7 @@ module Ci
end
def self.fabricate(project, stage)
- stage.statuses.ordered.latest
+ stage.latest_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/instance_variable.rb b/app/models/ci/instance_variable.rb
new file mode 100644
index 00000000000..c674f76d229
--- /dev/null
+++ b/app/models/ci/instance_variable.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+module Ci
+ class InstanceVariable < ApplicationRecord
+ extend Gitlab::Ci::Model
+ include Ci::NewHasVariable
+ include Ci::Maskable
+
+ alias_attribute :secret_value, :value
+
+ validates :key, uniqueness: {
+ message: "(%{value}) has already been taken"
+ }
+
+ scope :unprotected, -> { where(protected: false) }
+ after_commit { self.class.touch_redis_cache_timestamp }
+
+ class << self
+ def all_cached
+ cached_data[:all]
+ end
+
+ def unprotected_cached
+ cached_data[:unprotected]
+ end
+
+ def touch_redis_cache_timestamp(time = Time.current.to_f)
+ shared_backend.write(:ci_instance_variable_changed_at, time)
+ end
+
+ private
+
+ def cached_data
+ fetch_memory_cache(:ci_instance_variable_data) do
+ all_records = unscoped.all.to_a
+
+ { all: all_records, unprotected: all_records.reject(&:protected?) }
+ end
+ end
+
+ def fetch_memory_cache(key, &payload)
+ cache = process_backend.read(key)
+
+ if cache && !stale_cache?(cache)
+ cache[:data]
+ else
+ store_cache(key, &payload)
+ end
+ end
+
+ def stale_cache?(cache_info)
+ shared_timestamp = shared_backend.read(:ci_instance_variable_changed_at)
+ return true unless shared_timestamp
+
+ shared_timestamp.to_f > cache_info[:cached_at].to_f
+ end
+
+ def store_cache(key)
+ data = yield
+ time = Time.current.to_f
+
+ process_backend.write(key, data: data, cached_at: time)
+ touch_redis_cache_timestamp(time)
+ data
+ end
+
+ def shared_backend
+ Rails.cache
+ end
+
+ def process_backend
+ Gitlab::ProcessMemoryCache.cache_backend
+ end
+ end
+ end
+end
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index ef0701b3874..d931428dccd 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -12,7 +12,10 @@ module Ci
TEST_REPORT_FILE_TYPES = %w[junit].freeze
COVERAGE_REPORT_FILE_TYPES = %w[cobertura].freeze
+ ACCESSIBILITY_REPORT_FILE_TYPES = %w[accessibility].freeze
NON_ERASABLE_FILE_TYPES = %w[trace].freeze
+ TERRAFORM_REPORT_FILE_TYPES = %w[terraform].freeze
+ UNSUPPORTED_FILE_TYPES = %i[license_management].freeze
DEFAULT_FILE_NAMES = {
archive: nil,
metadata: nil,
@@ -20,6 +23,7 @@ module Ci
metrics_referee: nil,
network_referee: nil,
junit: 'junit.xml',
+ accessibility: 'gl-accessibility.json',
codequality: 'gl-code-quality-report.json',
sast: 'gl-sast-report.json',
dependency_scanning: 'gl-dependency-scanning-report.json',
@@ -32,7 +36,8 @@ module Ci
lsif: 'lsif.json',
dotenv: '.env',
cobertura: 'cobertura-coverage.xml',
- terraform: 'tfplan.json'
+ terraform: 'tfplan.json',
+ cluster_applications: 'gl-cluster-applications.json'
}.freeze
INTERNAL_TYPES = {
@@ -46,13 +51,15 @@ module Ci
metrics: :gzip,
metrics_referee: :gzip,
network_referee: :gzip,
- lsif: :gzip,
dotenv: :gzip,
cobertura: :gzip,
+ cluster_applications: :gzip,
+ lsif: :zip,
# All these file formats use `raw` as we need to store them uncompressed
# for Frontend to fetch the files and do analysis
# When they will be only used by backend, they can be `gzipped`.
+ accessibility: :raw,
codequality: :raw,
sast: :raw,
dependency_scanning: :raw,
@@ -64,15 +71,38 @@ module Ci
terraform: :raw
}.freeze
+ DOWNLOADABLE_TYPES = %w[
+ accessibility
+ archive
+ cobertura
+ codequality
+ container_scanning
+ dast
+ dependency_scanning
+ dotenv
+ junit
+ license_management
+ license_scanning
+ lsif
+ metrics
+ performance
+ sast
+ ].freeze
+
TYPE_AND_FORMAT_PAIRS = INTERNAL_TYPES.merge(REPORT_TYPES).freeze
+ # This is required since we cannot add a default to the database
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/215418
+ attribute :locked, :boolean, default: false
+
belongs_to :project
belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id
mount_uploader :file, JobArtifactUploader
validates :file_format, presence: true, unless: :trace?, on: :create
- validate :valid_file_format?, unless: :trace?, on: :create
+ validate :validate_supported_file_format!, on: :create
+ validate :validate_file_format!, unless: :trace?, on: :create
before_save :set_size, if: :file_changed?
update_project_statistics project_statistics_name: :build_artifacts_size
@@ -82,6 +112,7 @@ module Ci
scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) }
scope :with_files_stored_remotely, -> { where(file_store: ::JobArtifactUploader::Store::REMOTE) }
scope :for_sha, ->(sha, project_id) { joins(job: :pipeline).where(ci_pipelines: { sha: sha, project_id: project_id }) }
+ scope :for_ref, ->(ref, project_id) { joins(job: :pipeline).where(ci_pipelines: { ref: ref, project_id: project_id }) }
scope :for_job_name, ->(name) { joins(:job).where(ci_builds: { name: name }) }
scope :with_file_types, -> (file_types) do
@@ -98,10 +129,18 @@ module Ci
with_file_types(TEST_REPORT_FILE_TYPES)
end
+ scope :accessibility_reports, -> do
+ with_file_types(ACCESSIBILITY_REPORT_FILE_TYPES)
+ end
+
scope :coverage_reports, -> do
with_file_types(COVERAGE_REPORT_FILE_TYPES)
end
+ scope :terraform_reports, -> do
+ with_file_types(TERRAFORM_REPORT_FILE_TYPES)
+ end
+
scope :erasable, -> do
types = self.file_types.reject { |file_type| NON_ERASABLE_FILE_TYPES.include?(file_type) }.values
@@ -109,6 +148,8 @@ module Ci
end
scope :expired, -> (limit) { where('expire_at < ?', Time.now).limit(limit) }
+ scope :locked, -> { where(locked: true) }
+ scope :unlocked, -> { where(locked: [false, nil]) }
scope :scoped_project, -> { where('ci_job_artifacts.project_id = projects.id') }
@@ -133,7 +174,9 @@ module Ci
lsif: 15, # LSIF data for code navigation
dotenv: 16,
cobertura: 17,
- terraform: 18 # Transformed json
+ terraform: 18, # Transformed json
+ accessibility: 19,
+ cluster_applications: 20
}
enum file_format: {
@@ -161,7 +204,15 @@ module Ci
raw: Gitlab::Ci::Build::Artifacts::Adapters::RawStream
}.freeze
- def valid_file_format?
+ def validate_supported_file_format!
+ return if Feature.disabled?(:drop_license_management_artifact, project, default_enabled: true)
+
+ if UNSUPPORTED_FILE_TYPES.include?(self.file_type&.to_sym)
+ errors.add(:base, _("File format is no longer supported"))
+ end
+ end
+
+ def validate_file_format!
unless TYPE_AND_FORMAT_PAIRS[self.file_type&.to_sym] == self.file_format&.to_sym
errors.add(:base, _('Invalid file format with specified file type'))
end
diff --git a/app/models/ci/legacy_stage.rb b/app/models/ci/legacy_stage.rb
index f156219ea81..250306e2be4 100644
--- a/app/models/ci/legacy_stage.rb
+++ b/app/models/ci/legacy_stage.rb
@@ -41,6 +41,10 @@ module Ci
.fabricate!
end
+ def latest_statuses
+ statuses.ordered.latest
+ end
+
def statuses
@statuses ||= pipeline.statuses.where(stage: name)
end
diff --git a/app/models/ci/persistent_ref.rb b/app/models/ci/persistent_ref.rb
index 76139f5d676..91163c85a9e 100644
--- a/app/models/ci/persistent_ref.rb
+++ b/app/models/ci/persistent_ref.rb
@@ -14,16 +14,12 @@ module Ci
delegate :ref_exists?, :create_ref, :delete_refs, to: :repository
def exist?
- return unless enabled?
-
ref_exists?(path)
rescue
false
end
def create
- return unless enabled?
-
create_ref(sha, path)
rescue => e
Gitlab::ErrorTracking
@@ -31,8 +27,6 @@ module Ci
end
def delete
- return unless enabled?
-
delete_refs(path)
rescue Gitlab::Git::Repository::NoRepository
# no-op
@@ -44,11 +38,5 @@ module Ci
def path
"refs/#{Repository::REF_PIPELINES}/#{pipeline.id}"
end
-
- private
-
- def enabled?
- Feature.enabled?(:depend_on_persistent_pipeline_ref, project, default_enabled: true)
- end
end
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 8a3ca2e758c..5db1635f64d 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -82,7 +82,7 @@ module Ci
has_one :pipeline_config, class_name: 'Ci::PipelineConfig', inverse_of: :pipeline
- has_many :daily_report_results, class_name: 'Ci::DailyReportResult', foreign_key: :last_pipeline_id
+ has_many :daily_build_group_report_results, class_name: 'Ci::DailyBuildGroupReportResult', foreign_key: :last_pipeline_id
accepts_nested_attributes_for :variables, reject_if: :persisted?
@@ -115,8 +115,11 @@ module Ci
state_machine :status, initial: :created do
event :enqueue do
- transition [:created, :waiting_for_resource, :preparing, :skipped, :scheduled] => :pending
+ transition [:created, :manual, :waiting_for_resource, :preparing, :skipped, :scheduled] => :pending
transition [:success, :failed, :canceled] => :running
+
+ # this is needed to ensure tests to be covered
+ transition [:running] => :running
end
event :request_resource do
@@ -194,7 +197,7 @@ module Ci
# We wait a little bit to ensure that all BuildFinishedWorkers finish first
# because this is where some metrics like code coverage is parsed and stored
# in CI build records which the daily build metrics worker relies on.
- pipeline.run_after_commit { Ci::DailyReportResultsWorker.perform_in(10.minutes, pipeline.id) }
+ pipeline.run_after_commit { Ci::DailyBuildGroupReportResultsWorker.perform_in(10.minutes, pipeline.id) }
end
after_transition do |pipeline, transition|
@@ -393,16 +396,18 @@ module Ci
false
end
- ##
- # TODO We do not completely switch to persisted stages because of
- # race conditions with setting statuses gitlab-foss#23257.
- #
def ordered_stages
- return legacy_stages unless complete?
-
- if Feature.enabled?('ci_pipeline_persisted_stages', default_enabled: true)
+ if Feature.enabled?(:ci_atomic_processing, project, default_enabled: false)
+ # The `Ci::Stage` contains all up-to date data
+ # as atomic processing updates all data in-bulk
+ stages
+ elsif Feature.enabled?(:ci_pipeline_persisted_stages, default_enabled: true) && complete?
+ # The `Ci::Stage` contains up-to date data only for `completed` pipelines
+ # this is due to asynchronous processing of pipeline, and stages possibly
+ # not updated inline with processing of pipeline
stages
else
+ # In other cases, we need to calculate stages dynamically
legacy_stages
end
end
@@ -440,7 +445,7 @@ module Ci
end
def legacy_stages
- if Feature.enabled?(:ci_composite_status, default_enabled: false)
+ if Feature.enabled?(:ci_composite_status, project, default_enabled: false)
legacy_stages_using_composite_status
else
legacy_stages_using_sql
@@ -681,6 +686,8 @@ module Ci
variables.concat(merge_request.predefined_variables)
end
+ variables.append(key: 'CI_KUBERNETES_ACTIVE', value: 'true') if has_kubernetes_active?
+
if external_pull_request_event? && external_pull_request
variables.concat(external_pull_request.predefined_variables)
end
@@ -781,7 +788,7 @@ module Ci
end
def find_job_with_archive_artifacts(name)
- builds.latest.with_artifacts_archive.find_by_name(name)
+ builds.latest.with_downloadable_artifacts.find_by_name(name)
end
def latest_builds_with_artifacts
@@ -809,6 +816,14 @@ module Ci
end
end
+ def accessibility_reports
+ Gitlab::Ci::Reports::AccessibilityReports.new.tap do |accessibility_reports|
+ builds.latest.with_reports(Ci::JobArtifact.accessibility_reports).each do |build|
+ build.collect_accessibility_reports!(accessibility_reports)
+ end
+ end
+ end
+
def coverage_reports
Gitlab::Ci::Reports::CoverageReports.new.tap do |coverage_reports|
builds.latest.with_reports(Ci::JobArtifact.coverage_reports).each do |build|
@@ -817,6 +832,14 @@ module Ci
end
end
+ def terraform_reports
+ ::Gitlab::Ci::Reports::TerraformReports.new.tap do |terraform_reports|
+ builds.latest.with_reports(::Ci::JobArtifact.terraform_reports).each do |build|
+ build.collect_terraform_reports!(terraform_reports)
+ end
+ end
+ end
+
def has_exposed_artifacts?
complete? && builds.latest.with_exposed_artifacts.exists?
end
@@ -938,6 +961,14 @@ module Ci
end
end
+ # Set scheduling type of processables if they were created before scheduling_type
+ # data was deployed (https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22246).
+ def ensure_scheduling_type!
+ return unless ::Gitlab::Ci::Features.ensure_scheduling_type_enabled?
+
+ processables.populate_scheduling_type!
+ end
+
private
def pipeline_data
diff --git a/app/models/ci/pipeline_schedule.rb b/app/models/ci/pipeline_schedule.rb
index f5785000062..8c9ad343f32 100644
--- a/app/models/ci/pipeline_schedule.rb
+++ b/app/models/ci/pipeline_schedule.rb
@@ -6,6 +6,10 @@ module Ci
include Importable
include StripAttribute
include Schedulable
+ include Limitable
+
+ self.limit_name = 'ci_pipeline_schedules'
+ self.limit_scope = :project
belongs_to :project
belongs_to :owner, class_name: 'User'
diff --git a/app/models/ci/processable.rb b/app/models/ci/processable.rb
index c123bd7c33b..cc00500662d 100644
--- a/app/models/ci/processable.rb
+++ b/app/models/ci/processable.rb
@@ -49,7 +49,7 @@ module Ci
end
validates :type, presence: true
- validates :scheduling_type, presence: true, on: :create, if: :validate_scheduling_type?
+ validates :scheduling_type, presence: true, on: :create, unless: :importing?
delegate :merge_request?,
:merge_request_ref?,
@@ -83,7 +83,7 @@ module Ci
# Overriding scheduling_type enum's method for nil `scheduling_type`s
def scheduling_type_dag?
- super || find_legacy_scheduling_type == :dag
+ scheduling_type.nil? ? find_legacy_scheduling_type == :dag : super
end
# scheduling_type column of previous builds/bridges have not been populated,
@@ -100,10 +100,12 @@ module Ci
end
end
- private
+ def ensure_scheduling_type!
+ # If this has a scheduling_type, it means all processables in the pipeline already have.
+ return if scheduling_type
- def validate_scheduling_type?
- !importing? && Feature.enabled?(:validate_scheduling_type_of_processables, project)
+ pipeline.ensure_scheduling_type!
+ reset
end
end
end
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index 93bd42f8734..a316b4718e0 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -13,6 +13,7 @@ module Ci
belongs_to :pipeline
has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id
+ has_many :latest_statuses, -> { ordered.latest }, class_name: 'CommitStatus', foreign_key: :stage_id
has_many :processables, class_name: 'Ci::Processable', foreign_key: :stage_id
has_many :builds, foreign_key: :stage_id
has_many :bridges, foreign_key: :stage_id
@@ -42,8 +43,7 @@ module Ci
state_machine :status, initial: :created do
event :enqueue do
- transition [:created, :waiting_for_resource, :preparing] => :pending
- transition [:success, :failed, :canceled, :skipped] => :running
+ transition any - [:pending] => :pending
end
event :request_resource do