summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/ci
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/gitlab/ci')
-rw-r--r--spec/lib/gitlab/ci/build/image_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/image_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/config/entry/reports/coverage_report_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/config/external/file/artifact_spec.rb71
-rw-r--r--spec/lib/gitlab/ci/config/external/file/base_spec.rb75
-rw-r--r--spec/lib/gitlab/ci/config/external/file/local_spec.rb42
-rw-r--r--spec/lib/gitlab/ci/config/external/file/project_spec.rb83
-rw-r--r--spec/lib/gitlab/ci/config/external/file/remote_spec.rb48
-rw-r--r--spec/lib/gitlab/ci/config/external/file/template_spec.rb27
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper_spec.rb77
-rw-r--r--spec/lib/gitlab/ci/config/external/processor_spec.rb64
-rw-r--r--spec/lib/gitlab/ci/config_spec.rb74
-rw-r--r--spec/lib/gitlab/ci/parsers/security/common_spec.rb236
-rw-r--r--spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb662
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/limit/rate_limit_spec.rb179
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/security/report_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/reports/security/scanner_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/runner_releases_spec.rb114
-rw-r--r--spec/lib/gitlab/ci/runner_upgrade_check_spec.rb89
-rw-r--r--spec/lib/gitlab/ci/status/build/manual_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/templates/MATLAB_spec.rb26
-rw-r--r--spec/lib/gitlab/ci/templates/templates_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/templates/themekit_gitlab_ci_yaml_spec.rb60
-rw-r--r--spec/lib/gitlab/ci/variables/builder_spec.rb362
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb28
27 files changed, 1946 insertions, 445 deletions
diff --git a/spec/lib/gitlab/ci/build/image_spec.rb b/spec/lib/gitlab/ci/build/image_spec.rb
index 71cd57d317c..630dfcd06bb 100644
--- a/spec/lib/gitlab/ci/build/image_spec.rb
+++ b/spec/lib/gitlab/ci/build/image_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Gitlab::Ci::Build::Image do
subject { described_class.from_image(job) }
context 'when image is defined in job' do
- let(:image_name) { 'ruby:2.7' }
+ let(:image_name) { 'image:1.0' }
let(:job) { create(:ci_build, options: { image: image_name } ) }
context 'when image is defined as string' do
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
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
index 05ff1f3618b..3ba6a9059c6 100644
--- a/spec/lib/gitlab/ci/config_spec.rb
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe Gitlab::Ci::Config do
context 'when config is valid' do
let(:yml) do
<<-EOS
- image: ruby:2.7
+ image: image:1.0
rspec:
script:
@@ -32,7 +32,7 @@ RSpec.describe Gitlab::Ci::Config do
describe '#to_hash' do
it 'returns hash created from string' do
hash = {
- image: 'ruby:2.7',
+ image: 'image:1.0',
rspec: {
script: ['gem install rspec',
'rspec']
@@ -104,12 +104,32 @@ RSpec.describe Gitlab::Ci::Config do
end
it { is_expected.to contain_exactly('Jobs/Deploy.gitlab-ci.yml', 'Jobs/Build.gitlab-ci.yml') }
+
+ it 'stores includes' do
+ expect(config.metadata[:includes]).to contain_exactly(
+ { type: :template,
+ location: 'Jobs/Deploy.gitlab-ci.yml',
+ extra: {},
+ context_project: nil,
+ context_sha: nil },
+ { type: :template,
+ location: 'Jobs/Build.gitlab-ci.yml',
+ extra: {},
+ context_project: nil,
+ context_sha: nil },
+ { type: :remote,
+ location: 'https://example.com/gitlab-ci.yml',
+ extra: {},
+ context_project: nil,
+ context_sha: nil }
+ )
+ end
end
context 'when using extendable hash' do
let(:yml) do
<<-EOS
- image: ruby:2.7
+ image: image:1.0
rspec:
script: rspec
@@ -122,7 +142,7 @@ RSpec.describe Gitlab::Ci::Config do
it 'correctly extends the hash' do
hash = {
- image: 'ruby:2.7',
+ image: 'image:1.0',
rspec: { script: 'rspec' },
test: {
extends: 'rspec',
@@ -212,7 +232,7 @@ RSpec.describe Gitlab::Ci::Config do
let(:yml) do
<<-EOS
image:
- name: ruby:2.7
+ name: image:1.0
ports:
- 80
EOS
@@ -226,12 +246,12 @@ RSpec.describe Gitlab::Ci::Config do
context 'in the job image' do
let(:yml) do
<<-EOS
- image: ruby:2.7
+ image: image:1.0
test:
script: rspec
image:
- name: ruby:2.7
+ name: image:1.0
ports:
- 80
EOS
@@ -245,11 +265,11 @@ RSpec.describe Gitlab::Ci::Config do
context 'in the services' do
let(:yml) do
<<-EOS
- image: ruby:2.7
+ image: image:1.0
test:
script: rspec
- image: ruby:2.7
+ image: image:1.0
services:
- name: test
alias: test
@@ -325,7 +345,7 @@ RSpec.describe Gitlab::Ci::Config do
- project: '$MAIN_PROJECT'
ref: '$REF'
file: '$FILENAME'
- image: ruby:2.7
+ image: image:1.0
HEREDOC
end
@@ -364,7 +384,7 @@ RSpec.describe Gitlab::Ci::Config do
it 'returns a composed hash' do
composed_hash = {
before_script: local_location_hash[:before_script],
- image: "ruby:2.7",
+ image: "image:1.0",
rspec: { script: ["bundle exec rspec"] },
variables: remote_file_hash[:variables]
}
@@ -403,6 +423,26 @@ RSpec.describe Gitlab::Ci::Config do
end
end
end
+
+ it 'stores includes' do
+ expect(config.metadata[:includes]).to contain_exactly(
+ { type: :local,
+ location: local_location,
+ extra: {},
+ context_project: project.full_path,
+ context_sha: '12345' },
+ { type: :remote,
+ location: remote_location,
+ extra: {},
+ context_project: project.full_path,
+ context_sha: '12345' },
+ { type: :file,
+ location: '.gitlab-ci.yml',
+ extra: { project: main_project.full_path, ref: 'HEAD' },
+ context_project: project.full_path,
+ context_sha: '12345' }
+ )
+ end
end
context "when gitlab_ci.yml has invalid 'include' defined" do
@@ -481,7 +521,7 @@ RSpec.describe Gitlab::Ci::Config do
include:
- #{remote_location}
- image: ruby:2.7
+ image: image:1.0
HEREDOC
end
@@ -492,7 +532,7 @@ RSpec.describe Gitlab::Ci::Config do
end
it 'takes precedence' do
- expect(config.to_hash).to eq({ image: 'ruby:2.7' })
+ expect(config.to_hash).to eq({ image: 'image:1.0' })
end
end
@@ -699,7 +739,7 @@ RSpec.describe Gitlab::Ci::Config do
- #{local_location}
- #{other_file_location}
- image: ruby:2.7
+ image: image:1.0
HEREDOC
end
@@ -718,7 +758,7 @@ RSpec.describe Gitlab::Ci::Config do
it 'returns a composed hash' do
composed_hash = {
before_script: local_location_hash[:before_script],
- image: "ruby:2.7",
+ image: "image:1.0",
build: { stage: "build", script: "echo hello" },
rspec: { stage: "test", script: "bundle exec rspec" }
}
@@ -735,7 +775,7 @@ RSpec.describe Gitlab::Ci::Config do
- local: #{local_location}
rules:
- if: $CI_PROJECT_ID == "#{project_id}"
- image: ruby:2.7
+ image: image:1.0
HEREDOC
end
@@ -763,7 +803,7 @@ RSpec.describe Gitlab::Ci::Config do
- local: #{local_location}
rules:
- exists: "#{filename}"
- image: ruby:2.7
+ image: image:1.0
HEREDOC
end
diff --git a/spec/lib/gitlab/ci/parsers/security/common_spec.rb b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
index 1e96c717a4f..dfc5dec1481 100644
--- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
@@ -4,6 +4,18 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Security::Common do
describe '#parse!' do
+ let_it_be(:scanner_data) do
+ {
+ scan: {
+ scanner: {
+ id: "gemnasium",
+ name: "Gemnasium",
+ version: "2.1.0"
+ }
+ }
+ }
+ end
+
where(vulnerability_finding_signatures_enabled: [true, false])
with_them do
let_it_be(:pipeline) { create(:ci_pipeline) }
@@ -30,7 +42,9 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
describe 'schema validation' do
let(:validator_class) { Gitlab::Ci::Parsers::Security::Validators::SchemaValidator }
- let(:parser) { described_class.new('{}', report, vulnerability_finding_signatures_enabled, validate: validate) }
+ let(:data) { {}.merge(scanner_data) }
+ let(:json_data) { data.to_json }
+ let(:parser) { described_class.new(json_data, report, vulnerability_finding_signatures_enabled, validate: validate) }
subject(:parse_report) { parser.parse! }
@@ -38,172 +52,138 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
allow(validator_class).to receive(:new).and_call_original
end
- context 'when show_report_validation_warnings is enabled' do
- before do
- stub_feature_flags(show_report_validation_warnings: true)
- end
-
- context 'when the validate flag is set to `false`' do
- let(:validate) { false }
- let(:valid?) { false }
- let(:errors) { ['foo'] }
-
- before do
- allow_next_instance_of(validator_class) do |instance|
- allow(instance).to receive(:valid?).and_return(valid?)
- allow(instance).to receive(:errors).and_return(errors)
- end
-
- allow(parser).to receive_messages(create_scanner: true, create_scan: true)
- end
-
- it 'instantiates the validator with correct params' do
- parse_report
-
- expect(validator_class).to have_received(:new).with(report.type, {}, report.version)
- end
-
- context 'when the report data is not valid according to the schema' do
- it 'adds warnings to the report' do
- expect { parse_report }.to change { report.warnings }.from([]).to([{ message: 'foo', type: 'Schema' }])
- end
-
- it 'keeps the execution flow as normal' do
- parse_report
+ context 'when the validate flag is set to `false`' do
+ let(:validate) { false }
+ let(:valid?) { false }
+ let(:errors) { ['foo'] }
+ let(:warnings) { ['bar'] }
- expect(parser).to have_received(:create_scanner)
- expect(parser).to have_received(:create_scan)
- end
+ before do
+ allow_next_instance_of(validator_class) do |instance|
+ allow(instance).to receive(:valid?).and_return(valid?)
+ allow(instance).to receive(:errors).and_return(errors)
+ allow(instance).to receive(:warnings).and_return(warnings)
end
- context 'when the report data is valid according to the schema' do
- let(:valid?) { true }
- let(:errors) { [] }
-
- it 'does not add warnings to the report' do
- expect { parse_report }.not_to change { report.errors }
- end
-
- it 'keeps the execution flow as normal' do
- parse_report
-
- expect(parser).to have_received(:create_scanner)
- expect(parser).to have_received(:create_scan)
- end
- end
+ allow(parser).to receive_messages(create_scanner: true, create_scan: true)
end
- context 'when the validate flag is set to `true`' do
- let(:validate) { true }
- let(:valid?) { false }
- let(:errors) { ['foo'] }
+ it 'instantiates the validator with correct params' do
+ parse_report
- before do
- allow_next_instance_of(validator_class) do |instance|
- allow(instance).to receive(:valid?).and_return(valid?)
- allow(instance).to receive(:errors).and_return(errors)
- end
+ expect(validator_class).to have_received(:new).with(
+ report.type,
+ data.deep_stringify_keys,
+ report.version,
+ project: pipeline.project,
+ scanner: data.dig(:scan, :scanner).deep_stringify_keys
+ )
+ end
- allow(parser).to receive_messages(create_scanner: true, create_scan: true)
+ context 'when the report data is not valid according to the schema' do
+ it 'adds warnings to the report' do
+ expect { parse_report }.to change { report.warnings }.from([]).to(
+ [
+ { message: 'foo', type: 'Schema' },
+ { message: 'bar', type: 'Schema' }
+ ]
+ )
end
- it 'instantiates the validator with correct params' do
+ it 'keeps the execution flow as normal' do
parse_report
- expect(validator_class).to have_received(:new).with(report.type, {}, report.version)
+ expect(parser).to have_received(:create_scanner)
+ expect(parser).to have_received(:create_scan)
end
+ end
- context 'when the report data is not valid according to the schema' do
- it 'adds errors to the report' do
- expect { parse_report }.to change { report.errors }.from([]).to([{ message: 'foo', type: 'Schema' }])
- end
-
- it 'does not try to create report entities' do
- parse_report
+ context 'when the report data is valid according to the schema' do
+ let(:valid?) { true }
+ let(:errors) { [] }
+ let(:warnings) { [] }
- expect(parser).not_to have_received(:create_scanner)
- expect(parser).not_to have_received(:create_scan)
- end
+ it 'does not add errors to the report' do
+ expect { parse_report }.not_to change { report.errors }
end
- context 'when the report data is valid according to the schema' do
- let(:valid?) { true }
- let(:errors) { [] }
-
- it 'does not add errors to the report' do
- expect { parse_report }.not_to change { report.errors }.from([])
- end
+ it 'does not add warnings to the report' do
+ expect { parse_report }.not_to change { report.warnings }
+ end
- it 'keeps the execution flow as normal' do
- parse_report
+ it 'keeps the execution flow as normal' do
+ parse_report
- expect(parser).to have_received(:create_scanner)
- expect(parser).to have_received(:create_scan)
- end
+ expect(parser).to have_received(:create_scanner)
+ expect(parser).to have_received(:create_scan)
end
end
end
- context 'when show_report_validation_warnings is disabled' do
- before do
- stub_feature_flags(show_report_validation_warnings: false)
- end
-
- context 'when the validate flag is set as `false`' do
- let(:validate) { false }
+ context 'when the validate flag is set to `true`' do
+ let(:validate) { true }
+ let(:valid?) { false }
+ let(:errors) { ['foo'] }
+ let(:warnings) { ['bar'] }
- it 'does not run the validation logic' do
- parse_report
-
- expect(validator_class).not_to have_received(:new)
+ before do
+ allow_next_instance_of(validator_class) do |instance|
+ allow(instance).to receive(:valid?).and_return(valid?)
+ allow(instance).to receive(:errors).and_return(errors)
+ allow(instance).to receive(:warnings).and_return(warnings)
end
+
+ allow(parser).to receive_messages(create_scanner: true, create_scan: true)
end
- context 'when the validate flag is set as `true`' do
- let(:validate) { true }
- let(:valid?) { false }
+ it 'instantiates the validator with correct params' do
+ parse_report
- before do
- allow_next_instance_of(validator_class) do |instance|
- allow(instance).to receive(:valid?).and_return(valid?)
- allow(instance).to receive(:errors).and_return(['foo'])
- end
+ expect(validator_class).to have_received(:new).with(
+ report.type,
+ data.deep_stringify_keys,
+ report.version,
+ project: pipeline.project,
+ scanner: data.dig(:scan, :scanner).deep_stringify_keys
+ )
+ end
- allow(parser).to receive_messages(create_scanner: true, create_scan: true)
+ context 'when the report data is not valid according to the schema' do
+ it 'adds errors to the report' do
+ expect { parse_report }.to change { report.errors }.from([]).to(
+ [
+ { message: 'foo', type: 'Schema' },
+ { message: 'bar', type: 'Schema' }
+ ]
+ )
end
- it 'instantiates the validator with correct params' do
+ it 'does not try to create report entities' do
parse_report
- expect(validator_class).to have_received(:new).with(report.type, {}, report.version)
+ expect(parser).not_to have_received(:create_scanner)
+ expect(parser).not_to have_received(:create_scan)
end
+ end
- context 'when the report data is not valid according to the schema' do
- it 'adds errors to the report' do
- expect { parse_report }.to change { report.errors }.from([]).to([{ message: 'foo', type: 'Schema' }])
- end
-
- it 'does not try to create report entities' do
- parse_report
+ context 'when the report data is valid according to the schema' do
+ let(:valid?) { true }
+ let(:errors) { [] }
+ let(:warnings) { [] }
- expect(parser).not_to have_received(:create_scanner)
- expect(parser).not_to have_received(:create_scan)
- end
+ it 'does not add errors to the report' do
+ expect { parse_report }.not_to change { report.errors }.from([])
end
- context 'when the report data is valid according to the schema' do
- let(:valid?) { true }
-
- it 'does not add errors to the report' do
- expect { parse_report }.not_to change { report.errors }.from([])
- end
+ it 'does not add warnings to the report' do
+ expect { parse_report }.not_to change { report.warnings }.from([])
+ end
- it 'keeps the execution flow as normal' do
- parse_report
+ it 'keeps the execution flow as normal' do
+ parse_report
- expect(parser).to have_received(:create_scanner)
- expect(parser).to have_received(:create_scan)
- end
+ expect(parser).to have_received(:create_scanner)
+ expect(parser).to have_received(:create_scan)
end
end
end
diff --git a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
index c83427b68ef..f6409c8b01f 100644
--- a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
@@ -3,6 +3,18 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
+ let_it_be(:project) { create(:project) }
+
+ let(:scanner) do
+ {
+ 'id' => 'gemnasium',
+ 'name' => 'Gemnasium',
+ 'version' => '2.1.0'
+ }
+ end
+
+ let(:validator) { described_class.new(report_type, report_data, report_version, project: project, scanner: scanner) }
+
describe 'SUPPORTED_VERSIONS' do
schema_path = Rails.root.join("lib", "gitlab", "ci", "parsers", "security", "validators", "schemas")
@@ -47,48 +59,652 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
end
- using RSpec::Parameterized::TableSyntax
+ describe '#valid?' do
+ subject { validator.valid? }
- where(:report_type, :report_version, :expected_errors, :valid_data) do
- 'sast' | '10.0.0' | ['root is missing required keys: vulnerabilities'] | { 'version' => '10.0.0', 'vulnerabilities' => [] }
- :sast | '10.0.0' | ['root is missing required keys: vulnerabilities'] | { 'version' => '10.0.0', 'vulnerabilities' => [] }
- :secret_detection | '10.0.0' | ['root is missing required keys: vulnerabilities'] | { 'version' => '10.0.0', 'vulnerabilities' => [] }
- end
+ context 'when given a supported schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- with_them do
- let(:validator) { described_class.new(report_type, report_data, report_version) }
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
- describe '#valid?' do
- subject { validator.valid? }
+ it { is_expected.to be_truthy }
+ end
- context 'when given data is invalid according to the schema' do
- let(:report_data) { {} }
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
it { is_expected.to be_falsey }
+
+ it 'logs related information' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
+ security_report_failure: 'schema_validation_fails',
+ security_report_scanner_id: 'gemnasium',
+ security_report_scanner_version: '2.1.0'
+ )
+
+ subject
+ end
end
+ end
+
+ context 'when given a deprecated schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last }
- context 'when given data is valid according to the schema' do
- let(:report_data) { valid_data }
+ context 'and the report passes schema validation' do
+ let(:report_data) do
+ {
+ 'version' => '10.0.0',
+ 'vulnerabilities' => []
+ }
+ end
it { is_expected.to be_truthy }
+
+ it 'logs related information' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
+ security_report_failure: 'using_deprecated_schema_version',
+ security_report_scanner_id: 'gemnasium',
+ security_report_scanner_version: '2.1.0'
+ )
+
+ subject
+ end
end
- context 'when no report_version is provided' do
- let(:report_version) { nil }
- let(:report_data) { valid_data }
+ context 'and the report does not pass schema validation' do
+ context 'and enforce_security_report_validation is enabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: true)
+ end
+
+ let(:report_data) do
+ {
+ 'version' => 'V2.7.0'
+ }
+ end
- it 'does not fail' do
- expect { subject }.not_to raise_error
+ it { is_expected.to be_falsey }
+ end
+
+ context 'and enforce_security_report_validation is disabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: false)
+ end
+
+ let(:report_data) do
+ {
+ 'version' => 'V2.7.0'
+ }
+ end
+
+ it { is_expected.to be_truthy }
end
end
end
- describe '#errors' do
- let(:report_data) { { 'version' => '10.0.0' } }
+ context 'when given an unsupported schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { "12.37.0" }
+
+ context 'if enforce_security_report_validation is enabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: true)
+ end
+
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ it { is_expected.to be_falsey }
+
+ it 'logs related information' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
+ security_report_failure: 'using_unsupported_schema_version',
+ security_report_scanner_id: 'gemnasium',
+ security_report_scanner_version: '2.1.0'
+ )
+
+ subject
+ end
+ end
+
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ context 'and scanner information is empty' do
+ let(:scanner) { {} }
+
+ it 'logs related information' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
+ security_report_failure: 'schema_validation_fails',
+ security_report_scanner_id: nil,
+ security_report_scanner_version: nil
+ )
+
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
+ security_report_failure: 'using_unsupported_schema_version',
+ security_report_scanner_id: nil,
+ security_report_scanner_version: nil
+ )
+
+ subject
+ end
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ context 'if enforce_security_report_validation is disabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: false)
+ end
+
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ it { is_expected.to be_truthy }
+ end
+ end
+ end
+ end
- subject { validator.errors }
+ describe '#errors' do
+ subject { validator.errors }
- it { is_expected.to eq(expected_errors) }
+ context 'when given a supported schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
+
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ let(:expected_errors) { [] }
+
+ it { is_expected.to match_array(expected_errors) }
+ end
+
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ context 'if enforce_security_report_validation is enabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: project)
+ end
+
+ let(:expected_errors) do
+ [
+ 'root is missing required keys: vulnerabilities'
+ ]
+ end
+
+ it { is_expected.to match_array(expected_errors) }
+ end
+
+ context 'if enforce_security_report_validation is disabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: false)
+ end
+
+ let(:expected_errors) { [] }
+
+ it { is_expected.to match_array(expected_errors) }
+ end
+ end
+ end
+
+ context 'when given a deprecated schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last }
+
+ context 'and the report passes schema validation' do
+ let(:report_data) do
+ {
+ 'version' => '10.0.0',
+ 'vulnerabilities' => []
+ }
+ end
+
+ let(:expected_errors) { [] }
+
+ it { is_expected.to match_array(expected_errors) }
+ end
+
+ context 'and the report does not pass schema validation' do
+ context 'and enforce_security_report_validation is enabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: true)
+ end
+
+ let(:report_data) do
+ {
+ 'version' => 'V2.7.0'
+ }
+ end
+
+ let(:expected_errors) do
+ [
+ "property '/version' does not match pattern: ^[0-9]+\\.[0-9]+\\.[0-9]+$",
+ "root is missing required keys: vulnerabilities"
+ ]
+ end
+
+ it { is_expected.to match_array(expected_errors) }
+ end
+
+ context 'and enforce_security_report_validation is disabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: false)
+ end
+
+ let(:report_data) do
+ {
+ 'version' => 'V2.7.0'
+ }
+ end
+
+ let(:expected_errors) { [] }
+
+ it { is_expected.to match_array(expected_errors) }
+ end
+ end
+ end
+
+ context 'when given an unsupported schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { "12.37.0" }
+
+ context 'if enforce_security_report_validation is enabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: true)
+ end
+
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ let(:expected_errors) do
+ [
+ "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1"
+ ]
+ end
+
+ it { is_expected.to match_array(expected_errors) }
+ end
+
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ let(:expected_errors) do
+ [
+ "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1",
+ "root is missing required keys: vulnerabilities"
+ ]
+ end
+
+ it { is_expected.to match_array(expected_errors) }
+ end
+ end
+
+ context 'if enforce_security_report_validation is disabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: false)
+ end
+
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ let(:expected_errors) { [] }
+
+ it { is_expected.to match_array(expected_errors) }
+ end
+
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ let(:expected_errors) { [] }
+
+ it { is_expected.to match_array(expected_errors) }
+ end
+ end
+ end
+ end
+
+ describe '#deprecation_warnings' do
+ subject { validator.deprecation_warnings }
+
+ context 'when given a supported schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
+
+ let(:expected_deprecation_warnings) { [] }
+
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ it { is_expected.to match_array(expected_deprecation_warnings) }
+ end
+
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ it { is_expected.to match_array(expected_deprecation_warnings) }
+ end
+ end
+
+ context 'when given a deprecated schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last }
+ let(:expected_deprecation_warnings) do
+ [
+ "Version V2.7.0 for report type dast has been deprecated, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1"
+ ]
+ end
+
+ context 'and the report passes schema validation' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ it { is_expected.to match_array(expected_deprecation_warnings) }
+ end
+
+ context 'and the report does not pass schema validation' do
+ let(:report_data) do
+ {
+ 'version' => 'V2.7.0'
+ }
+ end
+
+ it { is_expected.to match_array(expected_deprecation_warnings) }
+ end
+ end
+
+ context 'when given an unsupported schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { "21.37.0" }
+ let(:expected_deprecation_warnings) { [] }
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ it { is_expected.to match_array(expected_deprecation_warnings) }
+ end
+ end
+
+ describe '#warnings' do
+ subject { validator.warnings }
+
+ context 'when given a supported schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
+
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ let(:expected_warnings) { [] }
+
+ it { is_expected.to match_array(expected_warnings) }
+ end
+
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ context 'if enforce_security_report_validation is enabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: project)
+ end
+
+ let(:expected_warnings) { [] }
+
+ it { is_expected.to match_array(expected_warnings) }
+ end
+
+ context 'if enforce_security_report_validation is disabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: false)
+ end
+
+ let(:expected_warnings) do
+ [
+ 'root is missing required keys: vulnerabilities'
+ ]
+ end
+
+ it { is_expected.to match_array(expected_warnings) }
+ end
+ end
+ end
+
+ context 'when given a deprecated schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last }
+
+ context 'and the report passes schema validation' do
+ let(:report_data) do
+ {
+ 'vulnerabilities' => []
+ }
+ end
+
+ let(:expected_warnings) { [] }
+
+ it { is_expected.to match_array(expected_warnings) }
+ end
+
+ context 'and the report does not pass schema validation' do
+ let(:report_data) do
+ {
+ 'version' => 'V2.7.0'
+ }
+ end
+
+ context 'and enforce_security_report_validation is enabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: true)
+ end
+
+ let(:expected_warnings) { [] }
+
+ it { is_expected.to match_array(expected_warnings) }
+ end
+
+ context 'and enforce_security_report_validation is disabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: false)
+ end
+
+ let(:expected_warnings) do
+ [
+ "property '/version' does not match pattern: ^[0-9]+\\.[0-9]+\\.[0-9]+$",
+ "root is missing required keys: vulnerabilities"
+ ]
+ end
+
+ it { is_expected.to match_array(expected_warnings) }
+ end
+ end
+ end
+
+ context 'when given an unsupported schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { "12.37.0" }
+
+ context 'if enforce_security_report_validation is enabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: true)
+ end
+
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ let(:expected_warnings) { [] }
+
+ it { is_expected.to match_array(expected_warnings) }
+ end
+
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ let(:expected_warnings) { [] }
+
+ it { is_expected.to match_array(expected_warnings) }
+ end
+ end
+
+ context 'if enforce_security_report_validation is disabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: false)
+ end
+
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ let(:expected_warnings) do
+ [
+ "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1"
+ ]
+ end
+
+ it { is_expected.to match_array(expected_warnings) }
+ end
+
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ let(:expected_warnings) do
+ [
+ "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1",
+ "root is missing required keys: vulnerabilities"
+ ]
+ end
+
+ it { is_expected.to match_array(expected_warnings) }
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/limit/rate_limit_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/limit/rate_limit_spec.rb
new file mode 100644
index 00000000000..aa8aec2af4a
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/limit/rate_limit_spec.rb
@@ -0,0 +1,179 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Gitlab::Ci::Pipeline::Chain::Limit::RateLimit, :freeze_time, :clean_gitlab_redis_rate_limiting do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:namespace) { create(:namespace) }
+ let_it_be(:project, reload: true) { create(:project, namespace: namespace) }
+
+ let(:save_incompleted) { false }
+ let(:throttle_message) do
+ 'Too many pipelines created in the last minute. Try again later.'
+ end
+
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project,
+ current_user: user,
+ save_incompleted: save_incompleted
+ )
+ end
+
+ let(:pipeline) { build(:ci_pipeline, project: project, source: source) }
+ let(:source) { 'push' }
+ let(:step) { described_class.new(pipeline, command) }
+
+ def perform(count: 2)
+ count.times { step.perform! }
+ end
+
+ context 'when the limit is exceeded' do
+ before do
+ allow(Gitlab::ApplicationRateLimiter).to receive(:rate_limits)
+ .and_return(pipelines_create: { threshold: 1, interval: 1.minute })
+
+ stub_feature_flags(ci_throttle_pipelines_creation_dry_run: false)
+ end
+
+ it 'does not persist the pipeline' do
+ perform
+
+ expect(pipeline).not_to be_persisted
+ expect(pipeline.errors.added?(:base, throttle_message)).to be_truthy
+ end
+
+ it 'breaks the chain' do
+ perform
+
+ expect(step.break?).to be_truthy
+ end
+
+ it 'creates a log entry' do
+ expect(Gitlab::AppJsonLogger).to receive(:info).with(
+ a_hash_including(
+ class: described_class.name,
+ project_id: project.id,
+ subscription_plan: project.actual_plan_name,
+ commit_sha: command.sha
+ )
+ )
+
+ perform
+ end
+
+ context 'with child pipelines' do
+ let(:source) { 'parent_pipeline' }
+
+ it 'does not break the chain' do
+ perform
+
+ expect(step.break?).to be_falsey
+ end
+
+ it 'does not invalidate the pipeline' do
+ perform
+
+ expect(pipeline.errors).to be_empty
+ end
+
+ it 'does not log anything' do
+ expect(Gitlab::AppJsonLogger).not_to receive(:info)
+
+ perform
+ end
+ end
+
+ context 'when saving incompleted pipelines' do
+ let(:save_incompleted) { true }
+
+ it 'does not persist the pipeline' do
+ perform
+
+ expect(pipeline).not_to be_persisted
+ expect(pipeline.errors.added?(:base, throttle_message)).to be_truthy
+ end
+
+ it 'breaks the chain' do
+ perform
+
+ expect(step.break?).to be_truthy
+ end
+ end
+
+ context 'when ci_throttle_pipelines_creation is disabled' do
+ before do
+ stub_feature_flags(ci_throttle_pipelines_creation: false)
+ end
+
+ it 'does not break the chain' do
+ perform
+
+ expect(step.break?).to be_falsey
+ end
+
+ it 'does not invalidate the pipeline' do
+ perform
+
+ expect(pipeline.errors).to be_empty
+ end
+
+ it 'does not log anything' do
+ expect(Gitlab::AppJsonLogger).not_to receive(:info)
+
+ perform
+ end
+ end
+
+ context 'when ci_throttle_pipelines_creation_dry_run is enabled' do
+ before do
+ stub_feature_flags(ci_throttle_pipelines_creation_dry_run: true)
+ end
+
+ it 'does not break the chain' do
+ perform
+
+ expect(step.break?).to be_falsey
+ end
+
+ it 'does not invalidate the pipeline' do
+ perform
+
+ expect(pipeline.errors).to be_empty
+ end
+
+ it 'creates a log entry' do
+ expect(Gitlab::AppJsonLogger).to receive(:info).with(
+ a_hash_including(
+ class: described_class.name,
+ project_id: project.id,
+ subscription_plan: project.actual_plan_name,
+ commit_sha: command.sha
+ )
+ )
+
+ perform
+ end
+ end
+ end
+
+ context 'when the limit is not exceeded' do
+ it 'does not break the chain' do
+ perform
+
+ expect(step.break?).to be_falsey
+ end
+
+ it 'does not invalidate the pipeline' do
+ perform
+
+ expect(pipeline.errors).to be_empty
+ end
+
+ it 'does not log anything' do
+ expect(Gitlab::AppJsonLogger).not_to receive(:info)
+
+ perform
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb
index 8e0b032e68c..ddd0de69d79 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::TemplateUsage do
%w(Template-1 Template-2).each do |expected_template|
expect(Gitlab::UsageDataCounters::CiTemplateUniqueCounter).to(
receive(:track_unique_project_event)
- .with(project_id: project.id, template: expected_template, config_source: pipeline.config_source)
+ .with(project: project, template: expected_template, config_source: pipeline.config_source, user: user)
)
end
diff --git a/spec/lib/gitlab/ci/reports/security/report_spec.rb b/spec/lib/gitlab/ci/reports/security/report_spec.rb
index 4dc1eca3859..ab0efb90901 100644
--- a/spec/lib/gitlab/ci/reports/security/report_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/report_spec.rb
@@ -184,6 +184,22 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do
end
end
+ describe 'warnings?' do
+ subject { report.warnings? }
+
+ context 'when the report does not have any errors' do
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when the report has warnings' do
+ before do
+ report.add_warning('foo', 'bar')
+ end
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
describe '#primary_scanner_order_to' do
let(:scanner_1) { build(:ci_reports_security_scanner) }
let(:scanner_2) { build(:ci_reports_security_scanner) }
diff --git a/spec/lib/gitlab/ci/reports/security/scanner_spec.rb b/spec/lib/gitlab/ci/reports/security/scanner_spec.rb
index 99f5d4723d3..eb406e01b24 100644
--- a/spec/lib/gitlab/ci/reports/security/scanner_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/scanner_spec.rb
@@ -109,6 +109,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Scanner do
{ external_id: 'gemnasium-maven', name: 'foo', vendor: 'bar' } | { external_id: 'gemnasium-python', name: 'foo', vendor: 'bar' } | -1
{ external_id: 'gemnasium-python', name: 'foo', vendor: 'bar' } | { external_id: 'bandit', name: 'foo', vendor: 'bar' } | 1
{ external_id: 'bandit', name: 'foo', vendor: 'bar' } | { external_id: 'semgrep', name: 'foo', vendor: 'bar' } | -1
+ { external_id: 'spotbugs', name: 'foo', vendor: 'bar' } | { external_id: 'semgrep', name: 'foo', vendor: 'bar' } | -1
{ external_id: 'semgrep', name: 'foo', vendor: 'bar' } | { external_id: 'unknown', name: 'foo', vendor: 'bar' } | -1
{ external_id: 'gemnasium', name: 'foo', vendor: 'bar' } | { external_id: 'gemnasium', name: 'foo', vendor: nil } | 1
end
diff --git a/spec/lib/gitlab/ci/runner_releases_spec.rb b/spec/lib/gitlab/ci/runner_releases_spec.rb
new file mode 100644
index 00000000000..9e4a8739c0f
--- /dev/null
+++ b/spec/lib/gitlab/ci/runner_releases_spec.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::RunnerReleases do
+ subject { described_class.instance }
+
+ describe '#releases' do
+ before do
+ subject.reset!
+
+ stub_application_setting(public_runner_releases_url: 'the release API URL')
+ allow(Gitlab::HTTP).to receive(:try_get).with('the release API URL').once { mock_http_response(response) }
+ end
+
+ def releases
+ subject.releases
+ end
+
+ shared_examples 'requests that follow cache status' do |validity_period|
+ context "almost #{validity_period.inspect} later" do
+ let(:followup_request_interval) { validity_period - 0.001.seconds }
+
+ it 'returns cached releases' do
+ releases
+
+ travel followup_request_interval do
+ expect(Gitlab::HTTP).not_to receive(:try_get)
+
+ expect(releases).to eq(expected_result)
+ end
+ end
+ end
+
+ context "after #{validity_period.inspect}" do
+ let(:followup_request_interval) { validity_period + 1.second }
+ let(:followup_response) { (response || []) + [{ 'name' => 'v14.9.2' }] }
+
+ it 'checks new releases' do
+ releases
+
+ travel followup_request_interval do
+ expect(Gitlab::HTTP).to receive(:try_get).with('the release API URL').once { mock_http_response(followup_response) }
+
+ expect(releases).to eq((expected_result || []) + [Gitlab::VersionInfo.new(14, 9, 2)])
+ end
+ end
+ end
+ end
+
+ context 'when response is nil' do
+ let(:response) { nil }
+ let(:expected_result) { nil }
+
+ it 'returns nil' do
+ expect(releases).to be_nil
+ end
+
+ it_behaves_like 'requests that follow cache status', 5.seconds
+
+ it 'performs exponential backoff on requests', :aggregate_failures do
+ start_time = Time.now.utc.change(usec: 0)
+
+ http_call_timestamp_offsets = []
+ allow(Gitlab::HTTP).to receive(:try_get).with('the release API URL') do
+ http_call_timestamp_offsets << Time.now.utc - start_time
+ mock_http_response(response)
+ end
+
+ # An initial HTTP request fails
+ travel_to(start_time)
+ subject.reset!
+ expect(releases).to be_nil
+
+ # Successive failed requests result in HTTP requests only after specific backoff periods
+ backoff_periods = [5, 10, 20, 40, 80, 160, 320, 640, 1280, 2560, 3600].map(&:seconds)
+ backoff_periods.each do |period|
+ travel(period - 1.second)
+ expect(releases).to be_nil
+
+ travel 1.second
+ expect(releases).to be_nil
+ end
+
+ expect(http_call_timestamp_offsets).to eq([0, 5, 15, 35, 75, 155, 315, 635, 1275, 2555, 5115, 8715])
+
+ # Finally a successful HTTP request results in releases being returned
+ allow(Gitlab::HTTP).to receive(:try_get).with('the release API URL').once { mock_http_response([{ 'name' => 'v14.9.1' }]) }
+ travel 1.hour
+ expect(releases).not_to be_nil
+ end
+ end
+
+ context 'when response is not nil' do
+ let(:response) { [{ 'name' => 'v14.9.1' }, { 'name' => 'v14.9.0' }] }
+ let(:expected_result) { [Gitlab::VersionInfo.new(14, 9, 0), Gitlab::VersionInfo.new(14, 9, 1)] }
+
+ it 'returns parsed and sorted Gitlab::VersionInfo objects' do
+ expect(releases).to eq(expected_result)
+ end
+
+ it_behaves_like 'requests that follow cache status', 1.day
+ end
+
+ def mock_http_response(response)
+ http_response = instance_double(HTTParty::Response)
+
+ allow(http_response).to receive(:success?).and_return(response.present?)
+ allow(http_response).to receive(:parsed_response).and_return(response)
+
+ http_response
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb b/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb
new file mode 100644
index 00000000000..b430da376dd
--- /dev/null
+++ b/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
+ include StubVersion
+ using RSpec::Parameterized::TableSyntax
+
+ describe '#check_runner_upgrade_status' do
+ subject(:result) { described_class.instance.check_runner_upgrade_status(runner_version) }
+
+ before do
+ runner_releases_double = instance_double(Gitlab::Ci::RunnerReleases)
+
+ allow(Gitlab::Ci::RunnerReleases).to receive(:instance).and_return(runner_releases_double)
+ allow(runner_releases_double).to receive(:releases).and_return(available_runner_releases.map { |v| ::Gitlab::VersionInfo.parse(v) })
+ end
+
+ context 'with available_runner_releases configured up to 14.1.1' do
+ let(:available_runner_releases) { %w[13.9.0 13.9.1 13.9.2 13.10.0 13.10.1 14.0.0 14.0.1 14.0.2 14.1.0 14.1.1 14.1.1-rc3] }
+
+ context 'with nil runner_version' do
+ let(:runner_version) { nil }
+
+ it 'raises :unknown' do
+ is_expected.to eq(:unknown)
+ end
+ end
+
+ context 'with invalid runner_version' do
+ let(:runner_version) { 'junk' }
+
+ it 'raises ArgumentError' do
+ expect { subject }.to raise_error(ArgumentError)
+ end
+ end
+
+ context 'with Gitlab::VERSION set to 14.1.123' do
+ before do
+ stub_version('14.1.123', 'deadbeef')
+
+ described_class.instance.reset!
+ end
+
+ context 'with a runner_version that is too recent' do
+ let(:runner_version) { 'v14.2.0' }
+
+ it 'returns :not_available' do
+ is_expected.to eq(:not_available)
+ end
+ end
+ end
+
+ context 'with Gitlab::VERSION set to 14.0.123' do
+ before do
+ stub_version('14.0.123', 'deadbeef')
+
+ described_class.instance.reset!
+ end
+
+ context 'with valid params' do
+ where(:runner_version, :expected_result) do
+ 'v14.1.0-rc3' | :not_available # not available since the GitLab instance is still on 14.0.x
+ 'v14.1.0~beta.1574.gf6ea9389' | :not_available # suffixes are correctly handled
+ 'v14.1.0/1.1.0' | :not_available # suffixes are correctly handled
+ 'v14.1.0' | :not_available # not available since the GitLab instance is still on 14.0.x
+ 'v14.0.1' | :recommended # recommended upgrade since 14.0.2 is available
+ 'v14.0.2' | :not_available # not available since 14.0.2 is the latest 14.0.x release available
+ 'v13.10.1' | :available # available upgrade: 14.1.1
+ 'v13.10.1~beta.1574.gf6ea9389' | :available # suffixes are correctly handled
+ 'v13.10.1/1.1.0' | :available # suffixes are correctly handled
+ 'v13.10.0' | :recommended # recommended upgrade since 13.10.1 is available
+ 'v13.9.2' | :recommended # recommended upgrade since backports are no longer released for this version
+ 'v13.9.0' | :recommended # recommended upgrade since backports are no longer released for this version
+ 'v13.8.1' | :recommended # recommended upgrade since build is too old (missing in records)
+ 'v11.4.1' | :recommended # recommended upgrade since build is too old (missing in records)
+ end
+
+ with_them do
+ it 'returns symbol representing expected upgrade status' do
+ is_expected.to be_a(Symbol)
+ is_expected.to eq(expected_result)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/build/manual_spec.rb b/spec/lib/gitlab/ci/status/build/manual_spec.rb
index 78193055139..150705c1e36 100644
--- a/spec/lib/gitlab/ci/status/build/manual_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/manual_spec.rb
@@ -3,15 +3,27 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Status::Build::Manual do
- let(:user) { create(:user) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:job) { create(:ci_build, :manual) }
subject do
- build = create(:ci_build, :manual)
- described_class.new(Gitlab::Ci::Status::Core.new(build, user))
+ described_class.new(Gitlab::Ci::Status::Core.new(job, user))
end
describe '#illustration' do
it { expect(subject.illustration).to include(:image, :size, :title, :content) }
+
+ context 'when the user can trigger the job' do
+ before do
+ job.project.add_maintainer(user)
+ end
+
+ it { expect(subject.illustration[:content]).to match /This job requires manual intervention to start/ }
+ end
+
+ context 'when the user can not trigger the job' do
+ it { expect(subject.illustration[:content]).to match /This job does not run automatically and must be started manually/ }
+ end
end
describe '.matches?' do
diff --git a/spec/lib/gitlab/ci/templates/MATLAB_spec.rb b/spec/lib/gitlab/ci/templates/MATLAB_spec.rb
new file mode 100644
index 00000000000..a12d69b67a6
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/MATLAB_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'MATLAB.gitlab-ci.yml' do
+ subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('MATLAB') }
+
+ describe 'the created pipeline' do
+ let_it_be(:project) { create(:project, :auto_devops, :custom_repo, files: { 'README.md' => '' }) }
+
+ let(:user) { project.first_owner }
+ let(:default_branch) { 'master' }
+ let(:pipeline_branch) { default_branch }
+ let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
+ let(:pipeline) { service.execute!(:push).payload }
+ let(:build_names) { pipeline.builds.pluck(:name) }
+
+ before do
+ stub_ci_pipeline_yaml_file(template.content)
+ end
+
+ it 'creates all jobs' do
+ expect(build_names).to include('command', 'test', 'test_artifacts_job')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/templates/templates_spec.rb b/spec/lib/gitlab/ci/templates/templates_spec.rb
index cdda7e953d0..ca096fcecc4 100644
--- a/spec/lib/gitlab/ci/templates/templates_spec.rb
+++ b/spec/lib/gitlab/ci/templates/templates_spec.rb
@@ -23,7 +23,8 @@ RSpec.describe 'CI YML Templates' do
exceptions = [
'Security/DAST.gitlab-ci.yml', # DAST stage is defined inside AutoDevops yml
'Security/DAST-API.gitlab-ci.yml', # no auto-devops
- 'Security/API-Fuzzing.gitlab-ci.yml' # no auto-devops
+ 'Security/API-Fuzzing.gitlab-ci.yml', # no auto-devops
+ 'ThemeKit.gitlab-ci.yml'
]
context 'when including available templates in a CI YAML configuration' do
diff --git a/spec/lib/gitlab/ci/templates/themekit_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/themekit_gitlab_ci_yaml_spec.rb
new file mode 100644
index 00000000000..4708108f404
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/themekit_gitlab_ci_yaml_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'ThemeKit.gitlab-ci.yml' do
+ before do
+ allow(Gitlab::Template::GitlabCiYmlTemplate).to receive(:excluded_patterns).and_return([])
+ end
+
+ subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('ThemeKit') }
+
+ describe 'the created pipeline' do
+ let(:pipeline_ref) { project.default_branch_or_main }
+ let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
+ let(:user) { project.first_owner }
+ let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
+ let(:pipeline) { service.execute!(:push).payload }
+ let(:build_names) { pipeline.builds.pluck(:name) }
+
+ before do
+ stub_ci_pipeline_yaml_file(template.content)
+ end
+
+ context 'on the default branch' do
+ it 'only creates staging deploy', :aggregate_failures do
+ expect(pipeline.errors).to be_empty
+ expect(build_names).to include('staging')
+ expect(build_names).not_to include('production')
+ end
+ end
+
+ context 'on a tag' do
+ let(:pipeline_ref) { '1.0' }
+
+ before do
+ project.repository.add_tag(user, pipeline_ref, project.default_branch_or_main)
+ end
+
+ it 'only creates a production deploy', :aggregate_failures do
+ expect(pipeline.errors).to be_empty
+ expect(build_names).to include('production')
+ expect(build_names).not_to include('staging')
+ end
+ end
+
+ context 'outside of the default branch' do
+ let(:pipeline_ref) { 'patch-1' }
+
+ before do
+ project.repository.create_branch(pipeline_ref, project.default_branch_or_main)
+ end
+
+ it 'has no jobs' do
+ expect { pipeline }.to raise_error(
+ Ci::CreatePipelineService::CreateError, 'No stages / jobs for this pipeline.'
+ )
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/variables/builder_spec.rb b/spec/lib/gitlab/ci/variables/builder_spec.rb
index 8552a06eab3..b9aa5f7c431 100644
--- a/spec/lib/gitlab/ci/variables/builder_spec.rb
+++ b/spec/lib/gitlab/ci/variables/builder_spec.rb
@@ -199,6 +199,20 @@ RSpec.describe Gitlab::Ci::Variables::Builder do
'O' => '15', 'P' => '15')
end
end
+
+ context 'with schedule variables' do
+ let_it_be(:schedule) { create(:ci_pipeline_schedule, project: project) }
+ let_it_be(:schedule_variable) { create(:ci_pipeline_schedule_variable, pipeline_schedule: schedule) }
+
+ before do
+ pipeline.update!(pipeline_schedule_id: schedule.id)
+ end
+
+ it 'includes schedule variables' do
+ expect(subject.to_runner_variables)
+ .to include(a_hash_including(key: schedule_variable.key, value: schedule_variable.value))
+ end
+ end
end
describe '#user_variables' do
@@ -278,6 +292,14 @@ RSpec.describe Gitlab::Ci::Variables::Builder do
end
shared_examples "secret CI variables" do
+ let(:protected_variable_item) do
+ Gitlab::Ci::Variables::Collection::Item.fabricate(protected_variable)
+ end
+
+ let(:unprotected_variable_item) do
+ Gitlab::Ci::Variables::Collection::Item.fabricate(unprotected_variable)
+ end
+
context 'when ref is branch' do
context 'when ref is protected' do
before do
@@ -338,189 +360,255 @@ RSpec.describe Gitlab::Ci::Variables::Builder do
let_it_be(:protected_variable) { create(:ci_instance_variable, protected: true) }
let_it_be(:unprotected_variable) { create(:ci_instance_variable, protected: false) }
- let(:protected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(protected_variable) }
- let(:unprotected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(unprotected_variable) }
-
include_examples "secret CI variables"
end
describe '#secret_group_variables' do
- subject { builder.secret_group_variables(ref: job.git_ref, environment: job.expanded_environment_name) }
+ subject { builder.secret_group_variables(environment: job.expanded_environment_name) }
let_it_be(:protected_variable) { create(:ci_group_variable, protected: true, group: group) }
let_it_be(:unprotected_variable) { create(:ci_group_variable, protected: false, group: group) }
- context 'with ci_variables_builder_memoize_secret_variables disabled' do
- before do
- stub_feature_flags(ci_variables_builder_memoize_secret_variables: false)
+ include_examples "secret CI variables"
+
+ context 'variables memoization' do
+ let_it_be(:scoped_variable) { create(:ci_group_variable, group: group, environment_scope: 'scoped') }
+
+ let(:environment) { job.expanded_environment_name }
+ let(:scoped_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(scoped_variable) }
+
+ context 'with protected environments' do
+ it 'memoizes the result by environment' do
+ expect(pipeline.project)
+ .to receive(:protected_for?)
+ .with(pipeline.jobs_git_ref)
+ .once.and_return(true)
+
+ expect_next_instance_of(described_class::Group) do |group_variables_builder|
+ expect(group_variables_builder)
+ .to receive(:secret_variables)
+ .with(environment: 'production', protected_ref: true)
+ .once
+ .and_call_original
+ end
+
+ 2.times do
+ expect(builder.secret_group_variables(environment: 'production'))
+ .to contain_exactly(unprotected_variable_item, protected_variable_item)
+ end
+ end
end
- let(:protected_variable_item) { protected_variable }
- let(:unprotected_variable_item) { unprotected_variable }
+ context 'with unprotected environments' do
+ it 'memoizes the result by environment' do
+ expect(pipeline.project)
+ .to receive(:protected_for?)
+ .with(pipeline.jobs_git_ref)
+ .once.and_return(false)
+
+ expect_next_instance_of(described_class::Group) do |group_variables_builder|
+ expect(group_variables_builder)
+ .to receive(:secret_variables)
+ .with(environment: nil, protected_ref: false)
+ .once
+ .and_call_original
+
+ expect(group_variables_builder)
+ .to receive(:secret_variables)
+ .with(environment: 'scoped', protected_ref: false)
+ .once
+ .and_call_original
+ end
+
+ 2.times do
+ expect(builder.secret_group_variables(environment: nil))
+ .to contain_exactly(unprotected_variable_item)
- include_examples "secret CI variables"
+ expect(builder.secret_group_variables(environment: 'scoped'))
+ .to contain_exactly(unprotected_variable_item, scoped_variable_item)
+ end
+ end
+ end
end
+ end
- context 'with ci_variables_builder_memoize_secret_variables enabled' do
- before do
- stub_feature_flags(ci_variables_builder_memoize_secret_variables: true)
- end
+ describe '#secret_project_variables' do
+ let_it_be(:protected_variable) { create(:ci_variable, protected: true, project: project) }
+ let_it_be(:unprotected_variable) { create(:ci_variable, protected: false, project: project) }
- let(:protected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(protected_variable) }
- let(:unprotected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(unprotected_variable) }
+ let(:environment) { job.expanded_environment_name }
- include_examples "secret CI variables"
+ subject { builder.secret_project_variables(environment: environment) }
- context 'variables memoization' do
- let_it_be(:scoped_variable) { create(:ci_group_variable, group: group, environment_scope: 'scoped') }
+ include_examples "secret CI variables"
- let(:ref) { job.git_ref }
- let(:environment) { job.expanded_environment_name }
- let(:scoped_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(scoped_variable) }
+ context 'variables memoization' do
+ let_it_be(:scoped_variable) { create(:ci_variable, project: project, environment_scope: 'scoped') }
- context 'with protected environments' do
- it 'memoizes the result by environment' do
- expect(pipeline.project)
- .to receive(:protected_for?)
- .with(pipeline.jobs_git_ref)
- .once.and_return(true)
+ let(:scoped_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(scoped_variable) }
- expect_next_instance_of(described_class::Group) do |group_variables_builder|
- expect(group_variables_builder)
- .to receive(:secret_variables)
- .with(environment: 'production', protected_ref: true)
- .once
- .and_call_original
- end
+ context 'with protected environments' do
+ it 'memoizes the result by environment' do
+ expect(pipeline.project)
+ .to receive(:protected_for?)
+ .with(pipeline.jobs_git_ref)
+ .once.and_return(true)
- 2.times do
- expect(builder.secret_group_variables(ref: ref, environment: 'production'))
- .to contain_exactly(unprotected_variable_item, protected_variable_item)
- end
+ expect_next_instance_of(described_class::Project) do |project_variables_builder|
+ expect(project_variables_builder)
+ .to receive(:secret_variables)
+ .with(environment: 'production', protected_ref: true)
+ .once
+ .and_call_original
+ end
+
+ 2.times do
+ expect(builder.secret_project_variables(environment: 'production'))
+ .to contain_exactly(unprotected_variable_item, protected_variable_item)
end
end
+ end
- context 'with unprotected environments' do
- it 'memoizes the result by environment' do
- expect(pipeline.project)
- .to receive(:protected_for?)
- .with(pipeline.jobs_git_ref)
- .once.and_return(false)
-
- expect_next_instance_of(described_class::Group) do |group_variables_builder|
- expect(group_variables_builder)
- .to receive(:secret_variables)
- .with(environment: nil, protected_ref: false)
- .once
- .and_call_original
-
- expect(group_variables_builder)
- .to receive(:secret_variables)
- .with(environment: 'scoped', protected_ref: false)
- .once
- .and_call_original
- end
-
- 2.times do
- expect(builder.secret_group_variables(ref: 'other', environment: nil))
- .to contain_exactly(unprotected_variable_item)
-
- expect(builder.secret_group_variables(ref: 'other', environment: 'scoped'))
- .to contain_exactly(unprotected_variable_item, scoped_variable_item)
- end
+ context 'with unprotected environments' do
+ it 'memoizes the result by environment' do
+ expect(pipeline.project)
+ .to receive(:protected_for?)
+ .with(pipeline.jobs_git_ref)
+ .once.and_return(false)
+
+ expect_next_instance_of(described_class::Project) do |project_variables_builder|
+ expect(project_variables_builder)
+ .to receive(:secret_variables)
+ .with(environment: nil, protected_ref: false)
+ .once
+ .and_call_original
+
+ expect(project_variables_builder)
+ .to receive(:secret_variables)
+ .with(environment: 'scoped', protected_ref: false)
+ .once
+ .and_call_original
+ end
+
+ 2.times do
+ expect(builder.secret_project_variables(environment: nil))
+ .to contain_exactly(unprotected_variable_item)
+
+ expect(builder.secret_project_variables(environment: 'scoped'))
+ .to contain_exactly(unprotected_variable_item, scoped_variable_item)
end
end
end
end
end
- describe '#secret_project_variables' do
- let_it_be(:protected_variable) { create(:ci_variable, protected: true, project: project) }
- let_it_be(:unprotected_variable) { create(:ci_variable, protected: false, project: project) }
+ describe '#config_variables' do
+ subject(:config_variables) { builder.config_variables }
- let(:ref) { job.git_ref }
- let(:environment) { job.expanded_environment_name }
+ context 'without project' do
+ before do
+ pipeline.update!(project_id: nil)
+ end
+
+ it { expect(config_variables.size).to eq(0) }
+ end
- subject { builder.secret_project_variables(ref: ref, environment: environment) }
+ context 'without repository' do
+ let(:project) { create(:project) }
+ let(:pipeline) { build(:ci_pipeline, ref: nil, sha: nil, project: project) }
- context 'with ci_variables_builder_memoize_secret_variables disabled' do
- before do
- stub_feature_flags(ci_variables_builder_memoize_secret_variables: false)
+ it { expect(config_variables['CI_COMMIT_SHA']).to be_nil }
+ end
+
+ context 'with protected variables' do
+ let_it_be(:instance_variable) do
+ create(:ci_instance_variable, :protected, key: 'instance_variable')
+ end
+
+ let_it_be(:group_variable) do
+ create(:ci_group_variable, :protected, group: group, key: 'group_variable')
end
- let(:protected_variable_item) { protected_variable }
- let(:unprotected_variable_item) { unprotected_variable }
+ let_it_be(:project_variable) do
+ create(:ci_variable, :protected, project: project, key: 'project_variable')
+ end
- include_examples "secret CI variables"
+ it 'does not include protected variables' do
+ expect(config_variables[instance_variable.key]).to be_nil
+ expect(config_variables[group_variable.key]).to be_nil
+ expect(config_variables[project_variable.key]).to be_nil
+ end
end
- context 'with ci_variables_builder_memoize_secret_variables enabled' do
- before do
- stub_feature_flags(ci_variables_builder_memoize_secret_variables: true)
+ context 'with scoped variables' do
+ let_it_be(:scoped_group_variable) do
+ create(:ci_group_variable,
+ group: group,
+ key: 'group_variable',
+ value: 'scoped',
+ environment_scope: 'scoped')
end
- let(:protected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(protected_variable) }
- let(:unprotected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(unprotected_variable) }
+ let_it_be(:group_variable) do
+ create(:ci_group_variable,
+ group: group,
+ key: 'group_variable',
+ value: 'unscoped')
+ end
- include_examples "secret CI variables"
+ let_it_be(:scoped_project_variable) do
+ create(:ci_variable,
+ project: project,
+ key: 'project_variable',
+ value: 'scoped',
+ environment_scope: 'scoped')
+ end
- context 'variables memoization' do
- let_it_be(:scoped_variable) { create(:ci_variable, project: project, environment_scope: 'scoped') }
+ let_it_be(:project_variable) do
+ create(:ci_variable,
+ project: project,
+ key: 'project_variable',
+ value: 'unscoped')
+ end
- let(:scoped_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(scoped_variable) }
+ it 'does not include scoped variables' do
+ expect(config_variables.to_hash[group_variable.key]).to eq('unscoped')
+ expect(config_variables.to_hash[project_variable.key]).to eq('unscoped')
+ end
+ end
- context 'with protected environments' do
- it 'memoizes the result by environment' do
- expect(pipeline.project)
- .to receive(:protected_for?)
- .with(pipeline.jobs_git_ref)
- .once.and_return(true)
+ context 'variables ordering' do
+ def var(name, value)
+ { key: name, value: value.to_s, public: true, masked: false }
+ end
- expect_next_instance_of(described_class::Project) do |project_variables_builder|
- expect(project_variables_builder)
- .to receive(:secret_variables)
- .with(environment: 'production', protected_ref: true)
- .once
- .and_call_original
- end
+ before do
+ allow(pipeline.project).to receive(:predefined_variables) { [var('A', 1), var('B', 1)] }
+ allow(pipeline).to receive(:predefined_variables) { [var('B', 2), var('C', 2)] }
+ allow(builder).to receive(:secret_instance_variables) { [var('C', 3), var('D', 3)] }
+ allow(builder).to receive(:secret_group_variables) { [var('D', 4), var('E', 4)] }
+ allow(builder).to receive(:secret_project_variables) { [var('E', 5), var('F', 5)] }
+ allow(pipeline).to receive(:variables) { [var('F', 6), var('G', 6)] }
+ allow(pipeline).to receive(:pipeline_schedule) { double(job_variables: [var('G', 7), var('H', 7)]) }
+ end
- 2.times do
- expect(builder.secret_project_variables(ref: ref, environment: 'production'))
- .to contain_exactly(unprotected_variable_item, protected_variable_item)
- end
- end
- end
+ it 'returns variables in order depending on resource hierarchy' do
+ expect(config_variables.to_runner_variables).to eq(
+ [var('A', 1), var('B', 1),
+ var('B', 2), var('C', 2),
+ var('C', 3), var('D', 3),
+ var('D', 4), var('E', 4),
+ var('E', 5), var('F', 5),
+ var('F', 6), var('G', 6),
+ var('G', 7), var('H', 7)])
+ end
- context 'with unprotected environments' do
- it 'memoizes the result by environment' do
- expect(pipeline.project)
- .to receive(:protected_for?)
- .with(pipeline.jobs_git_ref)
- .once.and_return(false)
-
- expect_next_instance_of(described_class::Project) do |project_variables_builder|
- expect(project_variables_builder)
- .to receive(:secret_variables)
- .with(environment: nil, protected_ref: false)
- .once
- .and_call_original
-
- expect(project_variables_builder)
- .to receive(:secret_variables)
- .with(environment: 'scoped', protected_ref: false)
- .once
- .and_call_original
- end
-
- 2.times do
- expect(builder.secret_project_variables(ref: 'other', environment: nil))
- .to contain_exactly(unprotected_variable_item)
-
- expect(builder.secret_project_variables(ref: 'other', environment: 'scoped'))
- .to contain_exactly(unprotected_variable_item, scoped_variable_item)
- end
- end
- end
+ it 'overrides duplicate keys depending on resource hierarchy' do
+ expect(config_variables.to_hash).to match(
+ 'A' => '1', 'B' => '2',
+ 'C' => '3', 'D' => '4',
+ 'E' => '5', 'F' => '6',
+ 'G' => '7', 'H' => '7')
end
end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index ebb5c91ebad..9b68ee2d6a2 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -842,7 +842,7 @@ module Gitlab
describe "Image and service handling" do
context "when extended docker configuration is used" do
it "returns image and service when defined" do
- config = YAML.dump({ image: { name: "ruby:2.7", entrypoint: ["/usr/local/bin/init", "run"] },
+ config = YAML.dump({ image: { name: "image:1.0", entrypoint: ["/usr/local/bin/init", "run"] },
services: ["mysql", { name: "docker:dind", alias: "docker",
entrypoint: ["/usr/local/bin/init", "run"],
command: ["/usr/local/bin/init", "run"] }],
@@ -860,7 +860,7 @@ module Gitlab
options: {
before_script: ["pwd"],
script: ["rspec"],
- image: { name: "ruby:2.7", entrypoint: ["/usr/local/bin/init", "run"] },
+ image: { name: "image:1.0", entrypoint: ["/usr/local/bin/init", "run"] },
services: [{ name: "mysql" },
{ name: "docker:dind", alias: "docker", entrypoint: ["/usr/local/bin/init", "run"],
command: ["/usr/local/bin/init", "run"] }]
@@ -874,10 +874,10 @@ module Gitlab
end
it "returns image and service when overridden for job" do
- config = YAML.dump({ image: "ruby:2.7",
+ config = YAML.dump({ image: "image:1.0",
services: ["mysql"],
before_script: ["pwd"],
- rspec: { image: { name: "ruby:3.0", entrypoint: ["/usr/local/bin/init", "run"] },
+ rspec: { image: { name: "image:1.0", entrypoint: ["/usr/local/bin/init", "run"] },
services: [{ name: "postgresql", alias: "db-pg",
entrypoint: ["/usr/local/bin/init", "run"],
command: ["/usr/local/bin/init", "run"] }, "docker:dind"],
@@ -894,7 +894,7 @@ module Gitlab
options: {
before_script: ["pwd"],
script: ["rspec"],
- image: { name: "ruby:3.0", entrypoint: ["/usr/local/bin/init", "run"] },
+ image: { name: "image:1.0", entrypoint: ["/usr/local/bin/init", "run"] },
services: [{ name: "postgresql", alias: "db-pg", entrypoint: ["/usr/local/bin/init", "run"],
command: ["/usr/local/bin/init", "run"] },
{ name: "docker:dind" }]
@@ -910,7 +910,7 @@ module Gitlab
context "when etended docker configuration is not used" do
it "returns image and service when defined" do
- config = YAML.dump({ image: "ruby:2.7",
+ config = YAML.dump({ image: "image:1.0",
services: ["mysql", "docker:dind"],
before_script: ["pwd"],
rspec: { script: "rspec" } })
@@ -926,7 +926,7 @@ module Gitlab
options: {
before_script: ["pwd"],
script: ["rspec"],
- image: { name: "ruby:2.7" },
+ image: { name: "image:1.0" },
services: [{ name: "mysql" }, { name: "docker:dind" }]
},
allow_failure: false,
@@ -938,10 +938,10 @@ module Gitlab
end
it "returns image and service when overridden for job" do
- config = YAML.dump({ image: "ruby:2.7",
+ config = YAML.dump({ image: "image:1.0",
services: ["mysql"],
before_script: ["pwd"],
- rspec: { image: "ruby:3.0", services: ["postgresql", "docker:dind"], script: "rspec" } })
+ rspec: { image: "image:1.0", services: ["postgresql", "docker:dind"], script: "rspec" } })
config_processor = Gitlab::Ci::YamlProcessor.new(config).execute
@@ -954,7 +954,7 @@ module Gitlab
options: {
before_script: ["pwd"],
script: ["rspec"],
- image: { name: "ruby:3.0" },
+ image: { name: "image:1.0" },
services: [{ name: "postgresql" }, { name: "docker:dind" }]
},
allow_failure: false,
@@ -1557,7 +1557,7 @@ module Gitlab
describe "Artifacts" do
it "returns artifacts when defined" do
config = YAML.dump({
- image: "ruby:2.7",
+ image: "image:1.0",
services: ["mysql"],
before_script: ["pwd"],
rspec: {
@@ -1583,7 +1583,7 @@ module Gitlab
options: {
before_script: ["pwd"],
script: ["rspec"],
- image: { name: "ruby:2.7" },
+ image: { name: "image:1.0" },
services: [{ name: "mysql" }],
artifacts: {
name: "custom_name",
@@ -2327,7 +2327,7 @@ module Gitlab
context 'when hidden job have a script definition' do
let(:config) do
YAML.dump({
- '.hidden_job' => { image: 'ruby:2.7', script: 'test' },
+ '.hidden_job' => { image: 'image:1.0', script: 'test' },
'normal_job' => { script: 'test' }
})
end
@@ -2338,7 +2338,7 @@ module Gitlab
context "when hidden job doesn't have a script definition" do
let(:config) do
YAML.dump({
- '.hidden_job' => { image: 'ruby:2.7' },
+ '.hidden_job' => { image: 'image:1.0' },
'normal_job' => { script: 'test' }
})
end