Commit 3b4742a7 authored by Jan Provaznik's avatar Jan Provaznik

Merge branch '26362_one_dimensional_matrix' into 'master'

One dimensional matrix support: fixes #26362

Closes #26362

See merge request gitlab-org/gitlab!42170
parents ff423740 fc24f704
......@@ -204,8 +204,13 @@ class CommitStatus < ApplicationRecord
# 'rspec:linux: 1/10' => 'rspec:linux'
common_name = name.to_s.gsub(%r{\d+[\s:\/\\]+\d+\s*}, '')
# 'rspec:linux: [aws, max memory]' => 'rspec:linux'
common_name.gsub!(%r{: \[.*, .*\]\s*\z}, '')
if ::Gitlab::Ci::Features.one_dimensional_matrix_enabled?
# 'rspec:linux: [aws, max memory]' => 'rspec:linux', 'rspec:linux: [aws]' => 'rspec:linux'
common_name.gsub!(%r{: \[.*\]\s*\z}, '')
else
# 'rspec:linux: [aws, max memory]' => 'rspec:linux', 'rspec:linux: [aws]' => 'rspec:linux: [aws]'
common_name.gsub!(%r{: \[.*, .*\]\s*\z}, '')
end
common_name.strip!
common_name
......
---
name: one_dimensional_matrix
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42170
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/256062
type: development
group: group::pipeline authoring
default_enabled: false
......@@ -3572,6 +3572,11 @@ job split into three separate jobs.
Use `matrix:` to configure different variables for jobs that are running in parallel.
There can be from 2 to 50 jobs.
In GitLab 13.5 and later, you can have one-dimensional matrices with a single job.
The ability to have one-dimensional matrices is [deployed behind a feature flag](../../user/feature_flags.md),
disabled by default. It's disabled on GitLab.com. To use it in a GitLab self-managed
instance, ask a GitLab administrator to [enable the `one_dimensional_matrix:` feature flag](../../administration/feature_flags.md). **(CORE-ONLY)**
Every job gets the same `CI_NODE_TOTAL` [environment variable](../variables/README.md#predefined-environment-variables) value, and a unique `CI_NODE_INDEX` value.
```yaml
......
......@@ -14,7 +14,7 @@ module Gitlab
validations do
validates :config, variables: { array_values: true }
validates :config, length: {
minimum: 2,
minimum: :minimum,
too_short: 'requires at least %{count} items'
}
end
......@@ -28,6 +28,10 @@ module Gitlab
.map { |key, value| [key.to_s, Array(value).map(&:to_s)] }
.to_h
end
def minimum
::Gitlab::Ci::Features.one_dimensional_matrix_enabled? ? 1 : 2
end
end
end
end
......
......@@ -62,6 +62,10 @@ module Gitlab
def self.new_artifact_file_reader_enabled?(project)
::Feature.enabled?(:ci_new_artifact_file_reader, project, default_enabled: true)
end
def self.one_dimensional_matrix_enabled?
::Feature.enabled?(:one_dimensional_matrix, default_enabled: false)
end
end
end
end
# frozen_string_literal: true
require 'fast_spec_helper'
require 'spec_helper'
require_dependency 'active_model'
RSpec.describe ::Gitlab::Ci::Config::Entry::Product::Matrix do
......@@ -46,33 +46,140 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Product::Matrix do
end
end
context 'when entry config has only one variable' do
let(:config) do
[
{
'VAR_1' => %w[test]
}
]
context 'with one_dimensional_matrix feature flag enabled' do
before do
stub_feature_flags(one_dimensional_matrix: true)
matrix.compose!
end
describe '#valid?' do
it { is_expected.not_to be_valid }
end
context 'when entry config has only one variable with multiple values' do
let(:config) do
[
{
'VAR_1' => %w[build test]
}
]
end
describe '#errors' do
it 'returns error about too many jobs' do
expect(matrix.errors)
.to include('variables config requires at least 2 items')
describe '#valid?' do
it { is_expected.to be_valid }
end
describe '#errors' do
it 'returns no errors' do
expect(matrix.errors)
.to be_empty
end
end
describe '#value' do
before do
matrix.compose!
end
it 'returns the value without raising an error' do
expect(matrix.value).to eq([{ 'VAR_1' => %w[build test] }])
end
end
context 'when entry config has only one variable with one value' do
let(:config) do
[
{
'VAR_1' => %w[test]
}
]
end
describe '#valid?' do
it { is_expected.to be_valid }
end
describe '#errors' do
it 'returns no errors' do
expect(matrix.errors)
.to be_empty
end
end
describe '#value' do
before do
matrix.compose!
end
it 'returns the value without raising an error' do
expect(matrix.value).to eq([{ 'VAR_1' => %w[test] }])
end
end
end
end
end
context 'with one_dimensional_matrix feature flag disabled' do
before do
stub_feature_flags(one_dimensional_matrix: false)
matrix.compose!
end
describe '#value' do
before do
matrix.compose!
context 'when entry config has only one variable with multiple values' do
let(:config) do
[
{
'VAR_1' => %w[build test]
}
]
end
it 'returns the value without raising an error' do
expect(matrix.value).to eq([{ 'VAR_1' => ['test'] }])
describe '#valid?' do
it { is_expected.not_to be_valid }
end
describe '#errors' do
it 'returns error about too many jobs' do
expect(matrix.errors)
.to include('variables config requires at least 2 items')
end
end
describe '#value' do
before do
matrix.compose!
end
it 'returns the value without raising an error' do
expect(matrix.value).to eq([{ 'VAR_1' => %w[build test] }])
end
end
context 'when entry config has only one variable with one value' do
let(:config) do
[
{
'VAR_1' => %w[test]
}
]
end
describe '#valid?' do
it { is_expected.not_to be_valid }
end
describe '#errors' do
it 'returns no errors' do
expect(matrix.errors)
.to include('variables config requires at least 2 items')
end
end
describe '#value' do
before do
matrix.compose!
end
it 'returns the value without raising an error' do
expect(matrix.value).to eq([{ 'VAR_1' => %w[test] }])
end
end
end
end
end
......
# frozen_string_literal: true
require 'fast_spec_helper'
# After Feature one_dimensional_matrix is removed, this can be changed back to fast_spec_helper
require 'spec_helper'
require_dependency 'active_model'
RSpec.describe Gitlab::Ci::Config::Entry::Product::Variables do
......@@ -45,43 +46,71 @@ RSpec.describe Gitlab::Ci::Config::Entry::Product::Variables do
end
end
context 'when entry value is not correct' do
shared_examples 'invalid variables' do |message|
describe '#errors' do
it 'saves errors' do
expect(entry.errors).to include(message)
end
context 'with one_dimensional_matrix feature flag enabled' do
context 'with only one variable' do
before do
stub_feature_flags(one_dimensional_matrix: true)
end
let(:config) { { VAR: 'test' } }
describe '#valid?' do
it 'is not valid' do
expect(entry).not_to be_valid
it 'is valid' do
expect(entry).to be_valid
end
end
describe '#errors' do
it 'does not append errors' do
expect(entry.errors).to be_empty
end
end
end
end
context 'with array' do
let(:config) { [:VAR, 'test'] }
context 'with one_dimensional_matrix feature flag disabled' do
context 'when entry value is not correct' do
before do
stub_feature_flags(one_dimensional_matrix: false)
end
shared_examples 'invalid variables' do |message|
describe '#errors' do
it 'saves errors' do
expect(entry.errors).to include(message)
end
end
it_behaves_like 'invalid variables', /should be a hash of key value pairs/
end
describe '#valid?' do
it 'is not valid' do
expect(entry).not_to be_valid
end
end
end
context 'with empty array' do
let(:config) { { VAR: 'test', VAR2: [] } }
context 'with array' do
let(:config) { [:VAR, 'test'] }
it_behaves_like 'invalid variables', /should be a hash of key value pairs/
end
it_behaves_like 'invalid variables', /should be a hash of key value pairs/
end
context 'with nested array' do
let(:config) { { VAR: 'test', VAR2: [1, [2]] } }
context 'with empty array' do
let(:config) { { VAR: 'test', VAR2: [] } }
it_behaves_like 'invalid variables', /should be a hash of key value pairs/
end
it_behaves_like 'invalid variables', /should be a hash of key value pairs/
end
context 'with only one variable' do
let(:config) { { VAR: 'test' } }
context 'with nested array' do
let(:config) { { VAR: 'test', VAR2: [1, [2]] } }
it_behaves_like 'invalid variables', /should be a hash of key value pairs/
end
it_behaves_like 'invalid variables', /variables config requires at least 2 items/
context 'with one_dimensional_matrix feature flag disabled' do
context 'with only one variable' do
let(:config) { { VAR: 'test' } }
it_behaves_like 'invalid variables', /variables config requires at least 2 items/
end
end
end
end
end
......
......@@ -493,47 +493,104 @@ RSpec.describe CommitStatus do
end
end
describe '#group_name' do
let(:commit_status) do
build(:commit_status, pipeline: pipeline, stage: 'test')
end
subject { commit_status.group_name }
context 'with the one_dimensional_matrix feature flag disabled' do
describe '#group_name' do
before do
stub_feature_flags(one_dimensional_matrix: false)
end
tests = {
'rspec:windows' => 'rspec:windows',
'rspec:windows 0' => 'rspec:windows 0',
'rspec:windows 0 test' => 'rspec:windows 0 test',
'rspec:windows 0 1' => 'rspec:windows',
'rspec:windows 0 1 name' => 'rspec:windows name',
'rspec:windows 0/1' => 'rspec:windows',
'rspec:windows 0/1 name' => 'rspec:windows name',
'rspec:windows 0:1' => 'rspec:windows',
'rspec:windows 0:1 name' => 'rspec:windows name',
'rspec:windows 10000 20000' => 'rspec:windows',
'rspec:windows 0 : / 1' => 'rspec:windows',
'rspec:windows 0 : / 1 name' => 'rspec:windows name',
'0 1 name ruby' => 'name ruby',
'0 :/ 1 name ruby' => 'name ruby',
'rspec: [aws]' => 'rspec: [aws]',
'rspec: [aws] 0/1' => 'rspec: [aws]',
'rspec: [aws, max memory]' => 'rspec',
'rspec:linux: [aws, max memory, data]' => 'rspec:linux',
'rspec: [inception: [something, other thing], value]' => 'rspec',
'rspec:windows 0/1: [name, other]' => 'rspec:windows',
'rspec:windows: [name, other] 0/1' => 'rspec:windows',
'rspec:windows: [name, 0/1] 0/1' => 'rspec:windows',
'rspec:windows: [0/1, name]' => 'rspec:windows',
'rspec:windows: [, ]' => 'rspec:windows',
'rspec:windows: [name]' => 'rspec:windows: [name]',
'rspec:windows: [name,other]' => 'rspec:windows: [name,other]'
}
let(:commit_status) do
build(:commit_status, pipeline: pipeline, stage: 'test')
end
subject { commit_status.group_name }
tests = {
'rspec:windows' => 'rspec:windows',
'rspec:windows 0' => 'rspec:windows 0',
'rspec:windows 0 test' => 'rspec:windows 0 test',
'rspec:windows 0 1' => 'rspec:windows',
'rspec:windows 0 1 name' => 'rspec:windows name',
'rspec:windows 0/1' => 'rspec:windows',
'rspec:windows 0/1 name' => 'rspec:windows name',
'rspec:windows 0:1' => 'rspec:windows',
'rspec:windows 0:1 name' => 'rspec:windows name',
'rspec:windows 10000 20000' => 'rspec:windows',
'rspec:windows 0 : / 1' => 'rspec:windows',
'rspec:windows 0 : / 1 name' => 'rspec:windows name',
'0 1 name ruby' => 'name ruby',
'0 :/ 1 name ruby' => 'name ruby',
'rspec: [aws]' => 'rspec: [aws]',
'rspec: [aws] 0/1' => 'rspec: [aws]',
'rspec: [aws, max memory]' => 'rspec',
'rspec:linux: [aws, max memory, data]' => 'rspec:linux',
'rspec: [inception: [something, other thing], value]' => 'rspec',
'rspec:windows 0/1: [name, other]' => 'rspec:windows',
'rspec:windows: [name, other] 0/1' => 'rspec:windows',
'rspec:windows: [name, 0/1] 0/1' => 'rspec:windows',
'rspec:windows: [0/1, name]' => 'rspec:windows',
'rspec:windows: [, ]' => 'rspec:windows',
'rspec:windows: [name]' => 'rspec:windows: [name]',
'rspec:windows: [name,other]' => 'rspec:windows: [name,other]'
}
tests.each do |name, group_name|
it "'#{name}' puts in '#{group_name}'" do
commit_status.name = name
is_expected.to eq(group_name)
end
end
end
end
tests.each do |name, group_name|
it "'#{name}' puts in '#{group_name}'" do
commit_status.name = name
context 'with one_dimensional_matrix feature flag enabled' do
describe '#group_name' do
before do
stub_feature_flags(one_dimensional_matrix: true)
end
is_expected.to eq(group_name)
let(:commit_status) do
build(:commit_status, pipeline: pipeline, stage: 'test')
end
subject { commit_status.group_name }
tests = {
'rspec:windows' => 'rspec:windows',
'rspec:windows 0' => 'rspec:windows 0',
'rspec:windows 0 test' => 'rspec:windows 0 test',
'rspec:windows 0 1' => 'rspec:windows',
'rspec:windows 0 1 name' => 'rspec:windows name',
'rspec:windows 0/1' => 'rspec:windows',
'rspec:windows 0/1 name' => 'rspec:windows name',
'rspec:windows 0:1' => 'rspec:windows',
'rspec:windows 0:1 name' => 'rspec:windows name',
'rspec:windows 10000 20000' => 'rspec:windows',
'rspec:windows 0 : / 1' => 'rspec:windows',
'rspec:windows 0 : / 1 name' => 'rspec:windows name',
'0 1 name ruby' => 'name ruby',
'0 :/ 1 name ruby' => 'name ruby',
'rspec: [aws]' => 'rspec',
'rspec: [aws] 0/1' => 'rspec',
'rspec: [aws, max memory]' => 'rspec',
'rspec:linux: [aws, max memory, data]' => 'rspec:linux',
'rspec: [inception: [something, other thing], value]' => 'rspec',
'rspec:windows 0/1: [name, other]' => 'rspec:windows',
'rspec:windows: [name, other] 0/1' => 'rspec:windows',
'rspec:windows: [name, 0/1] 0/1' => 'rspec:windows',
'rspec:windows: [0/1, name]' => 'rspec:windows',
'rspec:windows: [, ]' => 'rspec:windows',
'rspec:windows: [name]' => 'rspec:windows',
'rspec:windows: [name,other]' => 'rspec:windows'
}
tests.each do |name, group_name|
it "'#{name}' puts in '#{group_name}'" do
commit_status.name = name
is_expected.to eq(group_name)
end
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment