Class: Ci::Pipeline
- Inherits:
-
ApplicationRecord
show all
- Includes:
- AfterCommitQueue, AtomicInternalId, HasRef, HasStatus, Partitionable, EachBatch, FastDestroyAll::Helpers, FromUnion, Gitlab::Allowable, Gitlab::OptimisticLocking, Gitlab::Utils::StrongMemoize, IgnorableColumns, Importable, Presentable, SafelyChangeColumnDefault, 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
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::IGNORED_STATUSES, HasStatus::ORDERED_STATUSES, HasStatus::PASSED_WITH_WARNINGS_STATUSES, HasStatus::STARTED_STATUSES, HasStatus::STATUSES_ENUM, HasStatus::STOPPED_STATUSES, HasStatus::UnknownStatusError
Ci::Partitionable::MUTEX
ApplicationRecord::MAX_PLUCK
ResetOnUnionError::MAX_RESET_PERIOD
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, namespace_init, project_init, scope_attrs, scope_usage
log_optimistic_lock_retries, retry_lock, retry_lock_histogram, retry_lock_logger
#can?
#present
Methods included from HasStatus
#active?, #blocked?, #complete?, #incomplete?, #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
47
48
49
|
# File 'app/models/ci/pipeline.rb', line 47
def config_metadata
@config_metadata
end
|
Class Method Details
.auto_devops_pipelines_completed_total ⇒ Object
560
561
562
|
# File 'app/models/ci/pipeline.rb', line 560
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
556
557
558
|
# File 'app/models/ci/pipeline.rb', line 556
def self.bridgeable_statuses
::Ci::Pipeline::AVAILABLE_STATUSES - %w[created waiting_for_resource preparing pending]
end
|
.builds_count_in_alive_pipelines ⇒ Object
513
514
515
|
# File 'app/models/ci/pipeline.rb', line 513
def self.builds_count_in_alive_pipelines
created_after(24.hours.ago).alive.joins(:builds).count
end
|
.current_partition_value ⇒ Object
564
565
566
|
# File 'app/models/ci/pipeline.rb', line 564
def self.current_partition_value
100
end
|
.internal_sources ⇒ Object
552
553
554
|
# File 'app/models/ci/pipeline.rb', line 552
def self.internal_sources
sources.reject { |source| source == "external" }.values
end
|
.jobs_count_in_alive_pipelines ⇒ Object
509
510
511
|
# File 'app/models/ci/pipeline.rb', line 509
def self.jobs_count_in_alive_pipelines
created_after(24.hours.ago).alive.joins(:statuses).count
end
|
.last_finished_for_ref_id(ci_ref_id) ⇒ Object
540
541
542
|
# File 'app/models/ci/pipeline.rb', line 540
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
505
506
507
|
# File 'app/models/ci/pipeline.rb', line 505
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.
526
527
528
529
530
531
532
533
534
|
# File 'app/models/ci/pipeline.rb', line 526
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
501
502
503
|
# File 'app/models/ci/pipeline.rb', line 501
def self.latest_running_for_ref(ref)
newest_first(ref: ref).running.take
end
|
.latest_status(ref = nil) ⇒ Object
477
478
479
|
# File 'app/models/ci/pipeline.rb', line 477
def self.latest_status(ref = nil)
newest_first(ref: ref).pick(:status)
end
|
.latest_successful_for_ref(ref) ⇒ Object
481
482
483
|
# File 'app/models/ci/pipeline.rb', line 481
def self.latest_successful_for_ref(ref)
newest_first(ref: ref).success.take
end
|
.latest_successful_for_refs(refs) ⇒ Object
489
490
491
492
493
494
495
496
497
498
499
|
# File 'app/models/ci/pipeline.rb', line 489
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
485
486
487
|
# File 'app/models/ci/pipeline.rb', line 485
def self.latest_successful_for_sha(sha)
newest_first(sha: sha).success.take
end
|
.latest_successful_ids_per_project ⇒ Object
536
537
538
|
# File 'app/models/ci/pipeline.rb', line 536
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.
464
465
466
467
468
469
470
471
472
473
474
475
|
# File 'app/models/ci/pipeline.rb', line 464
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
|
.object_hierarchy(relation, options = {}) ⇒ Object
568
569
570
|
# File 'app/models/ci/pipeline.rb', line 568
def self.object_hierarchy(relation, options = {})
::Gitlab::Ci::PipelineObjectHierarchy.new(relation, options: options)
end
|
.total_duration ⇒ Object
548
549
550
|
# File 'app/models/ci/pipeline.rb', line 548
def self.total_duration
where.not(duration: nil).sum(:duration)
end
|
.truncate_sha(sha) ⇒ Object
544
545
546
|
# File 'app/models/ci/pipeline.rb', line 544
def self.truncate_sha(sha)
sha[0...8]
end
|
Instance Method Details
#accessibility_reports ⇒ Object
1134
1135
1136
1137
1138
1139
1140
|
# File 'app/models/ci/pipeline.rb', line 1134
def accessibility_reports
Gitlab::Ci::Reports::AccessibilityReports.new.tap do |accessibility_reports|
latest_report_builds(Ci::JobArtifact.of_report_type(:accessibility)).each do |build|
build.collect_accessibility_reports!(accessibility_reports)
end
end
end
|
#add_error_message(content) ⇒ Object
803
804
805
|
# File 'app/models/ci/pipeline.rb', line 803
def add_error_message(content)
add_message(:error, content)
end
|
#add_warning_message(content) ⇒ Object
807
808
809
|
# File 'app/models/ci/pipeline.rb', line 807
def add_warning_message(content)
add_message(:warning, content)
end
|
#age_in_minutes ⇒ Object
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
|
# File 'app/models/ci/pipeline.rb', line 1351
def age_in_minutes
return 0 unless persisted?
unless has_attribute?(:created_at)
raise ArgumentError, 'pipeline not fully loaded'
end
return 0 unless created_at
(Time.current - created_at).ceil / 60
end
|
#all_child_pipelines ⇒ Object
With only parent-child pipelines
1010
1011
1012
|
# File 'app/models/ci/pipeline.rb', line 1010
def all_child_pipelines
object_hierarchy(project_condition: :same).descendants
end
|
#all_merge_requests ⇒ Object
All the merge requests for which the current pipeline runs/ran against
902
903
904
905
906
907
908
909
910
|
# File 'app/models/ci/pipeline.rb', line 902
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
912
913
914
|
# File 'app/models/ci/pipeline.rb', line 912
def all_merge_requests_by_recency
all_merge_requests.order(id: :desc)
end
|
#all_worktree_paths ⇒ Object
1197
1198
1199
1200
1201
|
# File 'app/models/ci/pipeline.rb', line 1197
def all_worktree_paths
strong_memoize(:all_worktree_paths) do
project.repository.ls_files(sha)
end
end
|
#auto_canceled? ⇒ Boolean
689
690
691
|
# File 'app/models/ci/pipeline.rb', line 689
def auto_canceled?
canceled? && auto_canceled_by_id?
end
|
#batch_lookup_report_artifact_for_file_type(file_type) ⇒ Object
732
733
734
|
# File 'app/models/ci/pipeline.rb', line 732
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
736
737
738
739
740
741
742
743
744
745
|
# File 'app/models/ci/pipeline.rb', line 736
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
661
662
663
|
# File 'app/models/ci/pipeline.rb', line 661
def before_sha
super || Gitlab::Git::BLANK_SHA
end
|
#branch_updated? ⇒ Boolean
1170
1171
1172
1173
1174
|
# File 'app/models/ci/pipeline.rb', line 1170
def branch_updated?
strong_memoize(:branch_updated) do
push_details.branch_updated?
end
end
|
#bridge_triggered? ⇒ Boolean
1038
1039
1040
|
# File 'app/models/ci/pipeline.rb', line 1038
def bridge_triggered?
source_bridge.present?
end
|
#bridge_waiting? ⇒ Boolean
1042
1043
1044
|
# File 'app/models/ci/pipeline.rb', line 1042
def bridge_waiting?
source_bridge&.dependent?
end
|
#bridges_in_self_and_project_descendants ⇒ Object
963
964
965
|
# File 'app/models/ci/pipeline.rb', line 963
def bridges_in_self_and_project_descendants
Ci::Bridge.latest.where(pipeline: self_and_project_descendants)
end
|
#build_matchers ⇒ Object
1335
1336
1337
|
# File 'app/models/ci/pipeline.rb', line 1335
def build_matchers
self.builds.latest.build_matchers(project)
end
|
#build_with_artifacts_in_self_and_project_descendants(name) ⇒ Object
952
953
954
955
956
957
|
# File 'app/models/ci/pipeline.rb', line 952
def build_with_artifacts_in_self_and_project_descendants(name)
builds_in_self_and_project_descendants
.ordered_by_pipeline .with_downloadable_artifacts
.find_by_name(name)
end
|
#builds_in_self_and_project_descendants ⇒ Object
959
960
961
|
# File 'app/models/ci/pipeline.rb', line 959
def builds_in_self_and_project_descendants
Ci::Build.latest.where(pipeline: self_and_project_descendants)
end
|
#builds_with_coverage ⇒ Object
1088
1089
1090
|
# File 'app/models/ci/pipeline.rb', line 1088
def builds_with_coverage
builds.latest.with_coverage
end
|
#builds_with_failed_tests(limit: nil) ⇒ Object
1092
1093
1094
|
# File 'app/models/ci/pipeline.rb', line 1092
def builds_with_failed_tests(limit: nil)
latest_test_report_builds.failed.limit(limit)
end
|
#can_generate_codequality_reports? ⇒ Boolean
1116
1117
1118
|
# File 'app/models/ci/pipeline.rb', line 1116
def can_generate_codequality_reports?
complete_and_has_reports?(Ci::JobArtifact.of_report_type(:codequality))
end
|
#cancelable? ⇒ Boolean
685
686
687
|
# File 'app/models/ci/pipeline.rb', line 685
def cancelable?
cancelable_statuses.any?
end
|
#child? ⇒ Boolean
1046
1047
1048
1049
|
# File 'app/models/ci/pipeline.rb', line 1046
def child?
parent_pipeline? && parent_pipeline.present?
end
|
#cluster_agent_authorizations ⇒ Object
#codequality_reports ⇒ Object
1142
1143
1144
1145
1146
1147
1148
|
# File 'app/models/ci/pipeline.rb', line 1142
def codequality_reports
Gitlab::Ci::Reports::CodequalityReports.new.tap do |codequality_reports|
latest_report_builds(Ci::JobArtifact.of_report_type(:codequality)).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?`
673
674
675
|
# File 'app/models/ci/pipeline.rb', line 673
def commit
@commit ||= Commit.lazy(project, sha)
end
|
#complete_and_has_reports?(reports_scope) ⇒ Boolean
1100
1101
1102
1103
1104
1105
1106
|
# File 'app/models/ci/pipeline.rb', line 1100
def complete_and_has_reports?(reports_scope)
if Feature.enabled?(:mr_show_reports_immediately, project, type: :development)
latest_report_builds(reports_scope).exists?
else
complete? && has_reports?(reports_scope)
end
end
|
#complete_hierarchy_count ⇒ Object
Applies to all parent-child and multi-project pipelines
1034
1035
1036
|
# File 'app/models/ci/pipeline.rb', line 1034
def complete_hierarchy_count
upstream_root.self_and_downstreams.count
end
|
#coverage ⇒ Object
721
722
723
724
725
726
|
# File 'app/models/ci/pipeline.rb', line 721
def coverage
coverage_array = latest_statuses.map(&:coverage).compact
if coverage_array.size >= 1
coverage_array.sum / coverage_array.size
end
end
|
#created_successfully? ⇒ Boolean
1055
1056
1057
|
# File 'app/models/ci/pipeline.rb', line 1055
def created_successfully?
persisted? && failure_reason.blank?
end
|
#default_branch? ⇒ Boolean
1209
1210
1211
|
# File 'app/models/ci/pipeline.rb', line 1209
def default_branch?
ref == project.default_branch
end
|
#detached_merge_request_pipeline? ⇒ Boolean
1221
1222
1223
|
# File 'app/models/ci/pipeline.rb', line 1221
def detached_merge_request_pipeline?
merge_request? && target_sha.nil?
end
|
#detailed_status(current_user) ⇒ Object
1059
1060
1061
1062
1063
|
# File 'app/models/ci/pipeline.rb', line 1059
def detailed_status(current_user)
Gitlab::Ci::Status::Pipeline::Factory
.new(self.present, current_user)
.fabricate!
end
|
588
589
590
|
# File 'app/models/ci/pipeline.rb', line 588
def distinct_tags_count
ActsAsTaggableOn::Tagging.where(taggable: builds).count('distinct(tag_id)')
end
|
#ensure_ci_ref! ⇒ Object
1299
1300
1301
|
# File 'app/models/ci/pipeline.rb', line 1299
def ensure_ci_ref!
self.ci_ref = Ci::Ref.ensure_for(self)
end
|
#ensure_persistent_ref ⇒ Object
1303
1304
1305
1306
1307
|
# File 'app/models/ci/pipeline.rb', line 1303
def ensure_persistent_ref
return if persistent_ref.exist?
persistent_ref.create
end
|
#ensure_scheduling_type! ⇒ Object
1295
1296
1297
|
# File 'app/models/ci/pipeline.rb', line 1295
def ensure_scheduling_type!
processables.populate_scheduling_type!
end
|
#environments_in_self_and_project_descendants(deployment_status: nil) ⇒ Object
971
972
973
974
975
976
977
978
979
980
981
982
|
# File 'app/models/ci/pipeline.rb', line 971
def environments_in_self_and_project_descendants(deployment_status: nil)
expanded_environment_names =
jobs_in_self_and_project_descendants.joins(:metadata)
.where.not(Ci::BuildMetadata.table_name => { expanded_environment_name: nil })
.distinct("#{Ci::BuildMetadata.quoted_table_name}.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.
814
815
816
|
# File 'app/models/ci/pipeline.rb', line 814
def error_messages
messages.select(&:error?)
end
|
#external_pull_request? ⇒ Boolean
1217
1218
1219
|
# File 'app/models/ci/pipeline.rb', line 1217
def external_pull_request?
external_pull_request_id.present?
end
|
#find_job_with_archive_artifacts(name) ⇒ Object
1065
1066
1067
|
# File 'app/models/ci/pipeline.rb', line 1065
def find_job_with_archive_artifacts(name)
builds.latest.with_downloadable_artifacts.find_by_name(name)
end
|
#freeze_period? ⇒ Boolean
771
772
773
774
775
|
# File 'app/models/ci/pipeline.rb', line 771
def freeze_period?
strong_memoize(:freeze_period) do
project.freeze_periods.any?(&:active?)
end
end
|
#full_error_messages ⇒ Object
1261
1262
1263
|
# File 'app/models/ci/pipeline.rb', line 1261
def full_error_messages
errors ? errors.full_messages.to_sentence : ""
end
|
#git_author_email ⇒ Object
619
620
621
622
623
|
# File 'app/models/ci/pipeline.rb', line 619
def git_author_email
strong_memoize(:git_author_email) do
commit.try(:author_email)
end
end
|
#git_author_full_text ⇒ Object
625
626
627
628
629
|
# File 'app/models/ci/pipeline.rb', line 625
def git_author_full_text
strong_memoize(:git_author_full_text) do
commit.try(:author_full_text)
end
end
|
#git_author_name ⇒ Object
613
614
615
616
617
|
# File 'app/models/ci/pipeline.rb', line 613
def git_author_name
strong_memoize(:git_author_name) do
commit.try(:author_name)
end
end
|
#git_commit_description ⇒ Object
649
650
651
652
653
|
# File 'app/models/ci/pipeline.rb', line 649
def git_commit_description
strong_memoize(:git_commit_description) do
commit.try(:description)
end
end
|
#git_commit_full_title ⇒ Object
643
644
645
646
647
|
# File 'app/models/ci/pipeline.rb', line 643
def git_commit_full_title
strong_memoize(:git_commit_full_title) do
commit.try(:full_title)
end
end
|
#git_commit_message ⇒ Object
631
632
633
634
635
|
# File 'app/models/ci/pipeline.rb', line 631
def git_commit_message
strong_memoize(:git_commit_message) do
commit.try(:message)
end
end
|
#git_commit_timestamp ⇒ Object
655
656
657
658
659
|
# File 'app/models/ci/pipeline.rb', line 655
def git_commit_timestamp
strong_memoize(:git_commit_timestamp) do
commit.try(:timestamp)
end
end
|
#git_commit_title ⇒ Object
637
638
639
640
641
|
# File 'app/models/ci/pipeline.rb', line 637
def git_commit_title
strong_memoize(:git_commit_title) do
commit.try(:title)
end
end
|
#has_archive_artifacts? ⇒ Boolean
1158
1159
1160
|
# File 'app/models/ci/pipeline.rb', line 1158
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
1112
1113
1114
|
# File 'app/models/ci/pipeline.rb', line 1112
def has_codequality_mr_diff_report?
pipeline_artifacts&.report_exists?(:code_quality_mr_diff)
end
|
#has_coverage_reports? ⇒ Boolean
1108
1109
1110
|
# File 'app/models/ci/pipeline.rb', line 1108
def has_coverage_reports?
pipeline_artifacts&.report_exists?(:code_coverage)
end
|
#has_erasable_artifacts? ⇒ Boolean
1166
1167
1168
|
# File 'app/models/ci/pipeline.rb', line 1166
def has_erasable_artifacts?
complete? && builds.latest.with_erasable_artifacts.exists?
end
|
#has_exposed_artifacts? ⇒ Boolean
1162
1163
1164
|
# File 'app/models/ci/pipeline.rb', line 1162
def has_exposed_artifacts?
complete? && builds.latest.with_exposed_artifacts.exists?
end
|
#has_kubernetes_active? ⇒ Boolean
765
766
767
768
769
|
# File 'app/models/ci/pipeline.rb', line 765
def has_kubernetes_active?
strong_memoize(:has_kubernetes_active) do
project.deployment_platform&.active?
end
end
|
#has_reports?(reports_scope) ⇒ Boolean
1096
1097
1098
|
# File 'app/models/ci/pipeline.rb', line 1096
def has_reports?(reports_scope)
latest_report_builds(reports_scope).exists?
end
|
#has_test_reports? ⇒ Boolean
1345
1346
1347
1348
1349
|
# File 'app/models/ci/pipeline.rb', line 1345
def has_test_reports?
strong_memoize(:has_test_reports) do
has_reports?(::Ci::JobArtifact.of_report_type(:test))
end
end
|
#has_warnings? ⇒ Boolean
777
778
779
|
# File 'app/models/ci/pipeline.rb', line 777
def has_warnings?
number_of_warnings > 0
end
|
#has_yaml_errors? ⇒ Boolean
799
800
801
|
# File 'app/models/ci/pipeline.rb', line 799
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.
51
|
# File 'app/models/ci/pipeline.rb', line 51
alias_method :jobs_git_ref, :git_ref
|
#jobs_in_self_and_project_descendants ⇒ Object
967
968
969
|
# File 'app/models/ci/pipeline.rb', line 967
def jobs_in_self_and_project_descendants
Ci::Processable.latest.where(pipeline: self_and_project_descendants)
end
|
#latest? ⇒ Boolean
710
711
712
713
714
715
|
# File 'app/models/ci/pipeline.rb', line 710
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
1069
1070
1071
1072
1073
1074
|
# File 'app/models/ci/pipeline.rb', line 1069
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
753
754
755
756
757
758
759
760
761
762
763
|
# File 'app/models/ci/pipeline.rb', line 753
def latest_report_artifacts
::Gitlab::SafeRequestStore.fetch("pipeline:#{self.id}:latest_report_artifacts") do
::Ci::JobArtifact.where(
id: job_artifacts.all_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.all_reports) ⇒ Object
1076
1077
1078
|
# File 'app/models/ci/pipeline.rb', line 1076
def latest_report_builds(reports_scope = ::Ci::JobArtifact.all_reports)
builds.latest.with_artifacts(reports_scope)
end
|
#latest_report_builds_in_self_and_project_descendants(reports_scope = ::Ci::JobArtifact.all_reports) ⇒ Object
1084
1085
1086
|
# File 'app/models/ci/pipeline.rb', line 1084
def latest_report_builds_in_self_and_project_descendants(reports_scope = ::Ci::JobArtifact.all_reports)
builds_in_self_and_project_descendants.with_artifacts(reports_scope)
end
|
#latest_test_report_builds ⇒ Object
1080
1081
1082
|
# File 'app/models/ci/pipeline.rb', line 1080
def latest_test_report_builds
latest_report_builds(Ci::JobArtifact.of_report_type(:test)).preload(:project, :metadata)
end
|
#lazy_ref_commit ⇒ Object
rubocop: enable CodeReuse/ServiceClass
700
701
702
703
704
705
706
707
708
|
# File 'app/models/ci/pipeline.rb', line 700
def lazy_ref_commit
BatchLoader.for(ref).batch(key: project.id) 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
1225
1226
1227
|
# File 'app/models/ci/pipeline.rb', line 1225
def legacy_detached_merge_request_pipeline?
detached_merge_request_pipeline? && !merge_request_ref?
end
|
#legacy_trigger ⇒ Object
871
872
873
|
# File 'app/models/ci/pipeline.rb', line 871
def legacy_trigger
strong_memoize(:legacy_trigger) { trigger_requests.first }
end
|
#matches_sha_or_source_sha?(sha) ⇒ Boolean
1237
1238
1239
|
# File 'app/models/ci/pipeline.rb', line 1237
def matches_sha_or_source_sha?(sha)
self.sha == sha || self.source_sha == sha
end
|
#merge_request? ⇒ Boolean
1213
1214
1215
|
# File 'app/models/ci/pipeline.rb', line 1213
def merge_request?
merge_request_id.present? && merge_request.present?
end
|
#merge_request_diff ⇒ Object
1363
1364
1365
1366
1367
|
# File 'app/models/ci/pipeline.rb', line 1363
def merge_request_diff
return unless merge_request?
merge_request.merge_request_diff_for(merge_request_diff_sha)
end
|
#merge_request_event_type ⇒ Object
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
|
# File 'app/models/ci/pipeline.rb', line 1265
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
1320
1321
1322
|
# File 'app/models/ci/pipeline.rb', line 1320
def merge_train_pipeline?
false
end
|
#merged_result_pipeline? ⇒ Boolean
1229
1230
1231
|
# File 'app/models/ci/pipeline.rb', line 1229
def merged_result_pipeline?
merge_request? && target_sha.present?
end
|
#modified_paths ⇒ Object
Returns the modified paths.
The returned value is
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
|
# File 'app/models/ci/pipeline.rb', line 1181
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
|
#modified_paths_since(compare_to_sha) ⇒ Object
1193
1194
1195
|
# File 'app/models/ci/pipeline.rb', line 1193
def modified_paths_since(compare_to_sha)
project.repository.diff_stats(project.repository.merge_base(compare_to_sha, sha), sha).paths
end
|
#needs_processing? ⇒ Boolean
792
793
794
795
796
797
|
# File 'app/models/ci/pipeline.rb', line 792
def needs_processing?
statuses
.where(processed: [false, nil])
.latest
.exists?
end
|
#notes ⇒ Object
843
844
845
|
# File 'app/models/ci/pipeline.rb', line 843
def notes
project.notes.for_commit_id(sha)
end
|
#notes=(notes_to_save) ⇒ 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.
828
829
830
831
832
833
834
835
836
837
838
839
840
841
|
# File 'app/models/ci/pipeline.rb', line 828
def notes=(notes_to_save)
notes_to_save.reject! do |note_to_save|
notes.any? do |note|
[note_to_save.note, note_to_save.created_at.to_i] == [note.note, note.created_at.to_i]
end
end
notes_to_save.each do |note|
note[:id] = nil
note[:commit_id] = sha
note[:noteable_id] = self['id']
note.save!
end
end
|
#number_of_warnings ⇒ Object
781
782
783
784
785
786
787
788
789
790
|
# File 'app/models/ci/pipeline.rb', line 781
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
933
934
935
936
937
938
939
940
941
942
943
944
|
# File 'app/models/ci/pipeline.rb', line 933
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
1051
1052
1053
|
# File 'app/models/ci/pipeline.rb', line 1051
def parent?
child_pipelines.exists?
end
|
#persisted_variables ⇒ Object
879
880
881
882
883
884
885
886
|
# File 'app/models/ci/pipeline.rb', line 879
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
1277
1278
1279
|
# File 'app/models/ci/pipeline.rb', line 1277
def persistent_ref
@persistent_ref ||= PersistentRef.new(pipeline: self)
end
|
#protected_ref? ⇒ Boolean
867
868
869
|
# File 'app/models/ci/pipeline.rb', line 867
def protected_ref?
strong_memoize(:protected_ref) { project.protected_for?(git_ref) }
end
|
#queued_duration ⇒ Object
888
889
890
891
892
893
|
# File 'app/models/ci/pipeline.rb', line 888
def queued_duration
return unless started_at
seconds = (started_at - created_at).to_i
seconds unless seconds == 0
end
|
#ref_exists? ⇒ Boolean
597
598
599
600
601
|
# File 'app/models/ci/pipeline.rb', line 597
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
918
919
920
921
922
923
924
925
926
927
928
929
|
# File 'app/models/ci/pipeline.rb', line 918
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
For dependent bridge jobs we reset the upstream bridge recursively to reflect that a downstream pipeline is running again
1311
1312
1313
1314
1315
1316
1317
|
# File 'app/models/ci/pipeline.rb', line 1311
def reset_source_bridge!(current_user)
return unless bridge_waiting?
return unless current_user.can?(:update_pipeline, source_bridge.pipeline)
Ci::EnqueueJobService.new(source_bridge, current_user: current_user).execute(&:pending!) end
|
#retried ⇒ Object
717
718
719
|
# File 'app/models/ci/pipeline.rb', line 717
def retried
@retried ||= (statuses.order(id: :desc) - latest_statuses)
end
|
#retry_failed(current_user) ⇒ Object
rubocop: disable CodeReuse/ServiceClass
694
695
696
697
|
# File 'app/models/ci/pipeline.rb', line 694
def retry_failed(current_user)
Ci::RetryPipelineService.new(project, current_user)
.execute(self)
end
|
#retryable? ⇒ Boolean
681
682
683
|
# File 'app/models/ci/pipeline.rb', line 681
def retryable?
retryable_builds.any?
end
|
#root_ancestor ⇒ Object
Follow the parent-child relationships and return the top-level parent
1019
1020
1021
1022
1023
1024
1025
|
# File 'app/models/ci/pipeline.rb', line 1019
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
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
|
# File 'app/models/ci/pipeline.rb', line 1324
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_in_self_and_project_descendants(reports_scope).includes(pipeline: { project: :route }).each do |build| build.collect_security_reports!(security_reports, report_types: types_to_collect)
end
end
end
|
#self_and_downstreams ⇒ Object
With multi-project and parent-child pipelines
990
991
992
|
# File 'app/models/ci/pipeline.rb', line 990
def self_and_downstreams
object_hierarchy.base_and_descendants
end
|
#self_and_project_ancestors ⇒ Object
With only parent-child pipelines
1000
1001
1002
|
# File 'app/models/ci/pipeline.rb', line 1000
def self_and_project_ancestors
object_hierarchy(project_condition: :same).base_and_ancestors
end
|
#self_and_project_descendants ⇒ Object
With only parent-child pipelines
1005
1006
1007
|
# File 'app/models/ci/pipeline.rb', line 1005
def self_and_project_descendants
object_hierarchy(project_condition: :same).base_and_descendants
end
|
#self_and_project_descendants_complete? ⇒ Boolean
1014
1015
1016
|
# File 'app/models/ci/pipeline.rb', line 1014
def self_and_project_descendants_complete?
self_and_project_descendants.all?(&:complete?)
end
|
#self_and_upstreams ⇒ Object
With multi-project and parent-child pipelines
985
986
987
|
# File 'app/models/ci/pipeline.rb', line 985
def self_and_upstreams
object_hierarchy.base_and_ancestors
end
|
#set_status(new_status) ⇒ Object
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
|
# File 'app/models/ci/pipeline.rb', line 847
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
1245
1246
1247
1248
1249
1250
1251
|
# File 'app/models/ci/pipeline.rb', line 1245
def source_ref
if merge_request?
merge_request.source_branch
else
ref
end
end
|
#source_ref_path ⇒ Object
#source_ref_slug ⇒ Object
1253
1254
1255
|
# File 'app/models/ci/pipeline.rb', line 1253
def source_ref_slug
Gitlab::Utils.slugify(source_ref.to_s)
end
|
#stage(name) ⇒ Object
1257
1258
1259
|
# File 'app/models/ci/pipeline.rb', line 1257
def stage(name)
stages.find_by(name: name)
end
|
#stages_count ⇒ Object
576
577
578
|
# File 'app/models/ci/pipeline.rb', line 576
def stages_count
statuses.select(:stage).distinct.count
end
|
#stages_names ⇒ Object
592
593
594
595
|
# File 'app/models/ci/pipeline.rb', line 592
def stages_names
statuses.order(:stage_idx).distinct
.pluck(:stage, :stage_idx).map(&:first)
end
|
#stuck? ⇒ Boolean
677
678
679
|
# File 'app/models/ci/pipeline.rb', line 677
def stuck?
pending_builds.any?(&:stuck?)
end
|
584
585
586
|
# File 'app/models/ci/pipeline.rb', line 584
def tags_count
ActsAsTaggableOn::Tagging.where(taggable: builds).count
end
|
1150
1151
1152
1153
1154
1155
1156
|
# File 'app/models/ci/pipeline.rb', line 1150
def terraform_reports
::Gitlab::Ci::Reports::TerraformReports.new.tap do |terraform_reports|
latest_report_builds(::Ci::JobArtifact.of_report_type(:terraform)).each do |build|
build.collect_terraform_reports!(terraform_reports)
end
end
end
|
#test_report_summary ⇒ Object
1120
1121
1122
1123
1124
|
# File 'app/models/ci/pipeline.rb', line 1120
def test_report_summary
strong_memoize(:test_report_summary) do
Gitlab::Ci::Reports::TestReportSummary.new(latest_builds_report_results)
end
end
|
#test_reports ⇒ Object
1126
1127
1128
1129
1130
1131
1132
|
# File 'app/models/ci/pipeline.rb', line 1126
def test_reports
Gitlab::Ci::Reports::TestReport.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
1203
1204
1205
1206
1207
|
# File 'app/models/ci/pipeline.rb', line 1203
def top_level_worktree_paths
strong_memoize(:top_level_worktree_paths) do
project.repository.tree(sha).blobs.map(&:path)
end
end
|
#total_size ⇒ Object
580
581
582
|
# File 'app/models/ci/pipeline.rb', line 580
def total_size
statuses.count(:id)
end
|
#triggered_by?(current_user) ⇒ Boolean
1241
1242
1243
|
# File 'app/models/ci/pipeline.rb', line 1241
def triggered_by?(current_user)
user == current_user
end
|
#triggered_pipelines_with_preloads ⇒ Object
603
604
605
|
# File 'app/models/ci/pipeline.rb', line 603
def triggered_pipelines_with_preloads
triggered_pipelines.preload(:source_job)
end
|
#update_builds_coverage ⇒ Object
728
729
730
|
# File 'app/models/ci/pipeline.rb', line 728
def update_builds_coverage
builds.with_coverage_regex.without_coverage.each(&:update_coverage)
end
|
#update_duration ⇒ Object
#upstream_and_all_downstreams ⇒ Object
With multi-project and parent-child pipelines
995
996
997
|
# File 'app/models/ci/pipeline.rb', line 995
def upstream_and_all_downstreams
object_hierarchy.all_objects
end
|
#upstream_root ⇒ Object
Follow the upstream pipeline relationships, regardless of multi-project or parent-child, and return the top-level ancestor.
1029
1030
1031
|
# File 'app/models/ci/pipeline.rb', line 1029
def upstream_root
@upstream_root ||= object_hierarchy.base_and_ancestors(hierarchy_order: :desc).first
end
|
#uses_needs? ⇒ Boolean
572
573
574
|
# File 'app/models/ci/pipeline.rb', line 572
def uses_needs?
processables.where(scheduling_type: :dag).any?
end
|
#valid_commit_sha ⇒ Object
607
608
609
610
611
|
# File 'app/models/ci/pipeline.rb', line 607
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
875
876
877
|
# File 'app/models/ci/pipeline.rb', line 875
def variables_builder
@variables_builder ||= ::Gitlab::Ci::Variables::Builder.new(self)
end
|
#warning_messages(limit: nil) ⇒ Object
818
819
820
821
822
|
# File 'app/models/ci/pipeline.rb', line 818
def warning_messages(limit: nil)
messages.select(&:warning?).tap do |warnings|
break warnings.take(limit) if limit
end
end
|