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