summaryrefslogtreecommitdiff
path: root/lib/gitlab/ci/pipeline/seed/build.rb
blob: 7ec03d132c0c898b4d53f29c2e95b42e9d850b38 (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
# frozen_string_literal: true

module Gitlab
  module Ci
    module Pipeline
      module Seed
        class Build < Seed::Base
          include Gitlab::Utils::StrongMemoize

          delegate :dig, to: :@attributes

          # When the `ci_dag_limit_needs` is enabled it uses the lower limit
          LOW_NEEDS_LIMIT = 5
          HARD_NEEDS_LIMIT = 50

          def initialize(pipeline, attributes, previous_stages)
            @pipeline = pipeline
            @attributes = attributes
            @previous_stages = previous_stages
            @needs_attributes = dig(:needs_attributes)

            @only = Gitlab::Ci::Build::Policy
              .fabricate(attributes.delete(:only))
            @except = Gitlab::Ci::Build::Policy
              .fabricate(attributes.delete(:except))
          end

          def name
            dig(:name)
          end

          def included?
            strong_memoize(:inclusion) do
              all_of_only? &&
                none_of_except?
            end
          end

          def errors
            return unless included?

            strong_memoize(:errors) do
              needs_errors
            end
          end

          def attributes
            @attributes.merge(
              pipeline: @pipeline,
              project: @pipeline.project,
              user: @pipeline.user,
              ref: @pipeline.ref,
              tag: @pipeline.tag,
              trigger_request: @pipeline.legacy_trigger,
              protected: @pipeline.protected_ref?
            )
          end

          def bridge?
            attributes_hash = @attributes.to_h
            attributes_hash.dig(:options, :trigger).present? ||
              (attributes_hash.dig(:options, :bridge_needs).instance_of?(Hash) &&
               attributes_hash.dig(:options, :bridge_needs, :pipeline).present?)
          end

          def to_resource
            strong_memoize(:resource) do
              if bridge?
                ::Ci::Bridge.new(attributes)
              else
                ::Ci::Build.new(attributes)
              end
            end
          end

          private

          def all_of_only?
            @only.all? { |spec| spec.satisfied_by?(@pipeline, self) }
          end

          def none_of_except?
            @except.none? { |spec| spec.satisfied_by?(@pipeline, self) }
          end

          def needs_errors
            return if @needs_attributes.nil?

            if @needs_attributes.size > max_needs_allowed
              return [
                "#{name}: one job can only need #{max_needs_allowed} others, but you have listed #{@needs_attributes.size}. " \
                  "See needs keyword documentation for more details"
              ]
            end

            @needs_attributes.flat_map do |need|
              result = @previous_stages.any? do |stage|
                stage.seeds_names.include?(need[:name])
              end

              "#{name}: needs '#{need[:name]}'" unless result
            end.compact
          end

          def max_needs_allowed
            if Feature.enabled?(:ci_dag_limit_needs, @project, default_enabled: true)
              LOW_NEEDS_LIMIT
            else
              HARD_NEEDS_LIMIT
            end
          end
        end
      end
    end
  end
end