1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
# frozen_string_literal: true
module Gitlab
module Ci
class Config
module Interpolation
##
# Performs CI config file interpolation, and surfaces all possible interpolation errors.
#
class Interpolator
attr_reader :config, :args, :variables, :errors
def initialize(config, args, variables)
@config = config
@args = args.to_h
@variables = variables
@errors = []
@interpolated = false
end
def valid?
@errors.none?
end
def to_hash
@result.to_h
end
def error_message
# Interpolator can have multiple error messages, like: ["interpolation interrupted by errors", "unknown
# interpolation key: `abc`"] ?
#
# We are joining them together into a single one, because only one error can be surfaced when an external
# file gets included and is invalid. The limit to three error messages combined is more than required.
#
@errors.first(3).join(', ')
end
def interpolate!
return @errors.push(config.error) unless config.valid?
if inputs_without_header?
return @errors.push(
_('Given inputs not defined in the `spec` section of the included configuration file'))
end
return @result ||= config.content unless config.has_header?
return @errors.concat(header.errors) unless header.valid?
return @errors.concat(inputs.errors) unless inputs.valid?
return @errors.concat(context.errors) unless context.valid?
return @errors.concat(template.errors) unless template.valid?
@interpolated = true
@result ||= template.interpolated.to_h.deep_symbolize_keys
end
def interpolated?
@interpolated
end
private
def inputs_without_header?
args.any? && !config.has_header?
end
def header
@entry ||= Header::Root.new(config.header).tap do |header|
header.key = 'header'
header.compose!
end
end
def content
@content ||= config.content
end
def spec
@spec ||= header.inputs_value
end
def inputs
@inputs ||= Inputs.new(spec, args)
end
def context
@context ||= Context.new({ inputs: inputs.to_hash }, variables: variables)
end
def template
@template ||= Template.new(content, context)
end
end
end
end
end
end
|