Class: Ci::Pipeline
- Inherits:
-
ApplicationRecord
show all
- Includes:
- AfterCommitQueue, AtomicInternalId, HasRef, HasStatus, EachBatch, EnumWithNil, FastDestroyAll::Helpers, FromUnion, Gitlab::Allowable, Gitlab::OptimisticLocking, Gitlab::Utils::StrongMemoize, Importable, Presentable, ShaAttribute, UpdatedAtFilterable
- Defined in:
- app/models/ci/pipeline.rb
Constant Summary
collapse
- MAX_OPEN_MERGE_REQUESTS_REFS =
4
- PROJECT_ROUTE_AND_NAMESPACE_ROUTE =
{
project: [:project_feature, :route, { namespace: :route }]
}.freeze
- CONFIG_EXTENSION =
'.gitlab-ci.yml'
- DEFAULT_CONFIG_PATH =
CONFIG_EXTENSION
- CANCELABLE_STATUSES =
(Ci::HasStatus::CANCELABLE_STATUSES + ['manual']).freeze
- BridgeStatusError =
Class.new(StandardError)
AtomicInternalId::MissingValueError
Gitlab::OptimisticLocking::MAX_RETRIES
Constants included
from HasStatus
HasStatus::ACTIVE_STATUSES, HasStatus::ALIVE_STATUSES, HasStatus::AVAILABLE_STATUSES, HasStatus::BLOCKED_STATUS, HasStatus::COMPLETED_STATUSES, HasStatus::DEFAULT_STATUS, HasStatus::EXCLUDE_IGNORED_STATUSES, HasStatus::ORDERED_STATUSES, HasStatus::PASSED_WITH_WARNINGS_STATUSES, HasStatus::STARTED_STATUSES, HasStatus::STATUSES_ENUM, HasStatus::STOPPED_STATUSES, HasStatus::UnknownStatusError
ApplicationRecord::MAX_PLUCK
Instance Attribute Summary collapse
-
#config_metadata ⇒ Object
Ci::CreatePipelineService returns Ci::Pipeline so this is the only place where we can pass additional information from the service.
Attributes included from Importable
#imported, #importing
Class Method Summary
collapse
Instance Method Summary
collapse
#perform_fast_destroy
#run_after_commit, #run_after_commit_or_now
Methods included from HasRef
#branch?, #ref_slug
group_init, #internal_id_read_scope, #internal_id_scope_attrs, #internal_id_scope_usage, project_init, scope_attrs, scope_usage
#clear_memoization, #strong_memoize, #strong_memoized?
log_optimistic_lock_retries, retry_lock, retry_lock_histogram, retry_lock_logger
#can?
#present
Methods included from HasStatus
#active?, #blocked?, #complete?, #started?
model_name, table_name_prefix
cached_column_list, #create_or_load_association, declarative_enum, default_select_columns, id_in, id_not_in, iid_in, pluck_primary_key, 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
#serializable_hash
Instance Attribute Details
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. There is an open issue to address this: gitlab.com/gitlab-org/gitlab/-/issues/259010
42
43
44
|
# File 'app/models/ci/pipeline.rb', line 42
def config_metadata
@config_metadata
end
|
Class Method Details
.auto_devops_pipelines_completed_total ⇒ Object
462
463
464
|
# File 'app/models/ci/pipeline.rb', line 462
def self.auto_devops_pipelines_completed_total
@auto_devops_pipelines_completed_total ||= Gitlab::Metrics.counter(:auto_devops_pipelines_completed_total, 'Number of completed auto devops pipelines')
end
|
.bridgeable_statuses ⇒ Object
458
459
460
|
# File 'app/models/ci/pipeline.rb', line 458
def self.bridgeable_statuses
::Ci::Pipeline::AVAILABLE_STATUSES - %w[created waiting_for_resource preparing pending]
end
|
.internal_sources ⇒ Object
454
455
456
|
# File 'app/models/ci/pipeline.rb', line 454
def self.internal_sources
sources.reject { |source| source == "external" }.values
end
|
.jobs_count_in_alive_pipelines ⇒ Object
415
416
417
|
# File 'app/models/ci/pipeline.rb', line 415
def self.jobs_count_in_alive_pipelines
created_after(24.hours.ago).alive.joins(:builds).count
end
|
.last_finished_for_ref_id(ci_ref_id) ⇒ Object
442
443
444
|
# File 'app/models/ci/pipeline.rb', line 442
def self.last_finished_for_ref_id(ci_ref_id)
where(ci_ref_id: ci_ref_id).ci_sources.finished.order(id: :desc).select(:id).take
end
|
.latest_failed_for_ref(ref) ⇒ Object
411
412
413
|
# File 'app/models/ci/pipeline.rb', line 411
def self.latest_failed_for_ref(ref)
newest_first(ref: ref).failed.take
end
|
.latest_pipeline_per_commit(commits, ref = nil) ⇒ Object
Returns a Hash containing the latest pipeline for every given commit.
The keys of this Hash are the commit SHAs, the values the pipelines.
commits - The list of commit SHAs to get the pipelines for. ref - The ref to scope the data to (e.g. “master”). If the ref is not
given we simply get the latest pipelines for the commits, regardless
of what refs the pipelines belong to.
428
429
430
431
432
433
434
435
436
|
# File 'app/models/ci/pipeline.rb', line 428
def self.latest_pipeline_per_commit(commits, ref = nil)
sql = select('DISTINCT ON (sha) *')
.where(sha: commits)
.order(:sha, id: :desc)
sql = sql.where(ref: ref) if ref
sql.index_by(&:sha)
end
|
.latest_running_for_ref(ref) ⇒ Object
407
408
409
|
# File 'app/models/ci/pipeline.rb', line 407
def self.latest_running_for_ref(ref)
newest_first(ref: ref).running.take
end
|
.latest_status(ref = nil) ⇒ Object
383
384
385
|
# File 'app/models/ci/pipeline.rb', line 383
def self.latest_status(ref = nil)
newest_first(ref: ref).pluck(:status).first
end
|
.latest_successful_for_ref(ref) ⇒ Object
387
388
389
|
# File 'app/models/ci/pipeline.rb', line 387
def self.latest_successful_for_ref(ref)
newest_first(ref: ref).success.take
end
|
.latest_successful_for_refs(refs) ⇒ Object
395
396
397
398
399
400
401
402
403
404
405
|
# File 'app/models/ci/pipeline.rb', line 395
def self.latest_successful_for_refs(refs)
return Ci::Pipeline.none if refs.empty?
refs_values = refs.map { |ref| "(#{connection.quote(ref)})" }.join(",")
join_query = success.where("refs_values.ref = ci_pipelines.ref").order(id: :desc).limit(1)
Ci::Pipeline
.from("(VALUES #{refs_values}) refs_values (ref)")
.joins("INNER JOIN LATERAL (#{join_query.to_sql}) #{Ci::Pipeline.table_name} ON TRUE")
.index_by(&:ref)
end
|
.latest_successful_for_sha(sha) ⇒ Object
391
392
393
|
# File 'app/models/ci/pipeline.rb', line 391
def self.latest_successful_for_sha(sha)
newest_first(sha: sha).success.take
end
|
.latest_successful_ids_per_project ⇒ Object
438
439
440
|
# File 'app/models/ci/pipeline.rb', line 438
def self.latest_successful_ids_per_project
success.group(:project_id).select('max(id) as id')
end
|
.newest_first(ref: nil, sha: nil, limit: 100) ⇒ Object
Returns the pipelines in descending order (= newest first), optionally limited to a number of references.
ref - The name (or names) of the branch(es)/tag(s) to limit the list of
pipelines to.
sha - The commit SHA (or multiple SHAs) to limit the list of pipelines to. limit - This limits a backlog search, default to 100.
370
371
372
373
374
375
376
377
378
379
380
381
|
# File 'app/models/ci/pipeline.rb', line 370
def self.newest_first(ref: nil, sha: nil, limit: 100)
relation = order(id: :desc)
relation = relation.where(ref: ref) if ref
relation = relation.where(sha: sha) if sha
if limit
ids = relation.limit(limit).select(:id)
relation = relation.where(id: ids)
end
relation
end
|
.total_duration ⇒ Object
450
451
452
|
# File 'app/models/ci/pipeline.rb', line 450
def self.total_duration
where.not(duration: nil).sum(:duration)
end
|
.truncate_sha(sha) ⇒ Object
446
447
448
|
# File 'app/models/ci/pipeline.rb', line 446
def self.truncate_sha(sha)
sha[0...8]
end
|
Instance Method Details
#accessibility_reports ⇒ Object
1108
1109
1110
1111
1112
1113
1114
|
# File 'app/models/ci/pipeline.rb', line 1108
def accessibility_reports
Gitlab::Ci::Reports::AccessibilityReports.new.tap do |accessibility_reports|
latest_report_builds(Ci::JobArtifact.accessibility_reports).each do |build|
build.collect_accessibility_reports!(accessibility_reports)
end
end
end
|
#add_error_message(content) ⇒ Object
744
745
746
|
# File 'app/models/ci/pipeline.rb', line 744
def add_error_message(content)
add_message(:error, content)
end
|
#add_warning_message(content) ⇒ Object
748
749
750
|
# File 'app/models/ci/pipeline.rb', line 748
def add_warning_message(content)
add_message(:warning, content)
end
|
#all_merge_requests ⇒ Object
All the merge requests for which the current pipeline runs/ran against
911
912
913
914
915
916
917
918
919
|
# File 'app/models/ci/pipeline.rb', line 911
def all_merge_requests
@all_merge_requests ||=
if merge_request?
MergeRequest.where(id: merge_request_id)
else
MergeRequest.where(source_project_id: project_id, source_branch: ref)
.by_commit_sha(sha)
end
end
|
#all_merge_requests_by_recency ⇒ Object
921
922
923
|
# File 'app/models/ci/pipeline.rb', line 921
def all_merge_requests_by_recency
all_merge_requests.order(id: :desc)
end
|
#all_pipelines_in_hierarchy ⇒ Object
With multi-project and parent-child pipelines
991
992
993
|
# File 'app/models/ci/pipeline.rb', line 991
def all_pipelines_in_hierarchy
object_hierarchy.all_objects
end
|
#all_worktree_paths ⇒ Object
1171
1172
1173
1174
1175
|
# File 'app/models/ci/pipeline.rb', line 1171
def all_worktree_paths
strong_memoize(:all_worktree_paths) do
project.repository.ls_files(sha)
end
end
|
#authorized_cluster_agents ⇒ Object
1310
1311
1312
1313
1314
|
# File 'app/models/ci/pipeline.rb', line 1310
def authorized_cluster_agents
strong_memoize(:authorized_cluster_agents) do
::Clusters::AgentAuthorizationsFinder.new(project).execute.map(&:agent)
end
end
|
#auto_cancel_running(pipeline, retries: 1) ⇒ Object
626
627
628
629
630
631
632
|
# File 'app/models/ci/pipeline.rb', line 626
def auto_cancel_running(pipeline, retries: 1)
update(auto_canceled_by: pipeline)
cancel_running(retries: retries) do |job|
job.auto_canceled_by = pipeline
end
end
|
#auto_canceled? ⇒ Boolean
607
608
609
|
# File 'app/models/ci/pipeline.rb', line 607
def auto_canceled?
canceled? && auto_canceled_by_id?
end
|
#batch_lookup_report_artifact_for_file_type(file_type) ⇒ Object
673
674
675
|
# File 'app/models/ci/pipeline.rb', line 673
def batch_lookup_report_artifact_for_file_type(file_type)
batch_lookup_report_artifact_for_file_types([file_type])
end
|
#batch_lookup_report_artifact_for_file_types(file_types) ⇒ Object
677
678
679
680
681
682
683
684
685
686
|
# File 'app/models/ci/pipeline.rb', line 677
def batch_lookup_report_artifact_for_file_types(file_types)
file_types_to_search = []
file_types.each { |file_type| file_types_to_search.append(*::Ci::JobArtifact.associated_file_types_for(file_type.to_s)) }
latest_report_artifacts
.values_at(*file_types_to_search.uniq)
.flatten
.compact
.last
end
|
#before_sha ⇒ Object
579
580
581
|
# File 'app/models/ci/pipeline.rb', line 579
def before_sha
super || Gitlab::Git::BLANK_SHA
end
|
#branch_updated? ⇒ Boolean
1148
1149
1150
1151
1152
|
# File 'app/models/ci/pipeline.rb', line 1148
def branch_updated?
strong_memoize(:branch_updated) do
push_details.branch_updated?
end
end
|
#bridge_triggered? ⇒ Boolean
1020
1021
1022
|
# File 'app/models/ci/pipeline.rb', line 1020
def bridge_triggered?
source_bridge.present?
end
|
#bridge_waiting? ⇒ Boolean
1024
1025
1026
|
# File 'app/models/ci/pipeline.rb', line 1024
def bridge_waiting?
source_bridge&.dependent?
end
|
#build_matchers ⇒ Object
1306
1307
1308
|
# File 'app/models/ci/pipeline.rb', line 1306
def build_matchers
self.builds.latest.build_matchers(project)
end
|
#build_with_artifacts_in_self_and_descendants(name) ⇒ Object
961
962
963
964
965
966
|
# File 'app/models/ci/pipeline.rb', line 961
def build_with_artifacts_in_self_and_descendants(name)
builds_in_self_and_descendants
.ordered_by_pipeline .with_downloadable_artifacts
.find_by_name(name)
end
|
#builds_in_self_and_descendants ⇒ Object
968
969
970
|
# File 'app/models/ci/pipeline.rb', line 968
def builds_in_self_and_descendants
Ci::Build.latest.where(pipeline: self_and_descendants)
end
|
#builds_with_coverage ⇒ Object
1066
1067
1068
|
# File 'app/models/ci/pipeline.rb', line 1066
def builds_with_coverage
builds.latest.with_coverage
end
|
#builds_with_failed_tests(limit: nil) ⇒ Object
1070
1071
1072
|
# File 'app/models/ci/pipeline.rb', line 1070
def builds_with_failed_tests(limit: nil)
latest_test_report_builds.failed.limit(limit)
end
|
#can_generate_codequality_reports? ⇒ Boolean
1090
1091
1092
|
# File 'app/models/ci/pipeline.rb', line 1090
def can_generate_codequality_reports?
has_reports?(Ci::JobArtifact.codequality_reports)
end
|
#can_generate_coverage_reports? ⇒ Boolean
1082
1083
1084
|
# File 'app/models/ci/pipeline.rb', line 1082
def can_generate_coverage_reports?
has_reports?(Ci::JobArtifact.coverage_reports)
end
|
#cancel_running(retries: 1) ⇒ Object
611
612
613
614
615
616
617
618
619
620
621
622
623
624
|
# File 'app/models/ci/pipeline.rb', line 611
def cancel_running(retries: 1)
preloaded_relations = [:project, :pipeline, :deployment, :taggings]
retry_lock(cancelable_statuses, retries, name: 'ci_pipeline_cancel_running') do |cancelables|
cancelables.find_in_batches do |batch|
Preloaders::CommitStatusPreloader.new(batch).execute(preloaded_relations)
batch.each do |job|
yield(job) if block_given?
job.cancel
end
end
end
end
|
#cancelable? ⇒ Boolean
603
604
605
|
# File 'app/models/ci/pipeline.rb', line 603
def cancelable?
cancelable_statuses.any?
end
|
#child? ⇒ Boolean
1028
1029
1030
1031
|
# File 'app/models/ci/pipeline.rb', line 1028
def child?
parent_pipeline? && parent_pipeline.present?
end
|
#codequality_reports ⇒ Object
1124
1125
1126
1127
1128
1129
1130
|
# File 'app/models/ci/pipeline.rb', line 1124
def codequality_reports
Gitlab::Ci::Reports::CodequalityReports.new.tap do |codequality_reports|
latest_report_builds(Ci::JobArtifact.codequality_reports).each do |build|
build.collect_codequality_reports!(codequality_reports)
end
end
end
|
#commit ⇒ Object
NOTE: This is loaded lazily and will never be nil, even if the commit cannot be found.
Use constructs like: `pipeline.commit.present?`
591
592
593
|
# File 'app/models/ci/pipeline.rb', line 591
def commit
@commit ||= Commit.lazy(project, sha)
end
|
#coverage ⇒ Object
662
663
664
665
666
667
|
# File 'app/models/ci/pipeline.rb', line 662
def coverage
coverage_array = latest_statuses.map(&:coverage).compact
if coverage_array.size >= 1
coverage_array.sum / coverage_array.size
end
end
|
#coverage_reports ⇒ Object
1116
1117
1118
1119
1120
1121
1122
|
# File 'app/models/ci/pipeline.rb', line 1116
def coverage_reports
Gitlab::Ci::Reports::CoverageReports.new.tap do |coverage_reports|
latest_report_builds(Ci::JobArtifact.coverage_reports).includes(:project).find_each do |build|
build.collect_coverage_reports!(coverage_reports)
end
end
end
|
#created_successfully? ⇒ Boolean
1037
1038
1039
|
# File 'app/models/ci/pipeline.rb', line 1037
def created_successfully?
persisted? && failure_reason.blank?
end
|
#default_branch? ⇒ Boolean
1183
1184
1185
|
# File 'app/models/ci/pipeline.rb', line 1183
def default_branch?
ref == project.default_branch
end
|
#detached_merge_request_pipeline? ⇒ Boolean
1195
1196
1197
|
# File 'app/models/ci/pipeline.rb', line 1195
def detached_merge_request_pipeline?
merge_request? && target_sha.nil?
end
|
#detailed_status(current_user) ⇒ Object
1041
1042
1043
1044
1045
|
# File 'app/models/ci/pipeline.rb', line 1041
def detailed_status(current_user)
Gitlab::Ci::Status::Pipeline::Factory
.new(self.present, current_user)
.fabricate!
end
|
482
483
484
|
# File 'app/models/ci/pipeline.rb', line 482
def distinct_tags_count
ActsAsTaggableOn::Tagging.where(taggable: builds).count('distinct(tag_id)')
end
|
#ensure_ci_ref! ⇒ Object
1273
1274
1275
|
# File 'app/models/ci/pipeline.rb', line 1273
def ensure_ci_ref!
self.ci_ref = Ci::Ref.ensure_for(self)
end
|
#ensure_persistent_ref ⇒ Object
1277
1278
1279
1280
1281
|
# File 'app/models/ci/pipeline.rb', line 1277
def ensure_persistent_ref
return if persistent_ref.exist?
persistent_ref.create
end
|
#ensure_scheduling_type! ⇒ Object
1269
1270
1271
|
# File 'app/models/ci/pipeline.rb', line 1269
def ensure_scheduling_type!
processables.populate_scheduling_type!
end
|
#environments_in_self_and_descendants(deployment_status: nil) ⇒ Object
972
973
974
975
976
977
978
979
980
981
982
983
|
# File 'app/models/ci/pipeline.rb', line 972
def environments_in_self_and_descendants(deployment_status: nil)
expanded_environment_names =
builds_in_self_and_descendants.joins(:metadata)
.where.not('ci_builds_metadata.expanded_environment_name' => nil)
.distinct('ci_builds_metadata.expanded_environment_name')
.limit(100)
.pluck(:expanded_environment_name)
Environment.where(project: project, name: expanded_environment_names).with_deployment(sha, status: deployment_status)
end
|
#error_messages ⇒ Object
We can't use `messages.error` scope here because messages should also be read when the pipeline is not persisted. Using the scope will return no results as it would query persisted data.
755
756
757
|
# File 'app/models/ci/pipeline.rb', line 755
def error_messages
messages.select(&:error?)
end
|
#external_pull_request? ⇒ Boolean
1191
1192
1193
|
# File 'app/models/ci/pipeline.rb', line 1191
def external_pull_request?
external_pull_request_id.present?
end
|
#find_job_with_archive_artifacts(name) ⇒ Object
1047
1048
1049
|
# File 'app/models/ci/pipeline.rb', line 1047
def find_job_with_archive_artifacts(name)
builds.latest.with_downloadable_artifacts.find_by_name(name)
end
|
#find_stage_by_name!(name) ⇒ Object
1231
1232
1233
|
# File 'app/models/ci/pipeline.rb', line 1231
def find_stage_by_name!(name)
stages.find_by!(name: name)
end
|
#freeze_period? ⇒ Boolean
712
713
714
715
716
|
# File 'app/models/ci/pipeline.rb', line 712
def freeze_period?
strong_memoize(:freeze_period) do
Ci::FreezePeriodStatus.new(project: project).execute
end
end
|
#full_error_messages ⇒ Object
1235
1236
1237
|
# File 'app/models/ci/pipeline.rb', line 1235
def full_error_messages
errors ? errors.full_messages.to_sentence : ""
end
|
#git_author_email ⇒ Object
537
538
539
540
541
|
# File 'app/models/ci/pipeline.rb', line 537
def git_author_email
strong_memoize(:git_author_email) do
commit.try(:author_email)
end
end
|
#git_author_full_text ⇒ Object
543
544
545
546
547
|
# File 'app/models/ci/pipeline.rb', line 543
def git_author_full_text
strong_memoize(:git_author_full_text) do
commit.try(:author_full_text)
end
end
|
#git_author_name ⇒ Object
531
532
533
534
535
|
# File 'app/models/ci/pipeline.rb', line 531
def git_author_name
strong_memoize(:git_author_name) do
commit.try(:author_name)
end
end
|
#git_commit_description ⇒ Object
567
568
569
570
571
|
# File 'app/models/ci/pipeline.rb', line 567
def git_commit_description
strong_memoize(:git_commit_description) do
commit.try(:description)
end
end
|
#git_commit_full_title ⇒ Object
561
562
563
564
565
|
# File 'app/models/ci/pipeline.rb', line 561
def git_commit_full_title
strong_memoize(:git_commit_full_title) do
commit.try(:full_title)
end
end
|
#git_commit_message ⇒ Object
549
550
551
552
553
|
# File 'app/models/ci/pipeline.rb', line 549
def git_commit_message
strong_memoize(:git_commit_message) do
commit.try(:message)
end
end
|
#git_commit_timestamp ⇒ Object
573
574
575
576
577
|
# File 'app/models/ci/pipeline.rb', line 573
def git_commit_timestamp
strong_memoize(:git_commit_timestamp) do
commit.try(:timestamp)
end
end
|
#git_commit_title ⇒ Object
555
556
557
558
559
|
# File 'app/models/ci/pipeline.rb', line 555
def git_commit_title
strong_memoize(:git_commit_title) do
commit.try(:title)
end
end
|
#has_archive_artifacts? ⇒ Boolean
1140
1141
1142
|
# File 'app/models/ci/pipeline.rb', line 1140
def has_archive_artifacts?
complete? && builds.latest.with_existing_job_artifacts(Ci::JobArtifact.archive.or(Ci::JobArtifact.metadata)).exists?
end
|
#has_codequality_mr_diff_report? ⇒ Boolean
1086
1087
1088
|
# File 'app/models/ci/pipeline.rb', line 1086
def has_codequality_mr_diff_report?
pipeline_artifacts&.report_exists?(:code_quality_mr_diff)
end
|
#has_coverage_reports? ⇒ Boolean
1078
1079
1080
|
# File 'app/models/ci/pipeline.rb', line 1078
def has_coverage_reports?
pipeline_artifacts&.report_exists?(:code_coverage)
end
|
#has_expired_test_reports? ⇒ Boolean
1316
1317
1318
1319
1320
|
# File 'app/models/ci/pipeline.rb', line 1316
def has_expired_test_reports?
strong_memoize(:artifacts_expired) do
!has_reports?(::Ci::JobArtifact.test_reports.not_expired)
end
end
|
#has_exposed_artifacts? ⇒ Boolean
1144
1145
1146
|
# File 'app/models/ci/pipeline.rb', line 1144
def has_exposed_artifacts?
complete? && builds.latest.with_exposed_artifacts.exists?
end
|
#has_kubernetes_active? ⇒ Boolean
706
707
708
709
710
|
# File 'app/models/ci/pipeline.rb', line 706
def has_kubernetes_active?
strong_memoize(:has_kubernetes_active) do
project.deployment_platform&.active?
end
end
|
#has_reports?(reports_scope) ⇒ Boolean
1074
1075
1076
|
# File 'app/models/ci/pipeline.rb', line 1074
def has_reports?(reports_scope)
complete? && latest_report_builds(reports_scope).exists?
end
|
#has_warnings? ⇒ Boolean
718
719
720
|
# File 'app/models/ci/pipeline.rb', line 718
def has_warnings?
number_of_warnings > 0
end
|
#has_yaml_errors? ⇒ Boolean
740
741
742
|
# File 'app/models/ci/pipeline.rb', line 740
def has_yaml_errors?
yaml_errors.present?
end
|
#jobs_git_ref ⇒ Object
This is used to retain access to the method defined by `Ci::HasRef` before being overridden in this class.
46
|
# File 'app/models/ci/pipeline.rb', line 46
alias_method :jobs_git_ref, :git_ref
|
#latest? ⇒ Boolean
651
652
653
654
655
656
|
# File 'app/models/ci/pipeline.rb', line 651
def latest?
return false unless git_ref && commit.present?
return false if lazy_ref_commit.nil?
lazy_ref_commit.id == commit.id
end
|
#latest_builds_with_artifacts ⇒ Object
1051
1052
1053
1054
1055
1056
|
# File 'app/models/ci/pipeline.rb', line 1051
def latest_builds_with_artifacts
@latest_builds_with_artifacts ||= builds.latest.with_artifacts_not_expired.to_a
end
|
#latest_report_artifacts ⇒ Object
This batch loads the latest reports for each CI job artifact type (e.g. sast, dast, etc.) in a single SQL query to eliminate the need to do N different `job_artifacts.where(file_type: X).last` calls.
Return a hash of file type => array of 1 job artifact
694
695
696
697
698
699
700
701
702
703
704
|
# File 'app/models/ci/pipeline.rb', line 694
def latest_report_artifacts
::Gitlab::SafeRequestStore.fetch("pipeline:#{self.id}:latest_report_artifacts") do
::Ci::JobArtifact.where(
id: job_artifacts.with_reports
.select('max(ci_job_artifacts.id) as id')
.group(:file_type)
)
.preload(:job)
.group_by(&:file_type)
end
end
|
#latest_report_builds(reports_scope = ::Ci::JobArtifact.with_reports) ⇒ Object
1058
1059
1060
|
# File 'app/models/ci/pipeline.rb', line 1058
def latest_report_builds(reports_scope = ::Ci::JobArtifact.with_reports)
builds.latest.with_reports(reports_scope)
end
|
#latest_test_report_builds ⇒ Object
1062
1063
1064
|
# File 'app/models/ci/pipeline.rb', line 1062
def latest_test_report_builds
latest_report_builds(Ci::JobArtifact.test_reports).preload(:project)
end
|
#lazy_ref_commit ⇒ Object
rubocop: enable CodeReuse/ServiceClass
641
642
643
644
645
646
647
648
649
|
# File 'app/models/ci/pipeline.rb', line 641
def lazy_ref_commit
BatchLoader.for(ref).batch do |refs, loader|
next unless project.repository_exists?
project.repository.list_commits_by_ref_name(refs).then do |commits|
commits.each { |key, commit| loader.call(key, commits[key]) }
end
end
end
|
#legacy_detached_merge_request_pipeline? ⇒ Boolean
1199
1200
1201
|
# File 'app/models/ci/pipeline.rb', line 1199
def legacy_detached_merge_request_pipeline?
detached_merge_request_pipeline? && !merge_request_ref?
end
|
#legacy_stage(name) ⇒ Object
491
492
493
494
|
# File 'app/models/ci/pipeline.rb', line 491
def legacy_stage(name)
stage = Ci::LegacyStage.new(self, name: name)
stage unless stage.statuses_count == 0
end
|
#legacy_stages ⇒ Object
TODO: Remove usage of this method in templates
521
522
523
|
# File 'app/models/ci/pipeline.rb', line 521
def legacy_stages
legacy_stages_using_composite_status
end
|
#legacy_stages_using_composite_status ⇒ Object
502
503
504
505
506
507
508
509
510
511
512
513
514
|
# File 'app/models/ci/pipeline.rb', line 502
def legacy_stages_using_composite_status
stages = latest_statuses_ordered_by_stage.group_by(&:stage)
stages.map do |stage_name, jobs|
composite_status = Gitlab::Ci::Status::Composite
.new(jobs)
Ci::LegacyStage.new(self,
name: stage_name,
status: composite_status.status,
warnings: composite_status.warnings?)
end
end
|
#legacy_trigger ⇒ Object
807
808
809
|
# File 'app/models/ci/pipeline.rb', line 807
def legacy_trigger
strong_memoize(:legacy_trigger) { trigger_requests.first }
end
|
#matches_sha_or_source_sha?(sha) ⇒ Boolean
1211
1212
1213
|
# File 'app/models/ci/pipeline.rb', line 1211
def matches_sha_or_source_sha?(sha)
self.sha == sha || self.source_sha == sha
end
|
#merge_request? ⇒ Boolean
1187
1188
1189
|
# File 'app/models/ci/pipeline.rb', line 1187
def merge_request?
merge_request_id.present? && merge_request.present?
end
|
#merge_request_event_type ⇒ Object
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
|
# File 'app/models/ci/pipeline.rb', line 1239
def merge_request_event_type
return unless merge_request?
strong_memoize(:merge_request_event_type) do
if merged_result_pipeline?
:merged_result
elsif detached_merge_request_pipeline?
:detached
end
end
end
|
#merge_request_ref? ⇒ Boolean
#merge_train_pipeline? ⇒ Boolean
1291
1292
1293
|
# File 'app/models/ci/pipeline.rb', line 1291
def merge_train_pipeline?
false
end
|
#merged_result_pipeline? ⇒ Boolean
1203
1204
1205
|
# File 'app/models/ci/pipeline.rb', line 1203
def merged_result_pipeline?
merge_request? && target_sha.present?
end
|
#modified_paths ⇒ Object
Returns the modified paths.
The returned value is
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
|
# File 'app/models/ci/pipeline.rb', line 1159
def modified_paths
strong_memoize(:modified_paths) do
if merge_request?
merge_request.modified_paths
elsif branch_updated?
push_details.modified_paths
elsif external_pull_request?
external_pull_request.modified_paths
end
end
end
|
#needs_processing? ⇒ Boolean
733
734
735
736
737
738
|
# File 'app/models/ci/pipeline.rb', line 733
def needs_processing?
statuses
.where(processed: [false, nil])
.latest
.exists?
end
|
#notes ⇒ Object
778
779
780
|
# File 'app/models/ci/pipeline.rb', line 778
def notes
project.notes.for_commit_id(sha)
end
|
#notes=(notes) ⇒ Object
Manually set the notes for a Ci::Pipeline There is no ActiveRecord relation between Ci::Pipeline and notes as they are related to a commit sha. This method helps importing them using the Gitlab::ImportExport::Project::RelationFactory
class.
769
770
771
772
773
774
775
776
|
# File 'app/models/ci/pipeline.rb', line 769
def notes=(notes)
notes.each do |note|
note[:id] = nil
note[:commit_id] = sha
note[:noteable_id] = self['id']
note.save!
end
end
|
#number_of_warnings ⇒ Object
722
723
724
725
726
727
728
729
730
731
|
# File 'app/models/ci/pipeline.rb', line 722
def number_of_warnings
BatchLoader.for(id).batch(default_value: 0) do |pipeline_ids, loader|
::CommitStatus.where(commit_id: pipeline_ids)
.latest
.failed_but_allowed
.group(:commit_id)
.count
.each { |id, amount| loader.call(id, amount) }
end
end
|
#open_merge_requests_refs ⇒ Object
We cannot use `all_merge_requests`, due to race condition This returns a list of at most 4 open MRs
942
943
944
945
946
947
948
949
950
951
952
953
|
# File 'app/models/ci/pipeline.rb', line 942
def open_merge_requests_refs
strong_memoize(:open_merge_requests_refs) do
related_merge_requests
.opened
.limit(MAX_OPEN_MERGE_REQUESTS_REFS)
.order(id: :desc)
.preload(:target_project)
.select { |mr| can?(user, :read_merge_request, mr) }
.map { |mr| mr.to_reference(project, full: true) }
end
end
|
#parent? ⇒ Boolean
1033
1034
1035
|
# File 'app/models/ci/pipeline.rb', line 1033
def parent?
child_pipelines.exists?
end
|
#persisted_variables ⇒ Object
815
816
817
818
819
820
821
822
|
# File 'app/models/ci/pipeline.rb', line 815
def persisted_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
break variables unless persisted?
variables.append(key: 'CI_PIPELINE_ID', value: id.to_s)
variables.append(key: 'CI_PIPELINE_URL', value: Gitlab::Routing.url_helpers.project_pipeline_url(project, self))
end
end
|
#persistent_ref ⇒ Object
1251
1252
1253
|
# File 'app/models/ci/pipeline.rb', line 1251
def persistent_ref
@persistent_ref ||= PersistentRef.new(pipeline: self)
end
|
#predefined_commit_variables ⇒ Object
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
|
# File 'app/models/ci/pipeline.rb', line 848
def predefined_commit_variables
strong_memoize(:predefined_commit_variables) do
Gitlab::Ci::Variables::Collection.new.tap do |variables|
next variables unless sha.present?
variables.append(key: 'CI_COMMIT_SHA', value: sha)
variables.append(key: 'CI_COMMIT_SHORT_SHA', value: short_sha)
variables.append(key: 'CI_COMMIT_BEFORE_SHA', value: before_sha)
variables.append(key: 'CI_COMMIT_REF_NAME', value: source_ref)
variables.append(key: 'CI_COMMIT_REF_SLUG', value: source_ref_slug)
variables.append(key: 'CI_COMMIT_BRANCH', value: ref) if branch?
variables.append(key: 'CI_COMMIT_TAG', value: ref) if tag?
variables.append(key: 'CI_COMMIT_MESSAGE', value: git_commit_message.to_s)
variables.append(key: 'CI_COMMIT_TITLE', value: git_commit_full_title.to_s)
variables.append(key: 'CI_COMMIT_DESCRIPTION', value: git_commit_description.to_s)
variables.append(key: 'CI_COMMIT_REF_PROTECTED', value: (!!protected_ref?).to_s)
variables.append(key: 'CI_COMMIT_TIMESTAMP', value: git_commit_timestamp.to_s)
variables.append(key: 'CI_COMMIT_AUTHOR', value: git_author_full_text.to_s)
variables.append(key: 'CI_BUILD_REF', value: sha)
variables.append(key: 'CI_BUILD_BEFORE_SHA', value: before_sha)
variables.append(key: 'CI_BUILD_REF_NAME', value: source_ref)
variables.append(key: 'CI_BUILD_REF_SLUG', value: source_ref_slug)
variables.append(key: 'CI_BUILD_TAG', value: ref) if tag?
end
end
end
|
#predefined_merge_request_variables ⇒ Object
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
|
# File 'app/models/ci/pipeline.rb', line 877
def predefined_merge_request_variables
strong_memoize(:predefined_merge_request_variables) do
Gitlab::Ci::Variables::Collection.new.tap do |variables|
next variables unless merge_request?
variables.append(key: 'CI_MERGE_REQUEST_EVENT_TYPE', value: merge_request_event_type.to_s)
variables.append(key: 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', value: source_sha.to_s)
variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA', value: target_sha.to_s)
diff = self.merge_request_diff
if diff.present?
variables.append(key: 'CI_MERGE_REQUEST_DIFF_ID', value: diff.id.to_s)
variables.append(key: 'CI_MERGE_REQUEST_DIFF_BASE_SHA', value: diff.base_commit_sha)
end
variables.concat(merge_request.predefined_variables)
end
end
end
|
#predefined_variables ⇒ Object
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
|
# File 'app/models/ci/pipeline.rb', line 824
def predefined_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'CI_PIPELINE_IID', value: iid.to_s)
variables.append(key: 'CI_PIPELINE_SOURCE', value: source.to_s)
variables.append(key: 'CI_PIPELINE_CREATED_AT', value: created_at&.iso8601)
variables.concat(predefined_commit_variables)
variables.concat(predefined_merge_request_variables)
if open_merge_requests_refs.any?
variables.append(key: 'CI_OPEN_MERGE_REQUESTS', value: open_merge_requests_refs.join(','))
end
variables.append(key: 'CI_GITLAB_FIPS_MODE', value: 'true') if Gitlab::FIPS.enabled?
variables.append(key: 'CI_KUBERNETES_ACTIVE', value: 'true') if has_kubernetes_active?
variables.append(key: 'CI_DEPLOY_FREEZE', value: 'true') if freeze_period?
if external_pull_request_event? && external_pull_request
variables.concat(external_pull_request.predefined_variables)
end
end
end
|
#protected_ref? ⇒ Boolean
803
804
805
|
# File 'app/models/ci/pipeline.rb', line 803
def protected_ref?
strong_memoize(:protected_ref) { project.protected_for?(git_ref) }
end
|
#queued_duration ⇒ Object
897
898
899
900
901
902
|
# File 'app/models/ci/pipeline.rb', line 897
def queued_duration
return unless started_at
seconds = (started_at - created_at).to_i
seconds unless seconds == 0
end
|
#ref_exists? ⇒ Boolean
496
497
498
499
500
|
# File 'app/models/ci/pipeline.rb', line 496
def ref_exists?
project.repository.ref_exists?(git_ref)
rescue Gitlab::Git::Repository::NoRepository
false
end
|
This returns a list of MRs that point to the same source project/branch
927
928
929
930
931
932
933
934
935
936
937
938
|
# File 'app/models/ci/pipeline.rb', line 927
def related_merge_requests
if merge_request?
MergeRequest.where(
source_project_id: merge_request.source_project_id,
source_branch: merge_request.source_branch)
else
MergeRequest.where(
source_project_id: project_id,
source_branch: ref)
end
end
|
#reset_source_bridge!(current_user) ⇒ Object
1283
1284
1285
1286
1287
1288
|
# File 'app/models/ci/pipeline.rb', line 1283
def reset_source_bridge!(current_user)
return unless bridge_waiting?
source_bridge.pending!
Ci::AfterRequeueJobService.new(project, current_user).execute(source_bridge) end
|
#retried ⇒ Object
658
659
660
|
# File 'app/models/ci/pipeline.rb', line 658
def retried
@retried ||= (statuses.order(id: :desc) - latest_statuses)
end
|
#retry_failed(current_user) ⇒ Object
rubocop: disable CodeReuse/ServiceClass
635
636
637
638
|
# File 'app/models/ci/pipeline.rb', line 635
def retry_failed(current_user)
Ci::RetryPipelineService.new(project, current_user)
.execute(self)
end
|
#retryable? ⇒ Boolean
599
600
601
|
# File 'app/models/ci/pipeline.rb', line 599
def retryable?
retryable_builds.any?
end
|
#root_ancestor ⇒ Object
Follow the parent-child relationships and return the top-level parent
1006
1007
1008
1009
1010
1011
1012
|
# File 'app/models/ci/pipeline.rb', line 1006
def root_ancestor
return self unless child?
object_hierarchy(project_condition: :same)
.base_and_ancestors(hierarchy_order: :desc)
.first
end
|
#same_family_pipeline_ids ⇒ Object
#security_reports(report_types: []) ⇒ Object
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
|
# File 'app/models/ci/pipeline.rb', line 1295
def security_reports(report_types: [])
reports_scope = report_types.empty? ? ::Ci::JobArtifact.security_reports : ::Ci::JobArtifact.security_reports(file_types: report_types)
types_to_collect = report_types.empty? ? ::Ci::JobArtifact::SECURITY_REPORT_FILE_TYPES : report_types
::Gitlab::Ci::Reports::Security::Reports.new(self).tap do |security_reports|
latest_report_builds(reports_scope).each do |build|
build.collect_security_reports!(security_reports, report_types: types_to_collect)
end
end
end
|
#self_and_ancestors ⇒ Object
With only parent-child pipelines
996
997
998
|
# File 'app/models/ci/pipeline.rb', line 996
def self_and_ancestors
object_hierarchy(project_condition: :same).base_and_ancestors
end
|
#self_and_descendants ⇒ Object
With only parent-child pipelines
1001
1002
1003
|
# File 'app/models/ci/pipeline.rb', line 1001
def self_and_descendants
object_hierarchy(project_condition: :same).base_and_descendants
end
|
#self_and_upstreams ⇒ Object
With multi-project and parent-child pipelines
986
987
988
|
# File 'app/models/ci/pipeline.rb', line 986
def self_and_upstreams
object_hierarchy.base_and_ancestors
end
|
#set_status(new_status) ⇒ Object
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
|
# File 'app/models/ci/pipeline.rb', line 782
def set_status(new_status)
retry_optimistic_lock(self, name: 'ci_pipeline_set_status') do
case new_status
when 'created' then nil
when 'waiting_for_resource' then request_resource
when 'preparing' then prepare
when 'pending' then enqueue
when 'running' then run
when 'success' then succeed
when 'failed' then drop
when 'canceled' then cancel
when 'skipped' then skip
when 'manual' then block
when 'scheduled' then delay
else
raise Ci::HasStatus::UnknownStatusError,
"Unknown status `#{new_status}`"
end
end
end
|
#source_ref ⇒ Object
1219
1220
1221
1222
1223
1224
1225
|
# File 'app/models/ci/pipeline.rb', line 1219
def source_ref
if merge_request?
merge_request.source_branch
else
ref
end
end
|
#source_ref_path ⇒ Object
#source_ref_slug ⇒ Object
1227
1228
1229
|
# File 'app/models/ci/pipeline.rb', line 1227
def source_ref_slug
Gitlab::Utils.slugify(source_ref.to_s)
end
|
#stages_count ⇒ Object
470
471
472
|
# File 'app/models/ci/pipeline.rb', line 470
def stages_count
statuses.select(:stage).distinct.count
end
|
#stages_names ⇒ Object
486
487
488
489
|
# File 'app/models/ci/pipeline.rb', line 486
def stages_names
statuses.order(:stage_idx).distinct
.pluck(:stage, :stage_idx).map(&:first)
end
|
#stuck? ⇒ Boolean
595
596
597
|
# File 'app/models/ci/pipeline.rb', line 595
def stuck?
pending_builds.any?(&:stuck?)
end
|
478
479
480
|
# File 'app/models/ci/pipeline.rb', line 478
def tags_count
ActsAsTaggableOn::Tagging.where(taggable: builds).count
end
|
1132
1133
1134
1135
1136
1137
1138
|
# File 'app/models/ci/pipeline.rb', line 1132
def terraform_reports
::Gitlab::Ci::Reports::TerraformReports.new.tap do |terraform_reports|
latest_report_builds(::Ci::JobArtifact.terraform_reports).each do |build|
build.collect_terraform_reports!(terraform_reports)
end
end
end
|
#test_report_summary ⇒ Object
1094
1095
1096
1097
1098
|
# File 'app/models/ci/pipeline.rb', line 1094
def test_report_summary
strong_memoize(:test_report_summary) do
Gitlab::Ci::Reports::TestReportSummary.new(latest_builds_report_results)
end
end
|
#test_reports ⇒ Object
1100
1101
1102
1103
1104
1105
1106
|
# File 'app/models/ci/pipeline.rb', line 1100
def test_reports
Gitlab::Ci::Reports::TestReports.new.tap do |test_reports|
latest_test_report_builds.find_each do |build|
build.collect_test_reports!(test_reports)
end
end
end
|
#top_level_worktree_paths ⇒ Object
1177
1178
1179
1180
1181
|
# File 'app/models/ci/pipeline.rb', line 1177
def top_level_worktree_paths
strong_memoize(:top_level_worktree_paths) do
project.repository.tree(sha).blobs.map(&:path)
end
end
|
#total_size ⇒ Object
474
475
476
|
# File 'app/models/ci/pipeline.rb', line 474
def total_size
statuses.count(:id)
end
|
#triggered_by?(current_user) ⇒ Boolean
1215
1216
1217
|
# File 'app/models/ci/pipeline.rb', line 1215
def triggered_by?(current_user)
user == current_user
end
|
#triggered_pipelines_with_preloads ⇒ Object
516
517
518
|
# File 'app/models/ci/pipeline.rb', line 516
def triggered_pipelines_with_preloads
triggered_pipelines.preload(:source_job)
end
|
#update_builds_coverage ⇒ Object
669
670
671
|
# File 'app/models/ci/pipeline.rb', line 669
def update_builds_coverage
builds.with_coverage_regex.without_coverage.each(&:update_coverage)
end
|
#update_duration ⇒ Object
#upstream_root ⇒ Object
Follow the upstream pipeline relationships, regardless of multi-project or parent-child, and return the top-level ancestor.
1016
1017
1018
|
# File 'app/models/ci/pipeline.rb', line 1016
def upstream_root
object_hierarchy.base_and_ancestors(hierarchy_order: :desc).first
end
|
#uses_needs? ⇒ Boolean
466
467
468
|
# File 'app/models/ci/pipeline.rb', line 466
def uses_needs?
builds.where(scheduling_type: :dag).any?
end
|
#valid_commit_sha ⇒ Object
525
526
527
528
529
|
# File 'app/models/ci/pipeline.rb', line 525
def valid_commit_sha
if self.sha == Gitlab::Git::BLANK_SHA
self.errors.add(:sha, " cant be 00000000 (branch removal)")
end
end
|
#variables_builder ⇒ Object
811
812
813
|
# File 'app/models/ci/pipeline.rb', line 811
def variables_builder
@variables_builder ||= ::Gitlab::Ci::Variables::Builder.new(self)
end
|
#warning_messages(limit: nil) ⇒ Object
759
760
761
762
763
|
# File 'app/models/ci/pipeline.rb', line 759
def warning_messages(limit: nil)
messages.select(&:warning?).tap do |warnings|
break warnings.take(limit) if limit
end
end
|