diff options
Diffstat (limited to 'spec/lib/gitlab/ci/config')
11 files changed, 399 insertions, 120 deletions
diff --git a/spec/lib/gitlab/ci/config/entry/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb index e810d65d560..e16a9a7a74a 100644 --- a/spec/lib/gitlab/ci/config/entry/image_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb @@ -6,11 +6,11 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do let(:entry) { described_class.new(config) } context 'when configuration is a string' do - let(:config) { 'ruby:2.7' } + let(:config) { 'image:1.0' } describe '#value' do it 'returns image hash' do - expect(entry.value).to eq({ name: 'ruby:2.7' }) + expect(entry.value).to eq({ name: 'image:1.0' }) end end @@ -28,7 +28,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do describe '#image' do it "returns image's name" do - expect(entry.name).to eq 'ruby:2.7' + expect(entry.name).to eq 'image:1.0' end end @@ -46,7 +46,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do end context 'when configuration is a hash' do - let(:config) { { name: 'ruby:2.7', entrypoint: %w(/bin/sh run) } } + let(:config) { { name: 'image:1.0', entrypoint: %w(/bin/sh run) } } describe '#value' do it 'returns image hash' do @@ -68,7 +68,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do describe '#image' do it "returns image's name" do - expect(entry.name).to eq 'ruby:2.7' + expect(entry.name).to eq 'image:1.0' end end @@ -80,7 +80,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do context 'when configuration has ports' do let(:ports) { [{ number: 80, protocol: 'http', name: 'foobar' }] } - let(:config) { { name: 'ruby:2.7', entrypoint: %w(/bin/sh run), ports: ports } } + let(:config) { { name: 'image:1.0', entrypoint: %w(/bin/sh run), ports: ports } } let(:entry) { described_class.new(config, with_image_ports: image_ports) } let(:image_ports) { false } @@ -112,7 +112,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do end context 'when entry value is not correct' do - let(:config) { ['ruby:2.7'] } + let(:config) { ['image:1.0'] } describe '#errors' do it 'saves errors' do @@ -129,7 +129,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do end context 'when unexpected key is specified' do - let(:config) { { name: 'ruby:2.7', non_existing: 'test' } } + let(:config) { { name: 'image:1.0', non_existing: 'test' } } describe '#errors' do it 'saves errors' do diff --git a/spec/lib/gitlab/ci/config/entry/reports/coverage_report_spec.rb b/spec/lib/gitlab/ci/config/entry/reports/coverage_report_spec.rb index 588f53150ff..0fd9a83a4fa 100644 --- a/spec/lib/gitlab/ci/config/entry/reports/coverage_report_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/reports/coverage_report_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'fast_spec_helper' +require 'spec_helper' RSpec.describe Gitlab::Ci::Config::Entry::Reports::CoverageReport do let(:entry) { described_class.new(config) } diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb index daf58aff116..b9c32bc51be 100644 --- a/spec/lib/gitlab/ci/config/entry/root_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb @@ -31,7 +31,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do let(:hash) do { before_script: %w(ls pwd), - image: 'ruby:2.7', + image: 'image:1.0', default: {}, services: ['postgres:9.1', 'mysql:5.5'], variables: { VAR: 'root', VAR2: { value: 'val 2', description: 'this is var 2' } }, @@ -154,7 +154,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do { name: :rspec, script: %w[rspec ls], before_script: %w(ls pwd), - image: { name: 'ruby:2.7' }, + image: { name: 'image:1.0' }, services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }], stage: 'test', cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }], @@ -169,7 +169,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do { name: :spinach, before_script: [], script: %w[spinach], - image: { name: 'ruby:2.7' }, + image: { name: 'image:1.0' }, services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }], stage: 'test', cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }], @@ -186,7 +186,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do before_script: [], script: ["make changelog | tee release_changelog.txt"], release: { name: "Release $CI_TAG_NAME", tag_name: 'v0.06', description: "./release_changelog.txt" }, - image: { name: "ruby:2.7" }, + image: { name: "image:1.0" }, services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }], cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success' }], only: { refs: %w(branches tags) }, @@ -206,7 +206,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do { before_script: %w(ls pwd), after_script: ['make clean'], default: { - image: 'ruby:2.7', + image: 'image:1.0', services: ['postgres:9.1', 'mysql:5.5'] }, variables: { VAR: 'root' }, @@ -233,7 +233,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do rspec: { name: :rspec, script: %w[rspec ls], before_script: %w(ls pwd), - image: { name: 'ruby:2.7' }, + image: { name: 'image:1.0' }, services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }], stage: 'test', cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }], @@ -246,7 +246,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do spinach: { name: :spinach, before_script: [], script: %w[spinach], - image: { name: 'ruby:2.7' }, + image: { name: 'image:1.0' }, services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }], stage: 'test', cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }], diff --git a/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb b/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb index b59fc95a8cc..9da8d106862 100644 --- a/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb @@ -4,8 +4,9 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Config::External::File::Artifact do let(:parent_pipeline) { create(:ci_pipeline) } + let(:variables) {} let(:context) do - Gitlab::Ci::Config::External::Context.new(parent_pipeline: parent_pipeline) + Gitlab::Ci::Config::External::Context.new(variables: variables, parent_pipeline: parent_pipeline) end let(:external_file) { described_class.new(params, context) } @@ -29,14 +30,15 @@ RSpec.describe Gitlab::Ci::Config::External::File::Artifact do end describe '#valid?' do - shared_examples 'is invalid' do - it 'is not valid' do - expect(external_file).not_to be_valid - end + subject(:valid?) do + external_file.validate! + external_file.valid? + end + shared_examples 'is invalid' do it 'sets the expected error' do - expect(external_file.errors) - .to contain_exactly(expected_error) + expect(valid?).to be_falsy + expect(external_file.errors).to contain_exactly(expected_error) end end @@ -148,7 +150,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Artifact do context 'when file is not empty' do it 'is valid' do - expect(external_file).to be_valid + expect(valid?).to be_truthy expect(external_file.content).to be_present end @@ -160,6 +162,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Artifact do user: anything } expect(context).to receive(:mutate).with(expected_attrs).and_call_original + external_file.validate! external_file.content end end @@ -168,6 +171,58 @@ RSpec.describe Gitlab::Ci::Config::External::File::Artifact do end end end + + context 'when job is provided as a variable' do + let(:variables) do + Gitlab::Ci::Variables::Collection.new([ + { key: 'VAR1', value: 'a_secret_variable_value', masked: true } + ]) + end + + let(:params) { { artifact: 'generated.yml', job: 'a_secret_variable_value' } } + + context 'when job does not exist in the parent pipeline' do + let(:expected_error) do + 'Job `xxxxxxxxxxxxxxxxxxxxxxx` not found in parent pipeline or does not have artifacts!' + end + + it_behaves_like 'is invalid' + end + end + end + end + + describe '#metadata' do + let(:params) { { artifact: 'generated.yml' } } + + subject(:metadata) { external_file.metadata } + + it { + is_expected.to eq( + context_project: nil, + context_sha: nil, + type: :artifact, + location: 'generated.yml', + extra: { job_name: nil } + ) + } + + context 'when job name includes a masked variable' do + let(:variables) do + Gitlab::Ci::Variables::Collection.new([{ key: 'VAR1', value: 'a_secret_variable_value', masked: true }]) + end + + let(:params) { { artifact: 'generated.yml', job: 'a_secret_variable_value' } } + + it { + is_expected.to eq( + context_project: nil, + context_sha: nil, + type: :artifact, + location: 'generated.yml', + extra: { job_name: 'xxxxxxxxxxxxxxxxxxxxxxx' } + ) + } 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 536f48ecba6..280bebe1a7c 100644 --- a/spec/lib/gitlab/ci/config/external/file/base_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/base_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Base do end end - subject { test_class.new(location, context) } + subject(:file) { test_class.new(location, context) } before do allow_any_instance_of(test_class) @@ -32,7 +32,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Base do let(:location) { 'some-location' } it 'returns true' do - expect(subject).to be_matching + expect(file).to be_matching end end @@ -40,40 +40,45 @@ RSpec.describe Gitlab::Ci::Config::External::File::Base do let(:location) { nil } it 'returns false' do - expect(subject).not_to be_matching + expect(file).not_to be_matching end end end describe '#valid?' do + subject(:valid?) do + file.validate! + file.valid? + end + context 'when location is not a string' do let(:location) { %w(some/file.txt other/file.txt) } - it { is_expected.not_to be_valid } + it { is_expected.to be_falsy } end context 'when location is not a YAML file' do let(:location) { 'some/file.txt' } - it { is_expected.not_to be_valid } + it { is_expected.to be_falsy } end context 'when location has not a valid naming scheme' do let(:location) { 'some/file/.yml' } - it { is_expected.not_to be_valid } + it { is_expected.to be_falsy } end context 'when location is a valid .yml extension' do let(:location) { 'some/file/config.yml' } - it { is_expected.to be_valid } + it { is_expected.to be_truthy } end context 'when location is a valid .yaml extension' do let(:location) { 'some/file/config.yaml' } - it { is_expected.to be_valid } + it { is_expected.to be_truthy } end context 'when there are YAML syntax errors' do @@ -86,8 +91,8 @@ RSpec.describe Gitlab::Ci::Config::External::File::Base do end it 'is not a valid file' do - expect(subject).not_to be_valid - expect(subject.error_message).to eq('Included file `some/file/xxxxxxxxxxxxxxxx.yml` does not have valid YAML syntax!') + expect(valid?).to be_falsy + expect(file.error_message).to eq('Included file `some/file/xxxxxxxxxxxxxxxx.yml` does not have valid YAML syntax!') end end end @@ -103,8 +108,56 @@ RSpec.describe Gitlab::Ci::Config::External::File::Base do end it 'does expand hash to include the template' do - expect(subject.to_hash).to include(:before_script) + expect(file.to_hash).to include(:before_script) end end end + + describe '#metadata' do + let(:location) { 'some/file/config.yml' } + + subject(:metadata) { file.metadata } + + it { + is_expected.to eq( + context_project: nil, + context_sha: 'HEAD' + ) + } + end + + describe '#eql?' do + let(:location) { 'some/file/config.yml' } + + subject(:eql) { file.eql?(other_file) } + + context 'when the other file has the same params' do + let(:other_file) { test_class.new(location, context) } + + it { is_expected.to eq(true) } + end + + context 'when the other file has not the same params' do + let(:other_file) { test_class.new('some/other/file', context) } + + it { is_expected.to eq(false) } + end + end + + describe '#hash' do + let(:location) { 'some/file/config.yml' } + + subject(:filehash) { file.hash } + + context 'with a project' do + let(:project) { create(:project) } + let(:context_params) { { project: project, sha: 'HEAD', variables: variables } } + + it { is_expected.to eq([location, project.full_path, 'HEAD'].hash) } + end + + context 'without a project' do + it { is_expected.to eq([location, nil, 'HEAD'].hash) } + end + end end diff --git a/spec/lib/gitlab/ci/config/external/file/local_spec.rb b/spec/lib/gitlab/ci/config/external/file/local_spec.rb index b9314dfc44e..c0a0b0009ce 100644 --- a/spec/lib/gitlab/ci/config/external/file/local_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/local_spec.rb @@ -55,6 +55,11 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local do end describe '#valid?' do + subject(:valid?) do + local_file.validate! + local_file.valid? + end + context 'when is a valid local path' do let(:location) { '/lib/gitlab/ci/templates/existent-file.yml' } @@ -62,25 +67,19 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local do allow_any_instance_of(described_class).to receive(:fetch_local_content).and_return("image: 'ruby2:2'") end - it 'returns true' do - expect(local_file.valid?).to be_truthy - end + it { is_expected.to be_truthy } end context 'when it is not a valid local path' do let(:location) { '/lib/gitlab/ci/templates/non-existent-file.yml' } - it 'returns false' do - expect(local_file.valid?).to be_falsy - end + it { is_expected.to be_falsy } end context 'when it is not a yaml file' do let(:location) { '/config/application.rb' } - it 'returns false' do - expect(local_file.valid?).to be_falsy - end + it { is_expected.to be_falsy } end context 'when it is an empty file' do @@ -89,6 +88,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local do it 'returns false and adds an error message about an empty file' do allow_any_instance_of(described_class).to receive(:fetch_local_content).and_return("") + local_file.validate! expect(local_file.errors).to include("Local file `/lib/gitlab/ci/templates/xxxxxx/existent-file.yml` is empty!") end end @@ -98,7 +98,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local do let(:sha) { ':' } it 'returns false and adds an error message stating that included file does not exist' do - expect(local_file).not_to be_valid + expect(valid?).to be_falsy expect(local_file.errors).to include("Sha #{sha} is not valid!") end end @@ -140,6 +140,10 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local do let(:location) { '/lib/gitlab/ci/templates/secret_file.yml' } let(:variables) { Gitlab::Ci::Variables::Collection.new([{ 'key' => 'GITLAB_TOKEN', 'value' => 'secret_file', 'masked' => true }]) } + before do + local_file.validate! + end + it 'returns an error message' do expect(local_file.error_message).to eq("Local file `/lib/gitlab/ci/templates/xxxxxxxxxxx.yml` does not exist!") end @@ -174,6 +178,8 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local do allow(project.repository).to receive(:blob_data_at).with(sha, another_location) .and_return(another_content) + + local_file.validate! end it 'does expand hash to include the template' do @@ -181,4 +187,20 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local do end end end + + describe '#metadata' do + let(:location) { '/lib/gitlab/ci/templates/existent-file.yml' } + + subject(:metadata) { local_file.metadata } + + it { + is_expected.to eq( + context_project: project.full_path, + context_sha: '12345', + type: :local, + location: location, + extra: {} + ) + } + end end diff --git a/spec/lib/gitlab/ci/config/external/file/project_spec.rb b/spec/lib/gitlab/ci/config/external/file/project_spec.rb index 74720c0a3ca..5d3412a148b 100644 --- a/spec/lib/gitlab/ci/config/external/file/project_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/project_spec.rb @@ -66,6 +66,11 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do end describe '#valid?' do + subject(:valid?) do + project_file.validate! + project_file.valid? + end + context 'when a valid path is used' do let(:params) do { project: project.full_path, file: '/file.yml' } @@ -74,18 +79,16 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do let(:root_ref_sha) { project.repository.root_ref_sha } before do - stub_project_blob(root_ref_sha, '/file.yml') { 'image: ruby:2.7' } + stub_project_blob(root_ref_sha, '/file.yml') { 'image: image:1.0' } end - it 'returns true' do - expect(project_file).to be_valid - end + it { is_expected.to be_truthy } context 'when user does not have permission to access file' do let(:context_user) { create(:user) } it 'returns false' do - expect(project_file).not_to be_valid + expect(valid?).to be_falsy expect(project_file.error_message).to include("Project `#{project.full_path}` not found or access denied!") end end @@ -99,12 +102,10 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do let(:ref_sha) { project.commit('master').sha } before do - stub_project_blob(ref_sha, '/file.yml') { 'image: ruby:2.7' } + stub_project_blob(ref_sha, '/file.yml') { 'image: image:1.0' } end - it 'returns true' do - expect(project_file).to be_valid - end + it { is_expected.to be_truthy } end context 'when an empty file is used' do @@ -120,7 +121,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do end it 'returns false' do - expect(project_file).not_to be_valid + expect(valid?).to be_falsy expect(project_file.error_message).to include("Project `#{project.full_path}` file `/xxxxxxxxxxx.yml` is empty!") end end @@ -131,7 +132,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do end it 'returns false' do - expect(project_file).not_to be_valid + expect(valid?).to be_falsy expect(project_file.error_message).to include("Project `#{project.full_path}` reference `I-Do-Not-Exist` does not exist!") end end @@ -144,7 +145,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do end it 'returns false' do - expect(project_file).not_to be_valid + expect(valid?).to be_falsy expect(project_file.error_message).to include("Project `#{project.full_path}` file `/xxxxxxxxxxxxxxxxxxx.yml` does not exist!") end end @@ -155,10 +156,27 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do end it 'returns false' do - expect(project_file).not_to be_valid + expect(valid?).to be_falsy expect(project_file.error_message).to include('Included file `/invalid-file` does not have YAML extension!') end end + + context 'when non-existing project is used with a masked variable' do + let(:variables) do + Gitlab::Ci::Variables::Collection.new([ + { key: 'VAR1', value: 'a_secret_variable_value', masked: true } + ]) + end + + let(:params) do + { project: 'a_secret_variable_value', file: '/file.yml' } + end + + it 'returns false with masked project name' do + expect(valid?).to be_falsy + expect(project_file.error_message).to include("Project `xxxxxxxxxxxxxxxxxxxxxxx` not found or access denied!") + end + end end describe '#expand_context' do @@ -176,6 +194,45 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do end end + describe '#metadata' do + let(:params) do + { project: project.full_path, file: '/file.yml' } + end + + subject(:metadata) { project_file.metadata } + + it { + is_expected.to eq( + context_project: context_project.full_path, + context_sha: '12345', + type: :file, + location: '/file.yml', + extra: { project: project.full_path, ref: 'HEAD' } + ) + } + + context 'when project name and ref include masked variables' do + let(:variables) do + Gitlab::Ci::Variables::Collection.new([ + { key: 'VAR1', value: 'a_secret_variable_value1', masked: true }, + { key: 'VAR2', value: 'a_secret_variable_value2', masked: true } + ]) + end + + let(:params) { { project: 'a_secret_variable_value1', ref: 'a_secret_variable_value2', file: '/file.yml' } } + + it { + is_expected.to eq( + context_project: context_project.full_path, + context_sha: '12345', + type: :file, + location: '/file.yml', + extra: { project: 'xxxxxxxxxxxxxxxxxxxxxxxx', ref: 'xxxxxxxxxxxxxxxxxxxxxxxx' } + ) + } + end + end + private def stub_project_blob(ref, path) diff --git a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb index 2613bfbfdcf..5c07c87fd5a 100644 --- a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb @@ -54,22 +54,23 @@ RSpec.describe Gitlab::Ci::Config::External::File::Remote do end describe "#valid?" do + subject(:valid?) do + remote_file.validate! + remote_file.valid? + end + context 'when is a valid remote url' do before do stub_full_request(location).to_return(body: remote_file_content) end - it 'returns true' do - expect(remote_file.valid?).to be_truthy - end + it { is_expected.to be_truthy } end context 'with an irregular url' do let(:location) { 'not-valid://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' } - it 'returns false' do - expect(remote_file.valid?).to be_falsy - end + it { is_expected.to be_falsy } end context 'with a timeout' do @@ -77,25 +78,19 @@ RSpec.describe Gitlab::Ci::Config::External::File::Remote do allow(Gitlab::HTTP).to receive(:get).and_raise(Timeout::Error) end - it 'is falsy' do - expect(remote_file.valid?).to be_falsy - end + it { is_expected.to be_falsy } end context 'when is not a yaml file' do let(:location) { 'https://asdasdasdaj48ggerexample.com' } - it 'is falsy' do - expect(remote_file.valid?).to be_falsy - end + it { is_expected.to be_falsy } end context 'with an internal url' do let(:location) { 'http://localhost:8080' } - it 'is falsy' do - expect(remote_file.valid?).to be_falsy - end + it { is_expected.to be_falsy } end end @@ -142,7 +137,10 @@ RSpec.describe Gitlab::Ci::Config::External::File::Remote do end describe "#error_message" do - subject { remote_file.error_message } + subject(:error_message) do + remote_file.validate! + remote_file.error_message + end context 'when remote file location is not valid' do let(:location) { 'not-valid://gitlab.com/gitlab-org/gitlab-foss/blob/1234/?secret_file.yml' } @@ -201,4 +199,22 @@ RSpec.describe Gitlab::Ci::Config::External::File::Remote do is_expected.to be_empty end end + + describe '#metadata' do + before do + stub_full_request(location).to_return(body: remote_file_content) + end + + subject(:metadata) { remote_file.metadata } + + it { + is_expected.to eq( + context_project: nil, + context_sha: '12345', + type: :remote, + location: 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.xxxxxxxxxxx.yml', + extra: {} + ) + } + end end diff --git a/spec/lib/gitlab/ci/config/external/file/template_spec.rb b/spec/lib/gitlab/ci/config/external/file/template_spec.rb index 66a06de3d28..4da9a933a9f 100644 --- a/spec/lib/gitlab/ci/config/external/file/template_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/template_spec.rb @@ -45,12 +45,15 @@ RSpec.describe Gitlab::Ci::Config::External::File::Template do end describe "#valid?" do + subject(:valid?) do + template_file.validate! + template_file.valid? + end + context 'when is a valid template name' do let(:template) { 'Auto-DevOps.gitlab-ci.yml' } - it 'returns true' do - expect(template_file).to be_valid - end + it { is_expected.to be_truthy } end context 'with invalid template name' do @@ -59,7 +62,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Template do let(:context_params) { { project: project, sha: '12345', user: user, variables: variables } } it 'returns false' do - expect(template_file).not_to be_valid + expect(valid?).to be_falsy expect(template_file.error_message).to include('`xxxxxxxxxxxxxx.yml` is not a valid location!') end end @@ -68,7 +71,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Template do let(:template) { 'I-Do-Not-Have-This-Template.gitlab-ci.yml' } it 'returns false' do - expect(template_file).not_to be_valid + expect(valid?).to be_falsy expect(template_file.error_message).to include('Included file `I-Do-Not-Have-This-Template.gitlab-ci.yml` is empty or does not exist!') end end @@ -111,4 +114,18 @@ RSpec.describe Gitlab::Ci::Config::External::File::Template do is_expected.to be_empty end end + + describe '#metadata' do + subject(:metadata) { template_file.metadata } + + it { + is_expected.to eq( + context_project: project.full_path, + context_sha: '12345', + type: :template, + location: template, + extra: {} + ) + } + end end diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb index f69feba5e59..2d2adf09a42 100644 --- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb +++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb @@ -17,10 +17,12 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do let(:file_content) do <<~HEREDOC - image: 'ruby:2.7' + image: 'image:1.0' HEREDOC end + subject(:mapper) { described_class.new(values, context) } + before do stub_full_request(remote_url).to_return(body: file_content) @@ -30,13 +32,13 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do end describe '#process' do - subject { described_class.new(values, context).process } + subject(:process) { mapper.process } context "when single 'include' keyword is defined" do context 'when the string is a local file' do let(:values) do { include: local_file, - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'returns File instances' do @@ -48,7 +50,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context 'when the key is a local file hash' do let(:values) do { include: { 'local' => local_file }, - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'returns File instances' do @@ -59,7 +61,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context 'when the string is a remote file' do let(:values) do - { include: remote_url, image: 'ruby:2.7' } + { include: remote_url, image: 'image:1.0' } end it 'returns File instances' do @@ -71,7 +73,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context 'when the key is a remote file hash' do let(:values) do { include: { 'remote' => remote_url }, - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'returns File instances' do @@ -83,7 +85,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context 'when the key is a template file hash' do let(:values) do { include: { 'template' => template_file }, - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'returns File instances' do @@ -98,7 +100,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do let(:remote_url) { 'https://gitlab.com/secret-file.yml' } let(:values) do { include: { 'local' => local_file, 'remote' => remote_url }, - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'returns ambigious specification error' do @@ -109,7 +111,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context "when the key is a project's file" do let(:values) do { include: { project: project.full_path, file: local_file }, - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'returns File instances' do @@ -121,7 +123,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context "when the key is project's files" do let(:values) do { include: { project: project.full_path, file: [local_file, 'another_file_path.yml'] }, - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'returns two File instances' do @@ -135,7 +137,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context "when 'include' is defined as an array" do let(:values) do { include: [remote_url, local_file], - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'returns Files instances' do @@ -147,7 +149,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context "when 'include' is defined as an array of hashes" do let(:values) do { include: [{ remote: remote_url }, { local: local_file }], - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'returns Files instances' do @@ -158,7 +160,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context 'when it has ambigious match' do let(:values) do { include: [{ remote: remote_url, local: local_file }], - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'returns ambigious specification error' do @@ -170,7 +172,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context "when 'include' is not defined" do let(:values) do { - image: 'ruby:2.7' + image: 'image:1.0' } end @@ -185,11 +187,16 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do { 'local' => local_file }, { 'local' => local_file } ], - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'does not raise an exception' do - expect { subject }.not_to raise_error + expect { process }.not_to raise_error + end + + it 'has expanset with one' do + process + expect(mapper.expandset.size).to eq(1) end end @@ -199,7 +206,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do { 'local' => local_file }, { 'remote' => remote_url } ], - image: 'ruby:2.7' } + image: 'image:1.0' } end before do @@ -217,7 +224,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do { 'local' => local_file }, { 'remote' => remote_url } ], - image: 'ruby:2.7' } + image: 'image:1.0' } end before do @@ -269,7 +276,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context 'defined as an array' do let(:values) do { include: [full_local_file_path, remote_url], - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'expands the variable' do @@ -281,7 +288,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context 'defined as an array of hashes' do let(:values) do { include: [{ local: full_local_file_path }, { remote: remote_url }], - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'expands the variable' do @@ -303,7 +310,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context 'project name' do let(:values) do { include: { project: '$CI_PROJECT_PATH', file: local_file }, - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'expands the variable', :aggregate_failures do @@ -315,7 +322,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context 'with multiple files' do let(:values) do { include: { project: project.full_path, file: [full_local_file_path, 'another_file_path.yml'] }, - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'expands the variable' do @@ -327,7 +334,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do context 'when include variable has an unsupported type for variable expansion' do let(:values) do { include: { project: project.id, file: local_file }, - image: 'ruby:2.7' } + image: 'image:1.0' } end it 'does not invoke expansion for the variable', :aggregate_failures do @@ -365,7 +372,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do let(:values) do { include: [{ remote: remote_url }, { local: local_file, rules: [{ if: "$CI_PROJECT_ID == '#{project_id}'" }] }], - image: 'ruby:2.7' } + image: 'image:1.0' } end context 'when the rules matches' do @@ -385,5 +392,27 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do end end end + + context "when locations are same after masking variables" do + let(:variables) do + Gitlab::Ci::Variables::Collection.new([ + { 'key' => 'GITLAB_TOKEN', 'value' => 'secret-file1', 'masked' => true }, + { 'key' => 'GITLAB_TOKEN', 'value' => 'secret-file2', 'masked' => true } + ]) + end + + let(:values) do + { include: [ + { 'local' => 'hello/secret-file1.yml' }, + { 'local' => 'hello/secret-file2.yml' } + ], + image: 'ruby:2.7' } + end + + it 'has expanset with two' do + process + expect(mapper.expandset.size).to eq(2) + end + end end end diff --git a/spec/lib/gitlab/ci/config/external/processor_spec.rb b/spec/lib/gitlab/ci/config/external/processor_spec.rb index 97bd74721f2..56cd006717e 100644 --- a/spec/lib/gitlab/ci/config/external/processor_spec.rb +++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb @@ -22,10 +22,10 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do end describe "#perform" do - subject { processor.perform } + subject(:perform) { processor.perform } context 'when no external files defined' do - let(:values) { { image: 'ruby:2.7' } } + let(:values) { { image: 'image:1.0' } } it 'returns the same values' do expect(processor.perform).to eq(values) @@ -33,7 +33,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do end context 'when an invalid local file is defined' do - let(:values) { { include: '/lib/gitlab/ci/templates/non-existent-file.yml', image: 'ruby:2.7' } } + let(:values) { { include: '/lib/gitlab/ci/templates/non-existent-file.yml', image: 'image:1.0' } } it 'raises an error' do expect { processor.perform }.to raise_error( @@ -45,7 +45,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do context 'when an invalid remote file is defined' do let(:remote_file) { 'http://doesntexist.com/.gitlab-ci-1.yml' } - let(:values) { { include: remote_file, image: 'ruby:2.7' } } + let(:values) { { include: remote_file, image: 'image:1.0' } } before do stub_full_request(remote_file).and_raise(SocketError.new('Some HTTP error')) @@ -61,7 +61,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do context 'with a valid remote external file is defined' do let(:remote_file) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' } - let(:values) { { include: remote_file, image: 'ruby:2.7' } } + let(:values) { { include: remote_file, image: 'image:1.0' } } let(:external_file_content) do <<-HEREDOC before_script: @@ -95,7 +95,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do end context 'with a valid local external file is defined' do - let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'ruby:2.7' } } + let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'image:1.0' } } let(:local_file_content) do <<-HEREDOC before_script: @@ -133,7 +133,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do let(:values) do { include: external_files, - image: 'ruby:2.7' + image: 'image:1.0' } end @@ -165,7 +165,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do end context 'when external files are defined but not valid' do - let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'ruby:2.7' } } + let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'image:1.0' } } let(:local_file_content) { 'invalid content file ////' } @@ -187,7 +187,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do let(:values) do { include: remote_file, - image: 'ruby:2.7' + image: 'image:1.0' } end @@ -200,7 +200,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do it 'takes precedence' do stub_full_request(remote_file).to_return(body: remote_file_content) - expect(processor.perform[:image]).to eq('ruby:2.7') + expect(processor.perform[:image]).to eq('image:1.0') end end @@ -210,7 +210,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do include: [ { local: '/local/file.yml' } ], - image: 'ruby:2.7' + image: 'image:1.0' } end @@ -262,6 +262,18 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do expect(process_obs_count).to eq(3) end + + it 'stores includes' do + perform + + expect(context.includes).to contain_exactly( + { type: :local, location: '/local/file.yml', extra: {}, context_project: project.full_path, context_sha: '12345' }, + { type: :template, location: 'Ruby.gitlab-ci.yml', extra: {}, context_project: project.full_path, context_sha: '12345' }, + { type: :remote, location: 'http://my.domain.com/config.yml', extra: {}, context_project: project.full_path, context_sha: '12345' }, + { type: :file, location: '/templates/my-workflow.yml', extra: { project: another_project.full_path, ref: 'HEAD' }, context_project: project.full_path, context_sha: '12345' }, + { type: :local, location: '/templates/my-build.yml', extra: {}, context_project: another_project.full_path, context_sha: another_project.commit.sha } + ) + end end context 'when user is reporter of another project' do @@ -294,7 +306,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do context 'when config includes an external configuration file via SSL web request' do before do stub_full_request('https://sha256.badssl.com/fake.yml', ip_address: '8.8.8.8') - .to_return(body: 'image: ruby:2.6', status: 200) + .to_return(body: 'image: image:1.0', status: 200) stub_full_request('https://self-signed.badssl.com/fake.yml', ip_address: '8.8.8.9') .to_raise(OpenSSL::SSL::SSLError.new('SSL_connect returned=1 errno=0 state=error: certificate verify failed (self signed certificate)')) @@ -303,7 +315,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do context 'with an acceptable certificate' do let(:values) { { include: 'https://sha256.badssl.com/fake.yml' } } - it { is_expected.to include(image: 'ruby:2.6') } + it { is_expected.to include(image: 'image:1.0') } end context 'with a self-signed certificate' do @@ -319,7 +331,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do let(:values) do { include: { project: another_project.full_path, file: '/templates/my-build.yml' }, - image: 'ruby:2.7' + image: 'image:1.0' } end @@ -349,7 +361,7 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do project: another_project.full_path, file: ['/templates/my-build.yml', '/templates/my-test.yml'] }, - image: 'ruby:2.7' + image: 'image:1.0' } end @@ -377,13 +389,22 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do output = processor.perform expect(output.keys).to match_array([:image, :my_build, :my_test]) end + + it 'stores includes' do + perform + + expect(context.includes).to contain_exactly( + { type: :file, location: '/templates/my-build.yml', extra: { project: another_project.full_path, ref: 'HEAD' }, context_project: project.full_path, context_sha: '12345' }, + { type: :file, location: '/templates/my-test.yml', extra: { project: another_project.full_path, ref: 'HEAD' }, context_project: project.full_path, context_sha: '12345' } + ) + end end context 'when local file path has wildcard' do - let_it_be(:project) { create(:project, :repository) } + let(:project) { create(:project, :repository) } let(:values) do - { include: 'myfolder/*.yml', image: 'ruby:2.7' } + { include: 'myfolder/*.yml', image: 'image:1.0' } end before do @@ -412,6 +433,15 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do output = processor.perform expect(output.keys).to match_array([:image, :my_build, :my_test]) end + + it 'stores includes' do + perform + + expect(context.includes).to contain_exactly( + { type: :local, location: 'myfolder/file1.yml', extra: {}, context_project: project.full_path, context_sha: '12345' }, + { type: :local, location: 'myfolder/file2.yml', extra: {}, context_project: project.full_path, context_sha: '12345' } + ) + end end context 'when rules defined' do |