diff --git a/lib/gitlab/ci/config/header/root.rb b/lib/gitlab/ci/config/header/root.rb index 251682d13b40107e00e3db546c2b92868b77972f..fc5a6fcd95ab66b0d14697b98f2551c788f2450f 100644 --- a/lib/gitlab/ci/config/header/root.rb +++ b/lib/gitlab/ci/config/header/root.rb @@ -26,7 +26,7 @@ class Root < ::Gitlab::Config::Entry::Node inherit: false, default: {} - def inputs_value + def spec_inputs_value spec_entry.inputs_value end end diff --git a/lib/gitlab/ci/config/interpolation/access.rb b/lib/gitlab/ci/config/interpolation/access.rb index 1a8933c84bf7a0e2a33824d765cbed8104f588ae..5031c5b6de4d5ac134060c00ea47f687600af4ff 100644 --- a/lib/gitlab/ci/config/interpolation/access.rb +++ b/lib/gitlab/ci/config/interpolation/access.rb @@ -49,7 +49,7 @@ def evaluate! key = value.to_sym unless memo.respond_to?(:key?) && memo.key?(key) - break @errors.push("unknown input name provided: `#{key}` in `#{@content}`") + break @errors.push("unknown interpolation provided: `#{key}` in `#{@content}`") end memo.fetch(key) diff --git a/lib/gitlab/ci/config/interpolation/context.rb b/lib/gitlab/ci/config/interpolation/context.rb index 19ea619f7da3758558c79f1015b1e22dfec9f85b..00e924cc6227ded25400052bdd9adff4a3da10d1 100644 --- a/lib/gitlab/ci/config/interpolation/context.rb +++ b/lib/gitlab/ci/config/interpolation/context.rb @@ -16,7 +16,7 @@ class Context attr_reader :variables - def initialize(data, variables: []) + def initialize(data, variables:) @data = data @variables = Ci::Variables::Collection.fabricate(variables) diff --git a/lib/gitlab/ci/config/interpolation/interpolator.rb b/lib/gitlab/ci/config/interpolation/interpolator.rb index 406234cc5fe6c7927510fc8633fd0326bc06b9e7..9a5ce5b415023cd57cfb9a48288eec96be5ce804 100644 --- a/lib/gitlab/ci/config/interpolation/interpolator.rb +++ b/lib/gitlab/ci/config/interpolation/interpolator.rb @@ -8,12 +8,12 @@ module Interpolation # Performs CI config file interpolation, and surfaces all possible interpolation errors. # class Interpolator - attr_reader :config, :args, :variables, :errors + attr_reader :config, :args, :yaml_context, :errors - def initialize(config, args, variables) + def initialize(config, args, yaml_context) @config = config @args = args.nil? ? {} : args - @variables = variables + @yaml_context = yaml_context @errors = [] @interpolated = false end @@ -80,7 +80,7 @@ def content end def spec - @spec ||= header.inputs_value + @spec ||= header.spec_inputs_value end def inputs @@ -88,7 +88,9 @@ def inputs end def context - @context ||= Context.new({ inputs: inputs.to_hash }, variables: variables) + @context ||= Context.new( + { inputs: inputs.to_hash }, variables: yaml_context.variables + ) end def template diff --git a/lib/gitlab/ci/config/yaml/loader.rb b/lib/gitlab/ci/config/yaml/loader.rb index 41e38ce193e5cb1bc17dc5a609ec1a3ad5e175b0..6757c4c55e8929621a1a289d4f4a79b9ba0d1a4d 100644 --- a/lib/gitlab/ci/config/yaml/loader.rb +++ b/lib/gitlab/ci/config/yaml/loader.rb @@ -10,7 +10,7 @@ class Loader AVAILABLE_TAGS = [Config::Yaml::Tags::Reference].freeze MAX_DOCUMENTS = 2 - def initialize(content, inputs: {}, context: nil) + def initialize(content, inputs: {}, context: Config::Yaml::Context.new) @content = content @inputs = inputs @context = context @@ -21,8 +21,7 @@ def load return yaml_result unless yaml_result.valid? - variables = context&.variables || [] - interpolator = Interpolation::Interpolator.new(yaml_result, inputs, variables) + interpolator = Interpolation::Interpolator.new(yaml_result, inputs, context) interpolator.interpolate! diff --git a/lib/gitlab/config/entry/validators.rb b/lib/gitlab/config/entry/validators.rb index c4ddac1daf8096dc7900a3a54e90f9dcebeddfa7..49be2a8925d7e43c0268bcb4c4824ffb5b781b02 100644 --- a/lib/gitlab/config/entry/validators.rb +++ b/lib/gitlab/config/entry/validators.rb @@ -71,6 +71,8 @@ def validate_each(record, attribute, value) class AllowedArrayValuesValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) + return unless value.is_a?(Array) + unknown_values = value - options[:in] unless unknown_values.empty? record.errors.add(attribute, "contains unknown values: " + diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb index c4da8474cb753ce34e9f4f5c5f65997eb621e5d0..f80b1a2ad8e56f26b99283ff8a372f712525c313 100644 --- a/spec/factories/releases.rb +++ b/spec/factories/releases.rb @@ -41,7 +41,8 @@ if instance.project&.catalog_resource association :ci_catalog_resource_version, catalog_resource: instance.project&.catalog_resource, - release: instance + release: instance, + semver: tag end end end diff --git a/spec/lib/gitlab/ci/config/external/file/base_spec.rb b/spec/lib/gitlab/ci/config/external/file/base_spec.rb index c8122e010a90af8a1bd919fadb7d225389b0f37c..7f467c581bd6122dd406262ad3e87b6090e1de91 100644 --- a/spec/lib/gitlab/ci/config/external/file/base_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/base_spec.rb @@ -145,7 +145,7 @@ def initialize(params, ctx) it 'surfaces interpolation errors' do expect(valid?).to be_falsy expect(file.errors) - .to include('`some-location.yml`: unknown input name provided: `abcd` in `inputs.abcd`') + .to include('`some-location.yml`: unknown interpolation provided: `abcd` in `inputs.abcd`') end end diff --git a/spec/lib/gitlab/ci/config/header/root_spec.rb b/spec/lib/gitlab/ci/config/header/root_spec.rb index 55f77137619fc583c2b9858ec63b9fb6868638ea..a481d3f2c096217bb7f3ce82735f9805f6d1f954 100644 --- a/spec/lib/gitlab/ci/config/header/root_spec.rb +++ b/spec/lib/gitlab/ci/config/header/root_spec.rb @@ -109,7 +109,7 @@ it_behaves_like 'an invalid header' end - describe '#inputs_value' do + describe '#spec_inputs_value' do let(:header_hash) do { spec: { @@ -124,7 +124,7 @@ end it 'returns the inputs' do - expect(config.inputs_value).to eq({ + expect(config.spec_inputs_value).to eq({ foo: {}, bar: { default: 'baz' } }) diff --git a/spec/lib/gitlab/ci/config/interpolation/access_spec.rb b/spec/lib/gitlab/ci/config/interpolation/access_spec.rb index 08b7c27a984e85e0f9280da9025b79006b4b1510..3798c7177dd80986f621d68ad757eba9bd7abf02 100644 --- a/spec/lib/gitlab/ci/config/interpolation/access_spec.rb +++ b/spec/lib/gitlab/ci/config/interpolation/access_spec.rb @@ -52,7 +52,7 @@ it 'returns an error' do expect(subject).not_to be_valid - expect(subject.errors.first).to eq('unknown input name provided: `nonexistent` in `inputs.nonexistent`') + expect(subject.errors.first).to eq('unknown interpolation provided: `nonexistent` in `inputs.nonexistent`') end end @@ -61,7 +61,7 @@ it 'returns an error' do expect(subject).not_to be_valid - expect(subject.errors.first).to eq('unknown input name provided: `extra` in `inputs.data.extra`') + expect(subject.errors.first).to eq('unknown interpolation provided: `extra` in `inputs.data.extra`') end end end diff --git a/spec/lib/gitlab/ci/config/interpolation/block_spec.rb b/spec/lib/gitlab/ci/config/interpolation/block_spec.rb index 2aaff356e69084f36da50635fc3193b144a375ba..6616a7b5fadef6f0084b752bfa246654fa3bd989 100644 --- a/spec/lib/gitlab/ci/config/interpolation/block_spec.rb +++ b/spec/lib/gitlab/ci/config/interpolation/block_spec.rb @@ -40,7 +40,7 @@ it 'returns the access error' do expect(subject).not_to be_valid - expect(subject.errors.first).to eq('unknown input name provided: `undefined` in `inputs.undefined`') + expect(subject.errors.first).to eq('unknown interpolation provided: `undefined` in `inputs.undefined`') end end diff --git a/spec/lib/gitlab/ci/config/interpolation/context_spec.rb b/spec/lib/gitlab/ci/config/interpolation/context_spec.rb index 56a572312ebd52f8fe0f3cdb6751aab81d3b634e..908db21ea6898ee8a7c3e6a823fce0c2d632e97a 100644 --- a/spec/lib/gitlab/ci/config/interpolation/context_spec.rb +++ b/spec/lib/gitlab/ci/config/interpolation/context_spec.rb @@ -3,12 +3,14 @@ require 'fast_spec_helper' RSpec.describe Gitlab::Ci::Config::Interpolation::Context, feature_category: :pipeline_composition do - subject { described_class.new(ctx) } + subject { described_class.new(ctx, variables: variables) } let(:ctx) do { inputs: { key: 'abc' } } end + let(:variables) { [] } + describe '.fabricate' do context 'when given an unexpected object' do it 'raises an ArgumentError' do @@ -41,8 +43,52 @@ end it 'raises an exception' do - expect { described_class.new(ctx) } + expect { described_class.new(ctx, variables: variables) } .to raise_error(described_class::ContextTooComplexError) end end + + context 'when variables are provided' do + let(:variables) do + [ + { key: 'VAR1', value: 'value1' }, + { key: 'VAR2', value: 'value2' } + ] + end + + it 'stores variables as a Collection' do + expect(subject.variables).to be_a(Gitlab::Ci::Variables::Collection) + expect(subject.variables.to_hash).to include('VAR1' => 'value1', 'VAR2' => 'value2') + end + end + + describe '#fetch' do + let(:ctx) do + { inputs: { key: 'value' }, component: { name: 'test' } } + end + + it 'fetches a field from the context' do + expect(subject.fetch(:inputs)).to eq({ key: 'value' }) + expect(subject.fetch(:component)).to eq({ name: 'test' }) + end + + it 'raises KeyError when field does not exist' do + expect { subject.fetch(:nonexistent) }.to raise_error(KeyError) + end + end + + describe '#key?' do + let(:ctx) do + { inputs: { key: 'value' }, component: { name: 'test' } } + end + + it 'returns true for existing keys' do + expect(subject.key?(:inputs)).to be true + expect(subject.key?(:component)).to be true + end + + it 'returns false for non-existing keys' do + expect(subject.key?(:nonexistent)).to be false + end + end end diff --git a/spec/lib/gitlab/ci/config/interpolation/interpolator_spec.rb b/spec/lib/gitlab/ci/config/interpolation/interpolator_spec.rb index 45af1c105a90096be8905b3b21c4e0594380aa22..6ed4d70c9abd3831036c9ac571b130fa57304084 100644 --- a/spec/lib/gitlab/ci/config/interpolation/interpolator_spec.rb +++ b/spec/lib/gitlab/ci/config/interpolation/interpolator_spec.rb @@ -6,8 +6,9 @@ let_it_be(:project) { create(:project) } let(:result) { ::Gitlab::Ci::Config::Yaml::Result.new(config: [header, content]) } + let(:yaml_context) { ::Gitlab::Ci::Config::Yaml::Context.new } - subject { described_class.new(result, arguments, []) } + subject { described_class.new(result, arguments, yaml_context) } context 'when input data is valid' do let(:header) do @@ -144,8 +145,8 @@ subject.interpolate! expect(subject).not_to be_valid - expect(subject.errors).to include 'unknown input name provided: `abc` in `inputs.abc`' - expect(subject.error_message).to eq 'unknown input name provided: `abc` in `inputs.abc`' + expect(subject.errors).to include 'unknown interpolation provided: `abc` in `inputs.abc`' + expect(subject.error_message).to eq 'unknown interpolation provided: `abc` in `inputs.abc`' end end @@ -167,7 +168,7 @@ expect(subject).not_to be_valid expect(subject.error_message) - .to eq 'unknown input name provided: `something` in `inputs.something.abc`' + .to eq 'unknown interpolation provided: `something` in `inputs.something.abc`' end end diff --git a/spec/lib/gitlab/ci/config/yaml/context_spec.rb b/spec/lib/gitlab/ci/config/yaml/context_spec.rb index c755fedfa3b5c1b29196ef0d519dd2bcea52461b..eca928f8bc5f51f923d6276b9cf5c237b0a07d57 100644 --- a/spec/lib/gitlab/ci/config/yaml/context_spec.rb +++ b/spec/lib/gitlab/ci/config/yaml/context_spec.rb @@ -22,7 +22,7 @@ context 'without variables provided' do subject(:context) { described_class.new } - it 'defaults to empty hash' do + it 'defaults to empty array' do expect(context.variables).to eq([]) end end diff --git a/spec/models/ci/catalog/resource_spec.rb b/spec/models/ci/catalog/resource_spec.rb index b48b6ee422ddfa0c19ec6112ec30c5e3bbba0c35..4e864f85993b62a1220a0255e371caa3c68b88b9 100644 --- a/spec/models/ci/catalog/resource_spec.rb +++ b/spec/models/ci/catalog/resource_spec.rb @@ -354,21 +354,13 @@ let_it_be(:resource) { create(:ci_catalog_resource, project: project) } let_it_be_with_refind(:january_release) do - release = create(:release, :with_catalog_resource_version, project: project, tag: 'v1', + create(:release, :with_catalog_resource_version, project: project, tag: '1.0.0', released_at: '2023-01-01T00:00:00Z') - - release.catalog_resource_version.update!(semver: '1.0.0') - - release end let_it_be_with_refind(:february_release) do - release = create(:release, :with_catalog_resource_version, project: project, tag: 'v2', + create(:release, :with_catalog_resource_version, project: project, tag: '2.0.0', released_at: '2023-02-01T00:00:00Z') - - release.catalog_resource_version.update!(semver: '2.0.0') - - release end it 'has the expected latest_released_at value' do @@ -377,11 +369,9 @@ context 'when a new catalog resource version is created' do it 'updates the latest_released_at value' do - march_release = create(:release, :with_catalog_resource_version, project: project, tag: 'v3', + march_release = create(:release, :with_catalog_resource_version, project: project, tag: '3.0.0', released_at: '2023-03-01T00:00:00Z') - march_release.catalog_resource_version.update!(semver: '3.0.0') - expect(resource.reload.latest_released_at).to eq(march_release.released_at) end end diff --git a/spec/models/ci/catalog/resources/version_spec.rb b/spec/models/ci/catalog/resources/version_spec.rb index 5cae116bdafd797883070abb2cb885d1e45ea310..53bb654ef58b86b436713108af29ca7194db6e44 100644 --- a/spec/models/ci/catalog/resources/version_spec.rb +++ b/spec/models/ci/catalog/resources/version_spec.rb @@ -214,7 +214,8 @@ let_it_be(:resource) { create(:ci_catalog_resource, project: project) } let_it_be_with_reload(:release) do - create(:release, :with_catalog_resource_version, project: project, tag: 'v1', released_at: '2023-01-01T00:00:00Z') + create(:release, :with_catalog_resource_version, project: project, tag: '1.2.3', + released_at: '2023-01-01T00:00:00Z') end let(:version) { release.catalog_resource_version } diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb index 21cfbf3b8496c20d0c2bbf0e48b55165fcc34c81..49c2d4f3c7984fcee93479e49a29eecd27855d14 100644 --- a/spec/models/release_spec.rb +++ b/spec/models/release_spec.rb @@ -380,7 +380,8 @@ let_it_be(:resource) { create(:ci_catalog_resource, project: project) } let_it_be_with_reload(:release) do - create(:release, :with_catalog_resource_version, project: project, tag: 'v1', released_at: '2023-01-01T00:00:00Z') + create(:release, :with_catalog_resource_version, project: project, tag: 'v1.2.3', + released_at: '2023-01-01T00:00:00Z') end let(:version) { release.catalog_resource_version } diff --git a/spec/services/ci/create_pipeline_service/including_ci_components_spec.rb b/spec/services/ci/create_pipeline_service/including_ci_components_spec.rb index b4f931b11af166ff8f52b78a49f66e020cd707d3..19d9a43d4f21e7e98029a047d8a00d1f61022ce5 100644 --- a/spec/services/ci/create_pipeline_service/including_ci_components_spec.rb +++ b/spec/services/ci/create_pipeline_service/including_ci_components_spec.rb @@ -112,7 +112,7 @@ expect(pipeline).to be_persisted expect(pipeline.error_messages[0].content) - .to include 'unknown input name provided: `suite`' + .to include 'unknown interpolation provided: `suite`' end end