summaryrefslogtreecommitdiff
path: root/lib/gitlab/ci/config/entry/processable.rb
blob: f10c509d0cca2ec09f8c88fffb8c89c160e827d2 (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
# frozen_string_literal: true

module Gitlab
  module Ci
    class Config
      module Entry
        ##
        # Entry that represents a CI/CD Processable (a job)
        #
        module Processable
          extend ActiveSupport::Concern

          include ::Gitlab::Config::Entry::Configurable
          include ::Gitlab::Config::Entry::Attributable
          include ::Gitlab::Config::Entry::Inheritable

          PROCESSABLE_ALLOWED_KEYS = %i[extends stage only except rules variables
                                        inherit allow_failure when needs].freeze

          included do
            validations do
              validates :config, presence: true
              validates :name, presence: true
              validates :name, type: Symbol

              validates :config, disallowed_keys: {
                  in: %i[only except when start_in],
                  message: 'key may not be used with `rules`'
                },
                if: :has_rules?

              with_options allow_nil: true do
                validates :extends, array_of_strings_or_string: true
                validates :rules, array_of_hashes: true
              end
            end

            entry :stage, Entry::Stage,
              description: 'Pipeline stage this job will be executed into.',
              inherit: false

            entry :only, ::Gitlab::Ci::Config::Entry::Policy,
              description: 'Refs policy this job will be executed for.',
              default: ::Gitlab::Ci::Config::Entry::Policy::DEFAULT_ONLY,
              inherit: false

            entry :except, ::Gitlab::Ci::Config::Entry::Policy,
              description: 'Refs policy this job will be executed for.',
              inherit: false

            entry :rules, ::Gitlab::Ci::Config::Entry::Rules,
              description: 'List of evaluable Rules to determine job inclusion.',
              inherit: false,
              metadata: {
                allowed_when: %w[on_success on_failure always never manual delayed].freeze
              }

            entry :variables, ::Gitlab::Ci::Config::Entry::Variables,
              description: 'Environment variables available for this job.',
              inherit: false

            entry :inherit, ::Gitlab::Ci::Config::Entry::Inherit,
              description: 'Indicates whether to inherit defaults or not.',
              inherit: false,
              default: {}

            attributes :extends, :rules
          end

          def compose!(deps = nil)
            super do
              has_workflow_rules = deps&.workflow_entry&.has_rules?

              # If workflow:rules: or rules: are used
              # they are considered not compatible
              # with `only/except` defaults
              #
              # Context: https://gitlab.com/gitlab-org/gitlab/merge_requests/21742
              if has_rules? || has_workflow_rules
                # Remove only/except defaults
                # defaults are not considered as defined
                @entries.delete(:only) unless only_defined? # rubocop:disable Gitlab/ModuleWithInstanceVariables
                @entries.delete(:except) unless except_defined? # rubocop:disable Gitlab/ModuleWithInstanceVariables
              end

              unless has_workflow_rules
                validate_against_warnings
              end

              # inherit root variables
              @root_variables_value = deps&.variables_value # rubocop:disable Gitlab/ModuleWithInstanceVariables

              yield if block_given?
            end
          end

          def validate_against_warnings
            # If rules are valid format and workflow rules are not specified
            return unless rules_value
            return unless Gitlab::Ci::Features.raise_job_rules_without_workflow_rules_warning?

            last_rule = rules_value.last

            if last_rule&.keys == [:when] && last_rule[:when] != 'never'
              docs_url = 'read more: https://docs.gitlab.com/ee/ci/troubleshooting.html#pipeline-warnings'
              add_warning("may allow multiple pipelines to run for a single action due to `rules:when` clause with no `workflow:rules` - #{docs_url}")
            end
          end

          def name
            metadata[:name]
          end

          def overwrite_entry(deps, key, current_entry)
            return unless inherit_entry&.default_entry&.inherit?(key)
            return unless deps.default_entry

            deps.default_entry[key] unless current_entry.specified?
          end

          def value
            { name: name,
              stage: stage_value,
              extends: extends,
              rules: rules_value,
              variables: root_and_job_variables_value,
              only: only_value,
              except: except_value }.compact
          end

          def root_and_job_variables_value
            root_variables = @root_variables_value.to_h # rubocop:disable Gitlab/ModuleWithInstanceVariables
            root_variables = root_variables.select do |key, _|
              inherit_entry&.variables_entry&.inherit?(key)
            end

            root_variables.merge(variables_value.to_h)
          end
        end
      end
    end
  end
end