Support recursive extends:
in .gitlab-ci.yml
This commit is contained in:
parent
5b9a6ca00a
commit
58414c143f
5 changed files with 207 additions and 94 deletions
|
@ -1,31 +0,0 @@
|
|||
module Gitlab
|
||||
module Ci
|
||||
class Config
|
||||
class Extendable
|
||||
include Enumerable
|
||||
|
||||
ExtensionError = Class.new(StandardError)
|
||||
|
||||
def initialize(hash)
|
||||
@hash = hash
|
||||
end
|
||||
|
||||
def each
|
||||
@hash.each_pair do |key, value|
|
||||
next unless value.key?(:extends)
|
||||
|
||||
yield key, value.fetch(:extends).to_sym, value
|
||||
end
|
||||
end
|
||||
|
||||
def extend!
|
||||
@hash.tap do
|
||||
each do |key, extends, value|
|
||||
@hash[key] = @hash.fetch(extends).deep_merge(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
34
lib/gitlab/ci/config/extendable/collection.rb
Normal file
34
lib/gitlab/ci/config/extendable/collection.rb
Normal file
|
@ -0,0 +1,34 @@
|
|||
module Gitlab
|
||||
module Ci
|
||||
class Config
|
||||
module Extendable
|
||||
class Collection
|
||||
include Enumerable
|
||||
|
||||
ExtensionError = Class.new(StandardError)
|
||||
|
||||
def initialize(hash, context = hash)
|
||||
@hash = hash
|
||||
@context = context
|
||||
end
|
||||
|
||||
def each
|
||||
@hash.each_pair do |key, value|
|
||||
next unless value.key?(:extends)
|
||||
|
||||
yield Extendable::Entry.new(key, value, @context)
|
||||
end
|
||||
end
|
||||
|
||||
def extend!
|
||||
each do |entry|
|
||||
raise ExtensionError unless entry.valid?
|
||||
|
||||
@hash[entry.key] = entry.extend!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
51
lib/gitlab/ci/config/extendable/entry.rb
Normal file
51
lib/gitlab/ci/config/extendable/entry.rb
Normal file
|
@ -0,0 +1,51 @@
|
|||
module Gitlab
|
||||
module Ci
|
||||
class Config
|
||||
module Extendable
|
||||
class Entry
|
||||
attr_reader :key
|
||||
|
||||
def initialize(key, value, context, parent = nil)
|
||||
@key = key
|
||||
@value = value
|
||||
@context = context
|
||||
@parent = parent
|
||||
end
|
||||
|
||||
def valid?
|
||||
true
|
||||
end
|
||||
|
||||
# def circular_dependency?
|
||||
# @extends.to_s == @key.to_s
|
||||
# end
|
||||
|
||||
def base
|
||||
Extendable::Entry
|
||||
.new(extends, @context.fetch(extends), @context, self)
|
||||
.extend!
|
||||
end
|
||||
|
||||
def extensible?
|
||||
@value.key?(:extends)
|
||||
end
|
||||
|
||||
def extends
|
||||
@value.fetch(:extends).to_sym
|
||||
end
|
||||
|
||||
def extend!
|
||||
if extensible?
|
||||
original = @value.dup
|
||||
parent = base.dup
|
||||
|
||||
@value.clear.deep_merge!(parent).deep_merge!(original)
|
||||
else
|
||||
@value.to_h
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
122
spec/lib/gitlab/ci/config/extendable/collection_spec.rb
Normal file
122
spec/lib/gitlab/ci/config/extendable/collection_spec.rb
Normal file
|
@ -0,0 +1,122 @@
|
|||
require 'fast_spec_helper'
|
||||
|
||||
describe Gitlab::Ci::Config::Extendable::Collection do
|
||||
subject { described_class.new(hash) }
|
||||
|
||||
describe '#each' do
|
||||
context 'when there is extendable entry in the hash' do
|
||||
let(:test) do
|
||||
{ extends: 'something', only: %w[master] }
|
||||
end
|
||||
|
||||
let(:hash) do
|
||||
{ something: { script: 'ls' }, test: test }
|
||||
end
|
||||
|
||||
it 'yields the test hash' do
|
||||
expect { |b| subject.each(&b) }.to yield_control
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not extending using a hash' do
|
||||
let(:hash) do
|
||||
{ something: { extends: [1], script: 'ls' } }
|
||||
end
|
||||
|
||||
it 'yields invalid extends as well' do
|
||||
expect { |b| subject.each(&b) }.to yield_control
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#extend!' do
|
||||
context 'when a hash has a single simple extension' do
|
||||
let(:hash) do
|
||||
{
|
||||
something: {
|
||||
script: 'deploy',
|
||||
only: { variables: %w[$SOMETHING] }
|
||||
},
|
||||
|
||||
test: {
|
||||
extends: 'something',
|
||||
script: 'ls',
|
||||
only: { refs: %w[master] }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'extends a hash with a deep reverse merge' do
|
||||
expect(subject.extend!).to eq(
|
||||
something: {
|
||||
script: 'deploy',
|
||||
only: { variables: %w[$SOMETHING] }
|
||||
},
|
||||
|
||||
test: {
|
||||
extends: 'something',
|
||||
script: 'ls',
|
||||
only: {
|
||||
refs: %w[master],
|
||||
variables: %w[$SOMETHING]
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a hash uses recursive extensions' do
|
||||
let(:hash) do
|
||||
{
|
||||
test: {
|
||||
extends: 'something',
|
||||
script: 'ls',
|
||||
only: { refs: %w[master] }
|
||||
},
|
||||
|
||||
something: {
|
||||
extends: '.first',
|
||||
script: 'deploy',
|
||||
only: { variables: %w[$SOMETHING] }
|
||||
},
|
||||
|
||||
'.first': {
|
||||
script: 'run',
|
||||
only: { kubernetes: 'active' }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'extends a hash with a deep reverse merge' do
|
||||
expect(subject.extend!).to eq(
|
||||
'.first': {
|
||||
script: 'run',
|
||||
only: { kubernetes: 'active' }
|
||||
},
|
||||
|
||||
something: {
|
||||
extends: '.first',
|
||||
script: 'deploy',
|
||||
only: {
|
||||
kubernetes: 'active',
|
||||
variables: %w[$SOMETHING]
|
||||
}
|
||||
},
|
||||
|
||||
test: {
|
||||
extends: 'something',
|
||||
script: 'ls',
|
||||
only: {
|
||||
refs: %w[master],
|
||||
variables: %w[$SOMETHING],
|
||||
kubernetes: 'active'
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
pending 'when invalid `extends` is specified'
|
||||
pending 'when circular dependecy has been detected'
|
||||
end
|
||||
end
|
|
@ -1,63 +0,0 @@
|
|||
require 'fast_spec_helper'
|
||||
|
||||
describe Gitlab::Ci::Config::Extendable do
|
||||
subject { described_class.new(hash) }
|
||||
|
||||
describe '#each' do
|
||||
context 'when there is extendable entry in the hash' do
|
||||
let(:test) do
|
||||
{ extends: 'something', only: %w[master] }
|
||||
end
|
||||
|
||||
let(:hash) do
|
||||
{ something: { script: 'ls' }, test: test }
|
||||
end
|
||||
|
||||
it 'yields the test hash' do
|
||||
expect { |b| subject.each(&b) }
|
||||
.to yield_with_args(:test, :something, test)
|
||||
end
|
||||
end
|
||||
|
||||
pending 'when not extending using a hash'
|
||||
end
|
||||
|
||||
describe '#extend!' do
|
||||
context 'when a hash has a single simple extension' do
|
||||
let(:hash) do
|
||||
{
|
||||
something: {
|
||||
script: 'deploy',
|
||||
only: { variables: %w[$SOMETHING] }
|
||||
},
|
||||
test: {
|
||||
extends: 'something',
|
||||
script: 'ls',
|
||||
only: { refs: %w[master] }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'extends a hash with reverse merge' do
|
||||
expect(subject.extend!).to eq(
|
||||
something: {
|
||||
script: 'deploy',
|
||||
only: { variables: %w[$SOMETHING] }
|
||||
},
|
||||
test: {
|
||||
extends: 'something',
|
||||
script: 'ls',
|
||||
only: {
|
||||
refs: %w[master],
|
||||
variables: %w[$SOMETHING]
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
pending 'when a hash recursive extensions'
|
||||
|
||||
pending 'when invalid `extends` is specified'
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue