Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
250fede4
Commit
250fede4
authored
Mar 11, 2021
by
Pedro Pombeiro
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Address MR review comments
parent
d565ff04
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
510 additions
and
683 deletions
+510
-683
lib/expand_variables.rb
lib/expand_variables.rb
+0
-23
lib/gitlab/ci/pipeline/seed/build.rb
lib/gitlab/ci/pipeline/seed/build.rb
+2
-2
lib/gitlab/ci/variables/collection.rb
lib/gitlab/ci/variables/collection.rb
+28
-8
lib/gitlab/ci/variables/collection/item.rb
lib/gitlab/ci/variables/collection/item.rb
+6
-9
lib/gitlab/ci/variables/collection/sort.rb
lib/gitlab/ci/variables/collection/sort.rb
+1
-14
spec/lib/expand_variables_spec.rb
spec/lib/expand_variables_spec.rb
+0
-232
spec/lib/gitlab/ci/variables/collection/item_spec.rb
spec/lib/gitlab/ci/variables/collection/item_spec.rb
+20
-10
spec/lib/gitlab/ci/variables/collection/sort_spec.rb
spec/lib/gitlab/ci/variables/collection/sort_spec.rb
+134
-279
spec/lib/gitlab/ci/variables/collection_spec.rb
spec/lib/gitlab/ci/variables/collection_spec.rb
+319
-106
No files found.
lib/expand_variables.rb
View file @
250fede4
...
...
@@ -16,18 +16,6 @@ module ExpandVariables
end
end
# expand_variables_collection expands a Gitlab::Ci::Variables::Collection, ignoring unknown variable references.
# If a circular variable reference is found, the original Collection is returned
def
expand_variables_collection
(
variables
,
project
)
return
Gitlab
::
Ci
::
Variables
::
Collection
.
new
(
variables
)
if
Feature
.
disabled?
(
:variable_inside_variable
,
project
)
sorted_variables
=
variables
.
sorted_collection
(
project
)
return
sorted_variables
if
sorted_variables
.
errors
expand_sorted_variables_collection
(
sorted_variables
)
end
def
possible_var_reference?
(
value
)
return
unless
value
...
...
@@ -74,16 +62,5 @@ module ExpandVariables
variables
end
def
expand_sorted_variables_collection
(
sorted_variables
)
expanded_vars
=
{}
sorted_variables
.
each_with_object
(
Gitlab
::
Ci
::
Variables
::
Collection
.
new
)
do
|
item
,
collection
|
item
=
item
.
merge
(
value:
expand_existing
(
item
.
value
,
expanded_vars
))
if
item
.
depends_on
expanded_vars
.
store
(
item
[
:key
],
item
)
collection
.
append
(
item
)
end
end
end
end
lib/gitlab/ci/pipeline/seed/build.rb
View file @
250fede4
...
...
@@ -162,8 +162,8 @@ module Gitlab
end
def
variable_expansion_errors
sorted_collection
=
evaluate_context
.
variables
.
sorted_collection
(
@pipeline
.
project
)
errors
=
sort
ed_collection
.
errors
expanded_collection
=
evaluate_context
.
variables
.
expand_all
(
@pipeline
.
project
)
errors
=
expand
ed_collection
.
errors
[
"
#{
name
}
:
#{
errors
}
"
]
if
errors
end
...
...
lib/gitlab/ci/variables/collection.rb
View file @
250fede4
...
...
@@ -63,16 +63,36 @@ module Gitlab
Collection
.
new
(
@variables
.
reject
(
&
block
))
end
def
==
(
other
)
return
@variables
==
other
if
other
.
is_a?
(
Array
)
return
false
unless
other
.
class
==
self
.
class
@variables
==
other
.
variables
def
expand_value
(
value
,
keep_undefined:
false
)
value
.
gsub
(
ExpandVariables
::
VARIABLES_REGEXP
)
do
match
=
Regexp
.
last_match
result
=
@variables_by_key
[
match
[
1
]
||
match
[
2
]]
&
.
value
result
||=
match
[
0
]
if
keep_undefined
result
end
end
# Returns a sorted Collection object, and sets errors property in case of an error
def
sorted_collection
(
project
)
Sort
.
new
(
self
,
project
).
collection
def
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
protected
...
...
lib/gitlab/ci/variables/collection/item.rb
View file @
250fede4
...
...
@@ -7,26 +7,23 @@ module Gitlab
class
Item
include
Gitlab
::
Utils
::
StrongMemoize
attr_reader
:raw
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
value
.
is_a?
(
String
)
||
value
.
nil?
@variable
=
{
key:
key
,
value:
value
,
public:
public
,
file:
file
,
masked:
masked
}
@raw
=
raw
@variable
=
{
key:
key
,
value:
value
,
public:
public
,
file:
file
,
masked:
masked
,
raw:
raw
}
end
def
value
@variable
.
fetch
(
:value
)
end
def
[]
(
key
)
@variable
.
fetch
(
key
)
def
raw
@variable
.
fetch
(
:raw
)
end
def
merge
(
*
other_hashes
)
self
.
class
.
fabricate
(
@variable
.
merge
(
*
other_hashes
)
)
def
[]
(
key
)
@variable
.
fetch
(
key
)
end
def
==
(
other
)
...
...
@@ -50,7 +47,7 @@ module Gitlab
#
def
to_runner_variable
@variable
.
reject
do
|
hash_key
,
hash_value
|
hash_key
==
:file
&&
hash_value
==
false
(
hash_key
==
:file
||
hash_key
==
:raw
)
&&
hash_value
==
false
end
end
...
...
lib/gitlab/ci/variables/collection/sort.rb
View file @
250fede4
...
...
@@ -8,12 +8,11 @@ module Gitlab
include
TSort
include
Gitlab
::
Utils
::
StrongMemoize
def
initialize
(
collection
,
project
)
def
initialize
(
collection
)
raise
(
ArgumentError
,
"A Gitlab::Ci::Variables::Collection object was expected"
)
unless
collection
.
is_a?
(
Collection
)
@collection
=
collection
@project
=
project
end
def
valid?
...
...
@@ -23,8 +22,6 @@ module Gitlab
# errors sorts an array of variables, ignoring unknown variable references,
# and returning an error string if a circular variable reference is found
def
errors
return
if
Feature
.
disabled?
(
:variable_inside_variable
,
@project
)
strong_memoize
(
:errors
)
do
# Check for cyclic dependencies and build error message in that case
cyclic_vars
=
each_strongly_connected_component
.
filter_map
do
|
component
|
...
...
@@ -35,16 +32,6 @@ module Gitlab
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
def
tsort_each_node
(
&
block
)
...
...
spec/lib/expand_variables_spec.rb
View file @
250fede4
...
...
@@ -268,236 +268,4 @@ RSpec.describe ExpandVariables do
end
end
end
describe
'#expand_variables_collection'
do
context
'when FF :variable_inside_variable is disabled'
do
let_it_be
(
:project_with_flag_disabled
)
{
create
(
:project
)
}
let_it_be
(
:project_with_flag_enabled
)
{
create
(
:project
)
}
before
do
stub_feature_flags
(
variable_inside_variable:
[
project_with_flag_enabled
])
end
context
'table tests'
do
using
RSpec
::
Parameterized
::
TableSyntax
where
do
{
"empty array"
:
{
variables:
[]
},
"simple expansions"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
]
},
"complex expansion"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'key${variable}'
}
]
},
"out-of-order variable reference"
:
{
variables:
[
{
key:
'variable2'
,
value:
'key${variable}'
},
{
key:
'variable'
,
value:
'value'
}
]
},
"complex expansions with raw variable"
:
{
variables:
[
{
key:
'variable3'
,
value:
'key_${variable}_${variable2}'
},
{
key:
'variable'
,
value:
'$variable2'
,
raw:
true
},
{
key:
'variable2'
,
value:
'value2'
}
]
},
"array with cyclic dependency"
:
{
variables:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
]
}
}
end
with_them
do
subject
{
ExpandVariables
.
expand_variables_collection
(
variables
,
project_with_flag_disabled
)
}
it
'returns Collection'
do
is_expected
.
to
be_an_instance_of
(
Gitlab
::
Ci
::
Variables
::
Collection
)
end
it
'does not expand variables'
do
is_expected
.
to
eq
(
variables
)
end
end
end
end
context
'when FF :variable_inside_variable is enabled'
do
let_it_be
(
:project_with_flag_disabled
)
{
create
(
:project
)
}
let_it_be
(
:project_with_flag_enabled
)
{
create
(
:project
)
}
before
do
stub_feature_flags
(
variable_inside_variable:
[
project_with_flag_enabled
])
end
context
'table tests'
do
using
RSpec
::
Parameterized
::
TableSyntax
where
do
{
"empty array"
:
{
variables:
[],
result:
[]
},
"simple expansions"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
},
{
key:
'variable4'
,
value:
'key$variable$variable3'
}
],
result:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'keyvalueresult'
},
{
key:
'variable4'
,
value:
'keyvaluekeyvalueresult'
}
]
},
"complex expansion"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'key${variable}'
}
],
result:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'keyvalue'
}
]
},
"unused variables"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result2'
},
{
key:
'variable3'
,
value:
'result3'
},
{
key:
'variable4'
,
value:
'key$variable$variable3'
}
],
result:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result2'
},
{
key:
'variable3'
,
value:
'result3'
},
{
key:
'variable4'
,
value:
'keyvalueresult3'
}
]
},
"complex expansions"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'key${variable}${variable2}'
}
],
result:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'keyvalueresult'
}
]
},
"out-of-order expansion"
:
{
variables:
[
{
key:
'variable3'
,
value:
'key$variable2$variable'
},
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
}
],
result:
[
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable3'
,
value:
'keyresultvalue'
}
]
},
"out-of-order complex expansion"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'key${variable2}${variable}'
}
],
result:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'keyresultvalue'
}
]
},
"missing variable"
:
{
variables:
[
{
key:
'variable2'
,
value:
'key$variable'
}
],
result:
[
{
key:
'variable2'
,
value:
'key$variable'
}
]
},
"complex expansions with missing variable"
:
{
variables:
[
{
key:
'variable4'
,
value:
'key${variable}${variable2}${variable3}'
},
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable3'
,
value:
'value3'
}
],
result:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable3'
,
value:
'value3'
},
{
key:
'variable4'
,
value:
'keyvalue${variable2}value3'
}
]
},
"complex expansions with raw variable"
:
{
variables:
[
{
key:
'variable3'
,
value:
'key_${variable}_${variable2}'
},
{
key:
'variable'
,
value:
'$variable2'
,
raw:
true
},
{
key:
'variable2'
,
value:
'value2'
}
],
result:
[
{
key:
'variable'
,
value:
'$variable2'
,
raw:
true
},
{
key:
'variable2'
,
value:
'value2'
},
{
key:
'variable3'
,
value:
'key_$variable2_value2'
}
]
},
"cyclic dependency causes original array to be returned"
:
{
variables:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
],
result:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
]
}
}
end
with_them
do
let!
(
:collection
)
{
Gitlab
::
Ci
::
Variables
::
Collection
.
new
(
variables
)
}
subject!
do
ExpandVariables
.
expand_variables_collection
(
collection
,
project_with_flag_enabled
)
end
it
'returns Collection'
do
is_expected
.
to
be_an_instance_of
(
Gitlab
::
Ci
::
Variables
::
Collection
)
end
it
'expands variables'
do
is_expected
.
to
eq
(
result
)
end
it
'preserves raw attribute'
do
collection
.
each
do
|
v
|
k
=
v
[
:key
]
subject_item
=
subject
.
find
{
|
sv
|
sv
[
:key
]
==
k
}
expect
(
subject_item
.
raw
).
to
eq
(
v
.
raw
)
end
end
end
end
end
end
end
spec/lib/gitlab/ci/variables/collection/item_spec.rb
View file @
250fede4
...
...
@@ -180,16 +180,6 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do
end
end
describe
'#merge'
do
it
'behaves like hash merge'
do
item
=
described_class
.
new
(
**
variable
)
subject
=
item
.
merge
(
value:
'another thing'
)
expect
(
subject
).
not_to
eq
item
expect
(
subject
[
:value
]).
to
eq
'another thing'
end
end
describe
'#to_runner_variable'
do
context
'when variable is not a file-related'
do
it
'returns a runner-compatible hash representation'
do
...
...
@@ -212,6 +202,26 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do
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
it
'#depends_on contains names of dependencies'
do
runner_variable
=
described_class
.
new
(
key:
'CI_VAR'
,
value:
'${CI_VAR_2}-123-$CI_VAR_3'
)
...
...
spec/lib/gitlab/ci/variables/collection/sort_spec.rb
View file @
250fede4
...
...
@@ -4,15 +4,8 @@ require 'spec_helper'
RSpec
.
describe
Gitlab
::
Ci
::
Variables
::
Collection
::
Sort
do
describe
'#initialize with non-Collection value'
do
let_it_be
(
:project_with_flag_disabled
)
{
create
(
:project
)
}
let_it_be
(
:project_with_flag_enabled
)
{
create
(
:project
)
}
before
do
stub_feature_flags
(
variable_inside_variable:
[
project_with_flag_enabled
])
end
context
'when FF :variable_inside_variable is disabled'
do
subject
{
Gitlab
::
Ci
::
Variables
::
Collection
::
Sort
.
new
([]
,
project_with_flag_disabled
)
}
subject
{
Gitlab
::
Ci
::
Variables
::
Collection
::
Sort
.
new
([])
}
it
'raises ArgumentError'
do
expect
{
subject
}.
to
raise_error
(
ArgumentError
,
/Collection object was expected/
)
...
...
@@ -20,7 +13,7 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sort do
end
context
'when FF :variable_inside_variable is enabled'
do
subject
{
Gitlab
::
Ci
::
Variables
::
Collection
::
Sort
.
new
([]
,
project_with_flag_enabled
)
}
subject
{
Gitlab
::
Ci
::
Variables
::
Collection
::
Sort
.
new
([])
}
it
'raises ArgumentError'
do
expect
{
subject
}.
to
raise_error
(
ArgumentError
,
/Collection object was expected/
)
...
...
@@ -29,301 +22,163 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sort do
end
describe
'#errors'
do
context
'when FF :variable_inside_variable is disabled'
do
let_it_be
(
:project_with_flag_disabled
)
{
create
(
:project
)
}
let_it_be
(
:project_with_flag_enabled
)
{
create
(
:project
)
}
before
do
stub_feature_flags
(
variable_inside_variable:
[
project_with_flag_enabled
])
end
context
'table tests'
do
using
RSpec
::
Parameterized
::
TableSyntax
where
do
{
"empty array"
:
{
variables:
[]
},
"simple expansions"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
]
},
"complex expansion"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'key${variable}'
}
]
},
"complex expansions with missing variable for Windows"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable3'
,
value:
'key%variable%%variable2%'
}
]
},
"out-of-order variable reference"
:
{
variables:
[
{
key:
'variable2'
,
value:
'key${variable}'
},
{
key:
'variable'
,
value:
'value'
}
]
},
"array with cyclic dependency"
:
{
variables:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
]
},
"array with raw variable"
:
{
variables:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
,
raw:
true
}
]
}
context
'table tests'
do
using
RSpec
::
Parameterized
::
TableSyntax
where
do
{
"empty array"
:
{
variables:
[],
validation_result:
nil
},
"simple expansions"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
],
validation_result:
nil
},
"cyclic dependency"
:
{
variables:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
],
validation_result:
'circular variable reference detected: ["variable", "variable2", "variable3"]'
},
"array with raw variable"
:
{
variables:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
,
raw:
true
}
],
validation_result:
nil
},
"variable containing escaped variable reference"
:
{
variables:
[
{
key:
'variable_a'
,
value:
'value'
},
{
key:
'variable_b'
,
value:
'$$variable_a'
},
{
key:
'variable_c'
,
value:
'$variable_b'
}
],
validation_result:
nil
}
end
with_them
do
let
(
:collection
)
{
Gitlab
::
Ci
::
Variables
::
Collection
.
new
(
variables
)
}
subject
{
Gitlab
::
Ci
::
Variables
::
Collection
::
Sort
.
new
(
collection
,
project_with_flag_disabled
)
}
it
'does not report error'
do
expect
(
subject
.
errors
).
to
eq
(
nil
)
end
it
'valid? reports true'
do
expect
(
subject
.
valid?
).
to
eq
(
true
)
end
end
}
end
end
context
'when FF :variable_inside_variable is enabled'
do
let_it_be
(
:project_with_flag_disabled
)
{
create
(
:project
)
}
let_it_be
(
:project_with_flag_enabled
)
{
create
(
:project
)
}
before
do
stub_feature_flags
(
variable_inside_variable:
[
project_with_flag_enabled
])
end
with_them
do
let
(
:collection
)
{
Gitlab
::
Ci
::
Variables
::
Collection
.
new
(
variables
)
}
context
'table tests'
do
using
RSpec
::
Parameterized
::
TableSyntax
subject
{
Gitlab
::
Ci
::
Variables
::
Collection
::
Sort
.
new
(
collection
)
}
where
do
{
"empty array"
:
{
variables:
[],
validation_result:
nil
},
"simple expansions"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
],
validation_result:
nil
},
"cyclic dependency"
:
{
variables:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
],
validation_result:
'circular variable reference detected: ["variable", "variable2", "variable3"]'
},
"array with raw variable"
:
{
variables:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
,
raw:
true
}
],
validation_result:
nil
},
"variable containing escaped variable reference"
:
{
variables:
[
{
key:
'variable_a'
,
value:
'value'
},
{
key:
'variable_b'
,
value:
'$$variable_a'
},
{
key:
'variable_c'
,
value:
'$variable_b'
}
],
validation_result:
nil
}
}
it
'errors matches expected validation result'
do
expect
(
subject
.
errors
).
to
eq
(
validation_result
)
end
with_them
do
let
(
:collection
)
{
Gitlab
::
Ci
::
Variables
::
Collection
.
new
(
variables
)
}
subject
{
Gitlab
::
Ci
::
Variables
::
Collection
::
Sort
.
new
(
collection
,
project_with_flag_enabled
)
}
it
'errors matches expected validation result'
do
expect
(
subject
.
errors
).
to
eq
(
validation_result
)
end
it
'valid? matches expected validation result'
do
expect
(
subject
.
valid?
).
to
eq
(
validation_result
.
nil?
)
end
it
'valid? matches expected validation result'
do
expect
(
subject
.
valid?
).
to
eq
(
validation_result
.
nil?
)
end
it
'does not raise'
do
expect
{
subject
}.
not_to
raise_error
end
end
end
end
describe
'#collection'
do
context
'when FF :variable_inside_variable is disabled'
do
before
do
stub_feature_flags
(
variable_inside_variable:
false
)
end
context
'table tests'
do
using
RSpec
::
Parameterized
::
TableSyntax
where
do
{
"empty array"
:
{
variables:
[]
},
"simple expansions"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
]
},
"complex expansion"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'key${variable}'
}
]
},
"complex expansions with missing variable for Windows"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable3'
,
value:
'key%variable%%variable2%'
}
]
},
"out-of-order variable reference"
:
{
variables:
[
{
key:
'variable2'
,
value:
'key${variable}'
},
{
key:
'variable'
,
value:
'value'
}
]
},
"array with cyclic dependency"
:
{
variables:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
]
}
describe
'#tsort'
do
context
'table tests'
do
using
RSpec
::
Parameterized
::
TableSyntax
where
do
{
"empty array"
:
{
variables:
[],
result:
[]
},
"simple expansions, no reordering needed"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
],
result:
%w[variable variable2 variable3]
},
"complex expansion, reordering needed"
:
{
variables:
[
{
key:
'variable2'
,
value:
'key${variable}'
},
{
key:
'variable'
,
value:
'value'
}
],
result:
%w[variable variable2]
},
"unused variables"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable4'
,
value:
'key$variable$variable3'
},
{
key:
'variable2'
,
value:
'result2'
},
{
key:
'variable3'
,
value:
'result3'
}
],
result:
%w[variable variable3 variable4 variable2]
},
"missing variable"
:
{
variables:
[
{
key:
'variable2'
,
value:
'key$variable'
}
],
result:
%w[variable2]
},
"complex expansions with missing variable"
:
{
variables:
[
{
key:
'variable4'
,
value:
'key${variable}${variable2}${variable3}'
},
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable3'
,
value:
'value3'
}
],
result:
%w[variable variable3 variable4]
},
"raw variable does not get resolved"
:
{
variables:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
,
raw:
true
}
],
result:
%w[variable3 variable2 variable]
},
"variable containing escaped variable reference"
:
{
variables:
[
{
key:
'variable_c'
,
value:
'$variable_b'
},
{
key:
'variable_b'
,
value:
'$$variable_a'
},
{
key:
'variable_a'
,
value:
'value'
}
],
result:
%w[variable_a variable_b variable_c]
}
end
}
end
with_them
do
let_it_be
(
:project
)
{
create
(
:project
)
}
let
(
:collection
)
{
Gitlab
::
Ci
::
Variables
::
Collection
.
new
(
variables
)
}
with_them
do
let
(
:collection
)
{
Gitlab
::
Ci
::
Variables
::
Collection
.
new
(
variables
)
}
subject
{
Gitlab
::
Ci
::
Variables
::
Collection
::
Sort
.
new
(
collection
,
project
).
collection
}
subject
{
Gitlab
::
Ci
::
Variables
::
Collection
::
Sort
.
new
(
collection
).
tsort
}
it
'does not expand variables'
do
is_expected
.
to
be
(
collection
)
end
it
'returns correctly sorted variables'
do
expect
(
subject
.
map
{
|
var
|
var
[
:key
]
}).
to
eq
(
result
)
end
end
end
context
'when FF :variable_inside_variable is enabled'
do
before
do
stub_licensed_features
(
group_saml_group_sync:
true
)
stub_feature_flags
(
variable_inside_variable:
true
)
context
'cyclic dependency'
do
let
(
:variables
)
do
[
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
},
{
key:
'variable'
,
value:
'$variable2'
}
]
end
context
'table tests'
do
using
RSpec
::
Parameterized
::
TableSyntax
where
do
{
"empty array"
:
{
variables:
[],
result:
[]
},
"simple expansions, no reordering needed"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
],
result:
%w[variable variable2 variable3]
},
"complex expansion, reordering needed"
:
{
variables:
[
{
key:
'variable2'
,
value:
'key${variable}'
},
{
key:
'variable'
,
value:
'value'
}
],
result:
%w[variable variable2]
},
"unused variables"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable4'
,
value:
'key$variable$variable3'
},
{
key:
'variable2'
,
value:
'result2'
},
{
key:
'variable3'
,
value:
'result3'
}
],
result:
%w[variable variable3 variable4 variable2]
},
"missing variable"
:
{
variables:
[
{
key:
'variable2'
,
value:
'key$variable'
}
],
result:
%w[variable2]
},
"complex expansions with missing variable"
:
{
variables:
[
{
key:
'variable4'
,
value:
'key${variable}${variable2}${variable3}'
},
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable3'
,
value:
'value3'
}
],
result:
%w[variable variable3 variable4]
},
"cyclic dependency causes original array to be returned"
:
{
variables:
[
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
},
{
key:
'variable'
,
value:
'$variable2'
}
],
result:
%w[variable2 variable3 variable]
},
"raw variable does not get resolved"
:
{
variables:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
,
raw:
true
}
],
result:
%w[variable3 variable2 variable]
},
"variable containing escaped variable reference"
:
{
variables:
[
{
key:
'variable_c'
,
value:
'$variable_b'
},
{
key:
'variable_b'
,
value:
'$$variable_a'
},
{
key:
'variable_a'
,
value:
'value'
}
],
result:
%w[variable_a variable_b variable_c]
}
}
end
let
(
:collection
)
{
Gitlab
::
Ci
::
Variables
::
Collection
.
new
(
variables
)
}
with_them
do
let_it_be
(
:project
)
{
create
(
:project
)
}
let
(
:collection
)
{
Gitlab
::
Ci
::
Variables
::
Collection
.
new
(
variables
)
}
subject
{
Gitlab
::
Ci
::
Variables
::
Collection
::
Sort
.
new
(
collection
).
tsort
}
subject
{
Gitlab
::
Ci
::
Variables
::
Collection
::
Sort
.
new
(
collection
,
project
).
collection
}
it
'returns correctly sorted variables'
do
expect
(
subject
.
map
{
|
var
|
var
[
:key
]
}).
to
eq
(
result
)
end
end
it
'raises TSort::Cyclic'
do
expect
{
subject
}.
to
raise_error
(
TSort
::
Cyclic
)
end
end
end
...
...
spec/lib/gitlab/ci/variables/collection_spec.rb
View file @
250fede4
...
...
@@ -113,67 +113,6 @@ RSpec.describe Gitlab::Ci::Variables::Collection do
end
end
describe
'#=='
do
variable
=
{
key:
'VAR'
,
value:
'value'
,
public:
true
,
masked:
false
}
context
'on empty collection'
do
collection
=
described_class
.
new
([])
it
'returns false for an array containing variable hash'
do
expect
(
collection
==
[
variable
]).
to
eq
(
false
)
end
it
'returns false for an unexpected type'
do
expect
(
collection
==
variable
).
to
eq
(
false
)
end
it
'returns true for an empty array'
do
expect
(
collection
==
[]).
to
eq
(
true
)
end
it
'returns true for the same object'
do
expect
(
collection
).
to
eq
(
collection
)
end
it
'returns true for a similar object'
do
expect
(
collection
==
described_class
.
new
([])).
to
eq
(
true
)
end
end
context
'on collection with a variable'
do
collection
=
described_class
.
new
([
variable
])
it
'returns false for an array containing other variable'
do
expect
(
collection
==
[{
key:
'VAR'
,
value:
'different value'
}]).
to
eq
(
false
)
end
it
'returns false for an empty array'
do
expect
(
collection
==
[]).
to
eq
(
false
)
end
it
'returns false for an unexpected type'
do
expect
(
collection
==
variable
).
to
eq
(
false
)
end
it
'returns false for a Collection with a variable with different attribute value'
do
other
=
described_class
.
new
([{
key:
'VAR'
,
value:
'value'
,
public:
false
,
masked:
false
}])
expect
(
collection
==
other
).
to
eq
(
false
)
end
it
'returns true for an array containing variable hash'
do
expect
(
collection
==
[
variable
]).
to
eq
(
true
)
end
it
'returns true for the same object'
do
expect
(
collection
).
to
eq
(
collection
)
end
it
'returns true for a similar object'
do
expect
(
collection
==
described_class
.
new
([
variable
])).
to
eq
(
true
)
end
end
end
describe
'#size'
do
it
'returns zero for empty collection'
do
collection
=
described_class
.
new
([])
...
...
@@ -227,51 +166,6 @@ RSpec.describe Gitlab::Ci::Variables::Collection do
end
end
describe
'#sorted_collection'
do
let!
(
:project
)
{
create
(
:project
)
}
subject
{
collection
.
sorted_collection
(
project
)
}
context
'when FF :variable_inside_variable is disabled'
do
before
do
stub_feature_flags
(
variable_inside_variable:
false
)
end
let
(
:collection
)
do
described_class
.
new
.
append
(
key:
'A'
,
value:
'test-$B'
)
.
append
(
key:
'B'
,
value:
'test-$C'
)
.
append
(
key:
'C'
,
value:
'test'
)
end
it
{
is_expected
.
to
be
(
collection
)
}
end
context
'when FF :variable_inside_variable is enabled'
do
before
do
stub_feature_flags
(
variable_inside_variable:
[
project
])
end
let
(
:collection
)
do
described_class
.
new
.
append
(
key:
'A'
,
value:
'test-$B'
)
.
append
(
key:
'B'
,
value:
'test-$C'
)
.
append
(
key:
'C'
,
value:
'test'
)
end
it
{
is_expected
.
to
be_a
(
Gitlab
::
Ci
::
Variables
::
Collection
)
}
it
'returns sorted collection'
do
expect
(
subject
.
to_a
).
to
eq
(
[
{
key:
'C'
,
value:
'test'
,
masked:
false
,
public:
true
},
{
key:
'B'
,
value:
'test-$C'
,
masked:
false
,
public:
true
},
{
key:
'A'
,
value:
'test-$B'
,
masked:
false
,
public:
true
}
])
end
end
end
describe
'#reject'
do
let
(
:collection
)
do
described_class
.
new
...
...
@@ -291,4 +185,323 @@ RSpec.describe Gitlab::Ci::Variables::Collection do
expect
(
subject
.
to_runner_variables
).
to
eq
(
comp
)
end
end
describe
'#expand_value'
do
let
(
:collection
)
do
Gitlab
::
Ci
::
Variables
::
Collection
.
new
.
append
(
key:
'CI_JOB_NAME'
,
value:
'test-1'
)
.
append
(
key:
'CI_BUILD_ID'
,
value:
'1'
)
.
append
(
key:
'RAW_VAR'
,
value:
'$TEST1'
,
raw:
true
)
.
append
(
key:
'TEST1'
,
value:
'test-3'
)
end
context
'table tests'
do
using
RSpec
::
Parameterized
::
TableSyntax
where
do
{
"empty value"
:
{
value:
''
,
result:
''
,
keep_undefined:
false
},
"simple expansions"
:
{
value:
'key$TEST1-$CI_BUILD_ID'
,
result:
'keytest-3-1'
,
keep_undefined:
false
},
"complex expansion"
:
{
value:
'key${TEST1}-${CI_JOB_NAME}'
,
result:
'keytest-3-test-1'
,
keep_undefined:
false
},
"complex expansions with raw variable"
:
{
value:
'key${RAW_VAR}-${CI_JOB_NAME}'
,
result:
'key$TEST1-test-1'
,
keep_undefined:
false
},
"missing variable not keeping original"
:
{
value:
'key${MISSING_VAR}-${CI_JOB_NAME}'
,
result:
'key-test-1'
,
keep_undefined:
false
},
"missing variable keeping original"
:
{
value:
'key${MISSING_VAR}-${CI_JOB_NAME}'
,
result:
'key${MISSING_VAR}-test-1'
,
keep_undefined:
true
}
}
end
with_them
do
subject
{
collection
.
expand_value
(
value
,
keep_undefined:
keep_undefined
)
}
it
'matches expected expansion'
do
is_expected
.
to
eq
(
result
)
end
end
end
end
describe
'#expand_all'
do
context
'when FF :variable_inside_variable is disabled'
do
let_it_be
(
:project_with_flag_disabled
)
{
create
(
:project
)
}
let_it_be
(
:project_with_flag_enabled
)
{
create
(
:project
)
}
before
do
stub_feature_flags
(
variable_inside_variable:
[
project_with_flag_enabled
])
end
context
'table tests'
do
using
RSpec
::
Parameterized
::
TableSyntax
where
do
{
"empty array"
:
{
variables:
[],
keep_undefined:
false
},
"simple expansions"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
],
keep_undefined:
false
},
"complex expansion"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'key${variable}'
}
],
keep_undefined:
false
},
"out-of-order variable reference"
:
{
variables:
[
{
key:
'variable2'
,
value:
'key${variable}'
},
{
key:
'variable'
,
value:
'value'
}
],
keep_undefined:
false
},
"complex expansions with raw variable"
:
{
variables:
[
{
key:
'variable3'
,
value:
'key_${variable}_${variable2}'
},
{
key:
'variable'
,
value:
'$variable2'
,
raw:
true
},
{
key:
'variable2'
,
value:
'value2'
}
],
keep_undefined:
false
},
"array with cyclic dependency"
:
{
variables:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
],
keep_undefined:
true
}
}
end
with_them
do
let
(
:collection
)
{
Gitlab
::
Ci
::
Variables
::
Collection
.
new
(
variables
,
keep_undefined:
keep_undefined
)
}
subject
{
collection
.
expand_all
(
project_with_flag_disabled
)
}
it
'returns Collection'
do
is_expected
.
to
be_an_instance_of
(
Gitlab
::
Ci
::
Variables
::
Collection
)
end
it
'does not expand variables'
do
var_hash
=
variables
.
to_h
{
|
env
|
[
env
.
fetch
(
:key
),
env
.
fetch
(
:value
)]
}
.
with_indifferent_access
expect
(
subject
.
to_hash
).
to
eq
(
var_hash
)
end
end
end
end
context
'when FF :variable_inside_variable is enabled'
do
let_it_be
(
:project_with_flag_disabled
)
{
create
(
:project
)
}
let_it_be
(
:project_with_flag_enabled
)
{
create
(
:project
)
}
before
do
stub_feature_flags
(
variable_inside_variable:
[
project_with_flag_enabled
])
end
context
'table tests'
do
using
RSpec
::
Parameterized
::
TableSyntax
where
do
{
"empty array"
:
{
variables:
[],
keep_undefined:
false
,
result:
[]
},
"simple expansions"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
},
{
key:
'variable4'
,
value:
'key$variable$variable3'
}
],
keep_undefined:
false
,
result:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'keyvalueresult'
},
{
key:
'variable4'
,
value:
'keyvaluekeyvalueresult'
}
]
},
"complex expansion"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'key${variable}'
}
],
keep_undefined:
false
,
result:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'keyvalue'
}
]
},
"unused variables"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result2'
},
{
key:
'variable3'
,
value:
'result3'
},
{
key:
'variable4'
,
value:
'key$variable$variable3'
}
],
keep_undefined:
false
,
result:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result2'
},
{
key:
'variable3'
,
value:
'result3'
},
{
key:
'variable4'
,
value:
'keyvalueresult3'
}
]
},
"complex expansions"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'key${variable}${variable2}'
}
],
keep_undefined:
false
,
result:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'keyvalueresult'
}
]
},
"out-of-order expansion"
:
{
variables:
[
{
key:
'variable3'
,
value:
'key$variable2$variable'
},
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
}
],
keep_undefined:
false
,
result:
[
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable3'
,
value:
'keyresultvalue'
}
]
},
"out-of-order complex expansion"
:
{
variables:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'key${variable2}${variable}'
}
],
keep_undefined:
false
,
result:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable2'
,
value:
'result'
},
{
key:
'variable3'
,
value:
'keyresultvalue'
}
]
},
"missing variable"
:
{
variables:
[
{
key:
'variable2'
,
value:
'key$variable'
}
],
keep_undefined:
false
,
result:
[
{
key:
'variable2'
,
value:
'key'
}
]
},
"missing variable keeping original"
:
{
variables:
[
{
key:
'variable2'
,
value:
'key$variable'
}
],
keep_undefined:
true
,
result:
[
{
key:
'variable2'
,
value:
'key$variable'
}
]
},
"complex expansions with missing variable keeping original"
:
{
variables:
[
{
key:
'variable4'
,
value:
'key${variable}${variable2}${variable3}'
},
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable3'
,
value:
'value3'
}
],
keep_undefined:
true
,
result:
[
{
key:
'variable'
,
value:
'value'
},
{
key:
'variable3'
,
value:
'value3'
},
{
key:
'variable4'
,
value:
'keyvalue${variable2}value3'
}
]
},
"complex expansions with raw variable"
:
{
variables:
[
{
key:
'variable3'
,
value:
'key_${variable}_${variable2}'
},
{
key:
'variable'
,
value:
'$variable2'
,
raw:
true
},
{
key:
'variable2'
,
value:
'value2'
}
],
keep_undefined:
false
,
result:
[
{
key:
'variable'
,
value:
'$variable2'
,
raw:
true
},
{
key:
'variable2'
,
value:
'value2'
},
{
key:
'variable3'
,
value:
'key_$variable2_value2'
}
]
},
"cyclic dependency causes original array to be returned"
:
{
variables:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
],
keep_undefined:
false
,
result:
[
{
key:
'variable'
,
value:
'$variable2'
},
{
key:
'variable2'
,
value:
'$variable3'
},
{
key:
'variable3'
,
value:
'key$variable$variable2'
}
]
}
}
end
with_them
do
let
(
:collection
)
{
Gitlab
::
Ci
::
Variables
::
Collection
.
new
(
variables
)
}
subject
{
collection
.
expand_all
(
project_with_flag_enabled
,
keep_undefined:
keep_undefined
)
}
it
'returns Collection'
do
is_expected
.
to
be_an_instance_of
(
Gitlab
::
Ci
::
Variables
::
Collection
)
end
it
'expands variables'
do
var_hash
=
result
.
to_h
{
|
env
|
[
env
.
fetch
(
:key
),
env
.
fetch
(
:value
)]
}
.
with_indifferent_access
expect
(
subject
.
to_hash
).
to
eq
(
var_hash
)
end
it
'preserves raw attribute'
do
collection
.
each
do
|
v
|
k
=
v
[
:key
]
subject_item
=
subject
.
find
{
|
sv
|
sv
[
:key
]
==
k
}
expect
(
subject_item
.
raw
).
to
eq
(
v
.
raw
)
end
end
end
end
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment