gitlab_ci_yaml_processor.rb 5.0 KB
Newer Older
D
Douwe Maan 已提交
1 2
module Ci
  class GitlabCiYamlProcessor
3
    class ValidationError < StandardError; end
D
Douwe Maan 已提交
4

5
    include Gitlab::Ci::Config::Node::LegacyValidationHelpers
6

7
    attr_reader :path, :cache, :stages
D
Douwe Maan 已提交
8

9
    def initialize(config, path = nil)
10
      @ci_config = Gitlab::Ci::Config.new(config)
11 12
      @config = @ci_config.to_hash
      @path = path
D
Douwe Maan 已提交
13

14 15 16
      unless @ci_config.valid?
        raise ValidationError, @ci_config.errors.first
      end
D
Douwe Maan 已提交
17

18
      initial_parsing
19
    rescue Gitlab::Ci::Config::Loader::FormatError => e
20
      raise ValidationError, e.message
D
Douwe Maan 已提交
21 22
    end

23 24 25
    def jobs_for_ref(ref, tag = false, trigger_request = nil)
      @jobs.select do |_, job|
        process?(job[:only], job[:except], ref, tag, trigger_request)
26
      end
D
Douwe Maan 已提交
27 28
    end

29 30 31
    def jobs_for_stage_and_ref(stage, ref, tag = false, trigger_request = nil)
      jobs_for_ref(ref, tag, trigger_request).select do |_, job|
        job[:stage] == stage
D
Douwe Maan 已提交
32 33 34
      end
    end

35
    def builds_for_ref(ref, tag = false, trigger_request = nil)
K
Kamil Trzcinski 已提交
36
      jobs_for_ref(ref, tag, trigger_request).map do |name, _|
K
Kamil Trzcinski 已提交
37
        build_attributes(name)
38
      end
39 40
    end

41
    def builds_for_stage_and_ref(stage, ref, tag = false, trigger_request = nil)
42 43
      jobs_for_stage_and_ref(stage, ref, tag, trigger_request).map do |name, _|
        build_attributes(name)
44 45
      end
    end
G
Grzegorz Bizon 已提交
46

47
    def builds
48 49
      @jobs.map do |name, _|
        build_attributes(name)
50
      end
51 52
    end

53 54 55 56 57
    def build_attributes(name)
      job = @jobs[name.to_sym] || {}
      {
        stage_idx: @stages.index(job[:stage]),
        stage: job[:stage],
58
        commands: job[:commands],
59
        tag_list: job[:tags] || [],
60
        name: job[:name].to_s,
61
        allow_failure: job[:allow_failure] || false,
62 63
        only: job[:only],
        except: job[:except],
64
        when: job[:when] || 'on_success',
65
        environment: job[:environment_name],
66 67
        yaml_variables: yaml_variables(name),
        options: {
68 69
          image: job[:image],
          services: job[:services],
70
          artifacts: job[:artifacts],
71
          cache: job[:cache],
72
          dependencies: job[:dependencies],
73
          after_script: job[:after_script],
74
          environment: job[:environment],
75 76
        }.compact
      }
77 78
    end

K
Katarzyna Kobierska 已提交
79
    def self.validation_message(content)
K
Katarzyna Kobierska 已提交
80
      return 'Please provide content of .gitlab-ci.yml' if content.blank?
K
Katarzyna Kobierska 已提交
81

K
Katarzyna Kobierska 已提交
82 83 84 85 86
      begin
        Ci::GitlabCiYamlProcessor.new(content)
        nil
      rescue ValidationError, Psych::SyntaxError => e
        e.message
87 88 89
      end
    end

D
Douwe Maan 已提交
90 91 92
    private

    def initial_parsing
93 94 95
      ##
      # Global config
      #
96 97
      @before_script = @ci_config.before_script
      @image = @ci_config.image
98
      @after_script = @ci_config.after_script
99
      @services = @ci_config.services
100
      @variables = @ci_config.variables
101
      @stages = @ci_config.stages
102
      @cache = @ci_config.cache
103

104 105 106 107
      ##
      # Jobs
      #
      @jobs = @ci_config.jobs
108 109

      @jobs.each do |name, job|
110 111 112 113
        # logical validation for job

        validate_job_stage!(name, job)
        validate_job_dependencies!(name, job)
114
      end
D
Douwe Maan 已提交
115 116
    end

117
    def yaml_variables(name)
118 119 120
      variables = (@variables || {})
        .merge(job_variables(name))

121 122 123 124 125 126 127 128 129 130
      variables.map do |key, value|
        { key: key, value: value, public: true }
      end
    end

    def job_variables(name)
      job = @jobs[name.to_sym]
      return {} unless job

      job[:variables] || {}
D
Douwe Maan 已提交
131 132
    end

133
    def validate_job_stage!(name, job)
134 135
      return unless job[:stage]

136 137 138 139 140
      unless job[:stage].is_a?(String) && job[:stage].in?(@stages)
        raise ValidationError, "#{name} job: stage parameter should be #{@stages.join(", ")}"
      end
    end

141
    def validate_job_dependencies!(name, job)
142
      return unless job[:dependencies]
143

144
      stage_index = @stages.index(job[:stage])
145 146

      job[:dependencies].each do |dependency|
147
        raise ValidationError, "#{name} job: undefined dependency: #{dependency}" unless @jobs[dependency.to_sym]
148

149
        unless @stages.index(@jobs[dependency.to_sym][:stage]) < stage_index
150 151 152 153 154
          raise ValidationError, "#{name} job: dependency #{dependency} is not defined in prior stages"
        end
      end
    end

155
    def process?(only_params, except_params, ref, tag, trigger_request)
156
      if only_params.present?
157
        return false unless matching?(only_params, ref, tag, trigger_request)
158 159 160
      end

      if except_params.present?
161
        return false if matching?(except_params, ref, tag, trigger_request)
162 163 164 165 166
      end

      true
    end

167
    def matching?(patterns, ref, tag, trigger_request)
168
      patterns.any? do |pattern|
169
        match_ref?(pattern, ref, tag, trigger_request)
170 171 172
      end
    end

J
Jason Roehm 已提交
173
    def match_ref?(pattern, ref, tag, trigger_request)
174 175 176 177
      pattern, path = pattern.split('@', 2)
      return false if path && path != self.path
      return true if tag && pattern == 'tags'
      return true if !tag && pattern == 'branches'
J
Jason Roehm 已提交
178
      return true if trigger_request.present? && pattern == 'triggers'
179 180 181 182 183 184 185

      if pattern.first == "/" && pattern.last == "/"
        Regexp.new(pattern[1...-1]) =~ ref
      else
        pattern == ref
      end
    end
D
Douwe Maan 已提交
186
  end
187
end