diff options
Diffstat (limited to 'app/models/ci')
-rw-r--r-- | app/models/ci/bridge.rb | 8 | ||||
-rw-r--r-- | app/models/ci/build.rb | 137 | ||||
-rw-r--r-- | app/models/ci/build_metadata.rb | 3 | ||||
-rw-r--r-- | app/models/ci/freeze_period_status.rb | 28 | ||||
-rw-r--r-- | app/models/ci/job_artifact.rb | 40 | ||||
-rw-r--r-- | app/models/ci/job_token/scope.rb | 12 | ||||
-rw-r--r-- | app/models/ci/namespace_mirror.rb | 14 | ||||
-rw-r--r-- | app/models/ci/partition.rb | 6 | ||||
-rw-r--r-- | app/models/ci/pipeline.rb | 81 | ||||
-rw-r--r-- | app/models/ci/pipeline_artifact.rb | 6 | ||||
-rw-r--r-- | app/models/ci/pipeline_variable.rb | 5 | ||||
-rw-r--r-- | app/models/ci/processable.rb | 1 | ||||
-rw-r--r-- | app/models/ci/runner.rb | 17 | ||||
-rw-r--r-- | app/models/ci/stage.rb | 21 | ||||
-rw-r--r-- | app/models/ci/trigger.rb | 6 |
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? |