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 159 160 161 162
|
# 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, partition_id: processable.partition_id).latest
deps = find_dependencies(processable, deps)
from_dependencies(deps).to_a
end
end
def find_dependencies(processable, deps)
if processable.scheduling_type_dag?
from_needs(deps)
else
from_previous_stages(deps)
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)
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
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')
|