blob: 716d919487ddfcab1caf96b3959ea36b41fce6cc (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
# frozen_string_literal: true
module Ci
class BuildDependencies
include ::Gitlab::Utils::StrongMemoize
attr_reader :processable
def initialize(processable)
@processable = processable
end
def all
(local + cross_pipeline + cross_project).uniq
end
def invalid_local
local.reject(&:valid_dependency?)
end
def valid?
valid_local? && valid_cross_pipeline? && valid_cross_project?
end
private
# Dependencies can only be of Ci::Build type because only builds
# can create artifacts
def model_class
::Ci::Build
end
# Dependencies local to the given pipeline
def local
strong_memoize(:local) do
next [] if no_local_dependencies_specified?
next [] unless processable.pipeline_id # we don't have any dependency when creating the pipeline
deps = model_class.where(pipeline_id: processable.pipeline_id).latest
deps = from_previous_stages(deps)
deps = from_needs(deps)
from_dependencies(deps).to_a
end
end
# Dependencies from the same parent-pipeline hierarchy excluding
# the current job's pipeline
def cross_pipeline
strong_memoize(:cross_pipeline) do
fetch_dependencies_in_hierarchy
end
end
# Dependencies that are defined by project and ref
def cross_project
[]
end
def fetch_dependencies_in_hierarchy
deps_specifications = specified_cross_pipeline_dependencies
return [] if deps_specifications.empty?
deps_specifications = expand_variables_and_validate(deps_specifications)
jobs_in_pipeline_hierarchy(deps_specifications)
end
def jobs_in_pipeline_hierarchy(deps_specifications)
all_pipeline_ids = []
all_job_names = []
deps_specifications.each do |spec|
all_pipeline_ids << spec[:pipeline]
all_job_names << spec[:job]
end
model_class.latest.success
.in_pipelines(processable.pipeline.same_family_pipeline_ids)
.in_pipelines(all_pipeline_ids.uniq)
.by_name(all_job_names.uniq)
.select do |dependency|
# the query may not return exact matches pipeline-job, so we filter
# them separately.
deps_specifications.find do |spec|
spec[:pipeline] == dependency.pipeline_id &&
spec[:job] == dependency.name
end
end
end
def expand_variables_and_validate(specifications)
specifications.map do |spec|
pipeline = ExpandVariables.expand(spec[:pipeline].to_s, processable_variables).to_i
# current pipeline is not allowed because local dependencies
# should be used instead.
next if pipeline == processable.pipeline_id
job = ExpandVariables.expand(spec[:job], processable_variables)
{ job: job, pipeline: pipeline }
end.compact
end
def valid_cross_pipeline?
cross_pipeline.size == specified_cross_pipeline_dependencies.size
end
def valid_local?
local.all?(&:valid_dependency?)
end
def valid_cross_project?
true
end
def project
processable.project
end
def no_local_dependencies_specified?
processable.options[:dependencies]&.empty?
end
def from_previous_stages(scope)
scope.before_stage(processable.stage_idx)
end
def from_needs(scope)
return scope unless processable.scheduling_type_dag?
needs_names = processable.needs.artifacts.select(:name)
scope.where(name: needs_names)
end
def from_dependencies(scope)
return scope unless processable.options[:dependencies].present?
scope.where(name: processable.options[:dependencies])
end
def processable_variables
-> { processable.simple_variables_without_dependencies }
end
def specified_cross_pipeline_dependencies
strong_memoize(:specified_cross_pipeline_dependencies) do
next [] unless Feature.enabled?(:ci_cross_pipeline_artifacts_download, processable.project, default_enabled: true)
specified_cross_dependencies.select { |dep| dep[:pipeline] && dep[:artifacts] }
end
end
def specified_cross_dependencies
Array(processable.options[:cross_dependencies])
end
end
end
Ci::BuildDependencies.prepend_mod_with('Ci::BuildDependencies')
|