summaryrefslogtreecommitdiff
path: root/app/models/ci
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/ci')
-rw-r--r--app/models/ci/bridge.rb8
-rw-r--r--app/models/ci/build.rb137
-rw-r--r--app/models/ci/build_metadata.rb3
-rw-r--r--app/models/ci/freeze_period_status.rb28
-rw-r--r--app/models/ci/job_artifact.rb40
-rw-r--r--app/models/ci/job_token/scope.rb12
-rw-r--r--app/models/ci/namespace_mirror.rb14
-rw-r--r--app/models/ci/partition.rb6
-rw-r--r--app/models/ci/pipeline.rb81
-rw-r--r--app/models/ci/pipeline_artifact.rb6
-rw-r--r--app/models/ci/pipeline_variable.rb5
-rw-r--r--app/models/ci/processable.rb1
-rw-r--r--app/models/ci/runner.rb17
-rw-r--r--app/models/ci/stage.rb21
-rw-r--r--app/models/ci/trigger.rb6
15 files changed, 162 insertions, 223 deletions
diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb
index 3fda8693a58..323d759510e 100644
--- a/app/models/ci/bridge.rb
+++ b/app/models/ci/bridge.rb
@@ -75,9 +75,9 @@ module Ci
def self.clone_accessors
%i[pipeline project ref tag options name
- allow_failure stage stage_id stage_idx
+ allow_failure stage stage_idx
yaml_variables when description needs_attributes
- scheduling_type].freeze
+ scheduling_type ci_stage partition_id].freeze
end
def inherit_status_from_downstream!(pipeline)
@@ -183,6 +183,10 @@ module Ci
false
end
+ def prevent_rollback_deployment?
+ false
+ end
+
def expanded_environment_name
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index bf8817e6e78..4e58f877217 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -11,7 +11,7 @@ module Ci
include Presentable
include Importable
include Ci::HasRef
- include HasDeploymentName
+ include Ci::TrackEnvironmentUsage
extend ::Gitlab::Utils::Override
@@ -34,7 +34,7 @@ module Ci
DEPLOYMENT_NAMES = %w[deploy release rollout].freeze
- has_one :deployment, as: :deployable, class_name: 'Deployment'
+ has_one :deployment, as: :deployable, class_name: 'Deployment', inverse_of: :deployable
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
@@ -194,7 +194,7 @@ module Ci
after_save :stick_build_if_status_changed
after_create unless: :importing? do |build|
- run_after_commit { build.feature_flagged_execute_hooks }
+ run_after_commit { build.execute_hooks }
end
class << self
@@ -214,10 +214,11 @@ module Ci
def clone_accessors
%i[pipeline project ref tag options name
- allow_failure stage stage_id stage_idx trigger_request
+ allow_failure stage stage_idx trigger_request
yaml_variables when environment coverage_regex
description tag_list protected needs_attributes
- job_variables_attributes resource_group scheduling_type].freeze
+ job_variables_attributes resource_group scheduling_type
+ ci_stage partition_id].freeze
end
end
@@ -285,7 +286,7 @@ module Ci
build.run_after_commit do
BuildQueueWorker.perform_async(id)
- build.feature_flagged_execute_hooks
+ build.execute_hooks
end
end
@@ -313,7 +314,7 @@ module Ci
build.run_after_commit do
build.ensure_persistent_ref
- build.feature_flagged_execute_hooks
+ build.execute_hooks
end
end
@@ -442,6 +443,15 @@ module Ci
manual? && starts_environment? && deployment&.blocked?
end
+ def prevent_rollback_deployment?
+ strong_memoize(:prevent_rollback_deployment) do
+ Feature.enabled?(:prevent_outdated_deployment_jobs, project) &&
+ starts_environment? &&
+ project.ci_forward_deployment_enabled? &&
+ deployment&.older_than_last_successful_deployment?
+ end
+ end
+
def schedulable?
self.when == 'delayed' && options[:start_in].present?
end
@@ -703,25 +713,7 @@ module Ci
end
def has_test_reports?
- job_artifacts.test_reports.exists?
- end
-
- def has_old_trace?
- old_trace.present?
- end
-
- def trace=(data)
- raise NotImplementedError
- end
-
- def old_trace
- read_attribute(:trace)
- end
-
- def erase_old_trace!
- return unless has_old_trace?
-
- update_column(:trace, nil)
+ job_artifacts.of_report_type(:test).exists?
end
def ensure_trace_metadata!
@@ -780,14 +772,6 @@ module Ci
pending? && !any_runners_online?
end
- def feature_flagged_execute_hooks
- if Feature.enabled?(:execute_build_hooks_inline, project)
- execute_hooks
- else
- BuildHooksWorker.perform_async(self)
- end
- end
-
def execute_hooks
return unless project
return if user&.blocked?
@@ -823,41 +807,6 @@ module Ci
end
end
- def erase_erasable_artifacts!
- if project.refreshing_build_artifacts_size?
- Gitlab::ProjectStatsRefreshConflictsLogger.warn_artifact_deletion_during_stats_refresh(
- method: 'Ci::Build#erase_erasable_artifacts!',
- project_id: project_id
- )
- end
-
- destroyed_artifacts = job_artifacts.erasable.destroy_all # rubocop: disable Cop/DestroyAll
-
- Gitlab::Ci::Artifacts::Logger.log_deleted(destroyed_artifacts, 'Ci::Build#erase_erasable_artifacts!')
-
- destroyed_artifacts
- end
-
- def erase(opts = {})
- return false unless erasable?
-
- if project.refreshing_build_artifacts_size?
- Gitlab::ProjectStatsRefreshConflictsLogger.warn_artifact_deletion_during_stats_refresh(
- method: 'Ci::Build#erase',
- project_id: project_id
- )
- end
-
- # TODO: We should use DestroyBatchService here
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/369132
- destroyed_artifacts = job_artifacts.destroy_all # rubocop: disable Cop/DestroyAll
-
- Gitlab::Ci::Artifacts::Logger.log_deleted(destroyed_artifacts, 'Ci::Build#erase')
-
- erase_trace!
- update_erased!(opts[:erased_by])
- end
-
def erasable?
complete? && (artifacts? || has_job_artifacts? || has_trace?)
end
@@ -1004,15 +953,11 @@ module Ci
end
def collect_test_reports!(test_reports)
- test_reports.get_suite(test_suite_name).tap do |test_suite|
- each_report(Ci::JobArtifact.file_types_for_report(:test)) do |file_type, blob|
- Gitlab::Ci::Parsers.fabricate!(file_type).parse!(
- blob,
- test_suite,
- job: self
- )
- end
+ each_report(Ci::JobArtifact.file_types_for_report(:test)) do |file_type, blob|
+ Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, test_reports, job: self)
end
+
+ test_reports
end
def collect_accessibility_reports!(accessibility_report)
@@ -1154,18 +1099,6 @@ module Ci
.include?(exit_code)
end
- def track_deployment_usage
- Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_deployment_job', user_id) if user_id.present? && count_user_deployment?
- end
-
- def track_verify_usage
- Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_verify_environment_job', user_id) if user_id.present? && count_user_verification?
- end
-
- def count_user_verification?
- has_environment? && environment_action == 'verify'
- end
-
def each_report(report_types)
job_artifacts_for_types(report_types).each do |report_artifact|
report_artifact.each_blob do |blob|
@@ -1189,6 +1122,14 @@ module Ci
job_artifacts.map(&:file_type)
end
+ def test_suite_name
+ if matrix_build?
+ name
+ else
+ group_name
+ end
+ end
+
protected
def run_status_commit_hooks!
@@ -1199,14 +1140,6 @@ module Ci
private
- def test_suite_name
- if matrix_build?
- name
- else
- group_name
- end
- end
-
def matrix_build?
options.dig(:parallel, :matrix).present?
end
@@ -1245,14 +1178,6 @@ module Ci
job_artifacts.select { |artifact| artifact.file_type.in?(report_types) }
end
- def erase_trace!
- trace.erase!
- end
-
- def update_erased!(user = nil)
- self.update(erased_by: user, erased_at: Time.current, artifacts_expire_at: nil)
- end
-
def environment_url
options&.dig(:environment, :url) || persisted_environment&.external_url
end
@@ -1298,7 +1223,7 @@ module Ci
end
def observe_report_types
- return unless ::Gitlab.com? && Feature.enabled?(:report_artifact_build_completed_metrics_on_build_completion)
+ return unless ::Gitlab.com?
report_types = options&.dig(:artifacts, :reports)&.keys || []
diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb
index 5fc21ba3f28..3bdf2f90acb 100644
--- a/app/models/ci/build_metadata.rb
+++ b/app/models/ci/build_metadata.rb
@@ -9,7 +9,6 @@ module Ci
include Presentable
include ChronicDurationAttribute
include Gitlab::Utils::StrongMemoize
- include IgnorableColumns
self.table_name = 'ci_builds_metadata'
@@ -39,8 +38,6 @@ module Ci
job_timeout_source: 4
}
- ignore_columns :runner_features, remove_with: '15.1', remove_after: '2022-05-22'
-
def update_timeout_state
timeout = timeout_with_highest_precedence
diff --git a/app/models/ci/freeze_period_status.rb b/app/models/ci/freeze_period_status.rb
index befa935e750..e810bb3f229 100644
--- a/app/models/ci/freeze_period_status.rb
+++ b/app/models/ci/freeze_period_status.rb
@@ -13,32 +13,16 @@ module Ci
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
+ start_freeze_cron = Gitlab::Ci::CronParser.new(period.freeze_start, period.cron_timezone)
+ end_freeze_cron = Gitlab::Ci::CronParser.new(period.freeze_end, period.cron_timezone)
- private
+ start_freeze = start_freeze_cron.previous_time_from(time_zone_now)
+ end_freeze = end_freeze_cron.next_time_from(start_freeze)
- def previous_time(cron_parser)
- cron_parser.previous_time_from(time_zone_now)
+ start_freeze <= time_zone_now && time_zone_now <= end_freeze
end
- def next_time(cron_parser)
- cron_parser.next_time_from(time_zone_now)
- end
+ private
def time_zone_now
@time_zone_now ||= Time.zone.now
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index 71d33f0bb63..922806a21c3 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -2,6 +2,7 @@
module Ci
class JobArtifact < Ci::ApplicationRecord
+ include Ci::Partitionable
include IgnorableColumns
include AfterCommitQueue
include ObjectStorage::BackgroundMove
@@ -9,6 +10,7 @@ module Ci
include UsageStatistics
include Sortable
include Artifactable
+ include Lockable
include FileStoreMounter
include EachBatch
include Gitlab::Utils::StrongMemoize
@@ -22,8 +24,7 @@ module Ci
accessibility: %w[accessibility],
coverage: %w[cobertura],
codequality: %w[codequality],
- terraform: %w[terraform],
- sbom: %w[cyclonedx]
+ terraform: %w[terraform]
}.freeze
DEFAULT_FILE_NAMES = {
@@ -54,7 +55,7 @@ module Ci
requirements: 'requirements.json',
coverage_fuzzing: 'gl-coverage-fuzzing.json',
api_fuzzing: 'gl-api-fuzzing-report.json',
- cyclonedx: 'gl-sbom.cdx.zip'
+ cyclonedx: 'gl-sbom.cdx.json'
}.freeze
INTERNAL_TYPES = {
@@ -72,6 +73,7 @@ module Ci
cobertura: :gzip,
cluster_applications: :gzip, # DEPRECATED: https://gitlab.com/gitlab-org/gitlab/-/issues/361094
lsif: :zip,
+ cyclonedx: :gzip,
# Security reports and license scanning reports are raw artifacts
# because they used to be fetched by the frontend, but this is not the case anymore.
@@ -94,8 +96,7 @@ module Ci
terraform: :raw,
requirements: :raw,
coverage_fuzzing: :raw,
- api_fuzzing: :raw,
- cyclonedx: :zip
+ api_fuzzing: :raw
}.freeze
DOWNLOADABLE_TYPES = %w[
@@ -134,14 +135,16 @@ module Ci
mount_file_store_uploader JobArtifactUploader, skip_store_file: true
+ before_save :set_size, if: :file_changed?
after_save :store_file_in_transaction!, unless: :store_after_commit?
after_commit :store_file_after_transaction!, on: [:create, :update], if: :store_after_commit?
+ validates :job, presence: true
validates :file_format, presence: true, unless: :trace?, 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
+ partitionable scope: :job
scope :not_expired, -> { where('expire_at IS NULL OR expire_at > ?', Time.current) }
scope :for_sha, ->(sha, project_id) { joins(job: :pipeline).where(ci_pipelines: { sha: sha, project_id: project_id }) }
@@ -160,12 +163,6 @@ module Ci
where(file_type: types)
end
- REPORT_FILE_TYPES.each do |report_type, file_types|
- scope "#{report_type}_reports", -> do
- with_file_types(file_types)
- end
- end
-
scope :all_reports, -> do
with_file_types(REPORT_TYPES.keys.map(&:to_s))
end
@@ -229,25 +226,20 @@ module Ci
hashed_path: 2
}
- # `locked` will be populated from the source of truth on Ci::Pipeline
- # in order to clean up expired job artifacts in a performant way.
- # The values should be the same as `Ci::Pipeline.lockeds` with the
- # additional value of `unknown` to indicate rows that have not
- # yet been populated from the parent Ci::Pipeline
- enum locked: {
- unlocked: 0,
- artifacts_locked: 1,
- unknown: 2
- }, _prefix: :artifact
-
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
end
+ def self.of_report_type(report_type)
+ file_types = file_types_for_report(report_type)
+
+ with_file_types(file_types)
+ end
+
def self.file_types_for_report(report_type)
- REPORT_FILE_TYPES.fetch(report_type)
+ REPORT_FILE_TYPES.fetch(report_type) { raise ArgumentError, "Unrecognized report type: #{report_type}" }
end
def self.associated_file_types_for(file_type)
diff --git a/app/models/ci/job_token/scope.rb b/app/models/ci/job_token/scope.rb
index 3a5765aa00c..26a49d6a730 100644
--- a/app/models/ci/job_token/scope.rb
+++ b/app/models/ci/job_token/scope.rb
@@ -30,10 +30,7 @@ module Ci
end
def all_projects
- Project.from_union([
- Project.id_in(source_project),
- Project.id_in(target_project_ids)
- ], remove_duplicates: false)
+ Project.from_union(target_projects, remove_duplicates: false)
end
private
@@ -41,6 +38,13 @@ module Ci
def target_project_ids
Ci::JobToken::ProjectScopeLink.from_project(source_project).pluck(:target_project_id)
end
+
+ def target_projects
+ [
+ Project.id_in(source_project),
+ Project.id_in(target_project_ids)
+ ]
+ end
end
end
end
diff --git a/app/models/ci/namespace_mirror.rb b/app/models/ci/namespace_mirror.rb
index e8f08db597f..5ea51fbe0a7 100644
--- a/app/models/ci/namespace_mirror.rb
+++ b/app/models/ci/namespace_mirror.rb
@@ -43,20 +43,6 @@ module Ci
upsert({ namespace_id: event.namespace_id, traversal_ids: traversal_ids },
unique_by: :namespace_id)
-
- # It won't be necessary once we remove `sync_traversal_ids`.
- # More info: https://gitlab.com/gitlab-org/gitlab/-/issues/347541
- sync_children_namespaces!(event.namespace_id, traversal_ids)
- end
-
- private
-
- def sync_children_namespaces!(namespace_id, traversal_ids)
- by_group_and_descendants(namespace_id)
- .where.not(namespace_id: namespace_id)
- .update_all(
- "traversal_ids = ARRAY[#{sanitize_sql(traversal_ids.join(','))}]::int[] || traversal_ids[array_position(traversal_ids, #{sanitize_sql(namespace_id)}) + 1:]"
- )
end
end
end
diff --git a/app/models/ci/partition.rb b/app/models/ci/partition.rb
new file mode 100644
index 00000000000..d773038df01
--- /dev/null
+++ b/app/models/ci/partition.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Ci
+ class Partition < Ci::ApplicationRecord
+ end
+end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index a94330270e2..1e328c3c573 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -2,6 +2,7 @@
module Ci
class Pipeline < Ci::ApplicationRecord
+ include Ci::Partitionable
include Ci::HasStatus
include Importable
include AfterCommitQueue
@@ -31,7 +32,7 @@ module Ci
sha_attribute :source_sha
sha_attribute :target_sha
-
+ partitionable scope: ->(_) { Ci::Pipeline.current_partition_value }
# Ci::CreatePipelineService returns Ci::Pipeline so this is the only place
# where we can pass additional information from the service. This accessor
# is used for storing the processed metadata for linting purposes.
@@ -296,6 +297,12 @@ module Ci
end
end
+ after_transition any => ::Ci::Pipeline.completed_statuses do |pipeline|
+ pipeline.run_after_commit do
+ ::Ci::JobArtifacts::TrackArtifactReportWorker.perform_async(pipeline.id)
+ end
+ end
+
after_transition any => ::Ci::Pipeline.stopped_statuses do |pipeline|
pipeline.run_after_commit do
pipeline.persistent_ref.delete
@@ -422,6 +429,10 @@ module Ci
end
def self.jobs_count_in_alive_pipelines
+ created_after(24.hours.ago).alive.joins(:statuses).count
+ end
+
+ def self.builds_count_in_alive_pipelines
created_after(24.hours.ago).alive.joins(:builds).count
end
@@ -472,8 +483,12 @@ module Ci
@auto_devops_pipelines_completed_total ||= Gitlab::Metrics.counter(:auto_devops_pipelines_completed_total, 'Number of completed auto devops pipelines')
end
+ def self.current_partition_value
+ 100
+ end
+
def uses_needs?
- builds.where(scheduling_type: :dag).any?
+ processables.where(scheduling_type: :dag).any?
end
def stages_count
@@ -605,7 +620,7 @@ module Ci
if cascade_to_children
# cancel any bridges that could spin up new child pipelines
- cancel_jobs(bridges_in_self_and_descendants.cancelable, retries: retries, auto_canceled_by_pipeline_id: auto_canceled_by_pipeline_id)
+ cancel_jobs(bridges_in_self_and_project_descendants.cancelable, retries: retries, auto_canceled_by_pipeline_id: auto_canceled_by_pipeline_id)
cancel_children(auto_canceled_by_pipeline_id: auto_canceled_by_pipeline_id, execute_async: execute_async)
end
end
@@ -937,26 +952,26 @@ module Ci
).base_and_descendants.select(:id)
end
- def build_with_artifacts_in_self_and_descendants(name)
- builds_in_self_and_descendants
+ def build_with_artifacts_in_self_and_project_descendants(name)
+ builds_in_self_and_project_descendants
.ordered_by_pipeline # find job in hierarchical order
.with_downloadable_artifacts
.find_by_name(name)
end
- def builds_in_self_and_descendants
- Ci::Build.latest.where(pipeline: self_and_descendants)
+ def builds_in_self_and_project_descendants
+ Ci::Build.latest.where(pipeline: self_and_project_descendants)
end
- def bridges_in_self_and_descendants
- Ci::Bridge.latest.where(pipeline: self_and_descendants)
+ def bridges_in_self_and_project_descendants
+ Ci::Bridge.latest.where(pipeline: self_and_project_descendants)
end
- def environments_in_self_and_descendants(deployment_status: nil)
+ def environments_in_self_and_project_descendants(deployment_status: nil)
# We limit to 100 unique environments for application safety.
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/340781#note_699114700
expanded_environment_names =
- builds_in_self_and_descendants.joins(:metadata)
+ builds_in_self_and_project_descendants.joins(:metadata)
.where.not('ci_builds_metadata.expanded_environment_name' => nil)
.distinct('ci_builds_metadata.expanded_environment_name')
.limit(100)
@@ -971,17 +986,22 @@ module Ci
end
# With multi-project and parent-child pipelines
- def all_pipelines_in_hierarchy
+ def self_and_downstreams
+ object_hierarchy.base_and_descendants
+ end
+
+ # With multi-project and parent-child pipelines
+ def upstream_and_all_downstreams
object_hierarchy.all_objects
end
# With only parent-child pipelines
- def self_and_ancestors
+ def self_and_project_ancestors
object_hierarchy(project_condition: :same).base_and_ancestors
end
# With only parent-child pipelines
- def self_and_descendants
+ def self_and_project_descendants
object_hierarchy(project_condition: :same).base_and_descendants
end
@@ -990,8 +1010,8 @@ module Ci
object_hierarchy(project_condition: :same).descendants
end
- def self_and_descendants_complete?
- self_and_descendants.all?(&:complete?)
+ def self_and_project_descendants_complete?
+ self_and_project_descendants.all?(&:complete?)
end
# Follow the parent-child relationships and return the top-level parent
@@ -1006,7 +1026,12 @@ module Ci
# Follow the upstream pipeline relationships, regardless of multi-project or
# parent-child, and return the top-level ancestor.
def upstream_root
- object_hierarchy.base_and_ancestors(hierarchy_order: :desc).first
+ @upstream_root ||= object_hierarchy.base_and_ancestors(hierarchy_order: :desc).first
+ end
+
+ # Applies to all parent-child and multi-project pipelines
+ def complete_hierarchy_count
+ upstream_root.self_and_downstreams.count
end
def bridge_triggered?
@@ -1052,11 +1077,11 @@ module Ci
end
def latest_test_report_builds
- latest_report_builds(Ci::JobArtifact.test_reports).preload(:project, :metadata)
+ latest_report_builds(Ci::JobArtifact.of_report_type(:test)).preload(:project, :metadata)
end
- def latest_report_builds_in_self_and_descendants(reports_scope = ::Ci::JobArtifact.all_reports)
- builds_in_self_and_descendants.with_artifacts(reports_scope)
+ def latest_report_builds_in_self_and_project_descendants(reports_scope = ::Ci::JobArtifact.all_reports)
+ builds_in_self_and_project_descendants.with_artifacts(reports_scope)
end
def builds_with_coverage
@@ -1068,10 +1093,14 @@ module Ci
end
def has_reports?(reports_scope)
+ latest_report_builds(reports_scope).exists?
+ end
+
+ def complete_and_has_reports?(reports_scope)
if Feature.enabled?(:mr_show_reports_immediately, project, type: :development)
latest_report_builds(reports_scope).exists?
else
- complete? && latest_report_builds(reports_scope).exists?
+ complete? && has_reports?(reports_scope)
end
end
@@ -1084,7 +1113,7 @@ module Ci
end
def can_generate_codequality_reports?
- has_reports?(Ci::JobArtifact.codequality_reports)
+ complete_and_has_reports?(Ci::JobArtifact.of_report_type(:codequality))
end
def test_report_summary
@@ -1103,7 +1132,7 @@ module Ci
def accessibility_reports
Gitlab::Ci::Reports::AccessibilityReports.new.tap do |accessibility_reports|
- latest_report_builds(Ci::JobArtifact.accessibility_reports).each do |build|
+ latest_report_builds(Ci::JobArtifact.of_report_type(:accessibility)).each do |build|
build.collect_accessibility_reports!(accessibility_reports)
end
end
@@ -1111,7 +1140,7 @@ module Ci
def codequality_reports
Gitlab::Ci::Reports::CodequalityReports.new.tap do |codequality_reports|
- latest_report_builds(Ci::JobArtifact.codequality_reports).each do |build|
+ latest_report_builds(Ci::JobArtifact.of_report_type(:codequality)).each do |build|
build.collect_codequality_reports!(codequality_reports)
end
end
@@ -1119,7 +1148,7 @@ module Ci
def terraform_reports
::Gitlab::Ci::Reports::TerraformReports.new.tap do |terraform_reports|
- latest_report_builds(::Ci::JobArtifact.terraform_reports).each do |build|
+ latest_report_builds(::Ci::JobArtifact.of_report_type(:terraform)).each do |build|
build.collect_terraform_reports!(terraform_reports)
end
end
@@ -1307,7 +1336,7 @@ module Ci
def has_test_reports?
strong_memoize(:has_test_reports) do
- has_reports?(::Ci::JobArtifact.test_reports)
+ has_reports?(::Ci::JobArtifact.of_report_type(:test))
end
end
diff --git a/app/models/ci/pipeline_artifact.rb b/app/models/ci/pipeline_artifact.rb
index cdc3d69f754..6d22a875aab 100644
--- a/app/models/ci/pipeline_artifact.rb
+++ b/app/models/ci/pipeline_artifact.rb
@@ -7,6 +7,7 @@ module Ci
include UpdateProjectStatistics
include Artifactable
include FileStoreMounter
+ include Lockable
include Presentable
FILE_SIZE_LIMIT = 10.megabytes.freeze
@@ -52,7 +53,7 @@ module Ci
find_by(file_type: file_type)
end
- def create_or_replace_for_pipeline!(pipeline:, file_type:, file:, size:)
+ def create_or_replace_for_pipeline!(pipeline:, file_type:, file:, size:, locked: :unknown)
transaction do
pipeline.pipeline_artifacts.find_by_file_type(file_type)&.destroy!
@@ -62,7 +63,8 @@ module Ci
size: size,
file: file,
file_format: REPORT_TYPES[file_type],
- expire_at: EXPIRATION_DATE.from_now
+ expire_at: EXPIRATION_DATE.from_now,
+ locked: locked
)
end
rescue ActiveRecord::ActiveRecordError => err
diff --git a/app/models/ci/pipeline_variable.rb b/app/models/ci/pipeline_variable.rb
index 3dca77af051..6e4418bc360 100644
--- a/app/models/ci/pipeline_variable.rb
+++ b/app/models/ci/pipeline_variable.rb
@@ -2,13 +2,16 @@
module Ci
class PipelineVariable < Ci::ApplicationRecord
+ include Ci::Partitionable
include Ci::HasVariable
belongs_to :pipeline
+ partitionable scope: :pipeline
+
alias_attribute :secret_value, :value
- validates :key, presence: true
+ validates :key, :pipeline, presence: true
def hook_attrs
{ key: key, value: value }
diff --git a/app/models/ci/processable.rb b/app/models/ci/processable.rb
index a2ff49077be..09dc9d4bce1 100644
--- a/app/models/ci/processable.rb
+++ b/app/models/ci/processable.rb
@@ -3,6 +3,7 @@
module Ci
class Processable < ::CommitStatus
include Gitlab::Utils::StrongMemoize
+ include FromUnion
extend ::Gitlab::Utils::Override
has_one :resource, class_name: 'Ci::Resource', foreign_key: 'build_id', inverse_of: :processable
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 6c3754d84d0..28d9edcc135 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -8,15 +8,12 @@ module Ci
include ChronicDurationAttribute
include FromUnion
include TokenAuthenticatable
- include IgnorableColumns
include FeatureGate
include Gitlab::Utils::StrongMemoize
include TaggableQueries
include Presentable
include EachBatch
- ignore_column :semver, remove_with: '15.4', remove_after: '2022-08-22'
-
add_authentication_token_field :token, encrypted: :optional, expires_at: :compute_token_expiration, expiration_enforced?: :token_expiration_enforced?
enum access_level: {
@@ -351,6 +348,12 @@ module Ci
end
end
+ def owner_project
+ return unless project_type?
+
+ runner_projects.order(:id).first.project
+ end
+
def belongs_to_one_project?
runner_projects.count == 1
end
@@ -359,14 +362,6 @@ module Ci
runner_projects.limit(2).count(:all) > 1
end
- def assigned_to_group?
- runner_namespaces.any?
- end
-
- def assigned_to_project?
- runner_projects.any?
- end
-
def match_build_if_online?(build)
active? && online? && matches_build?(build)
end
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index f03d1e96a4b..46a9e3f6494 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -2,22 +2,31 @@
module Ci
class Stage < Ci::ApplicationRecord
+ include Ci::Partitionable
include Importable
include Ci::HasStatus
include Gitlab::OptimisticLocking
include Presentable
+ partitionable scope: :pipeline
+
enum status: Ci::HasStatus::STATUSES_ENUM
belongs_to :project
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 :retried_statuses, -> { ordered.retried }, 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
+ has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id, inverse_of: :ci_stage
+ has_many :latest_statuses, -> { ordered.latest },
+ class_name: 'CommitStatus',
+ foreign_key: :stage_id,
+ inverse_of: :ci_stage
+ has_many :retried_statuses, -> { ordered.retried },
+ class_name: 'CommitStatus',
+ foreign_key: :stage_id,
+ inverse_of: :ci_stage
+ has_many :processables, class_name: 'Ci::Processable', foreign_key: :stage_id, inverse_of: :ci_stage
+ has_many :builds, foreign_key: :stage_id, inverse_of: :ci_stage
+ has_many :bridges, foreign_key: :stage_id, inverse_of: :ci_stage
scope :ordered, -> { order(position: :asc) }
scope :in_pipelines, ->(pipelines) { where(pipeline: pipelines) }
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index c4db4754c52..1092b9c9564 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -6,6 +6,8 @@ module Ci
include Limitable
include IgnorableColumns
+ TRIGGER_TOKEN_PREFIX = 'glptt-'
+
ignore_column :ref, remove_with: '15.4', remove_after: '2022-08-22'
self.limit_name = 'pipeline_triggers'
@@ -22,7 +24,7 @@ module Ci
before_validation :set_default_values
def set_default_values
- self.token = SecureRandom.hex(15) if self.token.blank?
+ self.token = "#{TRIGGER_TOKEN_PREFIX}#{SecureRandom.hex(20)}" if self.token.blank?
end
def last_trigger_request
@@ -34,7 +36,7 @@ module Ci
end
def short_token
- token[0...4] if token.present?
+ token.delete_prefix(TRIGGER_TOKEN_PREFIX)[0...4] if token.present?
end
def can_access_project?