summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCan Eldem <celdem@gitlab.com>2020-01-16 10:24:11 +0000
committerCan Eldem <celdem@gitlab.com>2020-01-16 10:24:11 +0000
commit2074e7e5ea3012be6f3a72bd4af934a42b7202ca (patch)
treece9ec33580f0e6e3f9cf3df1aab2f2cf7b5601cb
parentf4f59927f87944a4d73be26416a7334521875f40 (diff)
parent34f162a4903d852d47bd5440839f7519eb6fa8f0 (diff)
Merge branch '12012-pipfile-lock' into 'master'v2.4.0
Add support for Pipfile.lock See merge request gitlab-org/security-products/license-management!103
-rw-r--r--.gitlab-ci.yml21
-rw-r--r--CHANGELOG.md4
-rw-r--r--CONTRIBUTING.md3
-rw-r--r--Dockerfile3
-rw-r--r--Gemfile.lock10
-rwxr-xr-xbin/test-all1
-rw-r--r--lib/license/management.rb16
-rw-r--r--lib/license/management/loggable.rb4
-rw-r--r--lib/license/management/python/pipenv.rb64
-rw-r--r--lib/license/management/python/pypi.rb44
-rw-r--r--lib/license/management/version.rb2
-rw-r--r--license-management.gemspec4
-rw-r--r--spec/fixtures/schema/v2.0.json74
-rw-r--r--spec/integration/dotnet/examples_spec.rb1
-rw-r--r--spec/integration/python/pip_spec.rb2
-rw-r--r--spec/integration/python/pipenv_spec.rb148
-rw-r--r--spec/integration/ruby/bundler_spec.rb2
-rw-r--r--spec/spec_helper.rb3
-rw-r--r--spec/support/integration_test_helper.rb3
-rw-r--r--spec/support/matchers.rb14
-rw-r--r--test/results/python-pipenv-v1.1.json102
-rw-r--r--test/results/python-pipenv-v1.json77
-rw-r--r--test/results/python-pipenv-v2.json75
-rwxr-xr-xtest/test.sh2
24 files changed, 670 insertions, 9 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 02c188c..626153b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -56,6 +56,8 @@ size:
script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker pull $TMP_IMAGE
+ - docker image ls $TMP_IMAGE
+ - docker image inspect $TMP_IMAGE
unit:
stage: test
@@ -145,6 +147,25 @@ QA:python3-pip-v2:
LM_REPORT_VERSION: 2
QA_RESULTS: python3-pip-v2
+QA:python3-pipenv:
+ extends: .QA
+ variables:
+ QA_RESULTS: python-pipenv-v1
+ QA_PROJECT: python-pipenv
+ QA_REF: pip-file-lock
+
+QA:python3-pipenv-v1-1:
+ extends: QA:python3-pipenv
+ variables:
+ LM_REPORT_VERSION: '1.1'
+ QA_RESULTS: python-pipenv-v1.1
+
+QA:python3-pipenv-v2:
+ extends: QA:python3-pipenv
+ variables:
+ LM_REPORT_VERSION: 2
+ QA_RESULTS: python-pipenv-v2
+
QA:ruby-bundler:
extends: .QA
variables:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4e82432..446baa1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# GitLab License management changelog
+## v2.4.0
+
+- Add support for `Pipfile.lock` (!103)
+
## v2.3.1
- Run gradle without tests by default. (!102)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d17d469..d8fbbde 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -31,6 +31,3 @@ open the issue in order to keep track of it and then open the relevant merge
request that potentially fixes it.
[ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues
-
-
-
diff --git a/Dockerfile b/Dockerfile
index 040ab84..f8c0f42 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -14,11 +14,12 @@ RUN npm install npm-install-peers
# install realpath, includes for python3, and pip for python3
# Install additional php packages for better composer package support
RUN add-apt-repository ppa:ondrej/php -y && apt-get update -y && \
+ apt-get upgrade -y --no-install-recommends && \
apt-get install -y --no-install-recommends \
bsdmainutils \
libjpeg8-dev \
zlib1g-dev \
- libpq-dev libmysqlclient-dev realpath dotnet-sdk-2.2 dotnet-sdk-3.0 \
+ libpq-dev libmysqlclient-dev realpath \
php7.1-mbstring php7.1-intl php7.1-xml php7.1-soap -y && \
git clone --depth 1 --branch v0.7.6 https://github.com/asdf-vm/asdf.git $HOME/.asdf && \
echo 'pip' >> $HOME/.default-python-packages && \
diff --git a/Gemfile.lock b/Gemfile.lock
index 4978f8d..60f69d1 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,13 +1,18 @@
PATH
remote: .
specs:
- license-management (2.3.1)
+ license-management (2.4.0)
license_finder (~> 5.11)
+ net-hippie (~> 0.3)
GEM
remote: https://rubygems.org/
specs:
+ addressable (2.7.0)
+ public_suffix (>= 2.0.2, < 5.0)
diff-lcs (1.3)
+ json-schema (2.8.1)
+ addressable (>= 2.4)
license_finder (5.11.1)
bundler
rubyzip (>= 1, < 3)
@@ -15,7 +20,9 @@ GEM
toml (= 0.2.0)
with_env (= 1.1.0)
xml-simple
+ net-hippie (0.3.0)
parslet (1.8.2)
+ public_suffix (4.0.3)
rspec (3.9.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
@@ -40,6 +47,7 @@ PLATFORMS
ruby
DEPENDENCIES
+ json-schema (~> 2.8)
license-management!
rspec (~> 3.9)
diff --git a/bin/test-all b/bin/test-all
index 44b4edc..64a5261 100755
--- a/bin/test-all
+++ b/bin/test-all
@@ -25,4 +25,5 @@ do
QA_PROJECT=js-yarn ./bin/test
QA_PROJECT=js-npm ./bin/test
QA_PROJECT=csharp-nuget-dotnetcore ./bin/test
+ QA_PROJECT=python-pipenv QA_REF=pip-file-lock ./bin/test
done
diff --git a/lib/license/management.rb b/lib/license/management.rb
index a6e0664..ebab5e2 100644
--- a/lib/license/management.rb
+++ b/lib/license/management.rb
@@ -2,9 +2,12 @@
require 'pathname'
require 'yaml'
+require 'json'
require 'license_finder'
require 'license/management/loggable'
require 'license/management/verifiable'
+require 'license/management/python/pipenv'
+require 'license/management/python/pypi'
require 'license/management/repository'
require 'license/management/report'
require 'license/management/version'
@@ -12,6 +15,12 @@ require 'license/management/version'
# This applies a monkey patch to the JsonReport found in the `license_finder` gem.
LicenseFinder::JsonReport.prepend(License::Management::Report)
+# This monkey patch can be removed once https://github.com/pivotal/LicenseFinder/pull/659 is released
+LicenseFinder::Scanner.const_set(
+ :PACKAGE_MANAGERS,
+ LicenseFinder::Scanner::PACKAGE_MANAGERS + [License::Management::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
class Bundler < PackageManager
@@ -31,5 +40,12 @@ module License
def self.root
Pathname.new(File.dirname(__FILE__)).join('../..')
end
+
+ def self.http
+ @http ||= Net::Hippie::Client.new.tap do |client|
+ client.logger = ::Logger.new('http.log')
+ client.follow_redirects = 3
+ end
+ end
end
end
diff --git a/lib/license/management/loggable.rb b/lib/license/management/loggable.rb
index a44d45d..165d8ca 100644
--- a/lib/license/management/loggable.rb
+++ b/lib/license/management/loggable.rb
@@ -10,6 +10,10 @@ module License
def log_info(message)
logger.info(self.class, message)
end
+
+ def log_error(message)
+ logger.info(self.class, message, color: :red)
+ end
end
end
end
diff --git a/lib/license/management/python/pipenv.rb b/lib/license/management/python/pipenv.rb
new file mode 100644
index 0000000..482fd25
--- /dev/null
+++ b/lib/license/management/python/pipenv.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+module License
+ module Management
+ class Pipenv < LicenseFinder::PackageManager
+ include Loggable
+
+ 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)
+ LicenseFinder::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
+ end
+
+ def ignored_groups
+ @ignored_groups.to_a || []
+ end
+ end
+ end
+end
diff --git a/lib/license/management/python/pypi.rb b/lib/license/management/python/pypi.rb
new file mode 100644
index 0000000..0397532
--- /dev/null
+++ b/lib/license/management/python/pypi.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'net/hippie'
+
+module License
+ module Management
+ class PyPI
+ include Loggable
+
+ def initialize(http)
+ @http = http
+ end
+
+ def definition_for(name, version)
+ uri = "https://pypi.org/pypi/#{name}/#{version}/json"
+ process(@http.with_retry { |client| client.get(uri) }).tap do |definition|
+ log_info([name, version, definition["license"]].inspect)
+ end
+ rescue *Net::Hippie::CONNECTION_ERRORS
+ {}
+ end
+
+ class << self
+ def definition(name, version)
+ @pypi ||= new(License::Management.http)
+ @pypi.definition_for(name, version)
+ end
+ end
+
+ private
+
+ def process(response)
+ return JSON.parse(response.body).fetch('info', {}) if ok?(response)
+
+ log_error([response.class, response.code, response.body].inspect)
+ {}
+ end
+
+ def ok?(response)
+ response.is_a?(Net::HTTPSuccess)
+ end
+ end
+ end
+end
diff --git a/lib/license/management/version.rb b/lib/license/management/version.rb
index 995dee4..a5e7b07 100644
--- a/lib/license/management/version.rb
+++ b/lib/license/management/version.rb
@@ -2,6 +2,6 @@
module License
module Management
- VERSION = '2.3.1'
+ VERSION = '2.4.0'
end
end
diff --git a/license-management.gemspec b/license-management.gemspec
index c58bbdc..60ab5e1 100644
--- a/license-management.gemspec
+++ b/license-management.gemspec
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
spec.summary = 'License Management job for GitLab CI.'
spec.description = 'License Management job for GitLab CI.'
spec.homepage = 'https://gitlab.com/gitlab-org/security-products/license-management'
- spec.license = 'GitLab EE'
+ spec.license = 'Nonstandard'
spec.metadata['allowed_push_host'] = 'https://example.com'
spec.metadata['homepage_uri'] = spec.homepage
@@ -28,5 +28,7 @@ Gem::Specification.new do |spec|
spec.require_paths = ['lib']
spec.add_dependency 'license_finder', '~> 5.11'
+ spec.add_dependency 'net-hippie', '~> 0.3'
+ spec.add_development_dependency 'json-schema', '~> 2.8'
spec.add_development_dependency 'rspec', '~> 3.9'
end
diff --git a/spec/fixtures/schema/v2.0.json b/spec/fixtures/schema/v2.0.json
new file mode 100644
index 0000000..6ade7ad
--- /dev/null
+++ b/spec/fixtures/schema/v2.0.json
@@ -0,0 +1,74 @@
+{
+ "$id": "https://gitlab.com/gitlab-org/security-products/license-management/blob/master/spec/fixtures/schema/v2.0.json",
+ "type": "object",
+ "required": [
+ "version",
+ "licenses",
+ "dependencies"
+ ],
+ "properties": {
+ "version": {
+ "type": "string",
+ "pattern": "^[0-9]+\\.[0-9]+$"
+ },
+ "licenses": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "url"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "url": {
+ "type": "uri"
+ }
+ }
+ }
+ },
+ "dependencies": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "name",
+ "url",
+ "description",
+ "paths",
+ "licenses"
+ ],
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "url": {
+ "type": "uri"
+ },
+ "description": {
+ "type": "string"
+ },
+ "paths": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "licenses": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/integration/dotnet/examples_spec.rb b/spec/integration/dotnet/examples_spec.rb
index 43caea0..5b2a913 100644
--- a/spec/integration/dotnet/examples_spec.rb
+++ b/spec/integration/dotnet/examples_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe ".NET Core" do
report = runner.scan(env: { 'LICENSE_FINDER_CLI_OPTS' => '--recursive' })
expect(report).not_to be_empty
+ expect(report).to match_schema(version: '2.0')
expect(report[:licenses].count).not_to be_zero
expect(report[:dependencies].count).not_to be_zero
end
diff --git a/spec/integration/python/pip_spec.rb b/spec/integration/python/pip_spec.rb
index 5ff5f60..b092189 100644
--- a/spec/integration/python/pip_spec.rb
+++ b/spec/integration/python/pip_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe "pip" do
report = runner.scan
expect(report).not_to be_empty
+ expect(report).to match_schema(version: '2.0')
expect(report[:version]).to start_with('2')
expect(report[:dependencies].map { |x| x[:name] }).to include("sentry-sdk")
expect(report[:dependencies].find { |x| x[:name] == 'sentry-sdk' }[:licenses]).to match_array(["BSD-4-Clause"])
@@ -39,6 +40,7 @@ RSpec.describe "pip" do
report = runner.scan
expect(report).not_to be_empty
+ expect(report).to match_schema(version: '2.0')
expect(report[:version]).to start_with('2')
expect(report[:licenses]).not_to be_empty
expect(report[:dependencies]).not_to be_empty
diff --git a/spec/integration/python/pipenv_spec.rb b/spec/integration/python/pipenv_spec.rb
new file mode 100644
index 0000000..1ea60fe
--- /dev/null
+++ b/spec/integration/python/pipenv_spec.rb
@@ -0,0 +1,148 @@
+require 'spec_helper'
+
+RSpec.describe "pipenv" do
+ context "when a project depends on a version 6 Pipfile.lock" do
+ let(:pipfile_lock_content) do
+ JSON.pretty_generate({
+ "_meta": {
+ "hash": { "sha256": "" },
+ "pipfile-spec": 6,
+ "requires": { "python_version": "3.8" },
+ "sources": [ { "name": "pypi", "url": "https://pypi.org/simple", "verify_ssl": true } ]
+ },
+ "default": {
+ "six": { "hashes": [], "index": "pypi", "version": "==1.13.0" }
+ },
+ "develop": {}
+ })
+ end
+
+ it 'produces a valid report' do
+ runner.add_file('Pipfile.lock', pipfile_lock_content)
+
+ report = runner.scan
+
+ expect(report).not_to be_empty
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:version]).not_to be_empty
+ expect(report[:licenses]).not_to be_empty
+ expect(report[:dependencies].map { |x| x[:name] }).to contain_exactly("six")
+ end
+ end
+
+ context "when a project depends on a version 3.2.1 Pipfile.lock" do
+ let(:pipfile_lock_content) do
+ JSON.pretty_generate({
+ "default": {
+ "crayons": { "version": "==0.1.2", "hash": "" },
+ "requirements-parser": { "version": "==0.1.0", "hash": "" },
+ "pexpect": { "version": "==4.2.1", "hash": "" },
+ "delegator.py": { "version": "==0.0.8", "hash": "" },
+ "backports.shutil_get_terminal_size": { "version": "==1.0.0", "hash": "" },
+ "ptyprocess": { "version": "==0.5.1", "hash": "" },
+ "parse": { "version": "==1.6.6", "hash": "" },
+ "toml": { "version": "==0.9.2", "hash": "" },
+ "colorama": { "version": "==0.3.7", "hash": "" },
+ "requests": { "version": "==2.13.0", "hash": "" },
+ "click": { "version": "==6.7", "hash": "" }
+ },
+ "develop": {
+ "packaging": { "version": "==16.8", "hash": "" },
+ "pytest": { "version": "==3.0.6", "hash": "" },
+ "setuptools": { "version": "==34.0.2", "hash": "" },
+ "pyparsing": { "version": "==2.1.10", "hash": "" },
+ "py": { "version": "==1.4.32", "hash": "" },
+ "six": { "version": "==1.10.0", "hash": "" },
+ "appdirs": { "version": "==1.4.0", "hash": "" }
+ },
+ "_meta": {
+ "sources": [ { "url": "https://pypi.python.org/simple", "verify_ssl": true } ],
+ "requires": {},
+ "Pipfile-sha256": "24f12b631b7c40b8c5eff934a1aef263ed04f5eaffb4acf4706442f3d23cba36"
+ }
+ })
+ end
+
+ it 'produces a valid report' do
+ runner.add_file('Pipfile.lock', pipfile_lock_content)
+
+ report = runner.scan
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report).not_to be_empty
+ expect(report[:version]).not_to be_empty
+ expect(report[:licenses]).not_to be_empty
+ expect(report[:dependencies].map { |x| x[:name] }).to match_array([
+ "appdirs",
+ "backports.shutil_get_terminal_size",
+ "click",
+ "colorama",
+ "crayons",
+ "delegator.py",
+ "packaging",
+ "parse",
+ "pexpect",
+ "ptyprocess",
+ "py",
+ "pyparsing",
+ "pytest",
+ "requests",
+ "requirements-parser",
+ "setuptools",
+ "six",
+ "toml",
+ ])
+ end
+ end
+
+ context "when a project depends on a version 5 Pipfile.lock" do
+ let(:pipfile_lock_content) do
+ JSON.pretty_generate({
+ "_meta": {
+ "hash": { "sha256": "" },
+ "host-environment-markers": {
+ "implementation_name": "cpython",
+ "implementation_version": "3.6.1",
+ "os_name": "posix",
+ "platform_machine": "x86_64",
+ "platform_python_implementation": "CPython",
+ "platform_release": "16.7.0",
+ "platform_system": "Darwin",
+ "platform_version": "Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64",
+ "python_full_version": "3.6.1",
+ "python_version": "3.6",
+ "sys_platform": "darwin"
+ },
+ "pipfile-spec": 5,
+ "requires": {},
+ "sources": [{ "name": "pypi", "url": "https://pypi.python.org/simple", "verify_ssl": true }]
+ },
+ "default": {
+ "certifi": { "hashes": ["", ""], "version": "==2017.7.27.1" },
+ "chardet": { "hashes": ["", ""], "version": "==3.0.4" },
+ "idna": { "hashes": ["", ""], "version": "==2.6" },
+ "requests": { "hashes": ["", ""], "version": "==2.18.4" },
+ "urllib3": { "hashes": ["", ""], "version": "==1.22" }
+ },
+ "develop": {
+ "py": { "hashes": ["", ""], "version": "==1.4.34" },
+ "pytest": { "hashes": ["", ""], "version": "==3.2.2" }
+ }
+ })
+ end
+
+ it 'produces a valid report' do
+ runner.add_file('Pipfile.lock', pipfile_lock_content)
+
+ report = runner.scan
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report).not_to be_empty
+ expect(report[:version]).not_to be_empty
+ expect(report[:licenses]).not_to be_empty
+ expect(report[:dependencies].map { |x| x[:name] }).to match_array([
+ 'certifi', 'chardet', 'idna', 'requests', 'urllib3', 'py', 'pytest'
+ ])
+ end
+ end
+end
diff --git a/spec/integration/ruby/bundler_spec.rb b/spec/integration/ruby/bundler_spec.rb
index 179da2a..2cb8f7d 100644
--- a/spec/integration/ruby/bundler_spec.rb
+++ b/spec/integration/ruby/bundler_spec.rb
@@ -14,6 +14,7 @@ gem 'saml-kit'
report = runner.scan
expect(report).not_to be_empty
+ expect(report).to match_schema(version: '2.0')
expect(report[:licenses]).not_to be_empty
expect(report[:dependencies].map { |x| x[:name] }).to include("saml-kit")
end
@@ -81,6 +82,7 @@ BUNDLED WITH
report = runner.scan
expect(report).not_to be_empty
+ expect(report).to match_schema(version: '2.0')
expect(report[:licenses]).not_to be_empty
expect(report[:dependencies].map { |x| x[:name] }).to include("saml-kit")
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index be7673c..1889335 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,6 +1,9 @@
require 'license/management'
require 'json'
+require 'securerandom'
+require 'json-schema'
require 'support/integration_test_helper'
+require 'support/matchers'
RSpec.configure do |config|
config.include IntegrationTestHelper, type: :integration
diff --git a/spec/support/integration_test_helper.rb b/spec/support/integration_test_helper.rb
index 485af1b..5ef00a1 100644
--- a/spec/support/integration_test_helper.rb
+++ b/spec/support/integration_test_helper.rb
@@ -2,7 +2,8 @@ module IntegrationTestHelper
class IntegrationTestRunner
attr_reader :project_path
- def initialize(project_path = Dir.mktmpdir('lm'))
+ def initialize(project_path = File.join(Dir.pwd, 'tmp', SecureRandom.uuid))
+ FileUtils.mkdir_p(project_path)
@project_path = project_path
end
diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb
new file mode 100644
index 0000000..1d1c263
--- /dev/null
+++ b/spec/support/matchers.rb
@@ -0,0 +1,14 @@
+RSpec::Matchers.define :match_schema do |version: '2.0'|
+ match do |actual|
+ schema = License::Management.root
+ .join("spec/fixtures/schema/v#{version}.json")
+ .to_s
+ @errors = JSON::Validator.fully_validate(schema, actual)
+ @errors.empty?
+ end
+
+ failure_message do |response|
+ "didn't match the schema for version #{version}" \
+ " The validation errors were:\n#{@errors.join("\n")}"
+ end
+end
diff --git a/test/results/python-pipenv-v1.1.json b/test/results/python-pipenv-v1.1.json
new file mode 100644
index 0000000..0528f88
--- /dev/null
+++ b/test/results/python-pipenv-v1.1.json
@@ -0,0 +1,102 @@
+{
+ "version": "1.1",
+ "licenses": [
+ {
+ "count": 1,
+ "name": "Apache 2.0"
+ },
+ {
+ "count": 1,
+ "name": "BSD"
+ },
+ {
+ "count": 1,
+ "name": "MIT"
+ },
+ {
+ "count": 1,
+ "name": "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)"
+ }
+ ],
+ "dependencies": [
+ {
+ "licenses": [
+ {
+ "name": "BSD",
+ "url": "http://en.wikipedia.org/wiki/BSD_licenses#4-clause_license_.28original_.22BSD_License.22.29"
+ }
+ ],
+ "license": {
+ "name": "BSD",
+ "url": "http://en.wikipedia.org/wiki/BSD_licenses#4-clause_license_.28original_.22BSD_License.22.29"
+ },
+ "dependency": {
+ "name": "django",
+ "url": "https://www.djangoproject.com/",
+ "description": "A high-level Python Web framework that encourages rapid development and clean, pragmatic design.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "licenses": [
+ {
+ "name": "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)",
+ "url": ""
+ }
+ ],
+ "license": {
+ "name": "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)"
+ },
+ "dependency": {
+ "name": "docutils",
+ "url": "http://docutils.sourceforge.net/",
+ "description": "Docutils -- Python Documentation Utilities",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "licenses": [
+ {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ }
+ ],
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "pytz",
+ "url": "http://pythonhosted.org/pytz",
+ "description": "World timezone definitions, modern and historical",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "licenses": [
+ {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ }
+ ],
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ "dependency": {
+ "name": "requests",
+ "url": "http://python-requests.org",
+ "description": "Python HTTP for Humans.",
+ "pathes": [
+ "."
+ ]
+ }
+ }
+ ]
+}
diff --git a/test/results/python-pipenv-v1.json b/test/results/python-pipenv-v1.json
new file mode 100644
index 0000000..6c0ae63
--- /dev/null
+++ b/test/results/python-pipenv-v1.json
@@ -0,0 +1,77 @@
+{
+ "licenses": [
+ {
+ "count": 1,
+ "name": "Apache 2.0"
+ },
+ {
+ "count": 1,
+ "name": "BSD"
+ },
+ {
+ "count": 1,
+ "name": "MIT"
+ },
+ {
+ "count": 1,
+ "name": "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)"
+ }
+ ],
+ "dependencies": [
+ {
+ "license": {
+ "name": "BSD",
+ "url": "http://en.wikipedia.org/wiki/BSD_licenses#4-clause_license_.28original_.22BSD_License.22.29"
+ },
+ "dependency": {
+ "name": "django",
+ "url": "https://www.djangoproject.com/",
+ "description": "A high-level Python Web framework that encourages rapid development and clean, pragmatic design.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)"
+ },
+ "dependency": {
+ "name": "docutils",
+ "url": "http://docutils.sourceforge.net/",
+ "description": "Docutils -- Python Documentation Utilities",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "pytz",
+ "url": "http://pythonhosted.org/pytz",
+ "description": "World timezone definitions, modern and historical",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ "dependency": {
+ "name": "requests",
+ "url": "http://python-requests.org",
+ "description": "Python HTTP for Humans.",
+ "pathes": [
+ "."
+ ]
+ }
+ }
+ ]
+}
diff --git a/test/results/python-pipenv-v2.json b/test/results/python-pipenv-v2.json
new file mode 100644
index 0000000..bdbeb14
--- /dev/null
+++ b/test/results/python-pipenv-v2.json
@@ -0,0 +1,75 @@
+{
+ "version": "2.0",
+ "licenses": [
+ {
+ "id": "Apache-2.0",
+ "name": "Apache License 2.0",
+ "url": "https://opensource.org/licenses/Apache-2.0",
+ "count": 1
+ },
+ {
+ "id": "BSD-4-Clause",
+ "name": "BSD 4-Clause \"Original\" or \"Old\" License",
+ "url": "http://directory.fsf.org/wiki/License:BSD_4Clause",
+ "count": 1
+ },
+ {
+ "id": "MIT",
+ "name": "MIT License",
+ "url": "https://opensource.org/licenses/MIT",
+ "count": 1
+ },
+ {
+ "id": "public domain, python, 2-clause bsd, gpl 3 (see copying.txt)",
+ "name": "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)",
+ "url": "",
+ "count": 1
+ }
+ ],
+ "dependencies": [
+ {
+ "name": "django",
+ "url": "https://www.djangoproject.com/",
+ "description": "A high-level Python Web framework that encourages rapid development and clean, pragmatic design.",
+ "paths": [
+ "."
+ ],
+ "licenses": [
+ "BSD-4-Clause"
+ ]
+ },
+ {
+ "name": "docutils",
+ "url": "http://docutils.sourceforge.net/",
+ "description": "Docutils -- Python Documentation Utilities",
+ "paths": [
+ "."
+ ],
+ "licenses": [
+ "public domain, python, 2-clause bsd, gpl 3 (see copying.txt)"
+ ]
+ },
+ {
+ "name": "pytz",
+ "url": "http://pythonhosted.org/pytz",
+ "description": "World timezone definitions, modern and historical",
+ "paths": [
+ "."
+ ],
+ "licenses": [
+ "MIT"
+ ]
+ },
+ {
+ "name": "requests",
+ "url": "http://python-requests.org",
+ "description": "Python HTTP for Humans.",
+ "paths": [
+ "."
+ ],
+ "licenses": [
+ "Apache-2.0"
+ ]
+ }
+ ]
+}
diff --git a/test/test.sh b/test/test.sh
index 042adc3..8d04a3a 100755
--- a/test/test.sh
+++ b/test/test.sh
@@ -22,4 +22,4 @@ mkdir -p /results/
cp "/code/$project/gl-license-management-report.json" "/results/$project-gl-license-management-report.json"
# Compare results with expected results.
-diff -u "/code/$project/gl-license-management-report.json" "/test/results/$results.json"
+diff -u "/test/results/$results.json" "/code/$project/gl-license-management-report.json"