1# frozen_string_literal: true 2 3module Gitlab 4 module Ci 5 class Config 6 module Entry 7 ## 8 # This class represents a global entry - root Entry for entire 9 # GitLab CI Configuration file. 10 # 11 class Root < ::Gitlab::Config::Entry::Node 12 include ::Gitlab::Config::Entry::Configurable 13 14 ALLOWED_KEYS = %i[default include before_script image services 15 after_script variables stages types cache workflow].freeze 16 17 validations do 18 validates :config, allowed_keys: ALLOWED_KEYS 19 end 20 21 # reserved: 22 # defines whether the node name is reserved 23 # the reserved name cannot be used a job name 24 # reserved should not be used as it will make 25 # breaking change to `.gitlab-ci.yml` 26 27 entry :default, Entry::Default, 28 description: 'Default configuration for all jobs.', 29 default: {} 30 31 entry :include, Entry::Includes, 32 description: 'List of external YAML files to include.', 33 reserved: true 34 35 entry :before_script, Entry::Script, 36 description: 'Script that will be executed before each job.', 37 reserved: true 38 39 entry :image, Entry::Image, 40 description: 'Docker image that will be used to execute jobs.', 41 reserved: true 42 43 entry :services, Entry::Services, 44 description: 'Docker images that will be linked to the container.', 45 reserved: true 46 47 entry :after_script, Entry::Script, 48 description: 'Script that will be executed after each job.', 49 reserved: true 50 51 entry :variables, Entry::Variables, 52 description: 'Environment variables that will be used.', 53 metadata: { use_value_data: true }, 54 reserved: true 55 56 entry :stages, Entry::Stages, 57 description: 'Configuration of stages for this pipeline.', 58 reserved: true 59 60 entry :types, Entry::Stages, 61 description: 'Deprecated: stages for this pipeline.', 62 reserved: true 63 64 entry :cache, Entry::Caches, 65 description: 'Configure caching between build jobs.', 66 reserved: true 67 68 entry :workflow, Entry::Workflow, 69 description: 'List of evaluable rules to determine Pipeline status', 70 default: {} 71 72 dynamic_helpers :jobs 73 74 delegate :before_script_value, 75 :image_value, 76 :services_value, 77 :after_script_value, 78 :cache_value, to: :default_entry 79 80 attr_reader :jobs_config 81 82 class << self 83 include ::Gitlab::Utils::StrongMemoize 84 85 def reserved_nodes_names 86 strong_memoize(:reserved_nodes_names) do 87 self.nodes.select do |_, node| 88 node.reserved? 89 end.keys 90 end 91 end 92 end 93 94 def initialize(config, **metadata) 95 super do 96 filter_jobs! 97 end 98 end 99 100 def compose!(_deps = nil) 101 super(self) do 102 compose_deprecated_entries! 103 compose_jobs! 104 end 105 end 106 107 private 108 109 # rubocop: disable CodeReuse/ActiveRecord 110 def compose_jobs! 111 factory = ::Gitlab::Config::Entry::Factory.new(Entry::Jobs) 112 .value(jobs_config) 113 .with(key: :jobs, parent: self, 114 description: 'Jobs definition for this pipeline') 115 116 @entries[:jobs] = factory.create! 117 end 118 # rubocop: enable CodeReuse/ActiveRecord 119 120 def compose_deprecated_entries! 121 ## 122 # Deprecated `:types` key workaround - if types are defined and 123 # stages are not defined we use types definition as stages. 124 # 125 if types_defined? && !stages_defined? 126 @entries[:stages] = @entries[:types] 127 end 128 129 @entries.delete(:types) 130 end 131 132 def filter_jobs! 133 return unless @config.is_a?(Hash) 134 135 @jobs_config = @config 136 .except(*self.class.reserved_nodes_names) 137 .select do |name, config| 138 Entry::Jobs.find_type(name, config).present? || ALLOWED_KEYS.exclude?(name) 139 end 140 141 @config = @config.except(*@jobs_config.keys) 142 end 143 end 144 end 145 end 146 end 147end 148