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