diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-20 23:50:22 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-20 23:50:22 +0000 |
commit | 9dc93a4519d9d5d7be48ff274127136236a3adb3 (patch) | |
tree | 70467ae3692a0e35e5ea56bcb803eb512a10bedb /spec/lib/gitlab/ci | |
parent | 4b0f34b6d759d6299322b3a54453e930c6121ff0 (diff) | |
download | gitlab-ce-9dc93a4519d9d5d7be48ff274127136236a3adb3.tar.gz |
Add latest changes from gitlab-org/gitlab@13-11-stable-eev13.11.0-rc43
Diffstat (limited to 'spec/lib/gitlab/ci')
33 files changed, 1027 insertions, 259 deletions
diff --git a/spec/lib/gitlab/ci/ansi2json/style_spec.rb b/spec/lib/gitlab/ci/ansi2json/style_spec.rb index ff70ff69aaa..87085950a9f 100644 --- a/spec/lib/gitlab/ci/ansi2json/style_spec.rb +++ b/spec/lib/gitlab/ci/ansi2json/style_spec.rb @@ -160,9 +160,9 @@ RSpec.describe Gitlab::Ci::Ansi2json::Style do with_them do it 'change the style' do style = described_class.new - style.update(initial_state) + style.update(initial_state) # rubocop:disable Rails/SaveBang - style.update(ansi_commands) + style.update(ansi_commands) # rubocop:disable Rails/SaveBang expect(style.to_s).to eq(result) end diff --git a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb index 179578fe0a8..d294eca7f15 100644 --- a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb @@ -107,6 +107,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Bridge do stage: 'test', only: { refs: %w[branches tags] }, variables: {}, + job_variables: {}, + root_variables_inheritance: true, scheduling_type: :stage) end end @@ -130,6 +132,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Bridge do stage: 'test', only: { refs: %w[branches tags] }, variables: {}, + job_variables: {}, + root_variables_inheritance: true, scheduling_type: :stage) end end @@ -284,6 +288,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Bridge do parallel: { matrix: [{ 'PROVIDER' => ['aws'], 'STACK' => %w(monitoring app1) }, { 'PROVIDER' => ['gcp'], 'STACK' => %w(data) }] }, variables: {}, + job_variables: {}, + root_variables_inheritance: true, scheduling_type: :stage ) end diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb index 064990667d5..cec1c97085b 100644 --- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb @@ -13,6 +13,14 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do end describe '#valid?' do + context 'with an empty hash as cache' do + let(:config) { {} } + + it 'is valid' do + expect(entry).to be_valid + end + end + context 'when configuration is valid with a single cache' do let(:config) { { key: 'key', paths: ["logs/"], untracked: true } } diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb index a4167003987..ffcd029172a 100644 --- a/spec/lib/gitlab/ci/config/entry/job_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb @@ -663,6 +663,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do after_script: %w[cleanup], only: { refs: %w[branches tags] }, variables: {}, + job_variables: {}, + root_variables_inheritance: true, scheduling_type: :stage) end end diff --git a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb index ac6b589ec6b..cb73044b62b 100644 --- a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb @@ -100,6 +100,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Jobs do stage: 'test', trigger: { project: 'my/project' }, variables: {}, + job_variables: {}, + root_variables_inheritance: true, scheduling_type: :stage }, regular_job: { @@ -109,6 +111,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Jobs do script: ['something'], stage: 'test', variables: {}, + job_variables: {}, + root_variables_inheritance: true, scheduling_type: :stage }) end diff --git a/spec/lib/gitlab/ci/config/entry/processable_spec.rb b/spec/lib/gitlab/ci/config/entry/processable_spec.rb index 04e80450263..016d59e98b9 100644 --- a/spec/lib/gitlab/ci/config/entry/processable_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/processable_spec.rb @@ -382,7 +382,9 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do context 'with only job variables' do it 'does return defined variables' do expect(entry.value).to include( - variables: { 'A' => 'job', 'B' => 'job' } + variables: { 'A' => 'job', 'B' => 'job' }, + job_variables: { 'A' => 'job', 'B' => 'job' }, + root_variables_inheritance: true ) end end @@ -394,9 +396,11 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do ).value end - it 'does return all variables and overwrite them' do + it 'does return job and root variables' do expect(entry.value).to include( - variables: { 'A' => 'job', 'B' => 'job', 'C' => 'root', 'D' => 'root' } + variables: { 'A' => 'job', 'B' => 'job', 'C' => 'root', 'D' => 'root' }, + job_variables: { 'A' => 'job', 'B' => 'job' }, + root_variables_inheritance: true ) end @@ -408,9 +412,11 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do } end - it 'does return only job variables' do + it 'does return job and root variables' do expect(entry.value).to include( - variables: { 'A' => 'job', 'B' => 'job' } + variables: { 'A' => 'job', 'B' => 'job' }, + job_variables: { 'A' => 'job', 'B' => 'job' }, + root_variables_inheritance: false ) end end @@ -423,9 +429,11 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do } end - it 'does return only job variables' do + it 'does return job and root variables' do expect(entry.value).to include( - variables: { 'A' => 'job', 'B' => 'job', 'D' => 'root' } + variables: { 'A' => 'job', 'B' => 'job', 'D' => 'root' }, + job_variables: { 'A' => 'job', 'B' => 'job' }, + root_variables_inheritance: ['D'] ) end end @@ -493,7 +501,9 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do name: :rspec, stage: 'test', only: { refs: %w[branches tags] }, - variables: {} + variables: {}, + job_variables: {}, + root_variables_inheritance: true ) end end diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb index 7b38c21788f..041eb748fc9 100644 --- a/spec/lib/gitlab/ci/config/entry/root_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb @@ -133,6 +133,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do stage: 'test', cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }], variables: { 'VAR' => 'root', 'VAR2' => 'val 2' }, + job_variables: {}, + root_variables_inheritance: true, ignore: false, after_script: ['make clean'], only: { refs: %w[branches tags] }, @@ -147,6 +149,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do stage: 'test', cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }], variables: { 'VAR' => 'root', 'VAR2' => 'val 2' }, + job_variables: {}, + root_variables_inheritance: true, ignore: false, after_script: ['make clean'], only: { refs: %w[branches tags] }, @@ -163,6 +167,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success' }], only: { refs: %w(branches tags) }, variables: { 'VAR' => 'job', 'VAR2' => 'val 2' }, + job_variables: { 'VAR' => 'job' }, + root_variables_inheritance: true, after_script: [], ignore: false, scheduling_type: :stage } @@ -188,6 +194,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do stage: 'test', cache: { key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }, variables: { 'VAR' => 'root', 'VAR2' => 'val 2' }, + job_variables: {}, + root_variables_inheritance: true, ignore: false, after_script: ['make clean'], only: { refs: %w[branches tags] }, @@ -202,6 +210,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do stage: 'test', cache: { key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }, variables: { 'VAR' => 'root', 'VAR2' => 'val 2' }, + job_variables: {}, + root_variables_inheritance: true, ignore: false, after_script: ['make clean'], only: { refs: %w[branches tags] }, @@ -218,6 +228,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do cache: { key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success' }, only: { refs: %w(branches tags) }, variables: { 'VAR' => 'job', 'VAR2' => 'val 2' }, + job_variables: { 'VAR' => 'job' }, + root_variables_inheritance: true, after_script: [], ignore: false, scheduling_type: :stage } @@ -267,6 +279,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do stage: 'test', cache: { key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }, variables: { 'VAR' => 'root' }, + job_variables: {}, + root_variables_inheritance: true, ignore: false, after_script: ['make clean'], only: { refs: %w[branches tags] }, @@ -279,6 +293,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do stage: 'test', cache: { key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }, variables: { 'VAR' => 'job' }, + job_variables: { 'VAR' => 'job' }, + root_variables_inheritance: true, ignore: false, after_script: ['make clean'], only: { refs: %w[branches tags] }, @@ -311,6 +327,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do stage: 'test', cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }], variables: { 'VAR' => 'root' }, + job_variables: {}, + root_variables_inheritance: true, ignore: false, after_script: ['make clean'], only: { refs: %w[branches tags] }, @@ -323,6 +341,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do stage: 'test', cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }], variables: { 'VAR' => 'job' }, + job_variables: { 'VAR' => 'job' }, + root_variables_inheritance: true, ignore: false, after_script: ['make clean'], only: { refs: %w[branches tags] }, diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb index 99f546ceb37..e5b008a482e 100644 --- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb +++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb @@ -324,5 +324,39 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do end end end + + context 'when local file path has wildcard' do + let(:project) { create(:project, :repository) } + + let(:values) do + { include: 'myfolder/*.yml' } + end + + before do + allow_next_instance_of(Repository) do |repository| + allow(repository).to receive(:search_files_by_wildcard_path).with('myfolder/*.yml', '123456') do + ['myfolder/file1.yml', 'myfolder/file2.yml'] + end + end + end + + it 'includes the matched local files' do + expect(subject).to contain_exactly(an_instance_of(Gitlab::Ci::Config::External::File::Local), + an_instance_of(Gitlab::Ci::Config::External::File::Local)) + + expect(subject.map(&:location)).to contain_exactly('myfolder/file1.yml', 'myfolder/file2.yml') + end + + context 'when the FF ci_wildcard_file_paths is disabled' do + before do + stub_feature_flags(ci_wildcard_file_paths: false) + end + + it 'cannot find any file returns an error message' do + expect(subject).to contain_exactly(an_instance_of(Gitlab::Ci::Config::External::File::Local)) + expect(subject[0].errors).to eq(['Local file `myfolder/*.yml` does not exist!']) + end + 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 d2d7116bb12..d657c3e943f 100644 --- a/spec/lib/gitlab/ci/config/external/processor_spec.rb +++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb @@ -366,5 +366,40 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do expect(output.keys).to match_array([:image, :my_build, :my_test]) end end + + context 'when local file path has wildcard' do + let_it_be(:project) { create(:project, :repository) } + + let(:values) do + { include: 'myfolder/*.yml', image: 'ruby:2.7' } + end + + before do + allow_next_instance_of(Repository) do |repository| + allow(repository).to receive(:search_files_by_wildcard_path).with('myfolder/*.yml', sha) do + ['myfolder/file1.yml', 'myfolder/file2.yml'] + end + + allow(repository).to receive(:blob_data_at).with(sha, 'myfolder/file1.yml') do + <<~HEREDOC + my_build: + script: echo Hello World + HEREDOC + end + + allow(repository).to receive(:blob_data_at).with(sha, 'myfolder/file2.yml') do + <<~HEREDOC + my_test: + script: echo Hello World + HEREDOC + end + end + end + + it 'fetches the matched files' do + output = processor.perform + expect(output.keys).to match_array([:image, :my_build, :my_test]) + end + end end end diff --git a/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb b/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb index fbf86927bd9..e5f0341c5fe 100644 --- a/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb +++ b/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb @@ -1,8 +1,12 @@ # frozen_string_literal: true require 'fast_spec_helper' +require 'support/helpers/stubbed_feature' +require 'support/helpers/stub_feature_flags' RSpec.describe Gitlab::Ci::Config::Normalizer::MatrixStrategy do + include StubFeatureFlags + describe '.applies_to?' do subject { described_class.applies_to?(config) } @@ -49,6 +53,10 @@ RSpec.describe Gitlab::Ci::Config::Normalizer::MatrixStrategy do variables: { 'PROVIDER' => 'aws', 'STACK' => 'app1' + }, + job_variables: { + 'PROVIDER' => 'aws', + 'STACK' => 'app1' } }, { @@ -58,6 +66,10 @@ RSpec.describe Gitlab::Ci::Config::Normalizer::MatrixStrategy do variables: { 'PROVIDER' => 'aws', 'STACK' => 'app2' + }, + job_variables: { + 'PROVIDER' => 'aws', + 'STACK' => 'app2' } }, { @@ -67,6 +79,10 @@ RSpec.describe Gitlab::Ci::Config::Normalizer::MatrixStrategy do variables: { 'PROVIDER' => 'ovh', 'STACK' => 'app' + }, + job_variables: { + 'PROVIDER' => 'ovh', + 'STACK' => 'app' } }, { @@ -76,6 +92,10 @@ RSpec.describe Gitlab::Ci::Config::Normalizer::MatrixStrategy do variables: { 'PROVIDER' => 'gcp', 'STACK' => 'app' + }, + job_variables: { + 'PROVIDER' => 'gcp', + 'STACK' => 'app' } } ] diff --git a/spec/lib/gitlab/ci/lint_spec.rb b/spec/lib/gitlab/ci/lint_spec.rb index 67324c09d86..aaa3a7a8b9d 100644 --- a/spec/lib/gitlab/ci/lint_spec.rb +++ b/spec/lib/gitlab/ci/lint_spec.rb @@ -92,7 +92,7 @@ RSpec.describe Gitlab::Ci::Lint do it 'sets merged_config' do root_config = YAML.safe_load(content, [Symbol]) included_config = YAML.safe_load(included_content, [Symbol]) - expected_config = included_config.merge(root_config).except(:include) + expected_config = included_config.merge(root_config).except(:include).deep_stringify_keys expect(subject.merged_yaml).to eq(expected_config.to_yaml) end diff --git a/spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb b/spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb index c6b8cf2a985..6a08e8f0b7f 100644 --- a/spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb +++ b/spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb @@ -131,7 +131,6 @@ RSpec.describe Gitlab::Ci::Parsers::Codequality::CodeClimate do expect { parse }.not_to raise_error expect(codequality_report.degradations_count).to eq(0) - expect(codequality_report.error_message).to eq("Invalid degradation format: The property '#/' did not contain a required property of 'location'") end end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb index 9ca5aeeea58..900dfec38e2 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb @@ -321,4 +321,25 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do it { is_expected.to be_falsey } end end + + describe '#increment_pipeline_failure_reason_counter' do + let(:command) { described_class.new } + let(:reason) { :size_limit_exceeded } + + subject { command.increment_pipeline_failure_reason_counter(reason) } + + it 'increments the error metric' do + counter = Gitlab::Metrics.counter(:gitlab_ci_pipeline_failure_reasons, 'desc') + expect { subject }.to change { counter.get(reason: reason.to_s) }.by(1) + end + + context 'when the reason is nil' do + let(:reason) { nil } + + it 'increments the error metric with unknown_failure' do + counter = Gitlab::Metrics.counter(:gitlab_ci_pipeline_failure_reasons, 'desc') + expect { subject }.to change { counter.get(reason: 'unknown_failure') }.by(1) + end + end + end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb index 4ae51ac8bf9..e30a78546af 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb @@ -16,8 +16,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules do describe '#perform!' do context 'when pipeline has been skipped by workflow configuration' do before do - allow(step).to receive(:workflow_passed?) - .and_return(false) + allow(step).to receive(:workflow_rules_result) + .and_return( + double(pass?: false, variables: {}) + ) step.perform! end @@ -33,12 +35,18 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules do it 'attaches an error to the pipeline' do expect(pipeline.errors[:base]).to include('Pipeline filtered out by workflow rules.') end + + it 'saves workflow_rules_result' do + expect(command.workflow_rules_result.variables).to eq({}) + end end context 'when pipeline has not been skipped by workflow configuration' do before do - allow(step).to receive(:workflow_passed?) - .and_return(true) + allow(step).to receive(:workflow_rules_result) + .and_return( + double(pass?: true, variables: { 'VAR1' => 'val2' }) + ) step.perform! end @@ -55,6 +63,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules do it 'attaches no errors' do expect(pipeline.errors).to be_empty end + + it 'saves workflow_rules_result' do + expect(command.workflow_rules_result.variables).to eq({ 'VAR1' => 'val2' }) + end end end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb new file mode 100644 index 00000000000..bcea6462790 --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Pipeline::Chain::Helpers do + let(:helper_class) do + Class.new do + include Gitlab::Ci::Pipeline::Chain::Helpers + + attr_accessor :pipeline, :command + + def initialize(pipeline, command) + self.pipeline = pipeline + self.command = command + end + end + end + + subject(:helper) { helper_class.new(pipeline, command) } + + let(:pipeline) { build(:ci_empty_pipeline) } + let(:command) { double(save_incompleted: true) } + let(:message) { 'message' } + + describe '.error' do + shared_examples 'error function' do + specify do + expect(pipeline).to receive(:drop!).with(drop_reason).and_call_original + expect(pipeline).to receive(:add_error_message).with(message).and_call_original + expect(pipeline).to receive(:ensure_project_iid!).twice.and_call_original + + subject.error(message, config_error: config_error, drop_reason: drop_reason) + + expect(pipeline.yaml_errors).to eq(yaml_error) + expect(pipeline.errors[:base]).to include(message) + end + end + + context 'when given a drop reason' do + context 'when config error is true' do + context 'sets the yaml error and overrides the drop reason' do + let(:drop_reason) { :config_error } + let(:config_error) { true } + let(:yaml_error) { message } + + it_behaves_like "error function" + end + end + + context 'when config error is false' do + context 'does not set the yaml error or override the drop reason' do + let(:drop_reason) { :size_limit_exceeded } + let(:config_error) { false } + let(:yaml_error) { nil } + + it_behaves_like "error function" + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb index 78363be7f36..23cdec61bb3 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb @@ -11,7 +11,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Chain::Limit::Deployments do let(:save_incompleted) { false } let(:command) do - double(:command, + Gitlab::Ci::Pipeline::Chain::Command.new( project: project, pipeline_seed: pipeline_seed, save_incompleted: save_incompleted @@ -49,6 +49,11 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Chain::Limit::Deployments do expect(pipeline.deployments_limit_exceeded?).to be true end + + it 'calls increment_pipeline_failure_reason_counter' do + counter = Gitlab::Metrics.counter(:gitlab_ci_pipeline_failure_reasons, 'desc') + expect { perform }.to change { counter.get(reason: 'deployments_limit_exceeded') }.by(1) + end end context 'when not saving incomplete pipelines' do @@ -71,6 +76,12 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Chain::Limit::Deployments do expect(pipeline.errors.messages).to include(base: ['Pipeline has too many deployments! Requested 2, but the limit is 1.']) end + + it 'increments the error metric' do + expect(command).to receive(:increment_pipeline_failure_reason_counter).with(:deployments_limit_exceeded) + + perform + end end it 'logs the error' do diff --git a/spec/lib/gitlab/ci/pipeline/chain/pipeline/process_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/pipeline/process_spec.rb new file mode 100644 index 00000000000..3885cea2d1b --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/pipeline/process_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Pipeline::Chain::Pipeline::Process do + let_it_be(:project) { build(:project) } + let_it_be(:user) { build(:user) } + let_it_be(:pipeline) { build(:ci_pipeline, project: project, id: 42) } + + let_it_be(:command) do + Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user) + end + + let(:step) { described_class.new(pipeline, command) } + + describe '#perform!' do + subject(:perform) { step.perform! } + + it 'schedules a job to process the pipeline' do + expect(Ci::InitialPipelineProcessWorker) + .to receive(:perform_async) + .with(42) + + perform + end + end + + describe '#break?' do + it { expect(step.break?).to be_falsey } + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index 5506b079d0f..62de4d2e96d 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -22,6 +22,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Populate do [ Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command), Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command), + Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules.new(pipeline, command), Gitlab::Ci::Pipeline::Chain::SeedBlock.new(pipeline, command), Gitlab::Ci::Pipeline::Chain::Seed.new(pipeline, command) ] @@ -95,6 +96,11 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Populate do it 'wastes pipeline iid' do expect(InternalId.ci_pipelines.where(project_id: project.id).last.last_value).to be > 0 end + + it 'increments the error metric' do + counter = Gitlab::Metrics.counter(:gitlab_ci_pipeline_failure_reasons, 'desc') + expect { run_chain }.to change { counter.get(reason: 'unknown_failure') }.by(1) + end end describe 'pipeline protect' do diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb index 80013cab6ee..264076859cb 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb @@ -3,24 +3,16 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do - let(:project) { create(:project, :repository) } - let(:user) { create(:user, developer_projects: [project]) } - let(:seeds_block) { } - - let(:command) do - Gitlab::Ci::Pipeline::Chain::Command.new( - project: project, - current_user: user, - origin_ref: 'master', - seeds_block: seeds_block) - end + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { create(:user, developer_projects: [project]) } + let(:seeds_block) { } + let(:command) { initialize_command } let(:pipeline) { build(:ci_pipeline, project: project) } describe '#perform!' do before do stub_ci_pipeline_yaml_file(YAML.dump(config)) - run_chain end let(:config) do @@ -28,23 +20,25 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end subject(:run_chain) do - [ - Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command), - Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command) - ].map(&:perform!) - - described_class.new(pipeline, command).perform! + run_previous_chain(pipeline, command) + perform_seed(pipeline, command) end it 'allocates next IID' do + run_chain + expect(pipeline.iid).to be_present end it 'ensures ci_ref' do + run_chain + expect(pipeline.ci_ref).to be_present end it 'sets the seeds in the command object' do + run_chain + expect(command.pipeline_seed).to be_a(Gitlab::Ci::Pipeline::Seed::Pipeline) expect(command.pipeline_seed.size).to eq 1 end @@ -59,6 +53,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end it 'correctly fabricates stages and builds' do + run_chain + seed = command.pipeline_seed expect(seed.stages.size).to eq 2 @@ -84,6 +80,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end it 'returns pipeline seed with jobs only assigned to master' do + run_chain + seed = command.pipeline_seed expect(seed.size).to eq 1 @@ -103,6 +101,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end it 'returns pipeline seed with jobs only assigned to schedules' do + run_chain + seed = command.pipeline_seed expect(seed.size).to eq 1 @@ -130,6 +130,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do let(:pipeline) { build(:ci_pipeline, project: project) } it 'returns seeds for kubernetes dependent job' do + run_chain + seed = command.pipeline_seed expect(seed.size).to eq 2 @@ -141,6 +143,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do context 'when kubernetes is not active' do it 'does not return seeds for kubernetes dependent job' do + run_chain + seed = command.pipeline_seed expect(seed.size).to eq 1 @@ -158,6 +162,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end it 'returns stage seeds only when variables expression is truthy' do + run_chain + seed = command.pipeline_seed expect(seed.size).to eq 1 @@ -171,8 +177,125 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end it 'does not execute the block' do + run_chain + expect(pipeline.variables.size).to eq(0) end end + + describe '#root_variables' do + let(:config) do + { + variables: { VAR1: 'var 1' }, + workflow: { + rules: [{ if: '$CI_PIPELINE_SOURCE', + variables: { VAR1: 'overridden var 1' } }, + { when: 'always' }] + }, + rspec: { script: 'rake' } + } + end + + let(:rspec_variables) { command.pipeline_seed.stages[0].statuses[0].variables.to_hash } + + it 'sends root variable with overridden by rules' do + run_chain + + expect(rspec_variables['VAR1']).to eq('overridden var 1') + end + + context 'when the FF ci_workflow_rules_variables is disabled' do + before do + stub_feature_flags(ci_workflow_rules_variables: false) + end + + it 'sends root variable' do + run_chain + + expect(rspec_variables['VAR1']).to eq('var 1') + end + end + end + + context 'N+1 queries' do + it 'avoids N+1 queries when calculating variables of jobs' do + pipeline1, command1 = prepare_pipeline1 + pipeline2, command2 = prepare_pipeline2 + + control = ActiveRecord::QueryRecorder.new do + perform_seed(pipeline1, command1) + end + + expect { perform_seed(pipeline2, command2) }.not_to exceed_query_limit( + control.count + expected_extra_queries + ) + end + + private + + def prepare_pipeline1 + config1 = { build: { stage: 'build', script: 'build' } } + stub_ci_pipeline_yaml_file(YAML.dump(config1)) + pipeline1 = build(:ci_pipeline, project: project) + command1 = initialize_command + + run_previous_chain(pipeline1, command1) + + [pipeline1, command1] + end + + def prepare_pipeline2 + config2 = { build1: { stage: 'build', script: 'build1' }, + build2: { stage: 'build', script: 'build2' }, + test: { stage: 'build', script: 'test' } } + stub_ci_pipeline_yaml_file(YAML.dump(config2)) + pipeline2 = build(:ci_pipeline, project: project) + command2 = initialize_command + + run_previous_chain(pipeline2, command2) + + [pipeline2, command2] + end + + def expected_extra_queries + extra_jobs = 2 + non_handled_sql_queries = 3 + + # 1. Ci::Build Load () SELECT "ci_builds".* FROM "ci_builds" + # WHERE "ci_builds"."type" = 'Ci::Build' + # AND "ci_builds"."commit_id" IS NULL + # AND ("ci_builds"."retried" = FALSE OR "ci_builds"."retried" IS NULL) + # AND (stage_idx < 1) + # 2. Ci::InstanceVariable Load => `Ci::InstanceVariable#cached_data` => already cached with `fetch_memory_cache` + # 3. Ci::Variable Load => `Project#ci_variables_for` => already cached with `Gitlab::SafeRequestStore` + + extra_jobs * non_handled_sql_queries + end + end + + private + + def run_previous_chain(pipeline, command) + [ + Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command), + Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command), + Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules.new(pipeline, command) + ].map(&:perform!) + end + + def perform_seed(pipeline, command) + described_class.new(pipeline, command).perform! + end + end + + private + + def initialize_command + Gitlab::Ci::Pipeline::Chain::Command.new( + project: project, + current_user: user, + origin_ref: 'master', + seeds_block: seeds_block + ) end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb index e55281f9705..caf3a053c4e 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do - let(:project) { create(:project) } - let(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } let(:pipeline) { build(:ci_empty_pipeline, user: user, project: project) } let!(:step) { described_class.new(pipeline, command) } @@ -42,6 +42,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do end let(:save_incompleted) { true } + let(:dot_com) { true } let(:command) do Gitlab::Ci::Pipeline::Chain::Command.new( project: project, current_user: user, yaml_processor_result: yaml_processor_result, save_incompleted: save_incompleted @@ -51,11 +52,79 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do describe '#perform!' do subject(:perform!) { step.perform! } - context 'when validation returns true' do + let(:validation_service_url) { 'https://validation-service.external/' } + + before do + stub_env('EXTERNAL_VALIDATION_SERVICE_URL', validation_service_url) + allow(Gitlab).to receive(:com?).and_return(dot_com) + allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('correlation-id') + end + + context 'with configuration values in ApplicationSetting' do + let(:alternate_validation_service_url) { 'https://alternate-validation-service.external/' } + let(:validation_service_token) { 'SECURE_TOKEN' } + let(:shorter_timeout) { described_class::DEFAULT_VALIDATION_REQUEST_TIMEOUT - 1 } + before do - allow(step).to receive(:validate_external).and_return(true) + stub_env('EXTERNAL_VALIDATION_SERVICE_TOKEN', 'TOKEN_IN_ENV') + allow(Gitlab::CurrentSettings.current_application_settings).to receive(:external_pipeline_validation_service_timeout).and_return(shorter_timeout) + allow(Gitlab::CurrentSettings.current_application_settings).to receive(:external_pipeline_validation_service_token).and_return(validation_service_token) + allow(Gitlab::CurrentSettings.current_application_settings).to receive(:external_pipeline_validation_service_url).and_return(alternate_validation_service_url) + end + + it 'uses those values rather than env vars or defaults' do + expect(::Gitlab::HTTP).to receive(:post) do |url, params| + expect(url).to eq(alternate_validation_service_url) + expect(params[:timeout]).to eq(shorter_timeout) + expect(params[:headers]).to include('X-Gitlab-Token' => validation_service_token) + expect(params[:timeout]).to eq(shorter_timeout) + end + + perform! + end + end + + it 'respects the defined payload schema' do + expect(::Gitlab::HTTP).to receive(:post) do |_url, params| + expect(params[:body]).to match_schema('/external_validation') + expect(params[:timeout]).to eq(described_class::DEFAULT_VALIDATION_REQUEST_TIMEOUT) + expect(params[:headers]).to eq({ 'X-Gitlab-Correlation-id' => 'correlation-id' }) + end + + perform! + end + + context 'with EXTERNAL_VALIDATION_SERVICE_TIMEOUT defined' do + before do + stub_env('EXTERNAL_VALIDATION_SERVICE_TIMEOUT', validation_service_timeout) + end + + context 'with valid value' do + let(:validation_service_timeout) { '1' } + + it 'uses defined timeout' do + expect(::Gitlab::HTTP).to receive(:post) do |_url, params| + expect(params[:timeout]).to eq(1) + end + + perform! + end + end + + context 'with invalid value' do + let(:validation_service_timeout) { '??' } + + it 'uses default timeout' do + expect(::Gitlab::HTTP).to receive(:post) do |_url, params| + expect(params[:timeout]).to eq(described_class::DEFAULT_VALIDATION_REQUEST_TIMEOUT) + end + + perform! + end end + end + shared_examples 'successful external authorization' do it 'does not drop the pipeline' do perform! @@ -76,9 +145,117 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do end end - context 'when validation return false' do + context 'when EXTERNAL_VALIDATION_SERVICE_TOKEN is set' do + before do + stub_env('EXTERNAL_VALIDATION_SERVICE_TOKEN', '123') + end + + it 'passes token in X-Gitlab-Token header' do + expect(::Gitlab::HTTP).to receive(:post) do |_url, params| + expect(params[:headers]).to include({ 'X-Gitlab-Token' => '123' }) + end + + perform! + end + end + + context 'when validation returns 200 OK' do + before do + stub_request(:post, validation_service_url).to_return(status: 200, body: "{}") + end + + it_behaves_like 'successful external authorization' + end + + context 'when validation returns 404 Not Found' do before do - allow(step).to receive(:validate_external).and_return(false) + stub_request(:post, validation_service_url).to_return(status: 404, body: "{}") + end + + it_behaves_like 'successful external authorization' + end + + context 'when validation returns 500 Internal Server Error' do + before do + stub_request(:post, validation_service_url).to_return(status: 500, body: "{}") + end + + it_behaves_like 'successful external authorization' + end + + context 'when validation raises exceptions' do + before do + stub_request(:post, validation_service_url).to_raise(Net::OpenTimeout) + end + + it_behaves_like 'successful external authorization' + + it 'logs exceptions' do + expect(Gitlab::ErrorTracking).to receive(:track_exception) + .with(instance_of(Net::OpenTimeout), { project_id: project.id }) + + perform! + end + end + + context 'when the feature flag is disabled' do + before do + stub_feature_flags(ci_external_validation_service: false) + stub_request(:post, validation_service_url) + end + + it 'does not drop the pipeline' do + perform! + + expect(pipeline.status).not_to eq('failed') + expect(pipeline.errors).to be_empty + end + + it 'does not break the chain' do + perform! + + expect(step.break?).to be false + end + + it 'does not make requests' do + perform! + + expect(WebMock).not_to have_requested(:post, validation_service_url) + end + end + + context 'when not on .com' do + let(:dot_com) { false } + + before do + stub_feature_flags(ci_external_validation_service: false) + stub_request(:post, validation_service_url).to_return(status: 404, body: "{}") + end + + it 'drops the pipeline' do + perform! + + expect(pipeline.status).to eq('failed') + expect(pipeline).to be_persisted + expect(pipeline.errors.to_a).to include('External validation failed') + end + + it 'breaks the chain' do + perform! + + expect(step.break?).to be true + end + + it 'logs the authorization' do + expect(Gitlab::AppLogger).to receive(:info).with(message: 'Pipeline not authorized', project_id: project.id, user_id: user.id) + + perform! + end + end + + context 'when validation returns 406 Not Acceptable' do + before do + stub_request(:post, validation_service_url).to_return(status: 406, body: "{}") end it 'drops the pipeline' do @@ -126,16 +303,4 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do end end end - - describe '#validation_service_payload' do - subject(:validation_service_payload) { step.send(:validation_service_payload, pipeline, command.yaml_processor_result.stages_attributes) } - - it 'respects the defined schema' do - expect(validation_service_payload).to match_schema('/external_validation') - end - - it 'does not fire sql queries' do - expect { validation_service_payload }.not_to exceed_query_limit(1) - end - end end diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb index 7ec6949f852..f97935feb86 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb @@ -6,10 +6,12 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do let_it_be(:project) { create(:project, :repository) } let_it_be(:head_sha) { project.repository.head_commit.id } let(:pipeline) { build(:ci_empty_pipeline, project: project, sha: head_sha) } + let(:root_variables) { [] } + let(:seed_context) { double(pipeline: pipeline, root_variables: root_variables) } let(:attributes) { { name: 'rspec', ref: 'master', scheduling_type: :stage } } let(:previous_stages) { [] } - let(:seed_build) { described_class.new(pipeline, attributes, previous_stages) } + let(:seed_build) { described_class.new(seed_context, attributes, previous_stages) } describe '#attributes' do subject { seed_build.attributes } @@ -75,8 +77,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do let(:attributes) do { name: 'rspec', ref: 'master', - yaml_variables: [{ key: 'VAR1', value: 'var 1', public: true }, - { key: 'VAR2', value: 'var 2', public: true }], + job_variables: [{ key: 'VAR1', value: 'var 1', public: true }, + { key: 'VAR2', value: 'var 2', public: true }], rules: [{ if: '$VAR == null', variables: { VAR1: 'new var 1', VAR3: 'var 3' } }] } end @@ -301,6 +303,133 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do it { is_expected.to match a_hash_including(options: { allow_failure_criteria: nil }) } end end + + context 'with workflow:rules:[variables:]' do + let(:attributes) do + { name: 'rspec', + ref: 'master', + yaml_variables: [{ key: 'VAR2', value: 'var 2', public: true }, + { key: 'VAR3', value: 'var 3', public: true }], + job_variables: [{ key: 'VAR2', value: 'var 2', public: true }, + { key: 'VAR3', value: 'var 3', public: true }], + root_variables_inheritance: root_variables_inheritance } + end + + context 'when the pipeline has variables' do + let(:root_variables) do + [{ key: 'VAR1', value: 'var overridden pipeline 1', public: true }, + { key: 'VAR2', value: 'var pipeline 2', public: true }, + { key: 'VAR3', value: 'var pipeline 3', public: true }, + { key: 'VAR4', value: 'new var pipeline 4', public: true }] + end + + context 'when root_variables_inheritance is true' do + let(:root_variables_inheritance) { true } + + it 'returns calculated yaml variables' do + expect(subject[:yaml_variables]).to match_array( + [{ key: 'VAR1', value: 'var overridden pipeline 1', public: true }, + { key: 'VAR2', value: 'var 2', public: true }, + { key: 'VAR3', value: 'var 3', public: true }, + { key: 'VAR4', value: 'new var pipeline 4', public: true }] + ) + end + + context 'when FF ci_workflow_rules_variables is disabled' do + before do + stub_feature_flags(ci_workflow_rules_variables: false) + end + + it 'returns existing yaml variables' do + expect(subject[:yaml_variables]).to match_array( + [{ key: 'VAR2', value: 'var 2', public: true }, + { key: 'VAR3', value: 'var 3', public: true }] + ) + end + end + end + + context 'when root_variables_inheritance is false' do + let(:root_variables_inheritance) { false } + + it 'returns job variables' do + expect(subject[:yaml_variables]).to match_array( + [{ key: 'VAR2', value: 'var 2', public: true }, + { key: 'VAR3', value: 'var 3', public: true }] + ) + end + end + + context 'when root_variables_inheritance is an array' do + let(:root_variables_inheritance) { %w(VAR1 VAR2 VAR3) } + + it 'returns calculated yaml variables' do + expect(subject[:yaml_variables]).to match_array( + [{ key: 'VAR1', value: 'var overridden pipeline 1', public: true }, + { key: 'VAR2', value: 'var 2', public: true }, + { key: 'VAR3', value: 'var 3', public: true }] + ) + end + end + end + + context 'when the pipeline has not a variable' do + let(:root_variables_inheritance) { true } + + it 'returns seed yaml variables' do + expect(subject[:yaml_variables]).to match_array( + [{ key: 'VAR2', value: 'var 2', public: true }, + { key: 'VAR3', value: 'var 3', public: true }]) + end + end + end + + context 'when the job rule depends on variables' do + let(:attributes) do + { name: 'rspec', + ref: 'master', + yaml_variables: [{ key: 'VAR1', value: 'var 1', public: true }], + job_variables: [{ key: 'VAR1', value: 'var 1', public: true }], + root_variables_inheritance: root_variables_inheritance, + rules: rules } + end + + let(:root_variables_inheritance) { true } + + context 'when the rules use job variables' do + let(:rules) do + [{ if: '$VAR1 == "var 1"', variables: { VAR1: 'overridden var 1', VAR2: 'new var 2' } }] + end + + it 'recalculates the variables' do + expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1', public: true }, + { key: 'VAR2', value: 'new var 2', public: true }) + end + end + + context 'when the rules use root variables' do + let(:root_variables) do + [{ key: 'VAR2', value: 'var pipeline 2', public: true }] + end + + let(:rules) do + [{ if: '$VAR2 == "var pipeline 2"', variables: { VAR1: 'overridden var 1', VAR2: 'overridden var 2' } }] + end + + it 'recalculates the variables' do + expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1', public: true }, + { key: 'VAR2', value: 'overridden var 2', public: true }) + end + + context 'when the root_variables_inheritance is false' do + let(:root_variables_inheritance) { false } + + it 'does not recalculate the variables' do + expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'var 1', public: true }) + end + end + end + end end describe '#bridge?' do @@ -377,7 +506,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do it 'does not have environment' do expect(subject).not_to be_has_environment expect(subject.environment).to be_nil - expect(subject.metadata).to be_nil + expect(subject.metadata&.expanded_environment_name).to be_nil expect(Environment.exists?(name: expected_environment_name)).to eq(false) end end @@ -1080,7 +1209,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do end let(:stage_seed) do - Gitlab::Ci::Pipeline::Seed::Stage.new(pipeline, stage_attributes, []) + Gitlab::Ci::Pipeline::Seed::Stage.new(seed_context, stage_attributes, []) end let(:previous_stages) { [stage_seed] } diff --git a/spec/lib/gitlab/ci/pipeline/seed/pipeline_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/pipeline_spec.rb index 860b07647bd..21be8660def 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/pipeline_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/pipeline_spec.rb @@ -6,6 +6,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Pipeline do let_it_be(:project) { create(:project, :repository) } let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + let(:seed_context) { double(pipeline: pipeline, root_variables: []) } + let(:stages_attributes) do [ { @@ -29,7 +31,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Pipeline do end subject(:seed) do - described_class.new(pipeline, stages_attributes) + described_class.new(seed_context, stages_attributes) end describe '#stages' do diff --git a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb index 4b9db9fa6c6..5b04d2abd88 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb @@ -6,6 +6,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Stage do let(:project) { create(:project, :repository) } let(:pipeline) { create(:ci_empty_pipeline, project: project) } let(:previous_stages) { [] } + let(:seed_context) { double(pipeline: pipeline, root_variables: []) } let(:attributes) do { name: 'test', @@ -16,7 +17,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Stage do end subject do - described_class.new(pipeline, attributes, previous_stages) + described_class.new(seed_context, attributes, previous_stages) end describe '#size' do diff --git a/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb index b322e55cb5a..8378d096fcf 100644 --- a/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb +++ b/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb @@ -6,15 +6,17 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do let(:comparer) { described_class.new(base_report, head_report) } let(:base_report) { Gitlab::Ci::Reports::CodequalityReports.new } let(:head_report) { Gitlab::Ci::Reports::CodequalityReports.new } - let(:degradation_1) { build(:codequality_degradation_1) } - let(:degradation_2) { build(:codequality_degradation_2) } + let(:major_degradation) { build(:codequality_degradation, :major) } + let(:minor_degradation) { build(:codequality_degradation, :major) } + let(:critical_degradation) { build(:codequality_degradation, :critical) } + let(:blocker_degradation) { build(:codequality_degradation, :blocker) } describe '#status' do subject(:report_status) { comparer.status } context 'when head report has an error' do before do - head_report.add_degradation(degradation_1) + head_report.add_degradation(major_degradation) end it 'returns status failed' do @@ -50,7 +52,7 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when head report has an error' do before do - head_report.add_degradation(degradation_1) + head_report.add_degradation(major_degradation) end it 'returns the number of new errors' do @@ -70,8 +72,8 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has an error and head has a different error' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + head_report.add_degradation(minor_degradation) end it 'counts the base report error as resolved' do @@ -81,7 +83,7 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has errors head has no errors' do before do - base_report.add_degradation(degradation_1) + base_report.add_degradation(major_degradation) end it 'counts the base report errors as resolved' do @@ -91,8 +93,8 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has errors and head has the same error' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_1) + base_report.add_degradation(major_degradation) + head_report.add_degradation(major_degradation) end it 'returns zero' do @@ -102,7 +104,7 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report does not have errors and head has errors' do before do - head_report.add_degradation(degradation_1) + head_report.add_degradation(major_degradation) end it 'returns zero' do @@ -124,7 +126,7 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has an error' do before do - base_report.add_degradation(degradation_1) + base_report.add_degradation(major_degradation) end it 'returns zero' do @@ -134,7 +136,7 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when head report has an error' do before do - head_report.add_degradation(degradation_1) + head_report.add_degradation(major_degradation) end it 'includes the head report error in the count' do @@ -144,8 +146,8 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has errors and head report has errors' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + head_report.add_degradation(minor_degradation) end it 'includes errors in the count' do @@ -155,9 +157,9 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has errors and head report has the same error' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + head_report.add_degradation(major_degradation) + head_report.add_degradation(minor_degradation) end it 'includes errors in the count' do @@ -179,20 +181,28 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has errors and head has the same error' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) - end - - it 'includes the base report errors' do - expect(existing_errors).to contain_exactly(degradation_1) + base_report.add_degradation(major_degradation) + base_report.add_degradation(critical_degradation) + base_report.add_degradation(blocker_degradation) + head_report.add_degradation(critical_degradation) + head_report.add_degradation(blocker_degradation) + head_report.add_degradation(major_degradation) + head_report.add_degradation(minor_degradation) + end + + it 'includes the base report errors sorted by severity' do + expect(existing_errors).to eq([ + blocker_degradation, + critical_degradation, + major_degradation + ]) end end context 'when base report has errors and head has a different error' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + head_report.add_degradation(minor_degradation) end it 'returns an empty array' do @@ -202,7 +212,7 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report does not have errors and head has errors' do before do - head_report.add_degradation(degradation_1) + head_report.add_degradation(major_degradation) end it 'returns an empty array' do @@ -224,19 +234,25 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has errors and head has more errors' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + head_report.add_degradation(critical_degradation) + head_report.add_degradation(minor_degradation) + head_report.add_degradation(blocker_degradation) + head_report.add_degradation(major_degradation) end - it 'includes errors not found in the base report' do - expect(new_errors).to eq([degradation_2]) + it 'includes errors not found in the base report sorted by severity' do + expect(new_errors).to eq([ + blocker_degradation, + critical_degradation, + minor_degradation + ]) end end context 'when base report has an error and head has no errors' do before do - base_report.add_degradation(degradation_1) + base_report.add_degradation(major_degradation) end it 'returns an empty array' do @@ -246,11 +262,11 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report does not have errors and head has errors' do before do - head_report.add_degradation(degradation_1) + head_report.add_degradation(major_degradation) end it 'returns the head report error' do - expect(new_errors).to eq([degradation_1]) + expect(new_errors).to eq([major_degradation]) end end @@ -268,9 +284,9 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report errors are still found in the head report' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + head_report.add_degradation(major_degradation) + head_report.add_degradation(minor_degradation) end it 'returns an empty array' do @@ -280,18 +296,25 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has errors and head has a different error' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + base_report.add_degradation(minor_degradation) + base_report.add_degradation(critical_degradation) + base_report.add_degradation(blocker_degradation) + head_report.add_degradation(major_degradation) end - it 'returns the base report error' do - expect(resolved_errors).to eq([degradation_1]) + it 'returns the base report errors not found in the head report, sorted by severity' do + expect(resolved_errors).to eq([ + blocker_degradation, + critical_degradation, + minor_degradation + ]) end end context 'when base report does not have errors and head has errors' do before do - head_report.add_degradation(degradation_1) + head_report.add_degradation(major_degradation) end it 'returns an empty array' do diff --git a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb index ae9b2f2c62b..3b0eaffc54e 100644 --- a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb +++ b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb @@ -34,8 +34,6 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReports do it 'sets location as an error' do codequality_report.add_degradation(invalid_degradation) - - expect(codequality_report.error_message).to eq("Invalid degradation format: The property '#/' did not contain a required property of 'location'") end end end @@ -79,4 +77,36 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReports do end end end + + describe '#sort_degradations!' do + let(:major) { build(:codequality_degradation, :major) } + let(:minor) { build(:codequality_degradation, :minor) } + let(:blocker) { build(:codequality_degradation, :blocker) } + let(:info) { build(:codequality_degradation, :info) } + let(:major_2) { build(:codequality_degradation, :major) } + let(:critical) { build(:codequality_degradation, :critical) } + let(:codequality_report) { described_class.new } + + before do + codequality_report.add_degradation(major) + codequality_report.add_degradation(minor) + codequality_report.add_degradation(blocker) + codequality_report.add_degradation(major_2) + codequality_report.add_degradation(info) + codequality_report.add_degradation(critical) + + codequality_report.sort_degradations! + end + + it 'sorts degradations based on severity' do + expect(codequality_report.degradations.values).to eq([ + blocker, + critical, + major, + major_2, + minor, + info + ]) + end + end end diff --git a/spec/lib/gitlab/ci/reports/test_failure_history_spec.rb b/spec/lib/gitlab/ci/reports/test_failure_history_spec.rb index 831bc5e9f37..9ee55177ca0 100644 --- a/spec/lib/gitlab/ci/reports/test_failure_history_spec.rb +++ b/spec/lib/gitlab/ci/reports/test_failure_history_spec.rb @@ -13,9 +13,9 @@ RSpec.describe Gitlab::Ci::Reports::TestFailureHistory, :aggregate_failures do subject(:load_history) { described_class.new([failed_rspec, failed_java], project).load! } before do - allow(Ci::TestCaseFailure) + allow(Ci::UnitTestFailure) .to receive(:recent_failures_count) - .with(project: project, test_case_keys: [failed_rspec.key, failed_java.key]) + .with(project: project, unit_test_keys: [failed_rspec.key, failed_java.key]) .and_return( failed_rspec.key => 2, failed_java.key => 1 diff --git a/spec/lib/gitlab/ci/runner_instructions_spec.rb b/spec/lib/gitlab/ci/runner_instructions_spec.rb index d1020026fe6..f872c631a50 100644 --- a/spec/lib/gitlab/ci/runner_instructions_spec.rb +++ b/spec/lib/gitlab/ci/runner_instructions_spec.rb @@ -6,7 +6,6 @@ RSpec.describe Gitlab::Ci::RunnerInstructions do using RSpec::Parameterized::TableSyntax let(:params) { {} } - let(:user) { create(:user) } describe 'OS' do Gitlab::Ci::RunnerInstructions::OS.each do |name, subject| @@ -37,7 +36,7 @@ RSpec.describe Gitlab::Ci::RunnerInstructions do end describe '#install_script' do - subject { described_class.new(current_user: user, **params) } + subject { described_class.new(**params) } context 'invalid params' do where(:current_params, :expected_error_message) do @@ -106,117 +105,18 @@ RSpec.describe Gitlab::Ci::RunnerInstructions do end end - context 'group' do - let(:group) { create(:group) } - - subject { described_class.new(current_user: user, group: group, **params) } - - context 'user is owner' do - before do - group.add_owner(user) - end - - with_them do - let(:params) { { os: commands.each_key.first, arch: 'foo' } } - - it 'have correct configurations' do - result = subject.register_command - - expect(result).to include("#{commands[commands.each_key.first]} register") - expect(result).to include("--registration-token #{group.runners_token}") - expect(result).to include("--url #{Gitlab::Routing.url_helpers.root_url(only_path: false)}") - end - end - end - - context 'user is not owner' do - where(:user_permission) do - [:maintainer, :developer, :reporter, :guest] - end - - with_them do - before do - create(:group_member, user_permission, group: group, user: user) - end - - it 'raises error' do - result = subject.register_command - - expect(result).to be_nil - expect(subject.errors).to include("Gitlab::Access::AccessDeniedError") - end - end - end - end - - context 'project' do - let(:project) { create(:project) } - - subject { described_class.new(current_user: user, project: project, **params) } - - context 'user is maintainer' do - before do - project.add_maintainer(user) - end - - with_them do - let(:params) { { os: commands.each_key.first, arch: 'foo' } } - - it 'have correct configurations' do - result = subject.register_command - - expect(result).to include("#{commands[commands.each_key.first]} register") - expect(result).to include("--registration-token #{project.runners_token}") - expect(result).to include("--url #{Gitlab::Routing.url_helpers.root_url(only_path: false)}") - end - end - end - - context 'user is not maintainer' do - where(:user_permission) do - [:developer, :reporter, :guest] - end - - with_them do - before do - create(:project_member, user_permission, project: project, user: user) - end - - it 'raises error' do - result = subject.register_command - - expect(result).to be_nil - expect(subject.errors).to include("Gitlab::Access::AccessDeniedError") - end - end - end - end - context 'instance' do - subject { described_class.new(current_user: user, **params) } - - context 'user is admin' do - let(:user) { create(:user, :admin) } - - with_them do - let(:params) { { os: commands.each_key.first, arch: 'foo' } } + subject { described_class.new(**params) } - it 'have correct configurations' do - result = subject.register_command - - expect(result).to include("#{commands[commands.each_key.first]} register") - expect(result).to include("--registration-token #{Gitlab::CurrentSettings.runners_registration_token}") - expect(result).to include("--url #{Gitlab::Routing.url_helpers.root_url(only_path: false)}") - end - end - end + with_them do + let(:params) { { os: commands.each_key.first, arch: 'foo' } } - context 'user is not admin' do - it 'raises error' do + it 'have correct configurations' do result = subject.register_command - expect(result).to be_nil - expect(subject.errors).to include("Gitlab::Access::AccessDeniedError") + expect(result).to include("#{commands[commands.each_key.first]} register") + expect(result).to include("--registration-token $REGISTRATION_TOKEN") + expect(result).to include("--url #{Gitlab::Routing.url_helpers.root_url(only_path: false)}") end end end diff --git a/spec/lib/gitlab/ci/status/build/common_spec.rb b/spec/lib/gitlab/ci/status/build/common_spec.rb index 924ee5ee1a4..c4e83c1796d 100644 --- a/spec/lib/gitlab/ci/status/build/common_spec.rb +++ b/spec/lib/gitlab/ci/status/build/common_spec.rb @@ -28,7 +28,7 @@ RSpec.describe Gitlab::Ci::Status::Build::Common do context 'when user does not have access to read build' do before do - project.update(public_builds: false) + project.update!(public_builds: false) end it { is_expected.not_to have_details } diff --git a/spec/lib/gitlab/ci/status/composite_spec.rb b/spec/lib/gitlab/ci/status/composite_spec.rb index 543cfe874ca..2b9523bd83d 100644 --- a/spec/lib/gitlab/ci/status/composite_spec.rb +++ b/spec/lib/gitlab/ci/status/composite_spec.rb @@ -6,13 +6,13 @@ RSpec.describe Gitlab::Ci::Status::Composite do let_it_be(:pipeline) { create(:ci_pipeline) } before_all do - @statuses = Ci::HasStatus::STATUSES_ENUM.map do |status, idx| + @statuses = Ci::HasStatus::STATUSES_ENUM.to_h do |status, idx| [status, create(:ci_build, pipeline: pipeline, status: status, importing: true)] - end.to_h + end - @statuses_with_allow_failure = Ci::HasStatus::STATUSES_ENUM.map do |status, idx| + @statuses_with_allow_failure = Ci::HasStatus::STATUSES_ENUM.to_h do |status, idx| [status, create(:ci_build, pipeline: pipeline, status: status, allow_failure: true, importing: true)] - end.to_h + end end describe '#status' do diff --git a/spec/lib/gitlab/ci/trace_spec.rb b/spec/lib/gitlab/ci/trace_spec.rb index 597e4ca9b03..0fe7c731f27 100644 --- a/spec/lib/gitlab/ci/trace_spec.rb +++ b/spec/lib/gitlab/ci/trace_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Trace, :clean_gitlab_redis_shared_state, factory_default: :keep do let_it_be(:project) { create_default(:project).freeze } - let_it_be_with_reload(:build) { create(:ci_build) } + let_it_be_with_reload(:build) { create(:ci_build, :success) } let(:trace) { described_class.new(build) } describe "associations" do @@ -63,9 +63,7 @@ RSpec.describe Gitlab::Ci::Trace, :clean_gitlab_redis_shared_state, factory_defa describe '#update_interval' do context 'it is not being watched' do - it 'returns 30 seconds' do - expect(trace.update_interval).to eq(30.seconds) - end + it { expect(trace.update_interval).to eq(60.seconds) } end context 'it is being watched' do diff --git a/spec/lib/gitlab/ci/variables/helpers_spec.rb b/spec/lib/gitlab/ci/variables/helpers_spec.rb index b45abf8c0e1..f13b334c10e 100644 --- a/spec/lib/gitlab/ci/variables/helpers_spec.rb +++ b/spec/lib/gitlab/ci/variables/helpers_spec.rb @@ -100,4 +100,50 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do it { is_expected.to eq(result) } end end + + describe '.inherit_yaml_variables' do + let(:from) do + [{ key: 'key1', value: 'value1' }, + { key: 'key2', value: 'value2' }] + end + + let(:to) do + [{ key: 'key2', value: 'value22' }, + { key: 'key3', value: 'value3' }] + end + + let(:inheritance) { true } + + let(:result) do + [{ key: 'key1', value: 'value1', public: true }, + { key: 'key2', value: 'value22', public: true }, + { key: 'key3', value: 'value3', public: true }] + end + + subject { described_class.inherit_yaml_variables(from: from, to: to, inheritance: inheritance) } + + it { is_expected.to eq(result) } + + context 'when inheritance is false' do + let(:inheritance) { false } + + let(:result) do + [{ key: 'key2', value: 'value22', public: true }, + { key: 'key3', value: 'value3', public: true }] + end + + it { is_expected.to eq(result) } + end + + context 'when inheritance is array' do + let(:inheritance) { ['key2'] } + + let(:result) do + [{ key: 'key2', value: 'value22', public: true }, + { key: 'key3', value: 'value3', public: true }] + end + + it { is_expected.to eq(result) } + end + end end diff --git a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb index 7e3cd7ec254..e345cd4de9b 100644 --- a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb @@ -24,7 +24,7 @@ module Gitlab let(:included_yml) do YAML.dump( - another_test: { stage: 'test', script: 'echo 2' } + { another_test: { stage: 'test', script: 'echo 2' } }.deep_stringify_keys ) end diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index 5462a587d16..ad94dfc9160 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -43,6 +43,8 @@ module Gitlab allow_failure: false, when: "on_success", yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage }) end @@ -74,6 +76,8 @@ module Gitlab allow_failure: false, when: 'on_success', yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage }) end @@ -111,7 +115,9 @@ module Gitlab tag_list: %w[A B], allow_failure: false, when: "on_success", - yaml_variables: [] + yaml_variables: [], + job_variables: [], + root_variables_inheritance: true }) end end @@ -158,6 +164,8 @@ module Gitlab allow_failure: false, when: "on_success", yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage }) end @@ -347,6 +355,8 @@ module Gitlab allow_failure: false, when: "on_success", yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage, options: { script: ["rspec"] }, only: { refs: ["branches"] } }] }, @@ -359,6 +369,8 @@ module Gitlab allow_failure: false, when: "on_success", yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage, options: { script: ["cap prod"] }, only: { refs: ["tags"] } }] }, @@ -372,7 +384,7 @@ module Gitlab end end - describe '#workflow_attributes' do + describe 'workflow attributes' do context 'with disallowed workflow:variables' do let(:config) do <<-EOYML @@ -403,11 +415,11 @@ module Gitlab end it 'parses the workflow:rules configuration' do - expect(subject.workflow_attributes[:rules]).to contain_exactly({ if: '$VAR == "value"' }) + expect(subject.workflow_rules).to contain_exactly({ if: '$VAR == "value"' }) end - it 'parses the root:variables as yaml_variables:' do - expect(subject.workflow_attributes[:yaml_variables]) + it 'parses the root:variables as #root_variables' do + expect(subject.root_variables) .to contain_exactly({ key: 'SUPPORTED', value: 'parsed', public: true }) end end @@ -425,11 +437,11 @@ module Gitlab end it 'parses the workflow:rules configuration' do - expect(subject.workflow_attributes[:rules]).to contain_exactly({ if: '$VAR == "value"' }) + expect(subject.workflow_rules).to contain_exactly({ if: '$VAR == "value"' }) end - it 'parses the root:variables as yaml_variables:' do - expect(subject.workflow_attributes[:yaml_variables]).to eq([]) + it 'parses the root:variables as #root_variables' do + expect(subject.root_variables).to eq([]) end end @@ -445,11 +457,11 @@ module Gitlab end it 'parses the workflow:rules configuration' do - expect(subject.workflow_attributes[:rules]).to be_nil + expect(subject.workflow_rules).to be_nil end - it 'parses the root:variables as yaml_variables:' do - expect(subject.workflow_attributes[:yaml_variables]) + it 'parses the root:variables as #root_variables' do + expect(subject.root_variables) .to contain_exactly({ key: 'SUPPORTED', value: 'parsed', public: true }) end end @@ -463,11 +475,11 @@ module Gitlab end it 'parses the workflow:rules configuration' do - expect(subject.workflow_attributes[:rules]).to be_nil + expect(subject.workflow_rules).to be_nil end - it 'parses the root:variables as yaml_variables:' do - expect(subject.workflow_attributes[:yaml_variables]).to eq([]) + it 'parses the root:variables as #root_variables' do + expect(subject.root_variables).to eq([]) end end end @@ -853,6 +865,8 @@ module Gitlab allow_failure: false, when: "on_success", yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage }) end @@ -861,7 +875,7 @@ module Gitlab config = YAML.dump({ image: "ruby:2.7", services: ["mysql"], before_script: ["pwd"], - rspec: { image: { name: "ruby:2.5", entrypoint: ["/usr/local/bin/init", "run"] }, + rspec: { image: { name: "ruby:3.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"], @@ -878,7 +892,7 @@ module Gitlab options: { before_script: ["pwd"], script: ["rspec"], - image: { name: "ruby:2.5", entrypoint: ["/usr/local/bin/init", "run"] }, + image: { name: "ruby:3.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" }] @@ -886,6 +900,8 @@ module Gitlab allow_failure: false, when: "on_success", yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage }) end @@ -915,6 +931,8 @@ module Gitlab allow_failure: false, when: "on_success", yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage }) end @@ -923,7 +941,7 @@ module Gitlab config = YAML.dump({ image: "ruby:2.7", services: ["mysql"], before_script: ["pwd"], - rspec: { image: "ruby:2.5", services: ["postgresql", "docker:dind"], script: "rspec" } }) + rspec: { image: "ruby:3.0", services: ["postgresql", "docker:dind"], script: "rspec" } }) config_processor = Gitlab::Ci::YamlProcessor.new(config).execute @@ -936,12 +954,14 @@ module Gitlab options: { before_script: ["pwd"], script: ["rspec"], - image: { name: "ruby:2.5" }, + image: { name: "ruby:3.0" }, services: [{ name: "postgresql" }, { name: "docker:dind" }] }, allow_failure: false, when: "on_success", yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage }) end @@ -951,7 +971,10 @@ module Gitlab describe 'Variables' do subject { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)).execute } - let(:build_variables) { subject.builds.first[:yaml_variables] } + let(:build) { subject.builds.first } + let(:yaml_variables) { build[:yaml_variables] } + let(:job_variables) { build[:job_variables] } + let(:root_variables_inheritance) { build[:root_variables_inheritance] } context 'when global variables are defined' do let(:variables) do @@ -967,10 +990,12 @@ module Gitlab end it 'returns global variables' do - expect(build_variables).to contain_exactly( + expect(yaml_variables).to contain_exactly( { key: 'VAR1', value: 'value1', public: true }, { key: 'VAR2', value: 'value2', public: true } ) + expect(job_variables).to eq([]) + expect(root_variables_inheritance).to eq(true) end end @@ -979,7 +1004,7 @@ module Gitlab { 'VAR1' => 'global1', 'VAR3' => 'global3', 'VAR4' => 'global4' } end - let(:job_variables) do + let(:build_variables) do { 'VAR1' => 'value1', 'VAR2' => 'value2' } end @@ -987,20 +1012,25 @@ module Gitlab { before_script: ['pwd'], variables: global_variables, - rspec: { script: 'rspec', variables: job_variables, inherit: inherit } + rspec: { script: 'rspec', variables: build_variables, inherit: inherit } } end context 'when no inheritance is specified' do let(:inherit) { } - it 'returns all unique variables' do - expect(build_variables).to contain_exactly( - { key: 'VAR4', value: 'global4', public: true }, + it 'returns all variables' do + expect(yaml_variables).to contain_exactly( + { key: 'VAR1', value: 'value1', public: true }, + { key: 'VAR2', value: 'value2', public: true }, { key: 'VAR3', value: 'global3', public: true }, + { key: 'VAR4', value: 'global4', public: true } + ) + expect(job_variables).to contain_exactly( { key: 'VAR1', value: 'value1', public: true }, { key: 'VAR2', value: 'value2', public: true } ) + expect(root_variables_inheritance).to eq(true) end end @@ -1008,22 +1038,32 @@ module Gitlab let(:inherit) { { variables: false } } it 'does not inherit variables' do - expect(build_variables).to contain_exactly( + expect(yaml_variables).to contain_exactly( { key: 'VAR1', value: 'value1', public: true }, { key: 'VAR2', value: 'value2', public: true } ) + expect(job_variables).to contain_exactly( + { key: 'VAR1', value: 'value1', public: true }, + { key: 'VAR2', value: 'value2', public: true } + ) + expect(root_variables_inheritance).to eq(false) end end context 'when specific variables are to inherited' do let(:inherit) { { variables: %w[VAR1 VAR4] } } - it 'returns all unique variables and inherits only specified variables' do - expect(build_variables).to contain_exactly( - { key: 'VAR4', value: 'global4', public: true }, + it 'returns all variables and inherits only specified variables' do + expect(yaml_variables).to contain_exactly( + { key: 'VAR1', value: 'value1', public: true }, + { key: 'VAR2', value: 'value2', public: true }, + { key: 'VAR4', value: 'global4', public: true } + ) + expect(job_variables).to contain_exactly( { key: 'VAR1', value: 'value1', public: true }, { key: 'VAR2', value: 'value2', public: true } ) + expect(root_variables_inheritance).to eq(%w[VAR1 VAR4]) end end end @@ -1042,10 +1082,15 @@ module Gitlab end it 'returns job variables' do - expect(build_variables).to contain_exactly( + expect(yaml_variables).to contain_exactly( + { key: 'VAR1', value: 'value1', public: true }, + { key: 'VAR2', value: 'value2', public: true } + ) + expect(job_variables).to contain_exactly( { key: 'VAR1', value: 'value1', public: true }, { key: 'VAR2', value: 'value2', public: true } ) + expect(root_variables_inheritance).to eq(true) end end @@ -1068,8 +1113,11 @@ module Gitlab # When variables config is empty, we assume this is a valid # configuration, see issue #18775 # - expect(build_variables).to be_an_instance_of(Array) - expect(build_variables).to be_empty + expect(yaml_variables).to be_an_instance_of(Array) + expect(yaml_variables).to be_empty + + expect(job_variables).to eq([]) + expect(root_variables_inheritance).to eq(true) end end end @@ -1084,8 +1132,11 @@ module Gitlab end it 'returns empty array' do - expect(build_variables).to be_an_instance_of(Array) - expect(build_variables).to be_empty + expect(yaml_variables).to be_an_instance_of(Array) + expect(yaml_variables).to be_empty + + expect(job_variables).to eq([]) + expect(root_variables_inheritance).to eq(true) end end end @@ -1717,6 +1768,8 @@ module Gitlab when: "on_success", allow_failure: false, yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage }) end @@ -2080,6 +2133,8 @@ module Gitlab when: "on_success", allow_failure: false, yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage ) expect(subject.builds[4]).to eq( @@ -2095,6 +2150,8 @@ module Gitlab when: "on_success", allow_failure: false, yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :dag ) end @@ -2122,6 +2179,8 @@ module Gitlab when: "on_success", allow_failure: false, yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage ) expect(subject.builds[4]).to eq( @@ -2139,6 +2198,8 @@ module Gitlab when: "on_success", allow_failure: false, yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :dag ) end @@ -2162,6 +2223,8 @@ module Gitlab when: "on_success", allow_failure: false, yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :dag ) end @@ -2193,6 +2256,8 @@ module Gitlab when: "on_success", allow_failure: false, yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :dag ) end @@ -2391,6 +2456,8 @@ module Gitlab when: "on_success", allow_failure: false, yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage }) end @@ -2438,6 +2505,8 @@ module Gitlab when: "on_success", allow_failure: false, yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage }) expect(subject.second).to eq({ @@ -2451,6 +2520,8 @@ module Gitlab when: "on_success", allow_failure: false, yaml_variables: [], + job_variables: [], + root_variables_inheritance: true, scheduling_type: :stage }) end |