Commit 42ea80cc authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'pedropombeiro/26345/6-add-expand_variable_collection' into 'master'

Add #expand_variable_collection to Variables::Collection

See merge request gitlab-org/gitlab!54507
parents 3611732d df1457dd
...@@ -154,8 +154,8 @@ module Gitlab ...@@ -154,8 +154,8 @@ module Gitlab
end end
def variable_expansion_errors def variable_expansion_errors
sorted_collection = evaluate_context.variables.sorted_collection(@pipeline.project) expanded_collection = evaluate_context.variables.sort_and_expand_all(@pipeline.project)
errors = sorted_collection.errors errors = expanded_collection.errors
["#{name}: #{errors}"] if errors ["#{name}: #{errors}"] if errors
end end
......
...@@ -63,10 +63,45 @@ module Gitlab ...@@ -63,10 +63,45 @@ module Gitlab
Collection.new(@variables.reject(&block)) Collection.new(@variables.reject(&block))
end end
# Returns a sorted Collection object, and sets errors property in case of an error def expand_value(value, keep_undefined: false)
def sorted_collection(project) value.gsub(ExpandVariables::VARIABLES_REGEXP) do
Sort.new(self, project).collection match = Regexp.last_match
result = @variables_by_key[match[1] || match[2]]&.value
result ||= match[0] if keep_undefined
result
end
end end
def sort_and_expand_all(project, keep_undefined: false)
return self if Feature.disabled?(:variable_inside_variable, project)
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
end end
......
...@@ -7,20 +7,21 @@ module Gitlab ...@@ -7,20 +7,21 @@ module Gitlab
class Item class Item
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
attr_reader :raw
def initialize(key:, value:, public: true, file: false, masked: false, raw: false) def initialize(key:, value:, public: true, file: false, masked: false, raw: false)
raise ArgumentError, "`#{key}` must be of type String or nil value, while it was: #{value.class}" unless raise ArgumentError, "`#{key}` must be of type String or nil value, while it was: #{value.class}" unless
value.is_a?(String) || value.nil? value.is_a?(String) || value.nil?
@variable = { key: key, value: value, public: public, file: file, masked: masked } @variable = { key: key, value: value, public: public, file: file, masked: masked, raw: raw }
@raw = raw
end end
def value def value
@variable.fetch(:value) @variable.fetch(:value)
end end
def raw
@variable.fetch(:raw)
end
def [](key) def [](key)
@variable.fetch(key) @variable.fetch(key)
end end
...@@ -46,7 +47,7 @@ module Gitlab ...@@ -46,7 +47,7 @@ module Gitlab
# #
def to_runner_variable def to_runner_variable
@variable.reject do |hash_key, hash_value| @variable.reject do |hash_key, hash_value|
hash_key == :file && hash_value == false (hash_key == :file || hash_key == :raw) && hash_value == false
end end
end end
...@@ -62,6 +63,12 @@ module Gitlab ...@@ -62,6 +63,12 @@ module Gitlab
raise ArgumentError, "Unknown `#{resource.class}` variable resource!" raise ArgumentError, "Unknown `#{resource.class}` variable resource!"
end end
end end
def to_s
return to_runner_variable.to_s unless depends_on
"#{to_runner_variable}, depends_on=#{depends_on}"
end
end end
end end
end end
......
...@@ -8,12 +8,11 @@ module Gitlab ...@@ -8,12 +8,11 @@ module Gitlab
include TSort include TSort
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
def initialize(collection, project) def initialize(collection)
raise(ArgumentError, "A Gitlab::Ci::Variables::Collection object was expected") unless raise(ArgumentError, "A Gitlab::Ci::Variables::Collection object was expected") unless
collection.is_a?(Collection) collection.is_a?(Collection)
@collection = collection @collection = collection
@project = project
end end
def valid? def valid?
...@@ -23,8 +22,6 @@ module Gitlab ...@@ -23,8 +22,6 @@ module Gitlab
# errors sorts an array of variables, ignoring unknown variable references, # errors sorts an array of variables, ignoring unknown variable references,
# and returning an error string if a circular variable reference is found # and returning an error string if a circular variable reference is found
def errors def errors
return if Feature.disabled?(:variable_inside_variable, @project)
strong_memoize(:errors) do strong_memoize(:errors) do
# Check for cyclic dependencies and build error message in that case # Check for cyclic dependencies and build error message in that case
cyclic_vars = each_strongly_connected_component.filter_map do |component| cyclic_vars = each_strongly_connected_component.filter_map do |component|
...@@ -35,16 +32,6 @@ module Gitlab ...@@ -35,16 +32,6 @@ module Gitlab
end end
end end
# collection sorts a collection of variables, ignoring unknown variable references.
# If a circular variable reference is found, a new collection with the original array and an error is returned
def collection
return @collection if Feature.disabled?(:variable_inside_variable, @project)
return Gitlab::Ci::Variables::Collection.new(@collection, errors) if errors
Gitlab::Ci::Variables::Collection.new(tsort)
end
private private
def tsort_each_node(&block) def tsort_each_node(&block)
......
...@@ -202,6 +202,26 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do ...@@ -202,6 +202,26 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do
end end
end end
context 'when variable is raw' do
it 'does not export raw value when it is false' do
runner_variable = described_class
.new(key: 'VAR', value: 'value', raw: false)
.to_runner_variable
expect(runner_variable)
.to eq(key: 'VAR', value: 'value', public: true, masked: false)
end
it 'exports raw value when it is true' do
runner_variable = described_class
.new(key: 'VAR', value: 'value', raw: true)
.to_runner_variable
expect(runner_variable)
.to eq(key: 'VAR', value: 'value', public: true, raw: true, masked: false)
end
end
context 'when referencing a variable' do context 'when referencing a variable' do
it '#depends_on contains names of dependencies' do it '#depends_on contains names of dependencies' do
runner_variable = described_class.new(key: 'CI_VAR', value: '${CI_VAR_2}-123-$CI_VAR_3') runner_variable = described_class.new(key: 'CI_VAR', value: '${CI_VAR_2}-123-$CI_VAR_3')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment