Class: Ci::JobArtifact

Inherits:
ApplicationRecord show all
Includes:
AfterCommitQueue, Artifactable, Lockable, Partitionable, EachBatch, FileStoreMounter, Gitlab::Utils::StrongMemoize, ObjectStorable, Sortable, UpdateProjectStatistics, UsageStatistics
Defined in:
app/models/ci/job_artifact.rb

Constant Summary collapse

STORE_COLUMN =
:file_store
PLAN_LIMIT_PREFIX =
'ci_max_artifact_size_'
MAX_EXPOSED_AS_LENGTH =
100
EXPOSED_PATH_REGEX =
/\A[^*]*\z/
InvalidArtifactError =
Class.new(StandardError)

Constants included from FileStoreMounter

FileStoreMounter::ALLOWED_FILE_FIELDS

Constants included from Artifactable

Artifactable::FILE_FORMAT_ADAPTERS, Artifactable::JUNIT_MAX_BYTES, Artifactable::NotSupportedAdapterError

Constants inherited from ApplicationRecord

ApplicationRecord::MAX_PLUCK

Constants included from HasCheckConstraints

HasCheckConstraints::NOT_NULL_CHECK_PATTERN

Constants included from ResetOnColumnErrors

ResetOnColumnErrors::MAX_RESET_PERIOD

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Gitlab::Ci::Artifacts::Logger

#log_artifacts_context, #log_artifacts_filesize, #log_build_dependencies, log_created, log_deleted

Methods included from AfterCommitQueue

#run_after_commit, #run_after_commit_or_now

Methods included from Partitionable

registered_models

Methods inherited from ApplicationRecord

model_name, table_name_prefix

Methods inherited from ApplicationRecord

===, cached_column_list, #create_or_load_association, current_transaction, declarative_enum, default_select_columns, delete_all_returning, #deleted_from_database?, id_in, id_not_in, iid_in, nullable_column?, primary_key_in, #readable_by?, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, #to_ability_name, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order

Methods included from Organizations::Sharding

#sharding_organization

Methods included from ResetOnColumnErrors

#reset_on_union_error, #reset_on_unknown_attribute_error

Methods included from Gitlab::SensitiveSerializableHash

#serializable_hash

Class Method Details

.archived_trace_exists_for?(job_id) ⇒ Boolean

Returns:

  • (Boolean)


166
167
168
# File 'app/models/ci/job_artifact.rb', line 166

def self.archived_trace_exists_for?(job_id)
  where(job_id: job_id).trace.take&.stored?
end

.artifacts_size_for(project) ⇒ Object



142
143
144
# File 'app/models/ci/job_artifact.rb', line 142

def self.artifacts_size_for(project)
  self.where(project: project).sum(:size)
end

.associated_file_types_for(file_type) ⇒ Object



128
129
130
131
132
# File 'app/models/ci/job_artifact.rb', line 128

def self.associated_file_types_for(file_type)
  return unless file_types.include?(file_type)

  [file_type]
end

.begin_fast_destroyObject

FastDestroyAll concerns rubocop: disable CodeReuse/ServiceClass



153
154
155
156
157
# File 'app/models/ci/job_artifact.rb', line 153

def self.begin_fast_destroy
  service = ::Ci::JobArtifacts::DestroyAssociationsService.new(self)
  service.destroy_records
  service
end

.erasable_file_typesObject



134
135
136
# File 'app/models/ci/job_artifact.rb', line 134

def self.erasable_file_types
  self.file_types.keys - Enums::Ci::JobArtifact.non_erasable_file_types
end

.file_types_for_report(report_type) ⇒ Object



124
125
126
# File 'app/models/ci/job_artifact.rb', line 124

def self.file_types_for_report(report_type)
  Enums::Ci::JobArtifact.report_file_types.fetch(report_type) { raise ArgumentError, "Unrecognized report type: #{report_type}" }
end

.finalize_fast_destroy(service) ⇒ Object

FastDestroyAll concerns



162
163
164
# File 'app/models/ci/job_artifact.rb', line 162

def self.finalize_fast_destroy(service)
  service.update_statistics
end

.max_artifact_size(type:, project:) ⇒ Object



170
171
172
173
174
175
176
177
178
179
# File 'app/models/ci/job_artifact.rb', line 170

