class Licensed::Dependency

Constants

Attributes

additional_terms[R]
errors[R]
name[R]
path[R]
version[R]

Public Class Methods

new(name:, version:, path:, search_root: nil, metadata: {}, errors: []) click to toggle source

Create a new project dependency

name - unique dependency name version - dependency version path - absolute file path to the dependency, to find license contents search_root - (optional) the root location to search for dependency license contents metadata - (optional) additional dependency data to cache errors - (optional) errors encountered when evaluating dependency

Returns a new dependency object. Dependency metadata and license contents are available if no errors are set on the dependency.

Calls superclass method
# File lib/licensed/dependency.rb, line 25
def initialize(name:, version:, path:, search_root: nil, metadata: {}, errors: [])
  @name = name
  @version = version
  @metadata = metadata
  @errors = errors
  path = path.to_s
  @path = path
  @additional_terms = []

  # enforcing absolute paths makes life much easier when determining
  # an absolute file path in #notices
  if File.exist?(path) && !Pathname.new(path).absolute?
    # this is an internal error related to source implementation and
    # should be raised, not stored to be handled by reporters
    raise ArgumentError, "dependency path #{path} must be absolute"
  end

  super(path, search_root: search_root, detect_readme: true, detect_packages: true)
end

Public Instance Methods

errors?() click to toggle source

Returns true if the dependency has any errors, false otherwise

# File lib/licensed/dependency.rb, line 55
def errors?
  errors.any?
end
exist?() click to toggle source

Returns whether the dependency exists locally

# File lib/licensed/dependency.rb, line 46
def exist?
  # some types of dependencies won't necessarily have a path that exists,
  # but they can still find license contents between the given path and
  # the search root
  # @root is defined
  File.exist?(path) || File.exist?(@root)
end
license_contents() click to toggle source

Returns the license text content from all matched sources except the package file, which doesn’t contain license text.

# File lib/licensed/dependency.rb, line 76
def license_contents
  files = matched_files.reject { |f| f == package_file }
                       .group_by(&:content)
                       .map { |content, sources| { "sources" => license_content_sources(sources), "text" => content } }

  files << generated_license_contents if files.empty?
  files.compact
end
license_key() click to toggle source

Returns a string representing the dependencys license

# File lib/licensed/dependency.rb, line 69
def license_key
  return "none" unless license
  license.key
end
metadata() click to toggle source

Returns a hash of basic metadata about the dependency - name, version, type, etc

# File lib/licensed/dependency.rb, line 103
def metadata
  {
    # can be overriden by values in @metadata
    "name" => name,
    "version" => version
  }.merge(
    @metadata
  )
end
notice_contents() click to toggle source

Returns legal notices found at the dependency path

# File lib/licensed/dependency.rb, line 93
def notice_contents
  Dir.glob(dir_path.join("*"))
     .grep(LEGAL_FILES_PATTERN)
     .select { |path| File.file?(path) }
     .sort # sorted by the path
     .map { |path| { "sources" => normalize_source_path(path), "text" => read_file_with_encoding_check(path) } }
     .select { |notice| notice["text"].length > 0 } # files with content only
end
project_files() click to toggle source

Override the behavior of Licensee::Projects::FSProject#project_files to include additional license terms

Calls superclass method
# File lib/licensed/dependency.rb, line 88
def project_files
  super + additional_license_terms_files
end
record() click to toggle source

Returns a record for this dependency including metadata and legal contents

# File lib/licensed/dependency.rb, line 60
def record
  @record ||= DependencyRecord.new(
    metadata: license_metadata,
    licenses: license_contents,
    notices: notice_contents
  )
end

Private Instance Methods

additional_license_terms_files() click to toggle source

Returns an array of Licensee::ProjectFiles::LicenseFile created from this dependency’s additional license terms

# File lib/licensed/dependency.rb, line 178
def additional_license_terms_files
  @additional_license_terms_files ||= begin
    files = additional_terms.map do |path|
      next unless File.file?(path)

      metadata = { dir: File.dirname(path), name: File.basename(path) }
      Licensee::ProjectFiles::LicenseFile.new(
        load_file(metadata),
        { source: "License terms loaded from #{metadata[:name]}" }
      )
    end
    files.compact
  end
end
generated_license_contents() click to toggle source

Returns a generated license content source and text for the dependency’s license if it exists and is not “other”

# File lib/licensed/dependency.rb, line 157
def generated_license_contents
  return unless license
  return if license.key == "other"
  return if license.text.nil?

  # strip copyright clauses and any extra newlines
  # many package managers don't provide enough information to
  # autogenerate a copyright clause
  text = license.text.lines
                     .reject { |l| l =~ Licensee::Matchers::Copyright::REGEX }
                     .join
                     .gsub(/\n\n\n/, "\n\n")

  {
    "sources" => "Auto-generated #{license.spdx_id} license text",
    "text" => text
  }
end
license_content_sources(files) click to toggle source

Returns the sources for a group of license file contents

Sources are returned as a single string with sources separated by “, ”

# File lib/licensed/dependency.rb, line 122
def license_content_sources(files)
  paths = Array(files).map do |file|
    next file[:uri] if file[:uri]
    next file[:source] if file[:source]

    path = dir_path.join(file[:dir], file[:name])
    normalize_source_path(path)
  end

  paths.join(", ")
end
license_metadata() click to toggle source

Returns the metadata that represents this dependency. This metadata is written to YAML in the dependencys cached text file

# File lib/licensed/dependency.rb, line 148
def license_metadata
  metadata.merge({
    # overrides all metadata values
    "license" => license_key
  })
end
normalize_source_path(path) click to toggle source
# File lib/licensed/dependency.rb, line 134
def normalize_source_path(path)
  path = Pathname.new(path) unless path.is_a?(Pathname)
  if path.fnmatch?(dir_path.join("**").to_path)
    # files under the dependency path return the relative path to the file
    path.relative_path_from(dir_path).to_path
  else
    # otherwise return the source_path as the immediate parent folder name
    # joined with the file name
    path.dirname.basename.join(path.basename).to_path
  end
end
read_file_with_encoding_check(file_path) click to toggle source
# File lib/licensed/dependency.rb, line 115
def read_file_with_encoding_check(file_path)
  File.read(file_path).encode("UTF-16", invalid: :replace, replace: "?").encode("UTF-8").rstrip
end