2018-12-13 13:39:08 +05:30
# frozen_string_literal: true
module Gitlab
module Ci
class Config
module External
class Mapper
2019-02-15 15:39:39 +05:30
include Gitlab :: Utils :: StrongMemoize
2023-03-04 22:38:38 +05:30
# Will be removed with FF ci_refactoring_external_mapper
2019-02-15 15:39:39 +05:30
FILE_CLASSES = [
External :: File :: Local ,
2020-04-08 14:13:33 +05:30
External :: File :: Project ,
2023-01-13 00:05:48 +05:30
External :: File :: Remote ,
External :: File :: Template ,
2020-04-08 14:13:33 +05:30
External :: File :: Artifact
2019-02-15 15:39:39 +05:30
] . freeze
2023-03-04 22:38:38 +05:30
# Will be removed with FF ci_refactoring_external_mapper
2023-01-13 00:05:48 +05:30
FILE_SUBKEYS = FILE_CLASSES . map { | f | f . name . demodulize . downcase } . freeze
2019-07-07 11:18:12 +05:30
Error = Class . new ( StandardError )
AmbigiousSpecificationError = Class . new ( Error )
TooManyIncludesError = Class . new ( Error )
2019-12-21 20:55:43 +05:30
def initialize ( values , context )
2023-03-04 22:38:38 +05:30
@locations = Array . wrap ( values . fetch ( :include , [ ] ) ) . compact
2019-12-21 20:55:43 +05:30
@context = context
2018-12-13 13:39:08 +05:30
end
def process
2023-03-04 22:38:38 +05:30
return [ ] if @locations . empty?
context . logger . instrument ( :config_mapper_process ) do
if :: Feature . enabled? ( :ci_refactoring_external_mapper , context . project )
process_without_instrumentation
else
legacy_process_without_instrumentation
end
2022-01-26 12:08:38 +05:30
end
end
private
2023-03-04 22:38:38 +05:30
attr_reader :context
2022-01-26 12:08:38 +05:30
delegate :expandset , :logger , to : :context
def process_without_instrumentation
2023-03-04 22:38:38 +05:30
locations = Normalizer . new ( context ) . process ( @locations )
locations = Filter . new ( context ) . process ( locations )
locations = LocationExpander . new ( context ) . process ( locations )
locations = VariablesExpander . new ( context ) . process ( locations )
files = Matcher . new ( context ) . process ( locations )
Verifier . new ( context ) . process ( files )
files
end
# This and the following methods will be removed with FF ci_refactoring_external_mapper
def legacy_process_without_instrumentation
@locations
2019-02-15 15:39:39 +05:30
. map ( & method ( :normalize_location ) )
2021-10-27 15:23:28 +05:30
. filter_map ( & method ( :verify_rules ) )
2021-01-29 00:20:46 +05:30
. flat_map ( & method ( :expand_project_files ) )
2021-04-29 21:17:54 +05:30
. flat_map ( & method ( :expand_wildcard_paths ) )
2021-03-08 18:12:59 +05:30
. map ( & method ( :expand_variables ) )
2019-02-15 15:39:39 +05:30
. map ( & method ( :select_first_matching ) )
2022-06-21 17:19:12 +05:30
. each ( & method ( :verify! ) )
2018-12-13 13:39:08 +05:30
end
2019-02-15 15:39:39 +05:30
# convert location if String to canonical form
2023-03-04 22:38:38 +05:30
def normalize_location ( location )
2019-02-15 15:39:39 +05:30
if location . is_a? ( String )
2021-03-08 18:12:59 +05:30
expanded_location = expand_variables ( location )
normalize_location_string ( expanded_location )
2019-02-15 15:39:39 +05:30
else
location . deep_symbolize_keys
end
end
2018-12-13 13:39:08 +05:30
2021-10-27 15:23:28 +05:30
def verify_rules ( location )
2022-01-26 12:08:38 +05:30
logger . instrument ( :config_mapper_rules ) do
verify_rules_without_instrumentation ( location )
end
end
def verify_rules_without_instrumentation ( location )
2021-10-27 15:23:28 +05:30
return unless Rules . new ( location [ :rules ] ) . evaluate ( context ) . pass?
location
end
2021-01-29 00:20:46 +05:30
def expand_project_files ( location )
return location unless location [ :project ]
Array . wrap ( location [ :file ] ) . map do | file |
location . merge ( file : file )
end
end
2021-04-29 21:17:54 +05:30
def expand_wildcard_paths ( location )
2022-01-26 12:08:38 +05:30
logger . instrument ( :config_mapper_wildcards ) do
expand_wildcard_paths_without_instrumentation ( location )
end
end
def expand_wildcard_paths_without_instrumentation ( location )
2021-04-29 21:17:54 +05:30
# We only support local files for wildcard paths
return location unless location [ :local ] && location [ :local ] . include? ( '*' )
context . project . repository . search_files_by_wildcard_path ( location [ :local ] , context . sha ) . map do | path |
{ local : path }
end
end
2019-02-15 15:39:39 +05:30
def normalize_location_string ( location )
2018-12-13 13:39:08 +05:30
if :: Gitlab :: UrlSanitizer . valid? ( location )
2019-02-15 15:39:39 +05:30
{ remote : location }
2018-12-13 13:39:08 +05:30
else
2019-02-15 15:39:39 +05:30
{ local : location }
end
end
def select_first_matching ( location )
2022-01-26 12:08:38 +05:30
logger . instrument ( :config_mapper_select ) do
select_first_matching_without_instrumentation ( location )
end
end
def select_first_matching_without_instrumentation ( location )
2019-02-15 15:39:39 +05:30
matching = FILE_CLASSES . map do | file_class |
file_class . new ( location , context )
end . select ( & :matching? )
2023-01-13 00:05:48 +05:30
if matching . one?
matching . first
elsif matching . empty?
raise AmbigiousSpecificationError , " ` #{ masked_location ( location . to_json ) } ` does not have a valid subkey for include. Valid subkeys are: ` #{ FILE_SUBKEYS . join ( '`, `' ) } ` "
else
raise AmbigiousSpecificationError , " Each include must use only one of: ` #{ FILE_SUBKEYS . join ( '`, `' ) } ` "
end
2019-02-15 15:39:39 +05:30
end
2021-03-08 18:12:59 +05:30
2022-06-21 17:19:12 +05:30
def verify! ( location_object )
verify_max_includes!
location_object . validate!
expandset . add ( location_object )
end
def verify_max_includes!
2022-08-13 15:12:31 +05:30
if expandset . count > = context . max_includes
raise TooManyIncludesError , " Maximum of #{ context . max_includes } nested includes are allowed! "
2022-06-21 17:19:12 +05:30
end
end
2021-03-08 18:12:59 +05:30
def expand_variables ( data )
2022-01-26 12:08:38 +05:30
logger . instrument ( :config_mapper_variables ) do
expand_variables_without_instrumentation ( data )
end
end
def expand_variables_without_instrumentation ( data )
2021-03-08 18:12:59 +05:30
if data . is_a? ( String )
expand ( data )
else
transform ( data )
end
end
def transform ( data )
data . transform_values do | values |
case values
when Array
values . map { | value | expand ( value . to_s ) }
when String
expand ( values )
else
values
end
end
end
def expand ( data )
2022-01-26 12:08:38 +05:30
ExpandVariables . expand ( data , - > { context . variables_hash } )
2021-03-08 18:12:59 +05:30
end
2022-04-01 21:47:47 +05:30
def masked_location ( location )
context . mask_variables_from ( location )
end
2018-12-13 13:39:08 +05:30
end
end
end
end
end