diff options
author | Dylan Griffith <dyl.griffith@gmail.com> | 2018-10-15 18:21:14 +0300 |
---|---|---|
committer | Dylan Griffith <dyl.griffith@gmail.com> | 2018-10-15 18:21:14 +0300 |
commit | 9e74d71a0de27d7f227dc7c7cd144faa0c8bc95c (patch) | |
tree | a09b5270dd86b68fe99e8f1d7e10c9ca1a0ac30a | |
parent | a9827357186e38e5732d8dae23d9d02b1f4c7218 (diff) |
Add ignore_if_missing key to include in CI YML (#52680)52680-include-ignore-if-missing
-rw-r--r-- | lib/gitlab/ci/config.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/ci/external/file/local.rb | 11 | ||||
-rw-r--r-- | lib/gitlab/ci/external/mapper.rb | 42 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config_spec.rb | 68 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/external/mapper_spec.rb | 91 |
5 files changed, 193 insertions, 21 deletions
diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb index fe98d25af29..6948bd01923 100644 --- a/lib/gitlab/ci/config.rb +++ b/lib/gitlab/ci/config.rb @@ -17,6 +17,8 @@ module Gitlab raise Config::ConfigError, e.message rescue ::Gitlab::Ci::External::Processor::FileError => e raise ::Gitlab::Ci::YamlProcessor::ValidationError, e.message + rescue ::Gitlab::Ci::External::Mapper::IncludeError => e + raise ::Gitlab::Ci::YamlProcessor::ValidationError, e.message end def valid? diff --git a/lib/gitlab/ci/external/file/local.rb b/lib/gitlab/ci/external/file/local.rb index 1aa7f687507..2b0a5487fe4 100644 --- a/lib/gitlab/ci/external/file/local.rb +++ b/lib/gitlab/ci/external/file/local.rb @@ -5,13 +5,14 @@ module Gitlab module External module File class Local < Base - attr_reader :location, :project, :sha + attr_reader :location, :project, :sha, :ignore_if_missing def initialize(location, opts = {}) super @project = opts.fetch(:project) @sha = opts.fetch(:sha) + @ignore_if_missing = opts.fetch(:ignore_if_missing) end def content @@ -25,7 +26,13 @@ module Gitlab private def fetch_local_content - project.repository.blob_data_at(sha, location) + content = project.repository.blob_data_at(sha, location) + + if content.nil? && @ignore_if_missing + return '{}' + end + + content end end end diff --git a/lib/gitlab/ci/external/mapper.rb b/lib/gitlab/ci/external/mapper.rb index 58bd6a19acf..cb79dff58b2 100644 --- a/lib/gitlab/ci/external/mapper.rb +++ b/lib/gitlab/ci/external/mapper.rb @@ -4,28 +4,56 @@ module Gitlab module Ci module External class Mapper + IncludeError = Class.new(StandardError) + def initialize(values, project, sha) - @locations = Array(values.fetch(:include, [])) + @values = values @project = project @sha = sha end def process - locations.map { |location| build_external_file(location) } + included = @values[:include] + + return [] if included.nil? + + if string_or_array_of_strings?(included) + included = Array(included).map do |path| + { + path: path, + ignore_if_missing: false + } + end + elsif included.is_a?(Hash) + included = [included] + end + + included.map {|i| build_external_file(i) } end private - attr_reader :locations, :project, :sha - - def build_external_file(location) + def build_external_file(included) + location = included.fetch(:path) if ::Gitlab::UrlSanitizer.valid?(location) + if included.fetch(:ignore_if_missing) + raise IncludeError, 'ignore_if_missing must be false or not included for remote files' + end + Gitlab::Ci::External::File::Remote.new(location) else - options = { project: project, sha: sha } - Gitlab::Ci::External::File::Local.new(location, options) + Gitlab::Ci::External::File::Local.new( + location, + project: @project, + sha: @sha, + ignore_if_missing: included.fetch(:ignore_if_missing, false) + ) end end + + def string_or_array_of_strings?(value) + value.is_a?(String) || (value.is_a?(Array) && value[0].is_a?(String)) + end end end end diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb index b43aca8a354..7d08c90c284 100644 --- a/spec/lib/gitlab/ci/config_spec.rb +++ b/spec/lib/gitlab/ci/config_spec.rb @@ -1,6 +1,4 @@ -require 'fast_spec_helper' - -require_dependency 'active_model' +require 'spec_helper' describe Gitlab::Ci::Config do let(:config) do @@ -193,6 +191,70 @@ describe Gitlab::Ci::Config do end end + context 'when ignore_if_missing true' do + let(:gitlab_ci_yml) do + <<~HEREDOC + include: + path: #{local_location} + ignore_if_missing: true + + + image: ruby:2.2 + HEREDOC + end + + context 'when remote url' do + let(:gitlab_ci_yml) do + <<~HEREDOC + include: + path: #{remote_location} + ignore_if_missing: true + + + image: ruby:2.2 + HEREDOC + end + + it 'raises an error' do + expect { config }.to raise_error( + ::Gitlab::Ci::YamlProcessor::ValidationError, + "ignore_if_missing must be false for remote files" + ) + end + end + + context 'when missing' do + before do + allow(project.repository) + .to receive(:blob_data_at) + .and_return(nil) + end + + it 'does nothing' do + expect(config.to_hash).to eq({ + image: 'ruby:2.2' + }) + end + end + + context 'when present' do + it 'does is included' do + expect(config.to_hash).to eq({ + image: 'ruby:2.2', + before_script: [ + "apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs", "ruby -v", + "which ruby", + "gem install bundler --no-ri --no-rdoc", + "bundle install --jobs $(nproc) \"${FLAGS[@]}\"" + ], + rspec: { + script: [ 'bundle exec rspec' ] + } + }) + end + end + end + context "when gitlab_ci.yml has invalid 'include' defined" do let(:gitlab_ci_yml) do <<~HEREDOC diff --git a/spec/lib/gitlab/ci/external/mapper_spec.rb b/spec/lib/gitlab/ci/external/mapper_spec.rb index d925d6af73d..6953a2ff464 100644 --- a/spec/lib/gitlab/ci/external/mapper_spec.rb +++ b/spec/lib/gitlab/ci/external/mapper_spec.rb @@ -26,7 +26,7 @@ describe Gitlab::Ci::External::Mapper do expect(subject).to be_an(Array) end - it 'returns File instances' do + it 'returns File::Local instances' do expect(subject.first).to be_an_instance_of(Gitlab::Ci::External::File::Local) end end @@ -40,10 +40,6 @@ describe Gitlab::Ci::External::Mapper do } end - before do - WebMock.stub_request(:get, remote_url).to_return(body: file_content) - end - it 'returns an array' do expect(subject).to be_an(Array) end @@ -54,6 +50,87 @@ describe Gitlab::Ci::External::Mapper do end end + context 'when include is a hash' do + context 'when ignore_if_missing is true' do + context 'when using a local file' do + let(:values) do + { + include: { + path: '/path1', + ignore_if_missing: true + } + } + end + + it 'returns expected File::Local instances' do + expect(subject.first).to be_an_instance_of(Gitlab::Ci::External::File::Local) + expect(subject.first.ignore_if_missing).to eq(true) + end + end + + context 'when using a remote file' do + let(:values) do + { + include: { + path: 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1235/.gitlab-ci-1.yml', + ignore_if_missing: true + } + } + end + + it 'should raise IncludeError' do + expect { subject } + .to raise_error( + Gitlab::Ci::External::Mapper::IncludeError, + 'ignore_if_missing must be false or not included for remote files' + ) + end + end + + context 'when using a local file' do + let(:values) do + { + include: '', + } + end + end + end + end + + context 'when include is an array of hashes' do + let(:values) do + { + include: [ + { + path: '/path1', + ignore_if_missing: true + }, + { + path: '/path2', + ignore_if_missing: false + }, + { + path: '/path3' + } + ] + } + end + + it 'returns expected File::Local instances' do + expect(subject[0]).to be_an_instance_of(Gitlab::Ci::External::File::Local) + expect(subject[0].location).to eq('/path1') + expect(subject[0].ignore_if_missing).to eq(true) + + expect(subject[1]).to be_an_instance_of(Gitlab::Ci::External::File::Local) + expect(subject[1].location).to eq('/path2') + expect(subject[1].ignore_if_missing).to eq(false) + + expect(subject[2]).to be_an_instance_of(Gitlab::Ci::External::File::Local) + expect(subject[2].location).to eq('/path3') + expect(subject[2].ignore_if_missing).to eq(false) + end + end + context "when 'include' is defined as an array" do let(:remote_url) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' } let(:values) do @@ -67,10 +144,6 @@ describe Gitlab::Ci::External::Mapper do } end - before do - WebMock.stub_request(:get, remote_url).to_return(body: file_content) - end - it 'returns an array' do expect(subject).to be_an(Array) end |