Commit c5225588 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Add support for only/except: variables CI/CD config

parent 4f472d49
...@@ -25,15 +25,31 @@ module Gitlab ...@@ -25,15 +25,31 @@ module Gitlab
include Entry::Validatable include Entry::Validatable
include Entry::Attributable include Entry::Attributable
attributes :refs, :kubernetes attributes :refs, :kubernetes, :variables
validations do validations do
validates :config, presence: true validates :config, presence: true
validates :config, allowed_keys: %i[refs kubernetes] validates :config, allowed_keys: %i[refs kubernetes variables]
validate :variables_expressions_syntax
with_options allow_nil: true do with_options allow_nil: true do
validates :refs, array_of_strings_or_regexps: true validates :refs, array_of_strings_or_regexps: true
validates :kubernetes, allowed_values: %w[active] validates :kubernetes, allowed_values: %w[active]
validates :variables, array_of_strings: true
end
def variables_expressions_syntax
return unless variables.is_a?(Array)
statements = variables.map do |statement|
::Gitlab::Ci::Pipeline::Expression::Statement.new(statement)
end
statements.each do |statement|
unless statement.valid?
errors.add(:variables, "Invalid expression #{statement.inspect}")
end
end
end end
end end
end end
......
...@@ -14,9 +14,11 @@ module Gitlab ...@@ -14,9 +14,11 @@ module Gitlab
%w[variable] %w[variable]
].freeze ].freeze
def initialize(statement, pipeline) def initialize(statement, pipeline = nil)
@lexer = Expression::Lexer.new(statement) @lexer = Expression::Lexer.new(statement)
return if pipeline.nil?
@variables = pipeline.variables.map do |variable| @variables = pipeline.variables.map do |variable|
[variable.key.to_sym, variable.value] [variable.key.to_sym, variable.value]
end end
...@@ -35,6 +37,16 @@ module Gitlab ...@@ -35,6 +37,16 @@ module Gitlab
def evaluate def evaluate
parse_tree.evaluate(@variables.to_h) parse_tree.evaluate(@variables.to_h)
end end
def inspect
"syntax: #{@lexer.lexemes.join(' ')}"
end
def valid?
parse_tree.is_a?(Lexeme::Base)
rescue StatementError
false
end
end end
end end
end end
......
...@@ -83,6 +83,31 @@ describe Gitlab::Ci::Config::Entry::Policy do ...@@ -83,6 +83,31 @@ describe Gitlab::Ci::Config::Entry::Policy do
end end
end end
context 'when specifying valid variables expressions policy' do
let(:config) { { variables: ['$VAR == null'] } }
it 'is a correct configuraton' do
expect(entry).to be_valid
expect(entry.value).to eq(config)
end
end
context 'when specifying variables expressions in invalid format' do
let(:config) { { variables: '$MY_VAR' } }
it 'reports an error about invalid format' do
expect(entry.errors).to include /should be an array of strings/
end
end
context 'when specifying invalid variables expressions statement' do
let(:config) { { variables: ['$MY_VAR =='] } }
it 'reports an error about invalid statement' do
expect(entry.errors).to include /invalid expression syntax: variable equals/
end
end
context 'when specifying unknown policy' do context 'when specifying unknown policy' do
let(:config) { { refs: ['master'], invalid: :something } } let(:config) { { refs: ['master'], invalid: :something } }
......
...@@ -11,6 +11,16 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do ...@@ -11,6 +11,16 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do
pipeline.variables.build([key: 'VARIABLE', value: 'my variable']) pipeline.variables.build([key: 'VARIABLE', value: 'my variable'])
end end
describe '.new' do
context 'when pipeline is not provided' do
it 'allows to properly initialize the statement' do
statement = described_class.new('$VARIABLE')
expect(statement.evaluate).to be_nil
end
end
end
describe '#parse_tree' do describe '#parse_tree' do
context 'when expression is empty' do context 'when expression is empty' do
let(:text) { '' } let(:text) { '' }
...@@ -24,18 +34,26 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do ...@@ -24,18 +34,26 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do
context 'when expression grammar is incorrect' do context 'when expression grammar is incorrect' do
table = [ table = [
'$VAR "text"', # missing operator '$VAR "text"', # missing operator
'== "123"', # invalid right side '== "123"', # invalid left side
"'single quotes'", # single quotes string '"some string"', # only string provided
'$VAR ==', # invalid right side '$VAR ==', # invalid right side
'12345', # unknown syntax '12345', # unknown syntax
'' # empty statement '' # empty statement
] ]
table.each do |syntax| table.each do |syntax|
it "raises an error when syntax is `#{syntax}`" do context "when expression grammar is #{syntax.inspect}" do
expect { described_class.new(syntax, pipeline).parse_tree } let(:text) { syntax }
it 'aises a statement error exception' do
expect { subject.parse_tree }
.to raise_error described_class::StatementError .to raise_error described_class::StatementError
end end
it 'is an invalid statement' do
expect(subject).not_to be_valid
end
end
end end
end end
...@@ -47,6 +65,10 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do ...@@ -47,6 +65,10 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do
expect(subject.parse_tree) expect(subject.parse_tree)
.to be_a Gitlab::Ci::Pipeline::Expression::Lexeme::Equals .to be_a Gitlab::Ci::Pipeline::Expression::Lexeme::Equals
end end
it 'is a valid statement' do
expect(subject).to be_valid
end
end end
context 'when using a single token' do context 'when using a single token' do
......
...@@ -1896,7 +1896,7 @@ describe Ci::Build do ...@@ -1896,7 +1896,7 @@ describe Ci::Build do
context 'when there are duplicated YAML variables' do context 'when there are duplicated YAML variables' do
before do before do
build.yaml_variables = [{ key: 'MYVAR', value: 'first', public: true }, build.yaml_variables = [{ key: 'MYVAR', value: 'first', public: true },
{ key: 'MYVAR', value: 'second', public: true}] { key: 'MYVAR', value: 'second', public: true }]
end end
it 'keeps the last occurence of a variable by given key' do it 'keeps the last occurence of a variable by given key' do
......
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