Commit 6150c3ff authored by Kamil Trzciński's avatar Kamil Trzciński

Expand variables only when needed

This makes us to expand variables only when needed,
instead of requesting all variables each time.

This specifically helps in situation when explicit name
of `environment: production` is used.
parent a5586948
...@@ -384,7 +384,7 @@ module Ci ...@@ -384,7 +384,7 @@ module Ci
return unless has_environment? return unless has_environment?
strong_memoize(:expanded_environment_name) do strong_memoize(:expanded_environment_name) do
ExpandVariables.expand(environment, simple_variables) ExpandVariables.expand(environment, -> { simple_variables })
end end
end end
......
...@@ -42,7 +42,7 @@ class UpdateDeploymentService ...@@ -42,7 +42,7 @@ class UpdateDeploymentService
return unless environment_url return unless environment_url
@expanded_environment_url = @expanded_environment_url =
ExpandVariables.expand(environment_url, variables) ExpandVariables.expand(environment_url, -> { variables })
end end
def environment_url def environment_url
......
...@@ -3,6 +3,20 @@ ...@@ -3,6 +3,20 @@
module ExpandVariables module ExpandVariables
class << self class << self
def expand(value, variables) def expand(value, variables)
variables_hash = nil
value.gsub(/\$([a-zA-Z_][a-zA-Z0-9_]*)|\${\g<1>}|%\g<1>%/) do
variables_hash ||= transform_variables(variables)
variables_hash[$1 || $2]
end
end
private
def transform_variables(variables)
# Lazily initialise variables
variables = variables.call if variables.is_a?(Proc)
# Convert hash array to variables # Convert hash array to variables
if variables.is_a?(Array) if variables.is_a?(Array)
variables = variables.reduce({}) do |hash, variable| variables = variables.reduce({}) do |hash, variable|
...@@ -11,9 +25,7 @@ module ExpandVariables ...@@ -11,9 +25,7 @@ module ExpandVariables
end end
end end
value.gsub(/\$([a-zA-Z_][a-zA-Z0-9_]*)|\${\g<1>}|%\g<1>%/) do variables
variables[$1 || $2]
end
end end
end end
end end
...@@ -4,62 +4,131 @@ require 'spec_helper' ...@@ -4,62 +4,131 @@ require 'spec_helper'
describe ExpandVariables do describe ExpandVariables do
describe '#expand' do describe '#expand' do
subject { described_class.expand(value, variables) } context 'table tests' do
using RSpec::Parameterized::TableSyntax
tests = [ where do
{ value: 'key', {
result: 'key', "no expansion": {
variables: [] }, value: 'key',
{ value: 'key$variable', result: 'key',
result: 'key', variables: []
variables: [] }, },
{ value: 'key$variable', "missing variable": {
result: 'keyvalue', value: 'key$variable',
variables: [ result: 'key',
{ key: 'variable', value: 'value' } variables: []
] }, },
{ value: 'key${variable}', "simple expansion": {
result: 'keyvalue', value: 'key$variable',
variables: [ result: 'keyvalue',
{ key: 'variable', value: 'value' } variables: [
] }, { key: 'variable', value: 'value' }
{ value: 'key$variable$variable2', ]
result: 'keyvalueresult', },
variables: [ "simple with hash of variables": {
{ key: 'variable', value: 'value' }, value: 'key$variable',
{ key: 'variable2', value: 'result' } result: 'keyvalue',
] }, variables: {
{ value: 'key${variable}${variable2}', 'variable' => 'value'
result: 'keyvalueresult', }
variables: [ },
{ key: 'variable', value: 'value' }, "complex expansion": {
{ key: 'variable2', value: 'result' } value: 'key${variable}',
] }, result: 'keyvalue',
{ value: 'key$variable2$variable', variables: [
result: 'keyresultvalue', { key: 'variable', value: 'value' }
variables: [ ]
{ key: 'variable', value: 'value' }, },
{ key: 'variable2', value: 'result' } "simple expansions": {
] }, value: 'key$variable$variable2',
{ value: 'key${variable2}${variable}', result: 'keyvalueresult',
result: 'keyresultvalue', variables: [
variables: [ { key: 'variable', value: 'value' },
{ key: 'variable', value: 'value' }, { key: 'variable2', value: 'result' }
{ key: 'variable2', value: 'result' } ]
] }, },
{ value: 'review/$CI_COMMIT_REF_NAME', "complex expansions": {
result: 'review/feature/add-review-apps', value: 'key${variable}${variable2}',
variables: [ result: 'keyvalueresult',
{ key: 'CI_COMMIT_REF_NAME', value: 'feature/add-review-apps' } variables: [
] } { key: 'variable', value: 'value' },
] { key: 'variable2', value: 'result' }
]
},
"complex expansions with missing variable": {
value: 'key${variable}${variable2}',
result: 'keyvalue',
variables: [
{ key: 'variable', value: 'value' }
]
},
"out-of-order expansion": {
value: 'key$variable2$variable',
result: 'keyresultvalue',
variables: [
{ key: 'variable', value: 'value' },
{ key: 'variable2', value: 'result' }
]
},
"out-of-order complex expansion": {
value: 'key${variable2}${variable}',
result: 'keyresultvalue',
variables: [
{ key: 'variable', value: 'value' },
{ key: 'variable2', value: 'result' }
]
},
"review-apps expansion": {
value: 'review/$CI_COMMIT_REF_NAME',
result: 'review/feature/add-review-apps',
variables: [
{ key: 'CI_COMMIT_REF_NAME', value: 'feature/add-review-apps' }
]
},
"do not lazily access variables when no expansion": {
value: 'key',
result: 'key',
variables: -> { raise NotImplementedError }
},
"lazily access variables": {
value: 'key$variable',
result: 'keyvalue',
variables: -> { [{ key: 'variable', value: 'value' }] }
}
}
end
with_them do
subject { ExpandVariables.expand(value, variables) } # rubocop:disable RSpec/DescribedClass
it { is_expected.to eq(result) }
end
end
context 'lazily inits variables' do
let(:variables) { -> { [{ key: 'variable', value: 'result' }] } }
subject { described_class.expand(value, variables) }
context 'when expanding variable' do
let(:value) { 'key$variable$variable2' }
it 'calls block at most once' do
expect(variables).to receive(:call).once.and_call_original
is_expected.to eq('keyresult')
end
end
context 'when no expansion is needed' do
let(:value) { 'key' }
tests.each do |test| it 'does not call block' do
context "#{test[:value]} resolves to #{test[:result]}" do expect(variables).not_to receive(:call)
let(:value) { test[:value] }
let(:variables) { test[:variables] }
it { is_expected.to eq(test[:result]) } is_expected.to eq('key')
end
end end
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