summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authormo khan <mo.khan@gmail.com>2020-01-13 12:48:20 -0700
committermo khan <mo.khan@gmail.com>2020-01-15 15:03:07 -0700
commite98681be9b19d2a01efdbe14e7e8695b05b60010 (patch)
tree86c461a6b6dc27fbd634dccff55e0304921f0b37 /lib
parent1c73ad594a26597086b76b4b08ef045247791b5f (diff)
Inject the Pipenv package manager
Diffstat (limited to 'lib')
-rw-r--r--lib/license/management.rb7
-rw-r--r--lib/license_finder/package_managers/pipenv.rb60
-rw-r--r--lib/license_finder/package_utils/pypi.rb41
3 files changed, 108 insertions, 0 deletions
diff --git a/lib/license/management.rb b/lib/license/management.rb
index a6e0664..a2e0008 100644
--- a/lib/license/management.rb
+++ b/lib/license/management.rb
@@ -2,7 +2,10 @@
require 'pathname'
require 'yaml'
+require 'json'
require 'license_finder'
+require 'license_finder/package_managers/pipenv'
+require 'license_finder/package_utils/pypi'
require 'license/management/loggable'
require 'license/management/verifiable'
require 'license/management/repository'
@@ -11,6 +14,10 @@ require 'license/management/version'
# This applies a monkey patch to the JsonReport found in the `license_finder` gem.
LicenseFinder::JsonReport.prepend(License::Management::Report)
+LicenseFinder::Scanner.const_set(
+ :PACKAGE_MANAGERS,
+ LicenseFinder::Scanner::PACKAGE_MANAGERS + [LicenseFinder::Pipenv]
+)
# This monkey patch can be removed once we upgrade to license_finder 5.9.2. Details [here](https://gitlab.com/gitlab-org/gitlab/issues/13748#note_235810786).
module LicenseFinder
diff --git a/lib/license_finder/package_managers/pipenv.rb b/lib/license_finder/package_managers/pipenv.rb
new file mode 100644
index 0000000..e35b85d
--- /dev/null
+++ b/lib/license_finder/package_managers/pipenv.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module LicenseFinder
+ class Pipenv < PackageManager
+ def initialize(options = {})
+ super
+ @lockfile = Pathname('Pipfile.lock')
+ end
+
+ def current_packages
+ @current_packages ||=
+ begin
+ packages = {}
+ each_dependency(groups: allowed_groups) do |name, data, group|
+ version = canonicalize(data['version'])
+ package = packages.fetch(key_for(name, version)) do |key|
+ packages[key] = build_package_for(name, version)
+ end
+ package.groups << group
+ end
+ packages.values
+ end
+ end
+
+ def possible_package_paths
+ project_path ? [project_path.join(@lockfile)] : [@lockfile]
+ end
+
+ private
+
+ def each_dependency(groups: [])
+ dependencies = JSON.parse(IO.read(detected_package_path))
+ groups.each do |group|
+ dependencies[group].each do |name, data|
+ yield name, data, group
+ end
+ end
+ end
+
+ def canonicalize(version)
+ version.sub(/^==/, '')
+ end
+
+ def build_package_for(name, version)
+ PipPackage.new(name, version, PyPI.definition(name, version))
+ end
+
+ def key_for(name, version)
+ "#{name}-#{version}"
+ end
+
+ def allowed_groups
+ %w[default develop] - ignored_groups.to_a
+ end
+
+ def ignored_groups
+ @ignored_groups || []
+ end
+ end
+end
diff --git a/lib/license_finder/package_utils/pypi.rb b/lib/license_finder/package_utils/pypi.rb
new file mode 100644
index 0000000..fac02ec
--- /dev/null
+++ b/lib/license_finder/package_utils/pypi.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'net/http'
+require 'openssl'
+
+module LicenseFinder
+ class PyPI
+ CONNECTION_ERRORS = [
+ EOFError,
+ Errno::ECONNREFUSED,
+ Errno::ECONNRESET,
+ Errno::ECONNRESET,
+ Errno::EHOSTUNREACH,
+ Errno::EINVAL,
+ Net::OpenTimeout,
+ Net::ProtocolError,
+ Net::ReadTimeout,
+ OpenSSL::OpenSSLError,
+ OpenSSL::SSL::SSLError,
+ SocketError,
+ Timeout::Error
+ ].freeze
+
+ class << self
+ def definition(name, version)
+ response = request("https://pypi.org/pypi/#{name}/#{version}/json")
+ response.is_a?(Net::HTTPSuccess) ? JSON.parse(response.body).fetch('info', {}) : {}
+ rescue *CONNECTION_ERRORS
+ {}
+ end
+
+ def request(location, limit = 10)
+ uri = URI(location)
+ http = Net::HTTP.new(uri.host, uri.port)
+ http.use_ssl = true
+ response = http.get(uri.request_uri).response
+ response.is_a?(Net::HTTPRedirection) && limit.positive? ? request(response['location'], limit - 1) : response
+ end
+ end
+ end
+end