diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-07-21 15:08:33 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-07-21 15:08:33 +0300 |
commit | 6c44b676312eb6cdffadef45f9ca3e29a8cc92ab (patch) | |
tree | 06666cd369ac9ad0533cec689f2c2b4fb826f797 /lib | |
parent | c1cea595b6a9b4d85424e9afd2cb765101ee04bf (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gitlab/ci/config/yaml/interpolator.rb | 6 | ||||
-rw-r--r-- | lib/gitlab/ci/interpolation/inputs.rb | 70 | ||||
-rw-r--r-- | lib/gitlab/ci/interpolation/inputs/base_input.rb | 92 | ||||
-rw-r--r-- | lib/gitlab/ci/interpolation/inputs/string_input.rb | 31 |
4 files changed, 198 insertions, 1 deletions
diff --git a/lib/gitlab/ci/config/yaml/interpolator.rb b/lib/gitlab/ci/config/yaml/interpolator.rb index d8c81bf8b98..bb6c3215661 100644 --- a/lib/gitlab/ci/config/yaml/interpolator.rb +++ b/lib/gitlab/ci/config/yaml/interpolator.rb @@ -73,7 +73,11 @@ module Gitlab end def inputs - @inputs ||= Ci::Input::Inputs.new(spec, args) + @inputs ||= if Feature.enabled?(:ci_interpolation_inputs_refactor) + Ci::Interpolation::Inputs.new(spec, args) + else + Ci::Input::Inputs.new(spec, args) + end end def context diff --git a/lib/gitlab/ci/interpolation/inputs.rb b/lib/gitlab/ci/interpolation/inputs.rb new file mode 100644 index 00000000000..910606e6ab2 --- /dev/null +++ b/lib/gitlab/ci/interpolation/inputs.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + module Interpolation + # Interpolation inputs provided by the user. + class Inputs + UnknownInputTypeError = Class.new(StandardError) + + TYPES = [ + StringInput + ].freeze + + def initialize(specs, args) + @specs = specs.to_h + @args = args.to_h + @inputs = [] + @errors = [] + + validate! + fabricate! + end + + def errors + @errors + @inputs.flat_map(&:errors) + end + + def valid? + errors.none? + end + + def to_hash + @inputs.inject({}) do |hash, input| + hash.merge(input.to_hash) + end + end + + private + + def validate! + unknown_inputs = @args.keys - @specs.keys + return if unknown_inputs.empty? + + @errors.push("unknown input arguments: #{unknown_inputs.join(', ')}") + end + + def fabricate! + @specs.each do |input_name, spec| + input_type = TYPES.find { |klass| klass.matches?(spec) } + + unless input_type + @errors.push( + "unknown input specification for `#{input_name}` (valid types: #{valid_type_names.join(', ')})") + next + end + + @inputs.push(input_type.new( + name: input_name, + spec: spec, + value: @args[input_name])) + end + end + + def valid_type_names + TYPES.map(&:type_name) + end + end + end + end +end diff --git a/lib/gitlab/ci/interpolation/inputs/base_input.rb b/lib/gitlab/ci/interpolation/inputs/base_input.rb new file mode 100644 index 00000000000..20b9bfacb17 --- /dev/null +++ b/lib/gitlab/ci/interpolation/inputs/base_input.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + module Interpolation + class Inputs + ## + # This is a common abstraction for all input types + class BaseInput + ArgumentNotValidError = Class.new(StandardError) + + # Checks whether the class matches the type in the specification + def self.matches?(spec) + raise NotImplementedError + end + + # Human readable type used in error messages + def self.type_name + raise NotImplementedError + end + + # Checks whether the provided value is of the given type + def valid_value?(value) + raise NotImplementedError + end + + attr_reader :errors, :name, :spec, :value + + def initialize(name:, spec:, value:) + @name = name + @errors = [] + + # Treat minimal spec definition (nil) as a valid hash: + # spec: + # inputs: + # website: + @spec = spec || {} # specification from input definition + @value = value # actual value provided by the user + + validate! + end + + def to_hash + raise ArgumentNotValidError unless valid? + + { name => actual_value } + end + + def valid? + @errors.none? + end + + private + + def validate! + return error('required value has not been provided') if required_input? && value.nil? + + # validate default value + return error("default value is not a #{self.class.type_name}") if !required_input? && !valid_value?(default) + + # validate provided value + error("provided value is not a #{self.class.type_name}") unless valid_value?(value) + end + + def error(message) + @errors.push("`#{name}` input: #{message}") + end + + def actual_value + # nil check is to support boolean values. + value.nil? ? default : value + end + + # An input specification without a default value is required. + # For example: + # ```yaml + # spec: + # inputs: + # website: + # ``` + def required_input? + !spec.key?(:default) + end + + def default + spec[:default] + end + end + end + end + end +end diff --git a/lib/gitlab/ci/interpolation/inputs/string_input.rb b/lib/gitlab/ci/interpolation/inputs/string_input.rb new file mode 100644 index 00000000000..7a042964cbb --- /dev/null +++ b/lib/gitlab/ci/interpolation/inputs/string_input.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + module Interpolation + class Inputs + class StringInput < BaseInput + def self.matches?(spec) + # The input spec can be `nil` when using a minimal specification + # and also when `type` is not specified. + # + # ```yaml + # spec: + # inputs: + # foo: + # ``` + spec.nil? || (spec.is_a?(Hash) && [nil, type_name].include?(spec[:type])) + end + + def self.type_name + 'string' + end + + def valid_value?(value) + value.nil? || value.is_a?(String) + end + end + end + end + end +end |