diff --git a/app/graphql/types/ci/config_variable_type.rb b/app/graphql/types/ci/config_variable_type.rb index 87ae026c2c1dafd4004646e6e062def7024f3b3b..5b5890fd5a56c806618c3ec8a632aaf260d77011 100644 --- a/app/graphql/types/ci/config_variable_type.rb +++ b/app/graphql/types/ci/config_variable_type.rb @@ -17,6 +17,10 @@ class ConfigVariableType < BaseObject # rubocop:disable Graphql/AuthorizeTypes field :value, GraphQL::Types::String, null: true, description: 'Value of the variable.' + + field :value_options, [GraphQL::Types::String], + null: true, + description: 'Value options for the variable.' end end end diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index bd9905ca652a0293fdb60fc95207a12d8184f522..f547326d8189478cfcdd486beda6360d07e4e2bc 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -10405,6 +10405,7 @@ CI/CD config variables. | `description` | [`String`](#string) | Description for the CI/CD config variable. | | `key` | [`String`](#string) | Name of the variable. | | `value` | [`String`](#string) | Value of the variable. | +| `valueOptions` | [`[String!]`](#string) | Value options for the variable. | ### `CiGroup` diff --git a/lib/gitlab/ci/config/entry/root.rb b/lib/gitlab/ci/config/entry/root.rb index b1b42cad74c34f080fba1973d8c433a04ed96a3b..1d7d8617c747806f69f7f720636d2dbbcea9df02 100644 --- a/lib/gitlab/ci/config/entry/root.rb +++ b/lib/gitlab/ci/config/entry/root.rb @@ -50,7 +50,7 @@ class Root < ::Gitlab::Config::Entry::Node entry :variables, Entry::Variables, description: 'Environment variables that will be used.', - metadata: { allowed_value_data: %i[value description] }, + metadata: { allowed_value_data: %i[value description], allow_array_value: true }, reserved: true entry :stages, Entry::Stages, diff --git a/lib/gitlab/ci/config/entry/variable.rb b/lib/gitlab/ci/config/entry/variable.rb index 253888aadebeb3319680d90bb3fb45ba2bac8dd0..54c153c8b07dfbd26189ff8cc2d2b5203a4ed73c 100644 --- a/lib/gitlab/ci/config/entry/variable.rb +++ b/lib/gitlab/ci/config/entry/variable.rb @@ -10,6 +10,7 @@ module Entry class Variable < ::Gitlab::Config::Entry::Simplifiable strategy :SimpleVariable, if: -> (config) { SimpleVariable.applies_to?(config) } strategy :ComplexVariable, if: -> (config) { ComplexVariable.applies_to?(config) } + strategy :ComplexArrayVariable, if: -> (config) { ComplexArrayVariable.applies_to?(config) } class SimpleVariable < ::Gitlab::Config::Entry::Node include ::Gitlab::Config::Entry::Validatable @@ -39,7 +40,7 @@ class ComplexVariable < ::Gitlab::Config::Entry::Node class << self def applies_to?(config) - config.is_a?(Hash) + config.is_a?(Hash) && !config[:value].is_a?(Array) end end @@ -86,6 +87,34 @@ def config_description_defined? end end + class ComplexArrayVariable < ComplexVariable + include ::Gitlab::Config::Entry::Validatable + + class << self + def applies_to?(config) + config.is_a?(Hash) && config[:value].is_a?(Array) + end + end + + validations do + validates :config_value, array_of_strings: true, allow_nil: false, if: :config_value_defined? + + validate do + next if opt(:allow_array_value) + + errors.add(:config, 'value must be an alphanumeric string') + end + end + + def value + config_value.first + end + + def value_with_data + super.merge(value_options: config_value).compact + end + end + class UnknownStrategy < ::Gitlab::Config::Entry::Node def errors ["variable definition must be either a string or a hash"] diff --git a/lib/gitlab/ci/config/entry/variables.rb b/lib/gitlab/ci/config/entry/variables.rb index 7fa0ee000667143a1a614119fe01a5ed75fb504a..4430a11dda73bd8b7b550f9ebed141871a67e756 100644 --- a/lib/gitlab/ci/config/entry/variables.rb +++ b/lib/gitlab/ci/config/entry/variables.rb @@ -36,7 +36,7 @@ def composable_class(_name, _config) end def composable_metadata - { allowed_value_data: opt(:allowed_value_data) } + { allowed_value_data: opt(:allowed_value_data), allow_array_value: opt(:allow_array_value) } end end end diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb index 64438a1594a46e66b9541dbf8747bf547f43bb79..a55e13e7c2d9eb70139cf25d2c011212c7708876 100644 --- a/spec/lib/gitlab/ci/config/entry/root_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb @@ -34,7 +34,11 @@ image: 'image:1.0', default: {}, services: ['postgres:9.1', 'mysql:5.5'], - variables: { VAR: 'root', VAR2: { value: 'val 2', description: 'this is var 2' } }, + variables: { + VAR: 'root', + VAR2: { value: 'val 2', description: 'this is var 2' }, + VAR3: { value: %w[val3 val3b], description: 'this is var 3' } + }, after_script: ['make clean'], stages: %w(build pages release), cache: { key: 'k', untracked: true, paths: ['public/'] }, @@ -83,7 +87,7 @@ end it 'sets correct variables value' do - expect(root.variables_value).to eq('VAR' => 'root', 'VAR2' => 'val 2') + expect(root.variables_value).to eq('VAR' => 'root', 'VAR2' => 'val 2', 'VAR3' => 'val3') end describe '#leaf?' do diff --git a/spec/lib/gitlab/ci/config/entry/variable_spec.rb b/spec/lib/gitlab/ci/config/entry/variable_spec.rb index 744a89d45095103be9489d9e7f145c18db59ab8e..076a5b32e92ba6ef004a201738c10164eae81e0f 100644 --- a/spec/lib/gitlab/ci/config/entry/variable_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/variable_spec.rb @@ -127,20 +127,6 @@ end end - context 'when config value is an array' do - let(:config) { { value: ['value'], description: 'description' } } - - describe '#valid?' do - it { is_expected.not_to be_valid } - end - - describe '#errors' do - subject(:errors) { entry.errors } - - it { is_expected.to include 'var1 config value must be an alphanumeric string' } - end - end - context 'when config description is a symbol' do let(:config) { { value: 'value', description: :description } } @@ -209,4 +195,42 @@ end end end + + describe 'ComplexArrayVariable' do + context 'when allow_array_value metadata is false' do + let(:config) { { value: %w[value value2], description: 'description' } } + let(:metadata) { { allow_array_value: false } } + + describe '#valid?' do + it { is_expected.not_to be_valid } + end + + describe '#errors' do + subject(:errors) { entry.errors } + + it { is_expected.to include 'var1 config value must be an alphanumeric string' } + end + end + + context 'when allow_array_value metadata is true' do + let(:config) { { value: %w[value value2], description: 'description' } } + let(:metadata) { { allowed_value_data: %i[value description], allow_array_value: true } } + + describe '#valid?' do + it { is_expected.to be_valid } + end + + describe '#value' do + subject(:value) { entry.value } + + it { is_expected.to eq('value') } + end + + describe '#value_with_data' do + subject(:value_with_data) { entry.value_with_data } + + it { is_expected.to eq(value: 'value', description: 'description', value_options: %w[value value2]) } + end + end + end end diff --git a/spec/lib/gitlab/ci/config/entry/variables_spec.rb b/spec/lib/gitlab/ci/config/entry/variables_spec.rb index ad7290d058960f2c7f29e6bd5d6d8ba20216c4ea..085f304094e4f1553b84ca43a458a4626768056e 100644 --- a/spec/lib/gitlab/ci/config/entry/variables_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/variables_spec.rb @@ -98,6 +98,62 @@ it_behaves_like 'invalid config', /must be either a string or a hash/ end + context 'when entry config value has unallowed value key-value pair and value is a string' do + let(:config) do + { 'VARIABLE_1' => { value: 'value', description: 'variable 1' } } + end + + context 'when there is no allowed_value_data metadata' do + it_behaves_like 'invalid config', /variable_1 config must be a string/ + end + + context 'when metadata has allow_array_value and allowed_value_data' do + let(:metadata) { { allowed_value_data: %i[value description], allow_array_value: true } } + + let(:result) do + { 'VARIABLE_1' => 'value' } + end + + it_behaves_like 'valid config' + + describe '#value_with_data' do + it 'returns variable with data' do + expect(entry.value_with_data).to eq( + 'VARIABLE_1' => { value: 'value', description: 'variable 1' } + ) + end + end + end + end + + context 'when entry config value has key-value pair and value is an array' do + let(:config) do + { 'VARIABLE_1' => { value: %w[value1 value2], description: 'variable 1' } } + end + + context 'when there is no allowed_value_data metadata' do + it_behaves_like 'invalid config', /variable_1 config value must be an alphanumeric string/ + end + + context 'when metadata has allow_array_value and allowed_value_data' do + let(:metadata) { { allowed_value_data: %i[value description], allow_array_value: true } } + + let(:result) do + { 'VARIABLE_1' => 'value1' } + end + + it_behaves_like 'valid config' + + describe '#value_with_data' do + it 'returns variable with data' do + expect(entry.value_with_data).to eq( + 'VARIABLE_1' => { value: 'value1', value_options: %w[value1 value2], description: 'variable 1' } + ) + end + end + end + end + context 'when entry config value has key-value pair and hash' do let(:config) do { 'VARIABLE_1' => { value: 'value 1', description: 'variable 1' }, diff --git a/spec/requests/api/graphql/ci/config_variables_spec.rb b/spec/requests/api/graphql/ci/config_variables_spec.rb index 2b5a5d0dc931c9b0b1ed534c70a37923b9df542c..17133d7ea6605ae8a5e03e3921b64c2e6b74250d 100644 --- a/spec/requests/api/graphql/ci/config_variables_spec.rb +++ b/spec/requests/api/graphql/ci/config_variables_spec.rb @@ -23,6 +23,7 @@ ciConfigVariables(sha: "#{sha}") { key value + valueOptions description } } @@ -52,14 +53,22 @@ post_graphql(query, current_user: user) expect(graphql_data.dig('project', 'ciConfigVariables')).to contain_exactly( + { + 'key' => 'KEY_VALUE_VAR', + 'value' => 'value x', + 'valueOptions' => nil, + 'description' => 'value of KEY_VALUE_VAR' + }, { 'key' => 'DB_NAME', 'value' => 'postgres', + 'valueOptions' => nil, 'description' => nil }, { 'key' => 'ENVIRONMENT_VAR', 'value' => 'env var value', + 'valueOptions' => ['env var value', 'env var value2'], 'description' => 'env var description' } ) diff --git a/spec/support/gitlab_stubs/gitlab_ci.yml b/spec/support/gitlab_stubs/gitlab_ci.yml index b6a66cfa2c62f1cf118d1fde7093d49b19e82abe..945235917654b3d1276e743318e78db054eef723 100644 --- a/spec/support/gitlab_stubs/gitlab_ci.yml +++ b/spec/support/gitlab_stubs/gitlab_ci.yml @@ -7,9 +7,12 @@ before_script: - bundle exec rake db:create variables: + KEY_VALUE_VAR: + value: 'value x' + description: 'value of KEY_VALUE_VAR' DB_NAME: postgres ENVIRONMENT_VAR: - value: 'env var value' + value: ['env var value', 'env var value2'] description: 'env var description' stages: