Class: Ci::Build
- Inherits:
-
Processable
show all
- Extended by:
- Gitlab::Utils::Override
- Includes:
- AfterCommitQueue, ChronicDurationAttribute, BulkInsertableTags, Contextable, Deployable, HasRef, Taggable, TrackEnvironmentUsage, EachBatch, Importable, Presentable, TokenAuthenticatable
- Defined in:
- app/models/ci/build.rb
Constant Summary
collapse
- RUNNER_FEATURES =
{
upload_multiple_artifacts: ->(build) { build.publishes_artifacts_reports? },
refspecs: ->(build) { build.merge_request_ref? },
artifacts_exclude: ->(build) { build.supports_artifacts_exclude? },
multi_build_steps: ->(build) { build.multi_build_steps? },
return_exit_code: ->(build) { build.exit_codes_defined? },
fallback_cache_keys: ->(build) { build.fallback_cache_keys_defined? }
}.freeze
- DEGRADATION_THRESHOLD_VARIABLE_NAME =
'DEGRADATION_THRESHOLD'
- RUNNERS_STATUS_CACHE_EXPIRATION =
1.minute
- DEPLOYMENT_NAMES =
%w[deploy release rollout].freeze
- TOKEN_PREFIX =
'glcbt-'
Ci::BulkInsertableTags::BULK_INSERT_TAG_THREAD_KEY
Constants inherited
from Processable
Processable::ACTIONABLE_WHEN
TaggableQueries::MAX_TAGS_IDS, TaggableQueries::TooManyTagsError
Constants included
from HasStatus
HasStatus::ACTIVE_STATUSES, HasStatus::ALIVE_STATUSES, HasStatus::AVAILABLE_STATUSES, HasStatus::BLOCKED_STATUS, HasStatus::CANCELABLE_STATUSES, HasStatus::COMPLETED_STATUSES, HasStatus::COMPLETED_WITH_MANUAL_STATUSES, HasStatus::DEFAULT_STATUS, HasStatus::EXECUTING_STATUSES, HasStatus::IGNORED_STATUSES, HasStatus::ORDERED_STATUSES, HasStatus::PASSED_WITH_WARNINGS_STATUSES, HasStatus::STARTED_STATUSES, HasStatus::STATUSES_ENUM, HasStatus::STOPPED_STATUSES, HasStatus::UnknownStatusError
ApplicationRecord::MAX_PLUCK
HasCheckConstraints::NOT_NULL_CHECK_PATTERN
ResetOnColumnErrors::MAX_RESET_PERIOD
Instance Attribute Summary
Attributes included from Importable
#importing, #user_contributions
Class Method Summary
collapse
Instance Method Summary
collapse
extended, extensions, included, method_added, override, prepended, queue_verification, verify!
#chronic_duration_attributes, #output_chronic_duration_attribute
Methods included from Taggable
#reload, #tag_list=
#count_user_deployment?, #deployment_name?, #track_deployment_usage, #track_verify_environment_usage, #verifies_environment?
Methods included from HasRef
#branch?, #git_ref, #ref_slug
#present
#run_after_commit, #run_after_commit_or_now
Methods included from Deployable
#accesses_environment?, #actual_persisted_environment, #deployment_job?, #deployment_status, #environment_action, #environment_auto_stop_in, #environment_options_for_permanent_storage, #environment_permanent_metadata, #environment_slug, #environment_status, #environment_tier, #environment_url, #expanded_auto_stop_in, #expanded_deployment_tier, #expanded_environment_name, #expanded_kubernetes_namespace, #has_environment_keyword?, #has_outdated_deployment?, #link_to_environment, #on_stop, #persisted_environment, #persisted_environment=, #prepares_environment?, #stop_action_successful?, #stops_environment?, #successful_deployment_status, #verifies_environment?
#manual_variables, #scoped_variables, #simple_variables, #simple_variables_without_dependencies, #track_duration, #unprotected_scoped_variables
#save_tags, with_bulk_insert_tags
Methods inherited from Processable
#aggregated_needs_names, #all_dependencies, #archived?, #assign_resource_from_resource_group, #dependency_variables, #enqueue_immediately?, #ensure_scheduling_type!, #expanded_environment_name, fabricate, #find_legacy_scheduling_type, #job_dependencies_with_accessible_artifacts, #manual_confirmation_message, #manual_job?, #needs_attributes, #other_manual_actions, #persisted_environment, populate_scheduling_type!, #redis_state, #retryable?, #scheduling_type_dag?, #scoped_user, select_with_aggregated_needs, #set_enqueue_immediately!, #when, #with_resource_group?
#artifacts_exposed_as, #artifacts_exposed_paths, #debug_trace_enabled?, #downstream_errors, #enable_debug_trace!, #exit_code, #exit_code=, #has_exposed_artifacts?, #id_tokens, #id_tokens=, #id_tokens?, #interruptible, #interruptible=, #options, #options=, #scoped_user_id, #secrets=, #timeout_human_readable_value, #timeout_source_value, #timeout_value, #update_timeout_state, #yaml_variables, #yaml_variables=
#archived?, #auto_canceled?, #duration, #exit_code=, #expire_etag_cache!, #group_name, #latest?, #locking_enabled?, locking_enabled?, names, #queued_duration, #recoverable?, #resource_parent, #retryable?, #sortable_name, #stage_name, #to_ability_name, update_as_processed!, #update_older_statuses_retried!
#tags_ids
#bulk_insert_associations!, bulk_inserts_enabled?, with_bulk_insert
Methods included from HasStatus
#active?, #blocked?, #complete?, #complete_or_manual?, #incomplete?, #started?
registered_models
table_name_prefix
===, 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!, sharding_keys, #to_ability_name, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order
#reset_on_union_error, #reset_on_unknown_attribute_error
Class Method Details
.build_matchers(project) ⇒ Object
427
428
429
430
431
|
# File 'app/models/ci/build.rb', line 427
def self.build_matchers(project)
return legacy_build_matchers(project) if Feature.disabled?(:ci_build_uses_job_definition_tag_list, project)
new_build_matchers(project)
end
|
.clone_accessors ⇒ Object
277
278
279
280
281
282
283
|
# File 'app/models/ci/build.rb', line 277
def clone_accessors
%i[pipeline project ref tag name allow_failure stage_idx when
environment coverage_regex description tag_list protected
needs_attributes job_variables_attributes resource_group
scheduling_type timeout timeout_source debug_trace_enabled
ci_stage partition_id execution_config_id inputs_attributes].freeze
end
|
.has_any_job_definition? ⇒ Boolean
TODO: remove this method with ‘ci_builds_metadata`
495
496
497
|
# File 'app/models/ci/build.rb', line 495
def self.has_any_job_definition?
left_joins(:job_definition_instance).limit(1).pick(:job_id).present?
end
|
.ids_in_merge_request(merge_request_id) ⇒ Object
481
482
483
|
# File 'app/models/ci/build.rb', line 481
def self.ids_in_merge_request(merge_request_id)
in_merge_request(merge_request_id).pluck(:id)
end
|
.keep_artifacts! ⇒ Object
489
490
491
492
|
# File 'app/models/ci/build.rb', line 489
def self.keep_artifacts!
update_all(artifacts_expire_at: nil)
Ci::JobArtifact.where(job: self.select(:id)).update_all(expire_at: nil)
end
|
.legacy_build_matchers(project) ⇒ Object
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
|
# File 'app/models/ci/build.rb', line 454
def self.legacy_build_matchers(project)
unique_params = [
:protected,
Arel.sql("(#{arel_tag_names_array.to_sql})")
]
group(*unique_params).pluck('array_agg(id)', *unique_params).map do |values|
Gitlab::Ci::Matching::BuildMatcher.new(
build_ids: values[0],
protected: values[1],
tag_list: values[2],
project: project
)
end
end
|
.model_name ⇒ Object
This is needed for url_for to work, as the controller is JobsController
269
270
271
|
# File 'app/models/ci/build.rb', line 269
def model_name
ActiveModel::Name.new(self, nil, 'job')
end
|
.new_build_matchers(project) ⇒ Object
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
|
# File 'app/models/ci/build.rb', line 433
def self.new_build_matchers(project)
unique_params = [
:protected,
Arel.sql(tag_names_array_query)
]
pluck_params = [
"array_agg(#{quoted_table_name}.id) as ids",
*unique_params
]
joins(:job_definition).group(*unique_params).pluck(*pluck_params).map do |values|
Gitlab::Ci::Matching::BuildMatcher.new(
build_ids: values[0],
protected: values[1],
tag_list: values[2],
project: project
)
end
end
|
.supported_keyset_orderings ⇒ Object
285
286
287
|
# File 'app/models/ci/build.rb', line 285
def supported_keyset_orderings
{ id: [:desc] }
end
|
.tag_names_array_query ⇒ Object
470
471
472
473
474
475
476
477
478
479
|
# File 'app/models/ci/build.rb', line 470
def self.tag_names_array_query
<<~SQL.squish
(
SELECT COALESCE(array_agg(tag_name ORDER BY tag_name), '{}')
FROM jsonb_array_elements_text(
#{Ci::JobDefinition.quoted_table_name}.config->'tag_list'
) AS tag_name
)
SQL
end
|
.taggings_join_model ⇒ Object
485
486
487
|
# File 'app/models/ci/build.rb', line 485
def self.taggings_join_model
::Ci::BuildTag
end
|
.with_preloads ⇒ Object
273
274
275
|
# File 'app/models/ci/build.rb', line 273
def with_preloads
preload(:job_artifacts_archive, :job_artifacts, :tags, project: [:namespace])
end
|
Instance Method Details
#action? ⇒ Boolean
609
610
611
|
# File 'app/models/ci/build.rb', line 609
def action?
ACTIONABLE_WHEN.include?(self.when)
end
|
#all_met_to_become_pending? ⇒ Boolean
637
638
639
|
# File 'app/models/ci/build.rb', line 637
def all_met_to_become_pending?
super && !any_unmet_prerequisites?
end
|
#all_queuing_entries ⇒ Object
We can have only one queuing entry or running build tracking entry, because there is a unique index on ‘build_id` in each table, but we need a relation to remove these entries more efficiently in a single statement without actually loading data.
1218
1219
1220
|
# File 'app/models/ci/build.rb', line 1218
def all_queuing_entries
::Ci::PendingBuild.where(build_id: id)
end
|
1222
1223
1224
|
# File 'app/models/ci/build.rb', line 1222
def all_runtime_metadata
::Ci::RunningBuild.where(build_id: id)
end
|
#allow_git_fetch ⇒ Object
779
780
781
|
# File 'app/models/ci/build.rb', line 779
def allow_git_fetch
project.build_allow_git_fetch
end
|
#allowed_to_fail_with_code?(exit_code) ⇒ Boolean
1248
1249
1250
1251
1252
1253
|
# File 'app/models/ci/build.rb', line 1248
def allowed_to_fail_with_code?(exit_code)
options
.dig(:allow_failure_criteria, :exit_codes)
.to_a
.include?(exit_code)
end
|
#any_runners_available? ⇒ Boolean
875
876
877
878
879
|
# File 'app/models/ci/build.rb', line 875
def any_runners_available?
cache_for_available_runners do
project.active_runners.exists?
end
end
|
#any_runners_online? ⇒ Boolean
869
870
871
872
873
|
# File 'app/models/ci/build.rb', line 869
def any_runners_online?
cache_for_online_runners do
project.any_online_runners? { |runner| runner.match_build_if_online?(self) }
end
end
|
#any_unmet_prerequisites? ⇒ Boolean
641
642
643
|
# File 'app/models/ci/build.rb', line 641
def any_unmet_prerequisites?
prerequisites.present?
end
|
#apple_app_store_variables ⇒ Object
723
724
725
726
727
|
# File 'app/models/ci/build.rb', line 723
def apple_app_store_variables
return [] unless apple_app_store_integration.try(:activated?)
Gitlab::Ci::Variables::Collection.new(apple_app_store_integration.ci_variables(protected_ref: pipeline.protected_ref?))
end
|
#artifact_access_setting_in_config ⇒ Object
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
|
# File 'app/models/ci/build.rb', line 910
def artifact_access_setting_in_config
artifacts_public = options.dig(:artifacts, :public)
artifacts_access = options.dig(:artifacts, :access)&.to_s
if artifacts_public.present? && artifacts_access.present?
raise ArgumentError, 'artifacts:public and artifacts:access are mutually exclusive'
end
return :public if artifacts_public == true || artifacts_access == 'all'
return :private if artifacts_public == false || artifacts_access == 'developer'
return :maintainer if artifacts_access == 'maintainer'
return :none if artifacts_access == 'none'
:public
end
|
#artifact_for_type(type) ⇒ Object
972
973
974
975
976
|
# File 'app/models/ci/build.rb', line 972
def artifact_for_type(type)
file_types = Ci::JobArtifact.associated_file_types_for(type)
file_types_ids = file_types&.map { |file_type| Ci::JobArtifact.file_types[file_type] }
job_artifacts.find_by(file_type: file_types_ids)
end
|
#artifacts? ⇒ Boolean
816
817
818
|
# File 'app/models/ci/build.rb', line 816
def artifacts?
!artifacts_expired? && artifacts_file&.exists?
end
|
#artifacts_expire_in ⇒ Object
949
950
951
|
# File 'app/models/ci/build.rb', line 949
def artifacts_expire_in
artifacts_expire_at - Time.current if artifacts_expire_at
end
|
#artifacts_expire_in=(value) ⇒ Object
953
954
955
956
|
# File 'app/models/ci/build.rb', line 953
def artifacts_expire_in=(value)
self.artifacts_expire_at =
(ChronicDuration.parse(value)&.seconds&.from_now if value)
end
|
#artifacts_expired? ⇒ Boolean
945
946
947
|
# File 'app/models/ci/build.rb', line 945
def artifacts_expired?
artifacts_expire_at&.past?
end
|
#artifacts_file ⇒ Object
804
805
806
|
# File 'app/models/ci/build.rb', line 804
def artifacts_file
job_artifacts_archive&.file
end
|
812
813
814
|
# File 'app/models/ci/build.rb', line 812
def artifacts_metadata
job_artifacts_metadata&.file
end
|
831
832
833
|
# File 'app/models/ci/build.rb', line 831
def artifacts_metadata?
artifacts? && artifacts_metadata&.exists?
end
|
#artifacts_metadata_entry(path, **options) ⇒ Object
926
927
928
929
930
931
932
933
934
935
|
# File 'app/models/ci/build.rb', line 926
def artifacts_metadata_entry(path, **options)
artifacts_metadata.open do |metadata_stream|
metadata = Gitlab::Ci::Build::Artifacts::Metadata.new(
metadata_stream,
path,
**options)
metadata.to_entry
end
end
|
#artifacts_no_access? ⇒ Boolean
904
905
906
907
908
|
# File 'app/models/ci/build.rb', line 904
def artifacts_no_access?
return false if job_artifacts_archive.nil?
job_artifacts_archive.none_access?
end
|
#artifacts_public? ⇒ Boolean
898
899
900
901
902
|
# File 'app/models/ci/build.rb', line 898
def artifacts_public?
return true if job_artifacts_archive.nil?
job_artifacts_archive.public_access?
end
|
#artifacts_size ⇒ Object
808
809
810
|
# File 'app/models/ci/build.rb', line 808
def artifacts_size
job_artifacts_archive&.size
end
|
#auto_retry_allowed? ⇒ Boolean
550
551
552
|
# File 'app/models/ci/build.rb', line 550
def auto_retry_allowed?
auto_retry.allowed?
end
|
#auto_retry_expected? ⇒ Boolean
554
555
556
|
# File 'app/models/ci/build.rb', line 554
def auto_retry_expected?
failed? && auto_retry_allowed?
end
|
#available_artifacts? ⇒ Boolean
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.
827
828
829
|
# File 'app/models/ci/build.rb', line 827
def available_artifacts?
(!artifacts_expired? || pipeline.artifacts_locked?) && job_artifacts_archive&.exists?
end
|
#base_variables ⇒ Object
747
748
749
750
751
752
753
754
|
# File 'app/models/ci/build.rb', line 747
def base_variables
::Gitlab::Ci::Variables::Collection.new
.concat(persisted_variables)
.concat(dependency_proxy_variables)
.concat(job_jwt_variables)
.concat(scoped_variables)
.concat(persisted_environment_variables)
end
|
#browsable_artifacts? ⇒ Boolean
894
895
896
|
# File 'app/models/ci/build.rb', line 894
def browsable_artifacts?
artifacts_metadata?
end
|
#build_matcher ⇒ Object
539
540
541
542
543
544
545
546
547
548
|
# File 'app/models/ci/build.rb', line 539
def build_matcher
strong_memoize(:build_matcher) do
Gitlab::Ci::Matching::BuildMatcher.new({
protected: protected?,
tag_list: tag_list,
build_ids: [id],
project: project
})
end
end
|
#cache ⇒ Object
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
|
# File 'app/models/ci/build.rb', line 996
def cache
cache = Array.wrap(options[:cache])
cache.each do |single_cache|
single_cache[:fallback_keys] = [] unless single_cache.key?(:fallback_keys)
end
if project.jobs_cache_index
cache = cache.map do |single_cache|
cache = single_cache.merge(key: "#{single_cache[:key]}-#{project.jobs_cache_index}")
fallback = cache.slice(:fallback_keys).transform_values { |keys| keys.map { |key| "#{key}-#{project.jobs_cache_index}" } }
cache.merge(fallback.compact)
end
end
return cache unless project.ci_separated_caches
cache.map do |entry|
type_suffix = !entry[:unprotect] && pipeline.protected_ref? ? 'protected' : 'non_protected'
cache = entry.merge(key: "#{entry[:key]}-#{type_suffix}")
fallback = cache.slice(:fallback_keys).transform_values { |keys| keys.map { |key| "#{key}-#{type_suffix}" } }
cache.merge(fallback.compact)
end
end
|
#can_auto_cancel_pipeline_on_job_failure? ⇒ Boolean
613
614
615
616
|
# File 'app/models/ci/build.rb', line 613
def can_auto_cancel_pipeline_on_job_failure?
!auto_retry_expected?
end
|
#cancel_gracefully? ⇒ Boolean
531
532
533
|
# File 'app/models/ci/build.rb', line 531
def cancel_gracefully?
!!runner_manager&.supports_after_script_on_cancel?
end
|
#cancelable? ⇒ Boolean
rubocop: enable CodeReuse/ServiceClass
624
625
626
|
# File 'app/models/ci/build.rb', line 624
def cancelable?
(active? || created?) && !canceling?
end
|
#ci_job_processed_rate_limited? ⇒ Boolean
509
510
511
512
513
514
|
# File 'app/models/ci/build.rb', line 509
def ci_job_processed_rate_limited?
Gitlab::ApplicationRateLimiter.throttled?(
:ci_job_processed_subscription,
scope: project
)
end
|
#collect_accessibility_reports!(accessibility_report) ⇒ Object
1113
1114
1115
1116
1117
1118
1119
|
# File 'app/models/ci/build.rb', line 1113
def collect_accessibility_reports!(accessibility_report)
each_report(Ci::JobArtifact.file_types_for_report(:accessibility)) do |file_type, blob|
Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, accessibility_report)
end
accessibility_report
end
|
#collect_codequality_reports!(codequality_report) ⇒ Object
1121
1122
1123
1124
1125
1126
1127
|
# File 'app/models/ci/build.rb', line 1121
def collect_codequality_reports!(codequality_report)
each_report(Ci::JobArtifact.file_types_for_report(:codequality)) do |file_type, blob|
Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, codequality_report, { project: project, commit_sha: pipeline.sha })
end
codequality_report
end
|
1129
1130
1131
1132
1133
1134
1135
|
# File 'app/models/ci/build.rb', line 1129
def collect_terraform_reports!(terraform_reports)
each_report(::Ci::JobArtifact.file_types_for_report(:terraform)) do |file_type, blob, report_artifact|
::Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, terraform_reports, artifact: report_artifact)
end
terraform_reports
end
|
#collect_test_reports!(test_reports) ⇒ Object
1105
1106
1107
1108
1109
1110
1111
|
# File 'app/models/ci/build.rb', line 1105
def collect_test_reports!(test_reports)
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
|
#create_queuing_entry! ⇒ Object
#debug_mode? ⇒ Boolean
1190
1191
1192
1193
1194
1195
|
# File 'app/models/ci/build.rb', line 1190
def debug_mode?
debug_trace_enabled? ||
Gitlab::Utils.to_boolean(variables['CI_DEBUG_SERVICES']&.value, default: false) ||
Gitlab::Utils.to_boolean(variables['CI_DEBUG_TRACE']&.value, default: false)
end
|
#degenerate! ⇒ Object
591
592
593
594
595
|
# File 'app/models/ci/build.rb', line 591
def degenerate!
super do
execution_config&.destroy
end
end
|
#degenerated? ⇒ Boolean
587
588
589
|
# File 'app/models/ci/build.rb', line 587
def degenerated?
super && execution_config_id.nil?
end
|
#degradation_threshold ⇒ Object
1171
1172
1173
1174
|
# File 'app/models/ci/build.rb', line 1171
def degradation_threshold
var = yaml_variables.find { |v| v[:key] == DEGRADATION_THRESHOLD_VARIABLE_NAME }
var[:value]&.to_i if var
end
|
#dependency_proxy_variables ⇒ Object
708
709
710
711
712
713
714
715
|
# File 'app/models/ci/build.rb', line 708
def dependency_proxy_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
break variables unless Gitlab.config.dependency_proxy.enabled
variables.append(key: 'CI_DEPENDENCY_PROXY_USER', value: ::Gitlab::Auth::CI_JOB_USER)
variables.append(key: 'CI_DEPENDENCY_PROXY_PASSWORD', value: token.to_s, public: false, masked: true)
end
end
|
#deploy_token_variables ⇒ Object
699
700
701
702
703
704
705
706
|
# File 'app/models/ci/build.rb', line 699
def deploy_token_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
break variables unless gitlab_deploy_token
variables.append(key: 'CI_DEPLOY_USER', value: gitlab_deploy_token.username)
variables.append(key: 'CI_DEPLOY_PASSWORD', value: gitlab_deploy_token.token, public: false, masked: true)
end
end
|
#detailed_status(current_user) ⇒ Object
558
559
560
561
562
|
# File 'app/models/ci/build.rb', line 558
def detailed_status(current_user)
Gitlab::Ci::Status::Build::Factory
.new(present, current_user)
.fabricate!
end
|
#diffblue_cover_variables ⇒ Object
735
736
737
738
739
|
# File 'app/models/ci/build.rb', line 735
def diffblue_cover_variables
return [] unless diffblue_cover_integration.try(:activated?)
Gitlab::Ci::Variables::Collection.new(diffblue_cover_integration.ci_variables)
end
|
#doom! ⇒ Object
Consider this object to have an unknown job problem
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
|
# File 'app/models/ci/build.rb', line 1142
def doom!
transaction do
now = Time.current
attributes = {
status: :failed,
failure_reason: :data_integrity_failure,
updated_at: now
}
attributes[:finished_at] = now unless finished_at.present?
update_columns(attributes)
all_queuing_entries.delete_all
all_runtime_metadata.delete_all
end
deployment&.sync_status_with(self)
::Gitlab::Ci::Pipeline::Metrics
.job_failure_reason_counter
.increment(reason: :data_integrity_failure)
Gitlab::AppLogger.info(
message: 'Build doomed',
class: self.class.name,
build_id: id,
pipeline_id: pipeline_id,
project_id: project_id)
end
|
#drop_with_exit_code!(failure_reason, exit_code) ⇒ Object
1197
1198
1199
1200
1201
1202
|
# File 'app/models/ci/build.rb', line 1197
def drop_with_exit_code!(failure_reason, exit_code)
failure_reason ||= :unknown_failure
result = drop!(::Gitlab::Ci::Build::Status::Reason.new(self, failure_reason, exit_code))
::Ci::TrackFailedBuildWorker.perform_async(id, exit_code, failure_reason)
result
end
|
#each_report(report_types) ⇒ Object
1255
1256
1257
1258
1259
1260
1261
1262
1263
|
# File 'app/models/ci/build.rb', line 1255
def each_report(report_types)
job_artifacts_for_types(report_types).each do |report_artifact|
next if report_artifact&.artifact_report&.faulty?
report_artifact.each_blob do |blob|
yield report_artifact.file_type, blob, report_artifact
end
end
end
|
#erasable? ⇒ Boolean
937
938
939
|
# File 'app/models/ci/build.rb', line 937
def erasable?
complete? && (artifacts? || has_job_artifacts? || has_trace?)
end
|
#erased? ⇒ Boolean
941
942
943
|
# File 'app/models/ci/build.rb', line 941
def erased?
!erased_at.nil?
end
|
#execute_hooks ⇒ Object
885
886
887
888
889
890
891
892
|
# File 'app/models/ci/build.rb', line 885
def execute_hooks
return unless project
return if user&.blocked?
return unless project.has_active_hooks?(:job_hooks) || project.has_active_integrations?(:job_hooks)
Ci::ExecuteBuildHooksWorker.perform_async(project.id, build_data)
end
|
#exit_codes_defined? ⇒ Boolean
1204
1205
1206
|
# File 'app/models/ci/build.rb', line 1204
def exit_codes_defined?
options.dig(:allow_failure_criteria, :exit_codes).present? || options.dig(:retry, :exit_codes).present?
end
|
#expanded_publish_path ⇒ Object
1078
1079
1080
|
# File 'app/models/ci/build.rb', line 1078
def expanded_publish_path
ExpandVariables.expand(publish_path.to_s, -> { base_variables.sort_and_expand_all })
end
|
#fallback_cache_keys_defined? ⇒ Boolean
1022
1023
1024
|
# File 'app/models/ci/build.rb', line 1022
def fallback_cache_keys_defined?
Array.wrap(options[:cache]).any? { |cache| cache[:fallback_keys].present? }
end
|
#features ⇒ Object
757
758
759
760
761
762
|
# File 'app/models/ci/build.rb', line 757
def features
{
trace_sections: true,
failure_reasons: self.class.failure_reasons.keys
}
end
|
#force_cancelable? ⇒ Boolean
628
629
630
|
# File 'app/models/ci/build.rb', line 628
def force_cancelable?
canceling? && supports_force_cancel?
end
|
#google_play_variables ⇒ Object
729
730
731
732
733
|
# File 'app/models/ci/build.rb', line 729
def google_play_variables
return [] unless google_play_integration.try(:activated?)
Gitlab::Ci::Variables::Collection.new(google_play_integration.ci_variables(protected_ref: pipeline.protected_ref?))
end
|
#harbor_variables ⇒ Object
717
718
719
720
721
|
# File 'app/models/ci/build.rb', line 717
def harbor_variables
return [] unless harbor_integration.try(:activated?)
Gitlab::Ci::Variables::Collection.new(harbor_integration.ci_variables)
end
|
#has_archived_trace? ⇒ Boolean
800
801
802
|
# File 'app/models/ci/build.rb', line 800
def has_archived_trace?
trace.archived?
end
|
#has_expired_locked_archive_artifacts? ⇒ Boolean
958
959
960
961
|
# File 'app/models/ci/build.rb', line 958
def has_expired_locked_archive_artifacts?
locked_artifacts? &&
artifacts_expire_at&.past?
end
|
#has_expiring_archive_artifacts? ⇒ Boolean
963
964
965
|
# File 'app/models/ci/build.rb', line 963
def has_expiring_archive_artifacts?
has_expiring_artifacts? && job_artifacts_archive.present?
end
|
#has_job_artifacts? ⇒ Boolean
835
836
837
|
# File 'app/models/ci/build.rb', line 835
def has_job_artifacts?
job_artifacts.any?
end
|
#has_live_trace? ⇒ Boolean
796
797
798
|
# File 'app/models/ci/build.rb', line 796
def has_live_trace?
trace.live?
end
|
855
856
857
|
# File 'app/models/ci/build.rb', line 855
def has_tags?
tag_list.any?
end
|
#has_terminal? ⇒ Boolean
1101
1102
1103
|
# File 'app/models/ci/build.rb', line 1101
def has_terminal?
running? && runner_session_url.present?
end
|
#has_test_reports? ⇒ Boolean
839
840
841
|
# File 'app/models/ci/build.rb', line 839
def has_test_reports?
job_artifacts.of_report_type(:test).exists?
end
|
#has_trace? ⇒ Boolean
792
793
794
|
# File 'app/models/ci/build.rb', line 792
def has_trace?
trace.exist?
end
|
#has_valid_build_dependencies? ⇒ Boolean
1030
1031
1032
|
# File 'app/models/ci/build.rb', line 1030
def has_valid_build_dependencies?
dependencies.valid?
end
|
#hide_secrets(data, metrics = ::Gitlab::Ci::Trace::Metrics.new) ⇒ Object
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
|
# File 'app/models/ci/build.rb', line 1086
def hide_secrets(data, metrics = ::Gitlab::Ci::Trace::Metrics.new)
return unless trace
data.dup.tap do |trace|
Gitlab::Ci::MaskSecret.mask!(trace, project.runners_token) if project
Gitlab::Ci::MaskSecret.mask!(trace, token) if token
metrics.increment_trace_operation(operation: :mutated) if trace != data
end
end
|
1240
1241
1242
1243
1244
1245
1246
|
# File 'app/models/ci/build.rb', line 1240
def inputs_attributes
strong_memoize(:inputs_attributes) do
inputs.map do |input|
input.attributes.except('id', 'job_id', 'created_at', 'updated_at')
end
end
end
|
#invalid_dependencies ⇒ Object
1034
1035
1036
|
# File 'app/models/ci/build.rb', line 1034
def invalid_dependencies
dependencies.invalid
end
|
#job_artifact_types ⇒ Object
1265
1266
1267
|
# File 'app/models/ci/build.rb', line 1265
def job_artifact_types
job_artifacts.map(&:file_type)
end
|
#job_variables_attributes ⇒ Object
1230
1231
1232
1233
1234
1235
1236
1237
1238
|
# File 'app/models/ci/build.rb', line 1230
def job_variables_attributes
strong_memoize(:job_variables_attributes) do
job_variables.internal_source.map do |variable|
variable.attributes.except('id', 'job_id', 'encrypted_value', 'encrypted_value_iv').tap do |attrs|
attrs[:value] = variable.value
end
end
end
end
|
#keep_artifacts! ⇒ Object
967
968
969
970
|
# File 'app/models/ci/build.rb', line 967
def keep_artifacts!
update(artifacts_expire_at: nil)
job_artifacts.update_all(expire_at: nil)
end
|
#locked_artifacts? ⇒ Boolean
820
821
822
|
# File 'app/models/ci/build.rb', line 820
def locked_artifacts?
pipeline.artifacts_locked? && artifacts_file&.exists?
end
|
#max_test_cases_per_report ⇒ Object
1184
1185
1186
1187
1188
|
# File 'app/models/ci/build.rb', line 1184
def max_test_cases_per_report
::Gitlab.com? ? 500_000 : 0
end
|
#merge_request ⇒ Object
764
765
766
767
768
|
# File 'app/models/ci/build.rb', line 764
def merge_request
strong_memoize(:merge_request) do
pipeline.all_merge_requests.order(iid: :asc).first
end
end
|
#multi_build_steps? ⇒ Boolean
1082
1083
1084
|
# File 'app/models/ci/build.rb', line 1082
def multi_build_steps?
options[:release]&.any?
end
|
#needs_maintainer_role_for_artifact_access? ⇒ Boolean
499
500
501
502
503
|
# File 'app/models/ci/build.rb', line 499
def needs_maintainer_role_for_artifact_access?
return false if job_artifacts_archive.nil?
job_artifacts_archive.maintainer_access?
end
|
#needs_touch? ⇒ Boolean
847
848
849
|
# File 'app/models/ci/build.rb', line 847
def needs_touch?
Time.current - updated_at > 15.minutes.to_i
end
|
#options_scheduled_at ⇒ Object
605
606
607
|
# File 'app/models/ci/build.rb', line 605
def options_scheduled_at
ChronicDuration.parse(options[:start_in])&.seconds&.from_now
end
|
#other_scheduled_actions ⇒ Object
564
565
566
|
# File 'app/models/ci/build.rb', line 564
def other_scheduled_actions
pipeline.scheduled_actions.reject { |action| action.name == name }
end
|
#pages ⇒ Object
576
577
578
579
580
|
# File 'app/models/ci/build.rb', line 576
def pages
return {} unless pages_generator? && publish_path_available?
{ publish: expanded_publish_path }
end
|
#pages_generator? ⇒ Boolean
568
569
570
571
572
573
574
|
# File 'app/models/ci/build.rb', line 568
def pages_generator?
return false unless Gitlab.config.pages.enabled
return false unless options.present?
return true if options[:pages].is_a?(Hash) || options[:pages] == true
options[:pages] != false && name == 'pages' end
|
#pages_variables ⇒ Object
741
742
743
744
745
|
# File 'app/models/ci/build.rb', line 741
def pages_variables
::Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'CI_PAGES_URL', value: project.pages_url(pages))
end
end
|
#persisted_environment_variables ⇒ Object
685
686
687
688
689
690
691
692
693
694
695
696
697
|
# File 'app/models/ci/build.rb', line 685
def persisted_environment_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
break variables unless persisted? && persisted_environment.present?
variables.append(key: 'CI_ENVIRONMENT_ID', value: persisted_environment.id.to_s)
variables.append(key: 'CI_ENVIRONMENT_SLUG', value: environment_slug)
variables.append(key: 'CI_ENVIRONMENT_URL', value: environment_url) if environment_url
end
end
|
#persisted_variables ⇒ Object
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
|
# File 'app/models/ci/build.rb', line 664
def persisted_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
break variables unless persisted?
variables
.concat(pipeline.persisted_variables)
.append(key: 'CI_JOB_ID', value: id.to_s)
.append(key: 'CI_JOB_URL', value: Gitlab::Routing.url_helpers.project_job_url(project, self))
.append(key: 'CI_JOB_TOKEN', value: token.to_s, public: false, masked: true)
.append(key: 'CI_JOB_STARTED_AT', value: started_at&.iso8601)
.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)
.concat(harbor_variables)
.concat(apple_app_store_variables)
.concat(google_play_variables)
.concat(diffblue_cover_variables)
end
end
|
#play(current_user, job_variables_attributes = nil) ⇒ Object
rubocop: disable CodeReuse/ServiceClass
619
620
621
|
# File 'app/models/ci/build.rb', line 619
def play(current_user, job_variables_attributes = nil)
Ci::PlayBuildService.new(current_user: current_user, build: self, variables: job_variables_attributes).execute
end
|
#playable? ⇒ Boolean
597
598
599
|
# File 'app/models/ci/build.rb', line 597
def playable?
action? && !archived? && (manual? || scheduled? || retryable?)
end
|
#publish_path ⇒ Object
1067
1068
1069
1070
1071
1072
|
# File 'app/models/ci/build.rb', line 1067
def publish_path
return unless options.present?
return options[:publish] unless options[:pages].is_a?(Hash)
options.dig(:pages, :publish) || options[:publish]
end
|
#publish_path_available? ⇒ Boolean
1074
1075
1076
|
# File 'app/models/ci/build.rb', line 1074
def publish_path_available?
publish_path.present?
end
|
#publishes_artifacts_reports? ⇒ Boolean
1059
1060
1061
|
# File 'app/models/ci/build.rb', line 1059
def publishes_artifacts_reports?
reports_definitions&.any?
end
|
#remove_pending_state! ⇒ Object
1176
1177
1178
|
# File 'app/models/ci/build.rb', line 1176
def remove_pending_state!
pending_state.try(:delete)
end
|
#remove_token! ⇒ Object
851
852
853
|
# File 'app/models/ci/build.rb', line 851
def remove_token!
update!(token_encrypted: nil)
end
|
#repo_url ⇒ Object
770
771
772
773
774
775
776
777
|
# File 'app/models/ci/build.rb', line 770
def repo_url
return unless token
auth = "#{::Gitlab::Auth::CI_JOB_USER}:#{token}@"
project.http_url_to_repo.sub(%r{^https?://}) do |prefix|
prefix + auth
end
end
|
#report_artifacts ⇒ Object
1137
1138
1139
|
# File 'app/models/ci/build.rb', line 1137
def report_artifacts
job_artifacts.all_reports
end
|
#retries_count ⇒ Object
632
633
634
|
# File 'app/models/ci/build.rb', line 632
def retries_count
pipeline.builds.retried.where(name: name).count
end
|
#run_on_status_commit(&block) ⇒ Object
1180
1181
1182
|
# File 'app/models/ci/build.rb', line 1180
def run_on_status_commit(&block)
status_commit_hooks.push(block)
end
|
#runnable? ⇒ Boolean
583
584
585
|
# File 'app/models/ci/build.rb', line 583
def runnable?
true
end
|
#runner_ack_wait_status ⇒ Symbol
Returns the current status of the runner acknowledgement wait process for two-phase job acceptance.
1305
1306
1307
1308
1309
1310
|
# File 'app/models/ci/build.rb', line 1305
def runner_ack_wait_status
return :not_waiting unless pending? && runner_id.present?
return :waiting if runner_manager_id_waiting_for_ack.present?
:wait_expired
end
|
#runner_required_feature_names ⇒ Object
1045
1046
1047
1048
1049
1050
1051
|
# File 'app/models/ci/build.rb', line 1045
def runner_required_feature_names
strong_memoize(:runner_required_feature_names) do
RUNNER_FEATURES.select do |feature, method|
method.call(self)
end.keys
end
end
|
#schedulable? ⇒ Boolean
601
602
603
|
# File 'app/models/ci/build.rb', line 601
def schedulable?
self.when == 'delayed' && options[:start_in].present?
end
|
#serializable_hash(options = {}) ⇒ Object
1097
1098
1099
|
# File 'app/models/ci/build.rb', line 1097
def serializable_hash(options = {})
super(options).merge(when: read_attribute(:when))
end
|
#shared_runner_build? ⇒ Boolean
1226
1227
1228
|
# File 'app/models/ci/build.rb', line 1226
def shared_runner_build?
runner&.instance_type?
end
|
#source ⇒ Object
1284
1285
1286
|
# File 'app/models/ci/build.rb', line 1284
def source
build_source&.source || pipeline.source
end
|
#stuck? ⇒ Boolean
881
882
883
|
# File 'app/models/ci/build.rb', line 881
def stuck?
pending? && !any_runners_online?
end
|
#supported_runner?(features) ⇒ Boolean
1053
1054
1055
1056
1057
|
# File 'app/models/ci/build.rb', line 1053
def supported_runner?(features)
runner_required_feature_names.all? do |feature_name|
features&.dig(feature_name)
end
end
|
#supports_artifacts_exclude? ⇒ Boolean
1063
1064
1065
|
# File 'app/models/ci/build.rb', line 1063
def supports_artifacts_exclude?
options&.dig(:artifacts, :exclude)&.any?
end
|
#supports_canceling? ⇒ Boolean
A Ci::Bridge may transition to ‘canceling` as a result of strategy: :depend but only a Ci::Build will transition to `canceling“ via `.cancel`
527
528
529
|
# File 'app/models/ci/build.rb', line 527
def supports_canceling?
cancel_gracefully?
end
|
#supports_force_cancel? ⇒ Boolean
535
536
537
|
# File 'app/models/ci/build.rb', line 535
def supports_force_cancel?
true
end
|
#tag_list ⇒ Object
860
861
862
863
864
865
866
|
# File 'app/models/ci/build.rb', line 860
def tag_list
return super if job_definition.nil?
return super if Feature.disabled?(:ci_build_uses_job_definition_tag_list, project)
Gitlab::Ci::Tags::TagList.new(job_definition.config.fetch(:tag_list, []))
end
|
#test_suite_name ⇒ Object
1269
1270
1271
1272
1273
1274
1275
|
# File 'app/models/ci/build.rb', line 1269
def test_suite_name
if matrix_build?
name
else
group_name
end
end
|
#time_in_queue_seconds ⇒ Object
1277
1278
1279
1280
1281
|
# File 'app/models/ci/build.rb', line 1277
def time_in_queue_seconds
return if queued_at.nil?
(::Time.current - queued_at).seconds.to_i
end
|
#token ⇒ Object
1289
1290
1291
1292
1293
|
# File 'app/models/ci/build.rb', line 1289
def token
return encoded_jwt if user&.composite_identity_enforced? || use_jwt_for_ci_cd_job_token?
super
end
|
#trace ⇒ Object
788
789
790
|
# File 'app/models/ci/build.rb', line 788
def trace
Gitlab::Ci::Trace.new(self)
end
|
#trigger_job_processed_subscriptions ⇒ Object
#trigger_job_status_change_subscription ⇒ Object
#triggered_by?(current_user) ⇒ Boolean
649
650
651
|
# File 'app/models/ci/build.rb', line 649
def triggered_by?(current_user)
user == current_user
end
|
#update_coverage ⇒ Object
783
784
785
786
|
# File 'app/models/ci/build.rb', line 783
def update_coverage
coverage = trace.(coverage_regex)
update(coverage: coverage) if coverage.present?
end
|
#valid_dependency? ⇒ Boolean
1038
1039
1040
1041
1042
1043
|
# File 'app/models/ci/build.rb', line 1038
def valid_dependency?
return false if artifacts_expired? && !pipeline.artifacts_locked?
return false if erased?
true
end
|
#variables ⇒ Object
All variables, including persisted environment variables.
656
657
658
659
660
661
662
|
# File 'app/models/ci/build.rb', line 656
def variables
strong_memoize(:variables) do
Gitlab::Ci::Variables::Collection.new
.concat(base_variables)
.concat(pages_variables)
end
end
|