lib/inspec/dependencies/requirement.rb in inspec-0.31.0 vs lib/inspec/dependencies/requirement.rb in inspec-0.32.0

- old
+ new

@@ -1,103 +1,143 @@ # encoding: utf-8 require 'inspec/fetcher' +require 'digest' module Inspec # # Inspec::Requirement represents a given profile dependency, where # appropriate we delegate to Inspec::Profile directly. # - class Requirement + class Requirement # rubocop:disable Metrics/ClassLength attr_reader :name, :dep, :cwd, :opts + attr_writer :dependencies + def self.from_metadata(dep, vendor_index, opts) + fail 'Cannot load empty dependency.' if dep.nil? || dep.empty? + name = dep[:name] || fail('You must provide a name for all dependencies') + version = dep[:version] + new(name, version, vendor_index, opts[:cwd], opts.merge(dep)) + end + + def self.from_lock_entry(entry, cwd, vendor_index) + req = new(entry['name'], + entry['version_constraints'], + vendor_index, + cwd, { url: entry['resolved_source'] }) + + locked_deps = [] + Array(entry['dependencies']).each do |dep_entry| + locked_deps << Inspec::Requirement.from_lock_entry(dep_entry, cwd, vendor_index) + end + + req.lock_deps(locked_deps) + req + end + def initialize(name, version_constraints, vendor_index, cwd, opts) @name = name - @dep = Gem::Dependency.new(name, - Gem::Requirement.new(Array(version_constraints)), - :runtime) + @version_requirement = Gem::Requirement.new(Array(version_constraints)) + @dep = Gem::Dependency.new(name, @version_requirement, :runtime) @vendor_index = vendor_index @opts = opts @cwd = cwd end - def matches_spec?(spec) - params = spec.profile.metadata.params - @dep.match?(params[:name], params[:version]) + def required_version + @version_requirement end + def source_version + profile.metadata.params[:version] + end + + def source_satisfies_spec? + name = profile.metadata.params[:name] + version = profile.metadata.params[:version] + @dep.match?(name, version) + end + + def to_hash + h = { + 'name' => name, + 'resolved_source' => source_url, + 'version_constraints' => @version_requirement.to_s, + } + + if !dependencies.empty? + h['dependencies'] = dependencies.map(&:to_hash) + end + + if is_vendored? + h['content_hash'] = content_hash + end + h + end + + def lock_deps(dep_array) + @dependencies = dep_array + end + + def is_vendored? + @vendor_index.exists?(@name, source_url) + end + + def content_hash + @content_hash ||= begin + archive_path = @vendor_index.archive_entry_for(@name, source_url) + fail "No vendored archive path for #{self}, cannot take content hash" if archive_path.nil? + Digest::SHA256.hexdigest File.read(archive_path) + end + end + def source_url - case source_type - when :local_path - "file://#{File.expand_path(opts[:path])}" - when :url - @opts[:url] + if opts[:path] + "file://#{File.expand_path(opts[:path], @cwd)}" + elsif opts[:url] + opts[:url] end end def local_path - @local_path ||= case source_type - when :local_path - File.expand_path(opts[:path], @cwd) + @local_path ||= if fetcher.class == Fetchers::Local + File.expand_path(fetcher.target, @cwd) else @vendor_index.prefered_entry_for(@name, source_url) end end - def source_type - @source_type ||= if @opts[:path] - :local_path - elsif opts[:url] - :url - else - fail "Cannot determine source type from #{opts}" - end + def fetcher + @fetcher ||= Inspec::Fetcher.resolve(source_url) + fail "No fetcher for #{name} (options: #{opts})" if @fetcher.nil? + @fetcher end - def fetcher_class - @fetcher_class ||= case source_type - when :local_path - Fetchers::Local - when :url - Fetchers::Url - else - fail "No known fetcher for dependency #{name} with source_type #{source_type}" - end - - fail "No fetcher for #{name} (options: #{opts})" if @fetcher_class.nil? - @fetcher_class - end - def pull - case source_type - when :local_path + # TODO(ssd): Dispatch on the class here is gross. Seems like + # Fetcher is missing an API we want. + if fetcher.class == Fetchers::Local || @vendor_index.exists?(@name, source_url) local_path else - if @vendor_index.exists?(@name, source_url) - local_path - else - archive = fetcher_class.download_archive(source_url) - @vendor_index.add(@name, source_url, archive.path) - end + @vendor_index.add(@name, source_url, fetcher.archive_path) end end + def dependencies + @dependencies ||= profile.metadata.dependencies.map do |r| + Inspec::Requirement.from_metadata(r, @vendor_index, cwd: @cwd) + end + end + def to_s - dep.to_s + "#{dep} (#{source_url})" end def path @path ||= pull end def profile return nil if path.nil? @profile ||= Inspec::Profile.for_target(path, {}) - end - - def self.from_metadata(dep, vendor_index, opts) - fail 'Cannot load empty dependency.' if dep.nil? || dep.empty? - name = dep[:name] || fail('You must provide a name for all dependencies') - version = dep[:version] - new(name, version, vendor_index, opts[:cwd], opts.merge(dep)) end end end