diff --git a/lib/gitlab/ci/config/extendable.rb b/lib/gitlab/ci/config/extendable.rb new file mode 100644 index 00000000000..dea0dbdb44b --- /dev/null +++ b/lib/gitlab/ci/config/extendable.rb @@ -0,0 +1,31 @@ +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 diff --git a/spec/lib/gitlab/ci/config/extendable_spec.rb b/spec/lib/gitlab/ci/config/extendable_spec.rb new file mode 100644 index 00000000000..a23fe560202 --- /dev/null +++ b/spec/lib/gitlab/ci/config/extendable_spec.rb @@ -0,0 +1,63 @@ +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