diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 026a5ac97ca..130f5b0892e 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -12,18 +12,14 @@ module Ci attr_reader :before_script, :after_script, :image, :services, :path, :cache def initialize(config, path = nil) - @config = YAML.safe_load(config, [Symbol], [], true) + @config = Gitlab::Ci::Config.new(config).to_hash @path = path - unless @config.is_a? Hash - raise ValidationError, "YAML should be a hash" - end - - @config = @config.deep_symbolize_keys - initial_parsing validate! + rescue Gitlab::Ci::Config::Loader::FormatError => e + raise ValidationError, e.message end def builds_for_stage_and_ref(stage, ref, tag = false, trigger_request = nil) diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb new file mode 100644 index 00000000000..ffe633d4b63 --- /dev/null +++ b/lib/gitlab/ci/config.rb @@ -0,0 +1,16 @@ +module Gitlab + module Ci + class Config + class LoaderError < StandardError; end + + def initialize(config) + loader = Loader.new(config) + @config = loader.load! + end + + def to_hash + @config + end + end + end +end diff --git a/lib/gitlab/ci/config/loader.rb b/lib/gitlab/ci/config/loader.rb new file mode 100644 index 00000000000..dbf6eb0edbe --- /dev/null +++ b/lib/gitlab/ci/config/loader.rb @@ -0,0 +1,25 @@ +module Gitlab + module Ci + class Config + class Loader + class FormatError < StandardError; end + + def initialize(config) + @config = YAML.safe_load(config, [Symbol], [], true) + end + + def valid? + @config.is_a?(Hash) + end + + def load! + unless valid? + raise FormatError, 'Invalid configuration format' + end + + @config.deep_symbolize_keys + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/config/loader_spec.rb b/spec/lib/gitlab/ci/config/loader_spec.rb new file mode 100644 index 00000000000..2d44b1f60f1 --- /dev/null +++ b/spec/lib/gitlab/ci/config/loader_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Gitlab::Ci::Config::Loader do + let(:loader) { described_class.new(yml) } + + context 'when yaml syntax is correct' do + let(:yml) { 'image: ruby:2.2' } + + describe '#valid?' do + it 'returns true' do + expect(loader.valid?).to be true + end + end + + describe '#load!' do + it 'returns a valid hash' do + expect(loader.load!).to eq(image: 'ruby:2.2') + end + end + end + + context 'when yaml syntax is incorrect' do + let(:yml) { '// incorrect' } + + describe '#valid?' do + it 'returns false' do + expect(loader.valid?).to be false + end + end + + describe '#load!' do + it 'raises error' do + expect { loader.load! }.to raise_error( + Gitlab::Ci::Config::Loader::FormatError, + 'Invalid configuration format' + ) + end + end + end + + context 'when yaml config is empty' do + let(:yml) { '' } + + describe '#valid?' do + it 'returns false' do + expect(loader.valid?).to be false + end + end + end +end diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb new file mode 100644 index 00000000000..4d46abe520f --- /dev/null +++ b/spec/lib/gitlab/ci/config_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +describe Gitlab::Ci::Config do + let(:config) do + described_class.new(yml) + end + + context 'when config is valid' do + let(:yml) do + <<-EOS + image: ruby:2.2 + + rspec: + script: + - gem install rspec + - rspec + EOS + end + + describe '#to_hash' do + it 'returns hash created from string' do + hash = { + image: 'ruby:2.2', + rspec: { + script: ['gem install rspec', + 'rspec'] + } + } + + expect(config.to_hash).to eq hash + end + end + + context 'when config is invalid' do + let(:yml) { '// invalid' } + + describe '.new' do + it 'raises error' do + expect { config }.to raise_error( + Gitlab::Ci::Config::Loader::FormatError, + /Invalid configuration format/ + ) + end + end + end + end +end