From c70146839009397b3a1a9ab9bd1108db3cb70c04 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 Nov 2016 10:31:45 +0100 Subject: Improve naming convention in ci configuration module --- lib/gitlab/ci/config.rb | 2 +- lib/gitlab/ci/config/entry/artifacts.rb | 35 ++++++ lib/gitlab/ci/config/entry/attributable.rb | 23 ++++ lib/gitlab/ci/config/entry/boolean.rb | 18 +++ lib/gitlab/ci/config/entry/cache.rb | 29 +++++ lib/gitlab/ci/config/entry/commands.rb | 33 +++++ lib/gitlab/ci/config/entry/configurable.rb | 78 ++++++++++++ lib/gitlab/ci/config/entry/environment.rb | 82 ++++++++++++ lib/gitlab/ci/config/entry/factory.rb | 73 +++++++++++ lib/gitlab/ci/config/entry/global.rb | 72 +++++++++++ lib/gitlab/ci/config/entry/hidden.rb | 22 ++++ lib/gitlab/ci/config/entry/image.rb | 18 +++ lib/gitlab/ci/config/entry/job.rb | 140 +++++++++++++++++++++ lib/gitlab/ci/config/entry/jobs.rb | 52 ++++++++ lib/gitlab/ci/config/entry/key.rb | 18 +++ .../ci/config/entry/legacy_validation_helpers.rb | 51 ++++++++ lib/gitlab/ci/config/entry/node.rb | 83 ++++++++++++ lib/gitlab/ci/config/entry/paths.rb | 18 +++ lib/gitlab/ci/config/entry/script.rb | 18 +++ lib/gitlab/ci/config/entry/services.rb | 18 +++ lib/gitlab/ci/config/entry/stage.rb | 22 ++++ lib/gitlab/ci/config/entry/stages.rb | 22 ++++ lib/gitlab/ci/config/entry/trigger.rb | 26 ++++ lib/gitlab/ci/config/entry/undefined.rb | 36 ++++++ lib/gitlab/ci/config/entry/unspecified.rb | 19 +++ lib/gitlab/ci/config/entry/validatable.rb | 27 ++++ lib/gitlab/ci/config/entry/validator.rb | 42 +++++++ lib/gitlab/ci/config/entry/validators.rb | 82 ++++++++++++ lib/gitlab/ci/config/entry/variables.rb | 22 ++++ lib/gitlab/ci/config/node/artifacts.rb | 35 ------ lib/gitlab/ci/config/node/attributable.rb | 23 ---- lib/gitlab/ci/config/node/boolean.rb | 18 --- lib/gitlab/ci/config/node/cache.rb | 29 ----- lib/gitlab/ci/config/node/commands.rb | 33 ----- lib/gitlab/ci/config/node/configurable.rb | 78 ------------ lib/gitlab/ci/config/node/entry.rb | 83 ------------ lib/gitlab/ci/config/node/environment.rb | 82 ------------ lib/gitlab/ci/config/node/factory.rb | 73 ----------- lib/gitlab/ci/config/node/global.rb | 72 ----------- lib/gitlab/ci/config/node/hidden.rb | 22 ---- lib/gitlab/ci/config/node/image.rb | 18 --- lib/gitlab/ci/config/node/job.rb | 140 --------------------- lib/gitlab/ci/config/node/jobs.rb | 52 -------- lib/gitlab/ci/config/node/key.rb | 18 --- .../ci/config/node/legacy_validation_helpers.rb | 51 -------- lib/gitlab/ci/config/node/paths.rb | 18 --- lib/gitlab/ci/config/node/script.rb | 18 --- lib/gitlab/ci/config/node/services.rb | 18 --- lib/gitlab/ci/config/node/stage.rb | 22 ---- lib/gitlab/ci/config/node/stages.rb | 22 ---- lib/gitlab/ci/config/node/trigger.rb | 26 ---- lib/gitlab/ci/config/node/undefined.rb | 38 ------ lib/gitlab/ci/config/node/unspecified.rb | 19 --- lib/gitlab/ci/config/node/validatable.rb | 27 ---- lib/gitlab/ci/config/node/validator.rb | 42 ------- lib/gitlab/ci/config/node/validators.rb | 82 ------------ lib/gitlab/ci/config/node/variables.rb | 22 ---- 57 files changed, 1180 insertions(+), 1182 deletions(-) create mode 100644 lib/gitlab/ci/config/entry/artifacts.rb create mode 100644 lib/gitlab/ci/config/entry/attributable.rb create mode 100644 lib/gitlab/ci/config/entry/boolean.rb create mode 100644 lib/gitlab/ci/config/entry/cache.rb create mode 100644 lib/gitlab/ci/config/entry/commands.rb create mode 100644 lib/gitlab/ci/config/entry/configurable.rb create mode 100644 lib/gitlab/ci/config/entry/environment.rb create mode 100644 lib/gitlab/ci/config/entry/factory.rb create mode 100644 lib/gitlab/ci/config/entry/global.rb create mode 100644 lib/gitlab/ci/config/entry/hidden.rb create mode 100644 lib/gitlab/ci/config/entry/image.rb create mode 100644 lib/gitlab/ci/config/entry/job.rb create mode 100644 lib/gitlab/ci/config/entry/jobs.rb create mode 100644 lib/gitlab/ci/config/entry/key.rb create mode 100644 lib/gitlab/ci/config/entry/legacy_validation_helpers.rb create mode 100644 lib/gitlab/ci/config/entry/node.rb create mode 100644 lib/gitlab/ci/config/entry/paths.rb create mode 100644 lib/gitlab/ci/config/entry/script.rb create mode 100644 lib/gitlab/ci/config/entry/services.rb create mode 100644 lib/gitlab/ci/config/entry/stage.rb create mode 100644 lib/gitlab/ci/config/entry/stages.rb create mode 100644 lib/gitlab/ci/config/entry/trigger.rb create mode 100644 lib/gitlab/ci/config/entry/undefined.rb create mode 100644 lib/gitlab/ci/config/entry/unspecified.rb create mode 100644 lib/gitlab/ci/config/entry/validatable.rb create mode 100644 lib/gitlab/ci/config/entry/validator.rb create mode 100644 lib/gitlab/ci/config/entry/validators.rb create mode 100644 lib/gitlab/ci/config/entry/variables.rb delete mode 100644 lib/gitlab/ci/config/node/artifacts.rb delete mode 100644 lib/gitlab/ci/config/node/attributable.rb delete mode 100644 lib/gitlab/ci/config/node/boolean.rb delete mode 100644 lib/gitlab/ci/config/node/cache.rb delete mode 100644 lib/gitlab/ci/config/node/commands.rb delete mode 100644 lib/gitlab/ci/config/node/configurable.rb delete mode 100644 lib/gitlab/ci/config/node/entry.rb delete mode 100644 lib/gitlab/ci/config/node/environment.rb delete mode 100644 lib/gitlab/ci/config/node/factory.rb delete mode 100644 lib/gitlab/ci/config/node/global.rb delete mode 100644 lib/gitlab/ci/config/node/hidden.rb delete mode 100644 lib/gitlab/ci/config/node/image.rb delete mode 100644 lib/gitlab/ci/config/node/job.rb delete mode 100644 lib/gitlab/ci/config/node/jobs.rb delete mode 100644 lib/gitlab/ci/config/node/key.rb delete mode 100644 lib/gitlab/ci/config/node/legacy_validation_helpers.rb delete mode 100644 lib/gitlab/ci/config/node/paths.rb delete mode 100644 lib/gitlab/ci/config/node/script.rb delete mode 100644 lib/gitlab/ci/config/node/services.rb delete mode 100644 lib/gitlab/ci/config/node/stage.rb delete mode 100644 lib/gitlab/ci/config/node/stages.rb delete mode 100644 lib/gitlab/ci/config/node/trigger.rb delete mode 100644 lib/gitlab/ci/config/node/undefined.rb delete mode 100644 lib/gitlab/ci/config/node/unspecified.rb delete mode 100644 lib/gitlab/ci/config/node/validatable.rb delete mode 100644 lib/gitlab/ci/config/node/validator.rb delete mode 100644 lib/gitlab/ci/config/node/validators.rb delete mode 100644 lib/gitlab/ci/config/node/variables.rb (limited to 'lib/gitlab/ci') diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb index bbfa6cf7d05..06599238d22 100644 --- a/lib/gitlab/ci/config.rb +++ b/lib/gitlab/ci/config.rb @@ -13,7 +13,7 @@ module Gitlab def initialize(config) @config = Loader.new(config).load! - @global = Node::Global.new(@config) + @global = Entry::Global.new(@config) @global.compose! end diff --git a/lib/gitlab/ci/config/entry/artifacts.rb b/lib/gitlab/ci/config/entry/artifacts.rb new file mode 100644 index 00000000000..b756b0d4555 --- /dev/null +++ b/lib/gitlab/ci/config/entry/artifacts.rb @@ -0,0 +1,35 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a configuration of job artifacts. + # + class Artifacts < Node + include Validatable + include Attributable + + ALLOWED_KEYS = %i[name untracked paths when expire_in] + + attributes ALLOWED_KEYS + + validations do + validates :config, type: Hash + validates :config, allowed_keys: ALLOWED_KEYS + + with_options allow_nil: true do + validates :name, type: String + validates :untracked, boolean: true + validates :paths, array_of_strings: true + validates :when, + inclusion: { in: %w[on_success on_failure always], + message: 'should be on_success, on_failure ' \ + 'or always' } + validates :expire_in, duration: true + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/attributable.rb b/lib/gitlab/ci/config/entry/attributable.rb new file mode 100644 index 00000000000..1c8b55ee4c4 --- /dev/null +++ b/lib/gitlab/ci/config/entry/attributable.rb @@ -0,0 +1,23 @@ +module Gitlab + module Ci + class Config + module Entry + module Attributable + extend ActiveSupport::Concern + + class_methods do + def attributes(*attributes) + attributes.flatten.each do |attribute| + define_method(attribute) do + return unless config.is_a?(Hash) + + config[attribute] + end + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/boolean.rb b/lib/gitlab/ci/config/entry/boolean.rb new file mode 100644 index 00000000000..f3357f85b99 --- /dev/null +++ b/lib/gitlab/ci/config/entry/boolean.rb @@ -0,0 +1,18 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a boolean value. + # + class Boolean < Node + include Validatable + + validations do + validates :config, boolean: true + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/cache.rb b/lib/gitlab/ci/config/entry/cache.rb new file mode 100644 index 00000000000..544e0953b17 --- /dev/null +++ b/lib/gitlab/ci/config/entry/cache.rb @@ -0,0 +1,29 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a cache configuration + # + class Cache < Node + include Configurable + + ALLOWED_KEYS = %i[key untracked paths] + + validations do + validates :config, allowed_keys: ALLOWED_KEYS + end + + node :key, Entry::Key, + description: 'Cache key used to define a cache affinity.' + + node :untracked, Entry::Boolean, + description: 'Cache all untracked files.' + + node :paths, Entry::Paths, + description: 'Specify which paths should be cached across builds.' + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/commands.rb b/lib/gitlab/ci/config/entry/commands.rb new file mode 100644 index 00000000000..65d19db249c --- /dev/null +++ b/lib/gitlab/ci/config/entry/commands.rb @@ -0,0 +1,33 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a job script. + # + class Commands < Node + include Validatable + + validations do + include LegacyValidationHelpers + + validate do + unless string_or_array_of_strings?(config) + errors.add(:config, + 'should be a string or an array of strings') + end + end + + def string_or_array_of_strings?(field) + validate_string(field) || validate_array_of_strings(field) + end + end + + def value + Array(@config) + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/configurable.rb b/lib/gitlab/ci/config/entry/configurable.rb new file mode 100644 index 00000000000..b6c7b05e369 --- /dev/null +++ b/lib/gitlab/ci/config/entry/configurable.rb @@ -0,0 +1,78 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # This mixin is responsible for adding DSL, which purpose is to + # simplifly process of adding child nodes. + # + # This can be used only if parent node is a configuration entry that + # holds a hash as a configuration value, for example: + # + # job: + # script: ... + # artifacts: ... + # + module Configurable + extend ActiveSupport::Concern + include Validatable + + included do + validations do + validates :config, type: Hash + end + end + + def compose!(deps = nil) + return unless valid? + + self.class.nodes.each do |key, factory| + factory + .value(@config[key]) + .with(key: key, parent: self) + + @entries[key] = factory.create! + end + + yield if block_given? + + @entries.each_value do |entry| + entry.compose!(deps) + end + end + + class_methods do + def nodes + Hash[(@nodes || {}).map { |key, factory| [key, factory.dup] }] + end + + private # rubocop:disable Lint/UselessAccessModifier + + def node(key, node, metadata) + factory = Entry::Factory.new(node) + .with(description: metadata[:description]) + + (@nodes ||= {}).merge!(key.to_sym => factory) + end + + def helpers(*nodes) + nodes.each do |symbol| + define_method("#{symbol}_defined?") do + @entries[symbol].specified? if @entries[symbol] + end + + define_method("#{symbol}_value") do + return unless @entries[symbol] && @entries[symbol].valid? + + @entries[symbol].value + end + + alias_method symbol.to_sym, "#{symbol}_value".to_sym + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/environment.rb b/lib/gitlab/ci/config/entry/environment.rb new file mode 100644 index 00000000000..b7b4b91eb51 --- /dev/null +++ b/lib/gitlab/ci/config/entry/environment.rb @@ -0,0 +1,82 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents an environment. + # + class Environment < Node + include Validatable + + ALLOWED_KEYS = %i[name url action on_stop] + + validations do + validate do + unless hash? || string? + errors.add(:config, 'should be a hash or a string') + end + end + + validates :name, presence: true + validates :name, + type: { + with: String, + message: Gitlab::Regex.environment_name_regex_message } + + validates :name, + format: { + with: Gitlab::Regex.environment_name_regex, + message: Gitlab::Regex.environment_name_regex_message } + + with_options if: :hash? do + validates :config, allowed_keys: ALLOWED_KEYS + + validates :url, + length: { maximum: 255 }, + addressable_url: true, + allow_nil: true + + validates :action, + inclusion: { in: %w[start stop], message: 'should be start or stop' }, + allow_nil: true + + validates :on_stop, type: String, allow_nil: true + end + end + + def hash? + @config.is_a?(Hash) + end + + def string? + @config.is_a?(String) + end + + def name + value[:name] + end + + def url + value[:url] + end + + def action + value[:action] || 'start' + end + + def on_stop + value[:on_stop] + end + + def value + case @config + when String then { name: @config, action: 'start' } + when Hash then @config + else {} + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/factory.rb b/lib/gitlab/ci/config/entry/factory.rb new file mode 100644 index 00000000000..9f5e393d191 --- /dev/null +++ b/lib/gitlab/ci/config/entry/factory.rb @@ -0,0 +1,73 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Factory class responsible for fabricating entry objects. + # + class Factory + class InvalidFactory < StandardError; end + + def initialize(entry) + @entry = entry + @metadata = {} + @attributes = {} + end + + def value(value) + @value = value + self + end + + def metadata(metadata) + @metadata.merge!(metadata) + self + end + + def with(attributes) + @attributes.merge!(attributes) + self + end + + def create! + raise InvalidFactory unless defined?(@value) + + ## + # We assume that unspecified entry is undefined. + # See issue #18775. + # + if @value.nil? + Entry::Unspecified.new( + fabricate_unspecified + ) + else + fabricate(@entry, @value) + end + end + + private + + def fabricate_unspecified + ## + # If entry has a default value we fabricate concrete node + # with default value. + # + if @entry.default.nil? + fabricate(Entry::Undefined) + else + fabricate(@entry, @entry.default) + end + end + + def fabricate(entry, value = nil) + entry.new(value, @metadata).tap do |node| + node.key = @attributes[:key] + node.parent = @attributes[:parent] + node.description = @attributes[:description] + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/global.rb b/lib/gitlab/ci/config/entry/global.rb new file mode 100644 index 00000000000..cdf314a4b37 --- /dev/null +++ b/lib/gitlab/ci/config/entry/global.rb @@ -0,0 +1,72 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # This class represents a global entry - root Entry for entire + # GitLab CI Configuration file. + # + class Global < Node + include Configurable + + node :before_script, Entry::Script, + description: 'Script that will be executed before each job.' + + node :image, Entry::Image, + description: 'Docker image that will be used to execute jobs.' + + node :services, Entry::Services, + description: 'Docker images that will be linked to the container.' + + node :after_script, Entry::Script, + description: 'Script that will be executed after each job.' + + node :variables, Entry::Variables, + description: 'Environment variables that will be used.' + + node :stages, Entry::Stages, + description: 'Configuration of stages for this pipeline.' + + node :types, Entry::Stages, + description: 'Deprecated: stages for this pipeline.' + + node :cache, Entry::Cache, + description: 'Configure caching between build jobs.' + + helpers :before_script, :image, :services, :after_script, + :variables, :stages, :types, :cache, :jobs + + def compose!(_deps = nil) + super(self) do + compose_jobs! + compose_deprecated_entries! + end + end + + private + + def compose_jobs! + factory = Entry::Factory.new(Entry::Jobs) + .value(@config.except(*self.class.nodes.keys)) + .with(key: :jobs, parent: self, + description: 'Jobs definition for this pipeline') + + @entries[:jobs] = factory.create! + end + + def compose_deprecated_entries! + ## + # Deprecated `:types` key workaround - if types are defined and + # stages are not defined we use types definition as stages. + # + if types_defined? && !stages_defined? + @entries[:stages] = @entries[:types] + end + + @entries.delete(:types) + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/hidden.rb b/lib/gitlab/ci/config/entry/hidden.rb new file mode 100644 index 00000000000..6fc3aa385bc --- /dev/null +++ b/lib/gitlab/ci/config/entry/hidden.rb @@ -0,0 +1,22 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a hidden CI/CD key. + # + class Hidden < Node + include Validatable + + validations do + validates :config, presence: true + end + + def relevant? + false + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/image.rb b/lib/gitlab/ci/config/entry/image.rb new file mode 100644 index 00000000000..b5050257688 --- /dev/null +++ b/lib/gitlab/ci/config/entry/image.rb @@ -0,0 +1,18 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a Docker image. + # + class Image < Node + include Validatable + + validations do + validates :config, type: String + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb new file mode 100644 index 00000000000..b4e69a04706 --- /dev/null +++ b/lib/gitlab/ci/config/entry/job.rb @@ -0,0 +1,140 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a concrete CI/CD job. + # + class Job < Node + include Configurable + include Attributable + + ALLOWED_KEYS = %i[tags script only except type image services allow_failure + type stage when artifacts cache dependencies before_script + after_script variables environment] + + attributes :tags, :allow_failure, :when, :dependencies + + validations do + validates :config, allowed_keys: ALLOWED_KEYS + + validates :config, presence: true + validates :name, presence: true + validates :name, type: Symbol + + with_options allow_nil: true do + validates :tags, array_of_strings: true + validates :allow_failure, boolean: true + validates :when, + inclusion: { in: %w[on_success on_failure always manual], + message: 'should be on_success, on_failure, ' \ + 'always or manual' } + + validates :dependencies, array_of_strings: true + end + end + + node :before_script, Entry::Script, + description: 'Global before script overridden in this job.' + + node :script, Entry::Commands, + description: 'Commands that will be executed in this job.' + + node :stage, Entry::Stage, + description: 'Pipeline stage this job will be executed into.' + + node :type, Entry::Stage, + description: 'Deprecated: stage this job will be executed into.' + + node :after_script, Entry::Script, + description: 'Commands that will be executed when finishing job.' + + node :cache, Entry::Cache, + description: 'Cache definition for this job.' + + node :image, Entry::Image, + description: 'Image that will be used to execute this job.' + + node :services, Entry::Services, + description: 'Services that will be used to execute this job.' + + node :only, Entry::Trigger, + description: 'Refs policy this job will be executed for.' + + node :except, Entry::Trigger, + description: 'Refs policy this job will be executed for.' + + node :variables, Entry::Variables, + description: 'Environment variables available for this job.' + + node :artifacts, Entry::Artifacts, + description: 'Artifacts configuration for this job.' + + node :environment, Entry::Environment, + description: 'Environment configuration for this job.' + + helpers :before_script, :script, :stage, :type, :after_script, + :cache, :image, :services, :only, :except, :variables, + :artifacts, :commands, :environment + + def compose!(deps = nil) + super do + if type_defined? && !stage_defined? + @entries[:stage] = @entries[:type] + end + + @entries.delete(:type) + end + + inherit!(deps) + end + + def name + @metadata[:name] + end + + def value + @config.merge(to_hash.compact) + end + + def commands + (before_script_value.to_a + script_value.to_a).join("\n") + end + + private + + def inherit!(deps) + return unless deps + + self.class.nodes.each_key do |key| + global_entry = deps[key] + job_entry = @entries[key] + + if global_entry.specified? && !job_entry.specified? + @entries[key] = global_entry + end + end + end + + def to_hash + { name: name, + before_script: before_script, + script: script, + commands: commands, + image: image, + services: services, + stage: stage, + cache: cache, + only: only, + except: except, + variables: variables_defined? ? variables : nil, + environment: environment_defined? ? environment : nil, + environment_name: environment_defined? ? environment[:name] : nil, + artifacts: artifacts, + after_script: after_script } + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/jobs.rb b/lib/gitlab/ci/config/entry/jobs.rb new file mode 100644 index 00000000000..5671a09480b --- /dev/null +++ b/lib/gitlab/ci/config/entry/jobs.rb @@ -0,0 +1,52 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a set of jobs. + # + class Jobs < Node + include Validatable + + validations do + validates :config, type: Hash + + validate do + unless has_visible_job? + errors.add(:config, 'should contain at least one visible job') + end + end + + def has_visible_job? + config.any? { |name, _| !hidden?(name) } + end + end + + def hidden?(name) + name.to_s.start_with?('.') + end + + def compose!(deps = nil) + super do + @config.each do |name, config| + node = hidden?(name) ? Entry::Hidden : Entry::Job + + factory = Entry::Factory.new(node) + .value(config || {}) + .metadata(name: name) + .with(key: name, parent: self, + description: "#{name} job definition.") + + @entries[name] = factory.create! + end + + @entries.each_value do |entry| + entry.compose!(deps) + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/key.rb b/lib/gitlab/ci/config/entry/key.rb new file mode 100644 index 00000000000..0e4c9fe6edc --- /dev/null +++ b/lib/gitlab/ci/config/entry/key.rb @@ -0,0 +1,18 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a key. + # + class Key < Node + include Validatable + + validations do + validates :config, key: true + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/legacy_validation_helpers.rb b/lib/gitlab/ci/config/entry/legacy_validation_helpers.rb new file mode 100644 index 00000000000..f01975aab5c --- /dev/null +++ b/lib/gitlab/ci/config/entry/legacy_validation_helpers.rb @@ -0,0 +1,51 @@ +module Gitlab + module Ci + class Config + module Entry + module LegacyValidationHelpers + private + + def validate_duration(value) + value.is_a?(String) && ChronicDuration.parse(value) + rescue ChronicDuration::DurationParseError + false + end + + def validate_array_of_strings(values) + values.is_a?(Array) && values.all? { |value| validate_string(value) } + end + + def validate_array_of_strings_or_regexps(values) + values.is_a?(Array) && values.all? { |value| validate_string_or_regexp(value) } + end + + def validate_variables(variables) + variables.is_a?(Hash) && + variables.all? { |key, value| validate_string(key) && validate_string(value) } + end + + def validate_string(value) + value.is_a?(String) || value.is_a?(Symbol) + end + + def validate_string_or_regexp(value) + return true if value.is_a?(Symbol) + return false unless value.is_a?(String) + + if value.first == '/' && value.last == '/' + Regexp.new(value[1...-1]) + else + true + end + rescue RegexpError + false + end + + def validate_boolean(value) + value.in?([true, false]) + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/node.rb b/lib/gitlab/ci/config/entry/node.rb new file mode 100644 index 00000000000..5eef2868cd6 --- /dev/null +++ b/lib/gitlab/ci/config/entry/node.rb @@ -0,0 +1,83 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Base abstract class for each configuration entry node. + # + class Node + class InvalidError < StandardError; end + + attr_reader :config, :metadata + attr_accessor :key, :parent, :description + + def initialize(config, **metadata) + @config = config + @metadata = metadata + @entries = {} + + @validator = self.class.validator.new(self) + @validator.validate(:new) + end + + def [](key) + @entries[key] || Entry::Undefined.new + end + + def compose!(deps = nil) + return unless valid? + + yield if block_given? + end + + def leaf? + @entries.none? + end + + def descendants + @entries.values + end + + def ancestors + @parent ? @parent.ancestors + [@parent] : [] + end + + def valid? + errors.none? + end + + def errors + @validator.messages + descendants.flat_map(&:errors) + end + + def value + if leaf? + @config + else + meaningful = @entries.select do |_key, value| + value.specified? && value.relevant? + end + + Hash[meaningful.map { |key, entry| [key, entry.value] }] + end + end + + def specified? + true + end + + def relevant? + true + end + + def self.default + end + + def self.validator + Validator + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/paths.rb b/lib/gitlab/ci/config/entry/paths.rb new file mode 100644 index 00000000000..68dad161149 --- /dev/null +++ b/lib/gitlab/ci/config/entry/paths.rb @@ -0,0 +1,18 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents an array of paths. + # + class Paths < Node + include Validatable + + validations do + validates :config, array_of_strings: true + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/script.rb b/lib/gitlab/ci/config/entry/script.rb new file mode 100644 index 00000000000..29ecd9995ca --- /dev/null +++ b/lib/gitlab/ci/config/entry/script.rb @@ -0,0 +1,18 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a script. + # + class Script < Node + include Validatable + + validations do + validates :config, array_of_strings: true + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/services.rb b/lib/gitlab/ci/config/entry/services.rb new file mode 100644 index 00000000000..84f8ab780f5 --- /dev/null +++ b/lib/gitlab/ci/config/entry/services.rb @@ -0,0 +1,18 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a configuration of Docker services. + # + class Services < Node + include Validatable + + validations do + validates :config, array_of_strings: true + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/stage.rb b/lib/gitlab/ci/config/entry/stage.rb new file mode 100644 index 00000000000..b7afaba1de8 --- /dev/null +++ b/lib/gitlab/ci/config/entry/stage.rb @@ -0,0 +1,22 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a stage for a job. + # + class Stage < Node + include Validatable + + validations do + validates :config, type: String + end + + def self.default + 'test' + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/stages.rb b/lib/gitlab/ci/config/entry/stages.rb new file mode 100644 index 00000000000..ec187bd3732 --- /dev/null +++ b/lib/gitlab/ci/config/entry/stages.rb @@ -0,0 +1,22 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a configuration for pipeline stages. + # + class Stages < Node + include Validatable + + validations do + validates :config, array_of_strings: true + end + + def self.default + %w[build test deploy] + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/trigger.rb b/lib/gitlab/ci/config/entry/trigger.rb new file mode 100644 index 00000000000..28b0a9ffe01 --- /dev/null +++ b/lib/gitlab/ci/config/entry/trigger.rb @@ -0,0 +1,26 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a trigger policy for the job. + # + class Trigger < Node + include Validatable + + validations do + include LegacyValidationHelpers + + validate :array_of_strings_or_regexps + + def array_of_strings_or_regexps + unless validate_array_of_strings_or_regexps(config) + errors.add(:config, 'should be an array of strings or regexps') + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/undefined.rb b/lib/gitlab/ci/config/entry/undefined.rb new file mode 100644 index 00000000000..b33b8238230 --- /dev/null +++ b/lib/gitlab/ci/config/entry/undefined.rb @@ -0,0 +1,36 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # This class represents an undefined entry. + # + class Undefined < Node + def initialize(*) + super(nil) + end + + def value + nil + end + + def valid? + true + end + + def errors + [] + end + + def specified? + false + end + + def relevant? + false + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/unspecified.rb b/lib/gitlab/ci/config/entry/unspecified.rb new file mode 100644 index 00000000000..fbb2551e870 --- /dev/null +++ b/lib/gitlab/ci/config/entry/unspecified.rb @@ -0,0 +1,19 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # This class represents an unspecified entry. + # + # It decorates original entry adding method that indicates it is + # unspecified. + # + class Unspecified < SimpleDelegator + def specified? + false + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/validatable.rb b/lib/gitlab/ci/config/entry/validatable.rb new file mode 100644 index 00000000000..f7f1b111571 --- /dev/null +++ b/lib/gitlab/ci/config/entry/validatable.rb @@ -0,0 +1,27 @@ +module Gitlab + module Ci + class Config + module Entry + module Validatable + extend ActiveSupport::Concern + + class_methods do + def validator + @validator ||= Class.new(Entry::Validator).tap do |validator| + if defined?(@validations) + @validations.each { |rules| validator.class_eval(&rules) } + end + end + end + + private + + def validations(&block) + (@validations ||= []).append(block) + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/validator.rb b/lib/gitlab/ci/config/entry/validator.rb new file mode 100644 index 00000000000..55343005fe3 --- /dev/null +++ b/lib/gitlab/ci/config/entry/validator.rb @@ -0,0 +1,42 @@ +module Gitlab + module Ci + class Config + module Entry + class Validator < SimpleDelegator + include ActiveModel::Validations + include Entry::Validators + + def initialize(entry) + super(entry) + @entry = entry + end + + def messages + errors.full_messages.map do |error| + "#{location} #{error}".downcase + end + end + + def self.name + 'Validator' + end + + private + + def location + predecessors = ancestors.map(&:key).compact + predecessors.append(key_name).join(':') + end + + def key_name + if key.blank? + @entry.class.name.demodulize.underscore.humanize + else + key + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/validators.rb b/lib/gitlab/ci/config/entry/validators.rb new file mode 100644 index 00000000000..8632dd0e233 --- /dev/null +++ b/lib/gitlab/ci/config/entry/validators.rb @@ -0,0 +1,82 @@ +module Gitlab + module Ci + class Config + module Entry + module Validators + class AllowedKeysValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + unknown_keys = record.config.try(:keys).to_a - options[:in] + + if unknown_keys.any? + record.errors.add(:config, 'contains unknown keys: ' + + unknown_keys.join(', ')) + end + end + end + + class ArrayOfStringsValidator < ActiveModel::EachValidator + include LegacyValidationHelpers + + def validate_each(record, attribute, value) + unless validate_array_of_strings(value) + record.errors.add(attribute, 'should be an array of strings') + end + end + end + + class BooleanValidator < ActiveModel::EachValidator + include LegacyValidationHelpers + + def validate_each(record, attribute, value) + unless validate_boolean(value) + record.errors.add(attribute, 'should be a boolean value') + end + end + end + + class DurationValidator < ActiveModel::EachValidator + include LegacyValidationHelpers + + def validate_each(record, attribute, value) + unless validate_duration(value) + record.errors.add(attribute, 'should be a duration') + end + end + end + + class KeyValidator < ActiveModel::EachValidator + include LegacyValidationHelpers + + def validate_each(record, attribute, value) + unless validate_string(value) + record.errors.add(attribute, 'should be a string or symbol') + end + end + end + + class TypeValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + type = options[:with] + raise unless type.is_a?(Class) + + unless value.is_a?(type) + message = options[:message] || "should be a #{type.name}" + record.errors.add(attribute, message) + end + end + end + + class VariablesValidator < ActiveModel::EachValidator + include LegacyValidationHelpers + + def validate_each(record, attribute, value) + unless validate_variables(value) + record.errors.add(attribute, 'should be a hash of key value pairs') + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/variables.rb b/lib/gitlab/ci/config/entry/variables.rb new file mode 100644 index 00000000000..f615874fa68 --- /dev/null +++ b/lib/gitlab/ci/config/entry/variables.rb @@ -0,0 +1,22 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents environment variables. + # + class Variables < Node + include Validatable + + validations do + validates :config, variables: true + end + + def self.default + {} + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/node/artifacts.rb b/lib/gitlab/ci/config/node/artifacts.rb deleted file mode 100644 index 844bd2fe998..00000000000 --- a/lib/gitlab/ci/config/node/artifacts.rb +++ /dev/null @@ -1,35 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents a configuration of job artifacts. - # - class Artifacts < Entry - include Validatable - include Attributable - - ALLOWED_KEYS = %i[name untracked paths when expire_in] - - attributes ALLOWED_KEYS - - validations do - validates :config, type: Hash - validates :config, allowed_keys: ALLOWED_KEYS - - with_options allow_nil: true do - validates :name, type: String - validates :untracked, boolean: true - validates :paths, array_of_strings: true - validates :when, - inclusion: { in: %w[on_success on_failure always], - message: 'should be on_success, on_failure ' \ - 'or always' } - validates :expire_in, duration: true - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/attributable.rb b/lib/gitlab/ci/config/node/attributable.rb deleted file mode 100644 index 221b666f9f6..00000000000 --- a/lib/gitlab/ci/config/node/attributable.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - module Attributable - extend ActiveSupport::Concern - - class_methods do - def attributes(*attributes) - attributes.flatten.each do |attribute| - define_method(attribute) do - return unless config.is_a?(Hash) - - config[attribute] - end - end - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/boolean.rb b/lib/gitlab/ci/config/node/boolean.rb deleted file mode 100644 index 84b03ee7832..00000000000 --- a/lib/gitlab/ci/config/node/boolean.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents a boolean value. - # - class Boolean < Entry - include Validatable - - validations do - validates :config, boolean: true - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/cache.rb b/lib/gitlab/ci/config/node/cache.rb deleted file mode 100644 index b4bda2841ac..00000000000 --- a/lib/gitlab/ci/config/node/cache.rb +++ /dev/null @@ -1,29 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents a cache configuration - # - class Cache < Entry - include Configurable - - ALLOWED_KEYS = %i[key untracked paths] - - validations do - validates :config, allowed_keys: ALLOWED_KEYS - end - - node :key, Node::Key, - description: 'Cache key used to define a cache affinity.' - - node :untracked, Node::Boolean, - description: 'Cache all untracked files.' - - node :paths, Node::Paths, - description: 'Specify which paths should be cached across builds.' - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/commands.rb b/lib/gitlab/ci/config/node/commands.rb deleted file mode 100644 index d7657ae314b..00000000000 --- a/lib/gitlab/ci/config/node/commands.rb +++ /dev/null @@ -1,33 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents a job script. - # - class Commands < Entry - include Validatable - - validations do - include LegacyValidationHelpers - - validate do - unless string_or_array_of_strings?(config) - errors.add(:config, - 'should be a string or an array of strings') - end - end - - def string_or_array_of_strings?(field) - validate_string(field) || validate_array_of_strings(field) - end - end - - def value - Array(@config) - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/configurable.rb b/lib/gitlab/ci/config/node/configurable.rb deleted file mode 100644 index 6b7ab2fdaf2..00000000000 --- a/lib/gitlab/ci/config/node/configurable.rb +++ /dev/null @@ -1,78 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # This mixin is responsible for adding DSL, which purpose is to - # simplifly process of adding child nodes. - # - # This can be used only if parent node is a configuration entry that - # holds a hash as a configuration value, for example: - # - # job: - # script: ... - # artifacts: ... - # - module Configurable - extend ActiveSupport::Concern - include Validatable - - included do - validations do - validates :config, type: Hash - end - end - - def compose!(deps = nil) - return unless valid? - - self.class.nodes.each do |key, factory| - factory - .value(@config[key]) - .with(key: key, parent: self) - - @entries[key] = factory.create! - end - - yield if block_given? - - @entries.each_value do |entry| - entry.compose!(deps) - end - end - - class_methods do - def nodes - Hash[(@nodes || {}).map { |key, factory| [key, factory.dup] }] - end - - private # rubocop:disable Lint/UselessAccessModifier - - def node(key, node, metadata) - factory = Node::Factory.new(node) - .with(description: metadata[:description]) - - (@nodes ||= {}).merge!(key.to_sym => factory) - end - - def helpers(*nodes) - nodes.each do |symbol| - define_method("#{symbol}_defined?") do - @entries[symbol].specified? if @entries[symbol] - end - - define_method("#{symbol}_value") do - return unless @entries[symbol] && @entries[symbol].valid? - - @entries[symbol].value - end - - alias_method symbol.to_sym, "#{symbol}_value".to_sym - end - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/entry.rb b/lib/gitlab/ci/config/node/entry.rb deleted file mode 100644 index 8717eabf81e..00000000000 --- a/lib/gitlab/ci/config/node/entry.rb +++ /dev/null @@ -1,83 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Base abstract class for each configuration entry node. - # - class Entry - class InvalidError < StandardError; end - - attr_reader :config, :metadata - attr_accessor :key, :parent, :description - - def initialize(config, **metadata) - @config = config - @metadata = metadata - @entries = {} - - @validator = self.class.validator.new(self) - @validator.validate(:new) - end - - def [](key) - @entries[key] || Node::Undefined.new - end - - def compose!(deps = nil) - return unless valid? - - yield if block_given? - end - - def leaf? - @entries.none? - end - - def descendants - @entries.values - end - - def ancestors - @parent ? @parent.ancestors + [@parent] : [] - end - - def valid? - errors.none? - end - - def errors - @validator.messages + descendants.flat_map(&:errors) - end - - def value - if leaf? - @config - else - meaningful = @entries.select do |_key, value| - value.specified? && value.relevant? - end - - Hash[meaningful.map { |key, entry| [key, entry.value] }] - end - end - - def specified? - true - end - - def relevant? - true - end - - def self.default - end - - def self.validator - Validator - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/environment.rb b/lib/gitlab/ci/config/node/environment.rb deleted file mode 100644 index 9a95ef43628..00000000000 --- a/lib/gitlab/ci/config/node/environment.rb +++ /dev/null @@ -1,82 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents an environment. - # - class Environment < Entry - include Validatable - - ALLOWED_KEYS = %i[name url action on_stop] - - validations do - validate do - unless hash? || string? - errors.add(:config, 'should be a hash or a string') - end - end - - validates :name, presence: true - validates :name, - type: { - with: String, - message: Gitlab::Regex.environment_name_regex_message } - - validates :name, - format: { - with: Gitlab::Regex.environment_name_regex, - message: Gitlab::Regex.environment_name_regex_message } - - with_options if: :hash? do - validates :config, allowed_keys: ALLOWED_KEYS - - validates :url, - length: { maximum: 255 }, - addressable_url: true, - allow_nil: true - - validates :action, - inclusion: { in: %w[start stop], message: 'should be start or stop' }, - allow_nil: true - - validates :on_stop, type: String, allow_nil: true - end - end - - def hash? - @config.is_a?(Hash) - end - - def string? - @config.is_a?(String) - end - - def name - value[:name] - end - - def url - value[:url] - end - - def action - value[:action] || 'start' - end - - def on_stop - value[:on_stop] - end - - def value - case @config - when String then { name: @config, action: 'start' } - when Hash then @config - else {} - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/factory.rb b/lib/gitlab/ci/config/node/factory.rb deleted file mode 100644 index 5387f29ad59..00000000000 --- a/lib/gitlab/ci/config/node/factory.rb +++ /dev/null @@ -1,73 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Factory class responsible for fabricating node entry objects. - # - class Factory - class InvalidFactory < StandardError; end - - def initialize(node) - @node = node - @metadata = {} - @attributes = {} - end - - def value(value) - @value = value - self - end - - def metadata(metadata) - @metadata.merge!(metadata) - self - end - - def with(attributes) - @attributes.merge!(attributes) - self - end - - def create! - raise InvalidFactory unless defined?(@value) - - ## - # We assume that unspecified entry is undefined. - # See issue #18775. - # - if @value.nil? - Node::Unspecified.new( - fabricate_unspecified - ) - else - fabricate(@node, @value) - end - end - - private - - def fabricate_unspecified - ## - # If node has a default value we fabricate concrete node - # with default value. - # - if @node.default.nil? - fabricate(Node::Undefined) - else - fabricate(@node, @node.default) - end - end - - def fabricate(node, value = nil) - node.new(value, @metadata).tap do |entry| - entry.key = @attributes[:key] - entry.parent = @attributes[:parent] - entry.description = @attributes[:description] - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/global.rb b/lib/gitlab/ci/config/node/global.rb deleted file mode 100644 index 2a2943c9288..00000000000 --- a/lib/gitlab/ci/config/node/global.rb +++ /dev/null @@ -1,72 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # This class represents a global entry - root node for entire - # GitLab CI Configuration file. - # - class Global < Entry - include Configurable - - node :before_script, Node::Script, - description: 'Script that will be executed before each job.' - - node :image, Node::Image, - description: 'Docker image that will be used to execute jobs.' - - node :services, Node::Services, - description: 'Docker images that will be linked to the container.' - - node :after_script, Node::Script, - description: 'Script that will be executed after each job.' - - node :variables, Node::Variables, - description: 'Environment variables that will be used.' - - node :stages, Node::Stages, - description: 'Configuration of stages for this pipeline.' - - node :types, Node::Stages, - description: 'Deprecated: stages for this pipeline.' - - node :cache, Node::Cache, - description: 'Configure caching between build jobs.' - - helpers :before_script, :image, :services, :after_script, - :variables, :stages, :types, :cache, :jobs - - def compose!(_deps = nil) - super(self) do - compose_jobs! - compose_deprecated_entries! - end - end - - private - - def compose_jobs! - factory = Node::Factory.new(Node::Jobs) - .value(@config.except(*self.class.nodes.keys)) - .with(key: :jobs, parent: self, - description: 'Jobs definition for this pipeline') - - @entries[:jobs] = factory.create! - end - - def compose_deprecated_entries! - ## - # Deprecated `:types` key workaround - if types are defined and - # stages are not defined we use types definition as stages. - # - if types_defined? && !stages_defined? - @entries[:stages] = @entries[:types] - end - - @entries.delete(:types) - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/hidden.rb b/lib/gitlab/ci/config/node/hidden.rb deleted file mode 100644 index fe4ee8a7fc6..00000000000 --- a/lib/gitlab/ci/config/node/hidden.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents a hidden CI/CD job. - # - class Hidden < Entry - include Validatable - - validations do - validates :config, presence: true - end - - def relevant? - false - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/image.rb b/lib/gitlab/ci/config/node/image.rb deleted file mode 100644 index 5d3c7c5eab0..00000000000 --- a/lib/gitlab/ci/config/node/image.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents a Docker image. - # - class Image < Entry - include Validatable - - validations do - validates :config, type: String - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/job.rb b/lib/gitlab/ci/config/node/job.rb deleted file mode 100644 index 603334d6793..00000000000 --- a/lib/gitlab/ci/config/node/job.rb +++ /dev/null @@ -1,140 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents a concrete CI/CD job. - # - class Job < Entry - include Configurable - include Attributable - - ALLOWED_KEYS = %i[tags script only except type image services allow_failure - type stage when artifacts cache dependencies before_script - after_script variables environment] - - attributes :tags, :allow_failure, :when, :dependencies - - validations do - validates :config, allowed_keys: ALLOWED_KEYS - - validates :config, presence: true - validates :name, presence: true - validates :name, type: Symbol - - with_options allow_nil: true do - validates :tags, array_of_strings: true - validates :allow_failure, boolean: true - validates :when, - inclusion: { in: %w[on_success on_failure always manual], - message: 'should be on_success, on_failure, ' \ - 'always or manual' } - - validates :dependencies, array_of_strings: true - end - end - - node :before_script, Node::Script, - description: 'Global before script overridden in this job.' - - node :script, Node::Commands, - description: 'Commands that will be executed in this job.' - - node :stage, Node::Stage, - description: 'Pipeline stage this job will be executed into.' - - node :type, Node::Stage, - description: 'Deprecated: stage this job will be executed into.' - - node :after_script, Node::Script, - description: 'Commands that will be executed when finishing job.' - - node :cache, Node::Cache, - description: 'Cache definition for this job.' - - node :image, Node::Image, - description: 'Image that will be used to execute this job.' - - node :services, Node::Services, - description: 'Services that will be used to execute this job.' - - node :only, Node::Trigger, - description: 'Refs policy this job will be executed for.' - - node :except, Node::Trigger, - description: 'Refs policy this job will be executed for.' - - node :variables, Node::Variables, - description: 'Environment variables available for this job.' - - node :artifacts, Node::Artifacts, - description: 'Artifacts configuration for this job.' - - node :environment, Node::Environment, - description: 'Environment configuration for this job.' - - helpers :before_script, :script, :stage, :type, :after_script, - :cache, :image, :services, :only, :except, :variables, - :artifacts, :commands, :environment - - def compose!(deps = nil) - super do - if type_defined? && !stage_defined? - @entries[:stage] = @entries[:type] - end - - @entries.delete(:type) - end - - inherit!(deps) - end - - def name - @metadata[:name] - end - - def value - @config.merge(to_hash.compact) - end - - def commands - (before_script_value.to_a + script_value.to_a).join("\n") - end - - private - - def inherit!(deps) - return unless deps - - self.class.nodes.each_key do |key| - global_entry = deps[key] - job_entry = @entries[key] - - if global_entry.specified? && !job_entry.specified? - @entries[key] = global_entry - end - end - end - - def to_hash - { name: name, - before_script: before_script, - script: script, - commands: commands, - image: image, - services: services, - stage: stage, - cache: cache, - only: only, - except: except, - variables: variables_defined? ? variables : nil, - environment: environment_defined? ? environment : nil, - environment_name: environment_defined? ? environment[:name] : nil, - artifacts: artifacts, - after_script: after_script } - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/jobs.rb b/lib/gitlab/ci/config/node/jobs.rb deleted file mode 100644 index d10e80d1a7d..00000000000 --- a/lib/gitlab/ci/config/node/jobs.rb +++ /dev/null @@ -1,52 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents a set of jobs. - # - class Jobs < Entry - include Validatable - - validations do - validates :config, type: Hash - - validate do - unless has_visible_job? - errors.add(:config, 'should contain at least one visible job') - end - end - - def has_visible_job? - config.any? { |name, _| !hidden?(name) } - end - end - - def hidden?(name) - name.to_s.start_with?('.') - end - - def compose!(deps = nil) - super do - @config.each do |name, config| - node = hidden?(name) ? Node::Hidden : Node::Job - - factory = Node::Factory.new(node) - .value(config || {}) - .metadata(name: name) - .with(key: name, parent: self, - description: "#{name} job definition.") - - @entries[name] = factory.create! - end - - @entries.each_value do |entry| - entry.compose!(deps) - end - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/key.rb b/lib/gitlab/ci/config/node/key.rb deleted file mode 100644 index f8b461ca098..00000000000 --- a/lib/gitlab/ci/config/node/key.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents a key. - # - class Key < Entry - include Validatable - - validations do - validates :config, key: true - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/legacy_validation_helpers.rb b/lib/gitlab/ci/config/node/legacy_validation_helpers.rb deleted file mode 100644 index 0c291efe6a5..00000000000 --- a/lib/gitlab/ci/config/node/legacy_validation_helpers.rb +++ /dev/null @@ -1,51 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - module LegacyValidationHelpers - private - - def validate_duration(value) - value.is_a?(String) && ChronicDuration.parse(value) - rescue ChronicDuration::DurationParseError - false - end - - def validate_array_of_strings(values) - values.is_a?(Array) && values.all? { |value| validate_string(value) } - end - - def validate_array_of_strings_or_regexps(values) - values.is_a?(Array) && values.all? { |value| validate_string_or_regexp(value) } - end - - def validate_variables(variables) - variables.is_a?(Hash) && - variables.all? { |key, value| validate_string(key) && validate_string(value) } - end - - def validate_string(value) - value.is_a?(String) || value.is_a?(Symbol) - end - - def validate_string_or_regexp(value) - return true if value.is_a?(Symbol) - return false unless value.is_a?(String) - - if value.first == '/' && value.last == '/' - Regexp.new(value[1...-1]) - else - true - end - rescue RegexpError - false - end - - def validate_boolean(value) - value.in?([true, false]) - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/paths.rb b/lib/gitlab/ci/config/node/paths.rb deleted file mode 100644 index 3c6d3a52966..00000000000 --- a/lib/gitlab/ci/config/node/paths.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents an array of paths. - # - class Paths < Entry - include Validatable - - validations do - validates :config, array_of_strings: true - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/script.rb b/lib/gitlab/ci/config/node/script.rb deleted file mode 100644 index 39328f0fade..00000000000 --- a/lib/gitlab/ci/config/node/script.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents a script. - # - class Script < Entry - include Validatable - - validations do - validates :config, array_of_strings: true - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/services.rb b/lib/gitlab/ci/config/node/services.rb deleted file mode 100644 index 481e2b66adc..00000000000 --- a/lib/gitlab/ci/config/node/services.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents a configuration of Docker services. - # - class Services < Entry - include Validatable - - validations do - validates :config, array_of_strings: true - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/stage.rb b/lib/gitlab/ci/config/node/stage.rb deleted file mode 100644 index cbc97641f5a..00000000000 --- a/lib/gitlab/ci/config/node/stage.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents a stage for a job. - # - class Stage < Entry - include Validatable - - validations do - validates :config, type: String - end - - def self.default - 'test' - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/stages.rb b/lib/gitlab/ci/config/node/stages.rb deleted file mode 100644 index b1fe45357ff..00000000000 --- a/lib/gitlab/ci/config/node/stages.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents a configuration for pipeline stages. - # - class Stages < Entry - include Validatable - - validations do - validates :config, array_of_strings: true - end - - def self.default - %w[build test deploy] - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/trigger.rb b/lib/gitlab/ci/config/node/trigger.rb deleted file mode 100644 index d8b31975088..00000000000 --- a/lib/gitlab/ci/config/node/trigger.rb +++ /dev/null @@ -1,26 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents a trigger policy for the job. - # - class Trigger < Entry - include Validatable - - validations do - include LegacyValidationHelpers - - validate :array_of_strings_or_regexps - - def array_of_strings_or_regexps - unless validate_array_of_strings_or_regexps(config) - errors.add(:config, 'should be an array of strings or regexps') - end - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/undefined.rb b/lib/gitlab/ci/config/node/undefined.rb deleted file mode 100644 index 33e78023539..00000000000 --- a/lib/gitlab/ci/config/node/undefined.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # This class represents an undefined node. - # - # Implements the Null Object pattern. - # - class Undefined < Entry - def initialize(*) - super(nil) - end - - def value - nil - end - - def valid? - true - end - - def errors - [] - end - - def specified? - false - end - - def relevant? - false - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/unspecified.rb b/lib/gitlab/ci/config/node/unspecified.rb deleted file mode 100644 index a7d1f6131b8..00000000000 --- a/lib/gitlab/ci/config/node/unspecified.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # This class represents an unspecified entry node. - # - # It decorates original entry adding method that indicates it is - # unspecified. - # - class Unspecified < SimpleDelegator - def specified? - false - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/validatable.rb b/lib/gitlab/ci/config/node/validatable.rb deleted file mode 100644 index 085e6e988d1..00000000000 --- a/lib/gitlab/ci/config/node/validatable.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - module Validatable - extend ActiveSupport::Concern - - class_methods do - def validator - @validator ||= Class.new(Node::Validator).tap do |validator| - if defined?(@validations) - @validations.each { |rules| validator.class_eval(&rules) } - end - end - end - - private - - def validations(&block) - (@validations ||= []).append(block) - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/validator.rb b/lib/gitlab/ci/config/node/validator.rb deleted file mode 100644 index 43c7e102b50..00000000000 --- a/lib/gitlab/ci/config/node/validator.rb +++ /dev/null @@ -1,42 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - class Validator < SimpleDelegator - include ActiveModel::Validations - include Node::Validators - - def initialize(node) - super(node) - @node = node - end - - def messages - errors.full_messages.map do |error| - "#{location} #{error}".downcase - end - end - - def self.name - 'Validator' - end - - private - - def location - predecessors = ancestors.map(&:key).compact - predecessors.append(key_name).join(':') - end - - def key_name - if key.blank? - @node.class.name.demodulize.underscore.humanize - else - key - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/validators.rb b/lib/gitlab/ci/config/node/validators.rb deleted file mode 100644 index e20908ad3cb..00000000000 --- a/lib/gitlab/ci/config/node/validators.rb +++ /dev/null @@ -1,82 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - module Validators - class AllowedKeysValidator < ActiveModel::EachValidator - def validate_each(record, attribute, value) - unknown_keys = record.config.try(:keys).to_a - options[:in] - - if unknown_keys.any? - record.errors.add(:config, 'contains unknown keys: ' + - unknown_keys.join(', ')) - end - end - end - - class ArrayOfStringsValidator < ActiveModel::EachValidator - include LegacyValidationHelpers - - def validate_each(record, attribute, value) - unless validate_array_of_strings(value) - record.errors.add(attribute, 'should be an array of strings') - end - end - end - - class BooleanValidator < ActiveModel::EachValidator - include LegacyValidationHelpers - - def validate_each(record, attribute, value) - unless validate_boolean(value) - record.errors.add(attribute, 'should be a boolean value') - end - end - end - - class DurationValidator < ActiveModel::EachValidator - include LegacyValidationHelpers - - def validate_each(record, attribute, value) - unless validate_duration(value) - record.errors.add(attribute, 'should be a duration') - end - end - end - - class KeyValidator < ActiveModel::EachValidator - include LegacyValidationHelpers - - def validate_each(record, attribute, value) - unless validate_string(value) - record.errors.add(attribute, 'should be a string or symbol') - end - end - end - - class TypeValidator < ActiveModel::EachValidator - def validate_each(record, attribute, value) - type = options[:with] - raise unless type.is_a?(Class) - - unless value.is_a?(type) - message = options[:message] || "should be a #{type.name}" - record.errors.add(attribute, message) - end - end - end - - class VariablesValidator < ActiveModel::EachValidator - include LegacyValidationHelpers - - def validate_each(record, attribute, value) - unless validate_variables(value) - record.errors.add(attribute, 'should be a hash of key value pairs') - end - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/config/node/variables.rb b/lib/gitlab/ci/config/node/variables.rb deleted file mode 100644 index 5f813f81f55..00000000000 --- a/lib/gitlab/ci/config/node/variables.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Gitlab - module Ci - class Config - module Node - ## - # Entry that represents environment variables. - # - class Variables < Entry - include Validatable - - validations do - validates :config, variables: true - end - - def self.default - {} - end - end - end - end - end -end -- cgit v1.2.1