summaryrefslogtreecommitdiff
path: root/lib/gitlab/ci/variables/collection.rb
blob: 09c75a2b3f1cac7879f9374ad9a8973be71d35f2 (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
# frozen_string_literal: true

module Gitlab
  module Ci
    module Variables
      class Collection
        include Enumerable

        attr_reader :errors

        def initialize(variables = [], errors = nil)
          @variables = []
          @variables_by_key = Hash.new { |h, k| h[k] = [] }
          @errors = errors

          variables.each { |variable| self.append(variable) }
        end

        def append(resource)
          item = Collection::Item.fabricate(resource)
          @variables.append(item)
          @variables_by_key[item[:key]] << item

          self
        end

        def compact
          Collection.new(select { |variable| !variable.value.nil? })
        end

        def concat(resources)
          return self if resources.nil?

          tap { resources.each { |variable| self.append(variable) } }
        end

        def each
          @variables.each { |variable| yield variable }
        end

        def +(other)
          self.class.new.tap do |collection|
            self.each { |variable| collection.append(variable) }
            other.each { |variable| collection.append(variable) }
          end
        end

        def [](key)
          all(key)&.last
        end

        def all(key)
          vars = @variables_by_key[key]
          vars unless vars.empty?
        end

        def size
          @variables.size
        end

        def to_runner_variables
          self.map(&:to_runner_variable)
        end

        def to_hash
          self.to_runner_variables
            .to_h { |env| [env.fetch(:key), env.fetch(:value)] }
            .with_indifferent_access
        end

        def reject(&block)
          Collection.new(@variables.reject(&block))
        end

        def expand_value(value, keep_undefined: false)
          value.gsub(Item::VARIABLES_REGEXP) do
            match = Regexp.last_match
            if match[:key]
              # we matched variable
              if variable = self[match[:key]]
                variable.value
              elsif keep_undefined
                match[0]
              end
            else
              # we escape sequence
              match[0]
            end
          end
        end

        def sort_and_expand_all(project, keep_undefined: false)
          return self if Feature.disabled?(:variable_inside_variable, project, default_enabled: :yaml)

          sorted = Sort.new(self)
          return self.class.new(self, sorted.errors) unless sorted.valid?

          new_collection = self.class.new

          sorted.tsort.each do |item|
            unless item.depends_on
              new_collection.append(item)
              next
            end

            # expand variables as they are added
            variable = item.to_runner_variable
            variable[:value] = new_collection.expand_value(variable[:value], keep_undefined: keep_undefined)
            new_collection.append(variable)
          end

          new_collection
        end

        def to_s
          "#{@variables_by_key.keys}, @errors='#{@errors}'"
        end

        protected

        attr_reader :variables
      end
    end
  end
end