diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-20 21:42:06 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-20 21:42:06 +0300 |
commit | 6e4e1050d9dba2b7b2523fdd1768823ab85feef4 (patch) | |
tree | 78be5963ec075d80116a932011d695dd33910b4e /app/models/ci | |
parent | 1ce776de4ae122aba3f349c02c17cebeaa8ecf07 (diff) |
Add latest changes from gitlab-org/gitlab@13-3-stable-ee
Diffstat (limited to 'app/models/ci')
-rw-r--r-- | app/models/ci/build.rb | 68 | ||||
-rw-r--r-- | app/models/ci/build_trace_chunk.rb | 39 | ||||
-rw-r--r-- | app/models/ci/build_trace_chunks/database.rb | 18 | ||||
-rw-r--r-- | app/models/ci/build_trace_chunks/fog.rb | 20 | ||||
-rw-r--r-- | app/models/ci/build_trace_chunks/redis.rb | 38 | ||||
-rw-r--r-- | app/models/ci/group.rb | 12 | ||||
-rw-r--r-- | app/models/ci/instance_variable.rb | 10 | ||||
-rw-r--r-- | app/models/ci/job_artifact.rb | 41 | ||||
-rw-r--r-- | app/models/ci/legacy_stage.rb | 2 | ||||
-rw-r--r-- | app/models/ci/pipeline.rb | 86 | ||||
-rw-r--r-- | app/models/ci/pipeline_artifact.rb | 37 | ||||
-rw-r--r-- | app/models/ci/pipeline_enums.rb | 4 | ||||
-rw-r--r-- | app/models/ci/ref.rb | 12 | ||||
-rw-r--r-- | app/models/ci/runner.rb | 2 | ||||
-rw-r--r-- | app/models/ci/stage.rb | 4 |
15 files changed, 208 insertions, 185 deletions
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 6c90645e997..af4e6bb0494 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -73,8 +73,7 @@ module Ci return unless has_environment? strong_memoize(:persisted_environment) do - deployment&.environment || - Environment.find_by(name: expanded_environment_name, project: project) + Environment.find_by(name: expanded_environment_name, project: project) end end @@ -351,7 +350,7 @@ module Ci after_transition any => [:failed] do |build| next unless build.project - if build.retry_failure? + if build.auto_retry_allowed? begin Ci::Build.retry(build, build.user) rescue Gitlab::Access::AccessDeniedError => ex @@ -373,6 +372,10 @@ module Ci end end + def auto_retry_allowed? + auto_retry.allowed? + end + def detailed_status(current_user) Gitlab::Ci::Status::Build::Factory .new(self, current_user) @@ -439,27 +442,6 @@ module Ci pipeline.builds.retried.where(name: self.name).count end - def retry_failure? - max_allowed_retries = nil - max_allowed_retries ||= options_retry_max if retry_on_reason_or_always? - max_allowed_retries ||= DEFAULT_RETRIES.fetch(failure_reason.to_sym, 0) - - max_allowed_retries > 0 && retries_count < max_allowed_retries - end - - def options_retry_max - options_retry[:max] - end - - def options_retry_when - options_retry.fetch(:when, ['always']) - end - - def retry_on_reason_or_always? - options_retry_when.include?(failure_reason.to_s) || - options_retry_when.include?('always') - end - def any_unmet_prerequisites? prerequisites.present? end @@ -474,8 +456,7 @@ module Ci strong_memoize(:expanded_environment_name) do # We're using a persisted expanded environment name in order to avoid # variable expansion per request. - if Feature.enabled?(:ci_persisted_expanded_environment_name, project, default_enabled: true) && - metadata&.expanded_environment_name.present? + if metadata&.expanded_environment_name.present? metadata.expanded_environment_name else ExpandVariables.expand(environment, -> { simple_variables }) @@ -543,8 +524,6 @@ module Ci end end - CI_REGISTRY_USER = 'gitlab-ci-token' - def persisted_variables Gitlab::Ci::Variables::Collection.new.tap do |variables| break variables unless persisted? @@ -556,7 +535,7 @@ module Ci .append(key: 'CI_JOB_TOKEN', value: token.to_s, public: false, masked: true) .append(key: 'CI_BUILD_ID', value: id.to_s) .append(key: 'CI_BUILD_TOKEN', value: token.to_s, public: false, masked: true) - .append(key: 'CI_REGISTRY_USER', value: CI_REGISTRY_USER) + .append(key: 'CI_REGISTRY_USER', value: ::Gitlab::Auth::CI_JOB_USER) .append(key: 'CI_REGISTRY_PASSWORD', value: token.to_s, public: false, masked: true) .append(key: 'CI_REPOSITORY_URL', value: repo_url.to_s, public: false) .concat(deploy_token_variables) @@ -615,7 +594,7 @@ module Ci def repo_url return unless token - auth = "gitlab-ci-token:#{token}@" + auth = "#{::Gitlab::Auth::CI_JOB_USER}:#{token}@" project.http_url_to_repo.sub(%r{^https?://}) do |prefix| prefix + auth end @@ -668,6 +647,13 @@ module Ci !artifacts_expired? && artifacts_file&.exists? end + # This method is similar to #artifacts? but it includes the artifacts + # locking mechanics. A new method was created to prevent breaking existing + # behavior and avoid introducing N+1s. + def available_artifacts? + (!artifacts_expired? || pipeline.artifacts_locked?) && job_artifacts_archive&.exists? + end + def artifacts_metadata? artifacts? && artifacts_metadata&.exists? end @@ -878,8 +864,7 @@ module Ci end def multi_build_steps? - options.dig(:release)&.any? && - Gitlab::Ci::Features.release_generation_enabled? + options.dig(:release)&.any? end def hide_secrets(trace) @@ -962,6 +947,12 @@ module Ci private + def auto_retry + strong_memoize(:auto_retry) do + Gitlab::Ci::Build::AutoRetry.new(self) + end + end + def dependencies strong_memoize(:dependencies) do Ci::BuildDependencies.new(self) @@ -1017,19 +1008,6 @@ module Ci end end - # The format of the retry option changed in GitLab 11.5: Before it was - # integer only, after it is a hash. New builds are created with the new - # format, but builds created before GitLab 11.5 and saved in database still - # have the old integer only format. This method returns the retry option - # normalized as a hash in 11.5+ format. - def options_retry - strong_memoize(:options_retry) do - value = options&.dig(:retry) - value = value.is_a?(Integer) ? { max: value } : value.to_h - value.with_indifferent_access - end - end - def has_expiring_artifacts? artifacts_expire_at.present? && artifacts_expire_at > Time.current end diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb index 0a7a0e0772b..407802baf09 100644 --- a/app/models/ci/build_trace_chunk.rb +++ b/app/models/ci/build_trace_chunk.rb @@ -75,18 +75,16 @@ module Ci def append(new_data, offset) raise ArgumentError, 'New data is missing' unless new_data - raise ArgumentError, 'Offset is out of range' if offset > size || offset < 0 + raise ArgumentError, 'Offset is out of range' if offset < 0 || offset > size raise ArgumentError, 'Chunk size overflow' if CHUNK_SIZE < (offset + new_data.bytesize) - in_lock(*lock_params) do # Write operation is atomic - unsafe_set_data!(data.byteslice(0, offset) + new_data) - end + in_lock(*lock_params) { unsafe_append_data!(new_data, offset) } schedule_to_persist if full? end def size - data&.bytesize.to_i + @size ||= current_store.size(self) || data&.bytesize end def start_offset @@ -118,7 +116,7 @@ module Ci raise FailedToPersistDataError, 'Data is not fulfilled in a bucket' end - old_store_class = self.class.get_store_class(data_store) + old_store_class = current_store self.raw_data = nil self.data_store = new_store @@ -128,16 +126,33 @@ module Ci end def get_data - self.class.get_store_class(data_store).data(self)&.force_encoding(Encoding::BINARY) # Redis/Database return UTF-8 string as default - rescue Excon::Error::NotFound - # If the data store is :fog and the file does not exist in the object storage, this method returns nil. + current_store.data(self)&.force_encoding(Encoding::BINARY) # Redis/Database return UTF-8 string as default end def unsafe_set_data!(value) raise ArgumentError, 'New data size exceeds chunk size' if value.bytesize > CHUNK_SIZE - self.class.get_store_class(data_store).set_data(self, value) + current_store.set_data(self, value) + @data = value + @size = value.bytesize + + save! if changed? + end + + def unsafe_append_data!(value, offset) + new_size = value.bytesize + offset + + if new_size > CHUNK_SIZE + raise ArgumentError, 'New data size exceeds chunk size' + end + + current_store.append_data(self, value, offset).then do |stored| + raise ArgumentError, 'Trace appended incorrectly' if stored != new_size + end + + @data = nil + @size = new_size save! if changed? end @@ -156,6 +171,10 @@ module Ci size == CHUNK_SIZE end + def current_store + self.class.get_store_class(data_store) + end + def lock_params ["trace_write:#{build_id}:chunks:#{chunk_index}", { ttl: WRITE_LOCK_TTL, diff --git a/app/models/ci/build_trace_chunks/database.rb b/app/models/ci/build_trace_chunks/database.rb index 73cb8abf381..3b8e23510d9 100644 --- a/app/models/ci/build_trace_chunks/database.rb +++ b/app/models/ci/build_trace_chunks/database.rb @@ -19,8 +19,22 @@ module Ci model.raw_data end - def set_data(model, data) - model.raw_data = data + def set_data(model, new_data) + model.raw_data = new_data + end + + def append_data(model, new_data, offset) + if offset > 0 + truncated_data = data(model).to_s.byteslice(0, offset) + new_data = truncated_data + new_data + end + + model.raw_data = new_data + model.raw_data.to_s.bytesize + end + + def size(model) + data(model).to_s.bytesize end def delete_data(model) diff --git a/app/models/ci/build_trace_chunks/fog.rb b/app/models/ci/build_trace_chunks/fog.rb index a849bd08427..b1e9fd1faeb 100644 --- a/app/models/ci/build_trace_chunks/fog.rb +++ b/app/models/ci/build_trace_chunks/fog.rb @@ -9,10 +9,26 @@ module Ci def data(model) connection.get_object(bucket_name, key(model))[:body] + rescue Excon::Error::NotFound + # If the object does not exist in the object storage, this method returns nil. end - def set_data(model, data) - connection.put_object(bucket_name, key(model), data) + def set_data(model, new_data) + connection.put_object(bucket_name, key(model), new_data) + end + + def append_data(model, new_data, offset) + if offset > 0 + truncated_data = data(model).to_s.byteslice(0, offset) + new_data = truncated_data + new_data + end + + set_data(model, new_data) + new_data.bytesize + end + + def size(model) + data(model).to_s.bytesize end def delete_data(model) diff --git a/app/models/ci/build_trace_chunks/redis.rb b/app/models/ci/build_trace_chunks/redis.rb index c3864f78b01..0ae563f6ce8 100644 --- a/app/models/ci/build_trace_chunks/redis.rb +++ b/app/models/ci/build_trace_chunks/redis.rb @@ -4,6 +4,32 @@ module Ci module BuildTraceChunks class Redis CHUNK_REDIS_TTL = 1.week + LUA_APPEND_CHUNK = <<~EOS.freeze + local key, new_data, offset = KEYS[1], ARGV[1], ARGV[2] + local length = new_data:len() + local expire = #{CHUNK_REDIS_TTL.seconds} + local current_size = redis.call("strlen", key) + offset = tonumber(offset) + + if offset == 0 then + -- overwrite everything + redis.call("set", key, new_data, "ex", expire) + return redis.call("strlen", key) + elseif offset > current_size then + -- offset range violation + return -1 + elseif offset + length >= current_size then + -- efficiently append or overwrite and append + redis.call("expire", key, expire) + return redis.call("setrange", key, offset, new_data) + else + -- append and truncate + local current_data = redis.call("get", key) + new_data = current_data:sub(1, offset) .. new_data + redis.call("set", key, new_data, "ex", expire) + return redis.call("strlen", key) + end + EOS def available? true @@ -21,6 +47,18 @@ module Ci end end + def append_data(model, new_data, offset) + Gitlab::Redis::SharedState.with do |redis| + redis.eval(LUA_APPEND_CHUNK, keys: [key(model)], argv: [new_data, offset]) + end + end + + def size(model) + Gitlab::Redis::SharedState.with do |redis| + redis.strlen(key(model)) + end + end + def delete_data(model) delete_keys([[model.build_id, model.chunk_index]]) end diff --git a/app/models/ci/group.rb b/app/models/ci/group.rb index 779c6c0396f..f0c035635b9 100644 --- a/app/models/ci/group.rb +++ b/app/models/ci/group.rb @@ -24,15 +24,9 @@ module Ci def status strong_memoize(:status) do - if ::Gitlab::Ci::Features.composite_status?(project) - Gitlab::Ci::Status::Composite - .new(@jobs) - .status - else - CommitStatus - .where(id: @jobs) - .legacy_status - end + Gitlab::Ci::Status::Composite + .new(@jobs) + .status end end diff --git a/app/models/ci/instance_variable.rb b/app/models/ci/instance_variable.rb index 628749b32cb..e083caa8751 100644 --- a/app/models/ci/instance_variable.rb +++ b/app/models/ci/instance_variable.rb @@ -14,12 +14,14 @@ module Ci alias_attribute :secret_value, :value validates :key, uniqueness: { - message: "(%{value}) has already been taken" + message: -> (object, data) { _("(%{value}) has already been taken") } } - validates :encrypted_value, length: { - maximum: 1024, - too_long: 'The encrypted value of the provided variable exceeds %{count} bytes. Variables over 700 characters risk exceeding the limit.' + validates :value, length: { + maximum: 10_000, + too_long: -> (object, data) do + _('The value of the provided variable exceeds the %{count} character limit') + end } scope :unprotected, -> { where(protected: false) } diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index dbeba1ece31..75c3ce98c95 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -8,6 +8,8 @@ module Ci include UsageStatistics include Sortable include IgnorableColumns + include Artifactable + include FileStoreMounter extend Gitlab::Ci::Model NotSupportedAdapterError = Class.new(StandardError) @@ -114,7 +116,7 @@ module Ci belongs_to :project belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id - mount_uploader :file, JobArtifactUploader + mount_file_store_uploader JobArtifactUploader validates :file_format, presence: true, unless: :trace?, on: :create validate :validate_supported_file_format!, on: :create @@ -123,8 +125,6 @@ module Ci update_project_statistics project_statistics_name: :build_artifacts_size - after_save :update_file_store, if: :saved_change_to_file? - scope :not_expired, -> { where('expire_at IS NULL OR expire_at > ?', Time.current) } scope :with_files_stored_locally, -> { where(file_store: ::JobArtifactUploader::Store::LOCAL) } scope :with_files_stored_remotely, -> { where(file_store: ::JobArtifactUploader::Store::REMOTE) } @@ -200,12 +200,6 @@ module Ci load_performance: 25 ## EE-specific } - enum file_format: { - raw: 1, - zip: 2, - gzip: 3 - }, _suffix: true - # `file_location` indicates where actual files are stored. # Ideally, actual files should be stored in the same directory, and use the same # convention to generate its path. However, sometimes we can't do so due to backward-compatibility. @@ -220,11 +214,6 @@ module Ci hashed_path: 2 } - FILE_FORMAT_ADAPTERS = { - gzip: Gitlab::Ci::Build::Artifacts::Adapters::GzipStream, - raw: Gitlab::Ci::Build::Artifacts::Adapters::RawStream - }.freeze - def validate_supported_file_format! return if Feature.disabled?(:drop_license_management_artifact, project, default_enabled: true) @@ -239,12 +228,6 @@ module Ci end end - def update_file_store - # The file.object_store is set during `uploader.store!` - # which happens after object is inserted/updated - self.update_column(:file_store, file.object_store) - end - def self.associated_file_types_for(file_type) return unless file_types.include?(file_type) @@ -284,7 +267,7 @@ module Ci def expire_in=(value) self.expire_at = if value - ChronicDuration.parse(value)&.seconds&.from_now + ::Gitlab::Ci::Build::Artifacts::ExpireInParser.new(value).seconds_from_now end end @@ -303,16 +286,12 @@ module Ci end def self.max_artifact_size(type:, project:) - max_size = if Feature.enabled?(:ci_max_artifact_size_per_type, project, default_enabled: false) - limit_name = "#{PLAN_LIMIT_PREFIX}#{type}" - - project.actual_limits.limit_for( - limit_name, - alternate_limit: -> { project.closest_setting(:max_artifacts_size) } - ) - else - project.closest_setting(:max_artifacts_size) - end + limit_name = "#{PLAN_LIMIT_PREFIX}#{type}" + + max_size = project.actual_limits.limit_for( + limit_name, + alternate_limit: -> { project.closest_setting(:max_artifacts_size) } + ) max_size&.megabytes.to_i end diff --git a/app/models/ci/legacy_stage.rb b/app/models/ci/legacy_stage.rb index 250306e2be4..df4368eccd5 100644 --- a/app/models/ci/legacy_stage.rb +++ b/app/models/ci/legacy_stage.rb @@ -32,7 +32,7 @@ module Ci end def status - @status ||= statuses.latest.slow_composite_status(project: project) + @status ||= statuses.latest.composite_status end def detailed_status(current_user) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index d4b439d648f..7762328d274 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -57,12 +57,12 @@ module Ci # the merge request's latest commit. has_many :merge_requests_as_head_pipeline, foreign_key: "head_pipeline_id", class_name: 'MergeRequest' - has_many :pending_builds, -> { pending }, foreign_key: :commit_id, class_name: 'Ci::Build' + has_many :pending_builds, -> { pending }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline has_many :failed_builds, -> { latest.failed }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline - has_many :retryable_builds, -> { latest.failed_or_canceled.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build' + has_many :retryable_builds, -> { latest.failed_or_canceled.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline has_many :cancelable_statuses, -> { cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus' - has_many :manual_actions, -> { latest.manual_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build' - has_many :scheduled_actions, -> { latest.scheduled_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build' + has_many :manual_actions, -> { latest.manual_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline + has_many :scheduled_actions, -> { latest.scheduled_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline has_many :auto_canceled_pipelines, class_name: 'Ci::Pipeline', foreign_key: 'auto_canceled_by_id' has_many :auto_canceled_jobs, class_name: 'CommitStatus', foreign_key: 'auto_canceled_by_id' @@ -83,6 +83,7 @@ module Ci has_many :daily_build_group_report_results, class_name: 'Ci::DailyBuildGroupReportResult', foreign_key: :last_pipeline_id has_many :latest_builds_report_results, through: :latest_builds, source: :report_results + has_many :pipeline_artifacts, class_name: 'Ci::PipelineArtifact', inverse_of: :pipeline, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent accepts_nested_attributes_for :variables, reject_if: :persisted? @@ -249,14 +250,6 @@ module Ci pipeline.run_after_commit { AutoDevops::DisableWorker.perform_async(pipeline.id) } end - - after_transition any => [:success] do |pipeline| - next unless Gitlab::Ci::Features.keep_latest_artifacts_for_ref_enabled?(pipeline.project) - - pipeline.run_after_commit do - Ci::PipelineSuccessUnlockArtifactsWorker.perform_async(pipeline.id) - end - end end scope :internal, -> { where(source: internal_sources) } @@ -416,7 +409,7 @@ module Ci def legacy_stage(name) stage = Ci::LegacyStage.new(self, name: name) - stage unless stage.statuses_count.zero? + stage unless stage.statuses_count == 0 end def ref_exists? @@ -425,40 +418,6 @@ module Ci false end - def ordered_stages - if ::Gitlab::Ci::Features.atomic_processing?(project) - # The `Ci::Stage` contains all up-to date data - # as atomic processing updates all data in-bulk - stages - elsif 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 - - def legacy_stages_using_sql - # TODO, this needs refactoring, see gitlab-foss#26481. - stages_query = statuses - .group('stage').select(:stage).order('max(stage_idx)') - - status_sql = statuses.latest.where('stage=sg.stage').legacy_status_sql - - warnings_sql = statuses.latest.select('COUNT(*)') - .where('stage=sg.stage').failed_but_allowed.to_sql - - stages_with_statuses = CommitStatus.from(stages_query, :sg) - .pluck('sg.stage', Arel.sql(status_sql), Arel.sql("(#{warnings_sql})")) - - stages_with_statuses.map do |stage| - Ci::LegacyStage.new(self, Hash[%i[name status warnings].zip(stage)]) - end - end - def legacy_stages_using_composite_status stages = latest_statuses_ordered_by_stage.group_by(&:stage) @@ -477,12 +436,9 @@ module Ci triggered_pipelines.preload(:source_job) end + # TODO: Remove usage of this method in templates def legacy_stages - if ::Gitlab::Ci::Features.composite_status?(project) - legacy_stages_using_composite_status - else - legacy_stages_using_sql - end + legacy_stages_using_composite_status end def valid_commit_sha @@ -665,7 +621,7 @@ module Ci end def has_warnings? - number_of_warnings.positive? + number_of_warnings > 0 end def number_of_warnings @@ -755,10 +711,6 @@ module Ci end end - def update_legacy_status - set_status(latest_builds_status.to_s) - end - def protected_ref? strong_memoize(:protected_ref) { project.protected_for?(git_ref) } end @@ -828,7 +780,7 @@ module Ci return unless started_at seconds = (started_at - created_at).to_i - seconds unless seconds.zero? + seconds unless seconds == 0 end def update_duration @@ -922,12 +874,6 @@ module Ci end end - def test_reports_count - Rails.cache.fetch(['project', project.id, 'pipeline', id, 'test_reports_count'], force: false) do - test_reports.total_count - 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| @@ -1061,10 +1007,6 @@ module Ci @persistent_ref ||= PersistentRef.new(pipeline: self) end - def find_successful_build_ids_by_names(names) - statuses.latest.success.where(name: names).pluck(:id) - end - def cacheable? Ci::PipelineEnums.ci_config_sources.key?(config_source.to_sym) end @@ -1084,8 +1026,6 @@ module Ci end def ensure_ci_ref! - return unless Gitlab::Ci::Features.pipeline_fixed_notifications? - self.ci_ref = Ci::Ref.ensure_for(self) end @@ -1123,12 +1063,6 @@ module Ci end end - def latest_builds_status - return 'failed' unless yaml_errors.blank? - - statuses.latest.slow_composite_status(project: project) || 'skipped' - end - def keep_around_commits return unless project diff --git a/app/models/ci/pipeline_artifact.rb b/app/models/ci/pipeline_artifact.rb new file mode 100644 index 00000000000..e7f51977ccd --- /dev/null +++ b/app/models/ci/pipeline_artifact.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +# This class is being used to persist additional artifacts after a pipeline completes, which is a great place to cache a computed result in object storage + +module Ci + class PipelineArtifact < ApplicationRecord + extend Gitlab::Ci::Model + include Artifactable + include FileStoreMounter + + FILE_STORE_SUPPORTED = [ + ObjectStorage::Store::LOCAL, + ObjectStorage::Store::REMOTE + ].freeze + + FILE_SIZE_LIMIT = 10.megabytes.freeze + + belongs_to :project, class_name: "Project", inverse_of: :pipeline_artifacts + belongs_to :pipeline, class_name: "Ci::Pipeline", inverse_of: :pipeline_artifacts + + validates :pipeline, :project, :file_format, :file, presence: true + validates :file_store, presence: true, inclusion: { in: FILE_STORE_SUPPORTED } + validates :size, presence: true, numericality: { less_than_or_equal_to: FILE_SIZE_LIMIT } + validates :file_type, presence: true + + mount_file_store_uploader Ci::PipelineArtifactUploader + before_save :set_size, if: :file_changed? + + enum file_type: { + code_coverage: 1 + } + + def set_size + self.size = file.size + end + end +end diff --git a/app/models/ci/pipeline_enums.rb b/app/models/ci/pipeline_enums.rb index 352dc56aac7..9d108ff0fa4 100644 --- a/app/models/ci/pipeline_enums.rb +++ b/app/models/ci/pipeline_enums.rb @@ -63,6 +63,10 @@ module Ci def self.ci_config_sources_values ci_config_sources.values end + + def self.non_ci_config_source_values + config_sources.values - ci_config_sources.values + end end end diff --git a/app/models/ci/ref.rb b/app/models/ci/ref.rb index 29b44575d65..3d8823728e7 100644 --- a/app/models/ci/ref.rb +++ b/app/models/ci/ref.rb @@ -3,6 +3,7 @@ module Ci class Ref < ApplicationRecord extend Gitlab::Ci::Model + include AfterCommitQueue include Gitlab::OptimisticLocking FAILING_STATUSES = %w[failed broken still_failing].freeze @@ -15,6 +16,7 @@ module Ci transition unknown: :success transition fixed: :success transition %i[failed broken still_failing] => :fixed + transition success: same end event :do_fail do @@ -29,6 +31,14 @@ module Ci state :fixed, value: 3 state :broken, value: 4 state :still_failing, value: 5 + + after_transition any => [:fixed, :success] do |ci_ref| + next unless ::Gitlab::Ci::Features.keep_latest_artifacts_for_ref_enabled?(ci_ref.project) + + ci_ref.run_after_commit do + Ci::PipelineSuccessUnlockArtifactsWorker.perform_async(ci_ref.last_finished_pipeline_id) + end + end end class << self @@ -47,8 +57,6 @@ module Ci end def update_status_by!(pipeline) - return unless Gitlab::Ci::Features.pipeline_fixed_notifications? - retry_lock(self) do next unless last_finished_pipeline_id == pipeline.id diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 1cd6c64841b..00ee45740bd 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -10,7 +10,7 @@ module Ci include TokenAuthenticatable include IgnorableColumns - add_authentication_token_field :token, encrypted: -> { Feature.enabled?(:ci_runners_tokens_optional_encryption, default_enabled: true) ? :optional : :required } + add_authentication_token_field :token, encrypted: -> { Feature.enabled?(:ci_runners_tokens_optional_encryption) ? :optional : :required } enum access_level: { not_protected: 0, diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index 41215601704..cc6bd1870b9 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -113,7 +113,7 @@ module Ci end def has_warnings? - number_of_warnings.positive? + number_of_warnings > 0 end def number_of_warnings @@ -138,7 +138,7 @@ module Ci end def latest_stage_status - statuses.latest.slow_composite_status(project: project) || 'skipped' + statuses.latest.composite_status || 'skipped' end end end |