require 'pdk' module PDK module Context autoload :None, 'pdk/context/none' autoload :Module, 'pdk/context/module' autoload :ControlRepo, 'pdk/context/control_repo' # Automatically determines the PDK Context given a path. Create will continue up the directory tree until it # finds a valid context # @return [PDK::Context::AbstractContext] Returns a PDK::Context::None if the context could not be determined def self.create(context_path) return PDK::Context::None.new(context_path) unless PDK::Util::Filesystem.directory?(context_path) previous = nil current = PDK::Util::Filesystem.expand_path(context_path) until !PDK::Util::Filesystem.directory?(current) || current == previous # Control Repo detection return PDK::Context::ControlRepo.new(current, context_path) if PDK.feature_flag?('controlrepo') && PDK::ControlRepo.control_repo_root?(current) # Puppet Module detection metadata_file = File.join(current, 'metadata.json') return PDK::Context::Module.new(current, context_path) if PDK::Util::Filesystem.file?(metadata_file) || PDK::Util.in_module_root?(context_path) previous = current current = PDK::Util::Filesystem.expand_path('..', current) end PDK::Context::None.new(context_path) end # Abstract class which all PDK Contexts will subclass from. # @abstract class AbstractContext # The root of this context, for example the module root when inside a module. This can be different from context_path # For example a Module context_path could be /path/to/module/manifests/ but the root_path will be /path/to/module as # that is the root of the Module context. Defaults to the context_path if not set. # @return [String] def root_path @root_path || @context_path end # The path used to create this context, for example the current working directory. This can be different from root_path # For example a Module context_path could be /path/to/module/manifests/ but the root_path will be /path/to/module as # that is the root of the Module context # @return [String] attr_reader :context_path # @param context_path [String] The path where this context was created from e.g. Dir.pwd def initialize(context_path) @context_path = context_path @root_path = nil end # Whether the current context is compatible with the PDK e.g. in a Module context, whether it has the correct metadata.json content # @return [Boolean] Default is not compatible def pdk_compatible? false end # The friendly name to display for this context # @api private # @abstract def display_name; end # The context which this context is in. For example a Module Context (/controlrepo/site/profile) can be inside of a Control Repo context (/controlrepo) # The default is to search in the parent directory of this context # @return [PDK::Context::AbstractContext, Nil] Returns the parent context or nil if there is no parent. def parent_context # Default detection is just look for the context in the parent directory of this context @parent_context || PDK::Context.create(File.dirname(root_path)) end # Writes the current context information, and parent contexts, to the PDK Debug Logger. # This is mainly used by the PDK CLI when in debug mode to assist users to figure out why the PDK is misbehaving. # @api private def to_debug_log current = self depth = 1 loop do PDK.logger.debug("Detected #{current.display_name} at #{current.root_path.nil? ? current.context_path : current.root_path}") current = current.parent_context break if current.nil? depth += 1 # Circuit breaker in case there are circular references break if depth > 20 end nil end # :nocov: There's nothing to test here def to_s "#<#{self.class}:#{object_id}>#{context_path}" end # :nocov: end end end