def self.max_artifact_size(type:, project:)
  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

.of_report_type(report_type) ⇒ Object



118
119
120
121
122
# File 'app/models/ci/job_artifact.rb', line 118

def self.of_report_type(report_type)
  file_types = file_types_for_report(report_type)

  with_file_types(file_types)
end

.pluck_job_idObject



146
147
148
# File 'app/models/ci/job_artifact.rb', line 146

def self.pluck_job_id
  pluck(:job_id)
end

.total_sizeObject



138
139
140
# File 'app/models/ci/job_artifact.rb', line 138

def self.total_size
  self.sum(:size)
end

Instance Method Details

#each_blob(&blk) ⇒ Object



248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'app/models/ci/job_artifact.rb', line 248

def each_blob(&blk)
  if junit? && artifact_report.nil?
    build_artifact_report(status: :validated, validation_error: nil, project_id: project_id)
  end

  super
rescue InvalidArtifactError => e
  artifact_report&.assign_attributes(status: :faulty, validation_error: e.message)

  raise e
ensure
  artifact_report&.save! if persisted?
end

#expire_inObject



205
206
207
# File 'app/models/ci/job_artifact.rb', line 205

def expire_in
  expire_at - Time.current if expire_at
end

#expire_in=(value) ⇒ Object



209
210
211
212
# File 'app/models/ci/job_artifact.rb', line 209

def expire_in=(value)
  self.expire_at =
    (::Gitlab::Ci::Build::DurationParser.new(value).seconds_from_now if value)
end

#expired?Boolean

Returns:

  • (Boolean)


197
198
199
# File 'app/models/ci/job_artifact.rb', line 197

def expired?
  expire_at.present? && expire_at.past?
end

#expiring?Boolean

Returns:

  • (Boolean)


201
202
203
# File 'app/models/ci/job_artifact.rb', line 201

def expiring?
  expire_at.present? && expire_at.future?
end

#hashed_path?Boolean

Returns:

  • (Boolean)


191
192
193
194
195
# File 'app/models/ci/job_artifact.rb', line 191

def hashed_path?
  return true if trace? # ArchiveLegacyTraces background migration might not have `file_location` column

  super || self.file_location.nil?
end

#local_store?Boolean

Returns:

  • (Boolean)


187
188
189
# File 'app/models/ci/job_artifact.rb', line 187

def local_store?
  [nil, ::JobArtifactUploader::Store::LOCAL].include?(self.file_store)
end

#maintainer_access?Boolean

Returns:

  • (Boolean)


244
245
246
# File 'app/models/ci/job_artifact.rb', line 244

def maintainer_access?
  maintainer_accessibility?
end

#none_access?Boolean

Returns:

  • (Boolean)


240
241
242
# File 'app/models/ci/job_artifact.rb', line 240

def none_access?
  none_accessibility?
end

#public_access?Boolean

Returns:

  • (Boolean)


236
237
238
# File 'app/models/ci/job_artifact.rb', line 236

def public_access?
  public_accessibility?
end

#store_after_commit?Boolean

Returns:

  • (Boolean)


230
231
232
233
234
# File 'app/models/ci/job_artifact.rb', line 230

def store_after_commit?
  strong_memoize(:store_after_commit) do
    trace? && JobArtifactUploader.direct_upload_enabled?
  end
end

#stored?Boolean

Returns:

  • (Boolean)


214
215
216
# File 'app/models/ci/job_artifact.rb', line 214

def stored?
  file&.file&.exists?
end

#to_deleted_object_attrs(pick_up_at = nil) ⇒ Object



218
219
220
221
222
223
224
225
226
227
228
# File 'app/models/ci/job_artifact.rb', line 218

def to_deleted_object_attrs(pick_up_at = nil)
  store_dir_value, file_value = resolve_file_path_for_deletion

  {
    file_store: file_store,
    store_dir: store_dir_value,
    file: file_value,
    pick_up_at: set_pick_up_at(pick_up_at),
    project_id: project_id
  }
end

#validate_file_format!Object



181
182
183
184
185
# File 'app/models/ci/job_artifact.rb', line 181

def validate_file_format!
  unless Enums::Ci::JobArtifact.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