license_finder 2.0.1 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +7 -13
- data/.travis/install_bower.sh +5 -0
- data/.travis/install_gradle.sh +12 -0
- data/.travis/install_rebar.sh +12 -0
- data/.travis/install_virtualenv.sh +9 -0
- data/CHANGELOG.rdoc +15 -0
- data/CONTRIBUTING.md +21 -0
- data/Rakefile +2 -2
- data/TODO.md +0 -3
- data/bin/license_finder_pip.py +15 -11
- data/lib/license_finder/core.rb +3 -1
- data/lib/license_finder/license/definitions.rb +9 -0
- data/lib/license_finder/license/templates/GPLv3.txt +674 -0
- data/lib/license_finder/logger.rb +11 -0
- data/lib/license_finder/package.rb +1 -1
- data/lib/license_finder/package_managers/bower.rb +10 -6
- data/lib/license_finder/package_managers/cocoa_pods.rb +17 -6
- data/lib/license_finder/package_managers/cocoa_pods_package.rb +2 -2
- data/lib/license_finder/package_managers/gradle.rb +3 -1
- data/lib/license_finder/package_managers/npm.rb +4 -1
- data/lib/license_finder/package_managers/pip.rb +14 -7
- data/lib/license_finder/package_managers/pip_package.rb +5 -5
- data/lib/license_finder/packages/activation.rb +1 -17
- data/lib/license_finder/packages/licensing.rb +9 -13
- data/lib/license_finder/reports/csv_report.rb +5 -1
- data/lib/license_finder/version.rb +1 -1
- data/license_finder.gemspec +0 -1
- data/spec/lib/license_finder/license/definitions_spec.rb +6 -0
- data/spec/lib/license_finder/package_managers/pip_package_spec.rb +12 -2
- data/spec/lib/license_finder/package_managers/pip_spec.rb +4 -4
- data/spec/lib/license_finder/packages/activation_spec.rb +9 -15
- data/spec/lib/license_finder/reports/csv_report_spec.rb +3 -3
- data/spec/spec_helper.rb +3 -4
- metadata +7 -16
@@ -28,6 +28,17 @@ module LicenseFinder
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
def activation activation
|
32
|
+
preamble = sprintf("package %s:", activation.package.name)
|
33
|
+
if activation.sources.empty?
|
34
|
+
log activation.package.class, sprintf("%s no licenses found", preamble)
|
35
|
+
else
|
36
|
+
activation.sources.each do |source|
|
37
|
+
log activation.package.class, sprintf("%s found license '%s' %s", preamble, activation.license.name, source)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
31
42
|
def log prefix, string
|
32
43
|
raise NotImplementedError, "#log must be implemented"
|
33
44
|
end
|
@@ -3,17 +3,21 @@ require 'json'
|
|
3
3
|
module LicenseFinder
|
4
4
|
class Bower < PackageManager
|
5
5
|
def current_packages
|
6
|
-
|
7
|
-
|
8
|
-
json = JSON(output)
|
9
|
-
|
10
|
-
json.fetch("dependencies",[]).map do |package|
|
11
|
-
BowerPackage.new(package[1], logger: logger)
|
6
|
+
bower_output.map do |package|
|
7
|
+
BowerPackage.new(package, logger: logger)
|
12
8
|
end
|
13
9
|
end
|
14
10
|
|
15
11
|
private
|
16
12
|
|
13
|
+
def bower_output
|
14
|
+
output = `bower list --json -l action`
|
15
|
+
|
16
|
+
JSON(output)
|
17
|
+
.fetch("dependencies", {})
|
18
|
+
.values
|
19
|
+
end
|
20
|
+
|
17
21
|
def package_path
|
18
22
|
project_path.join('bower.json')
|
19
23
|
end
|
@@ -5,14 +5,17 @@ module LicenseFinder
|
|
5
5
|
def current_packages
|
6
6
|
podfile = YAML.load_file(lockfile_path)
|
7
7
|
|
8
|
-
acknowledgements = read_plist(acknowledgements_path)["PreferenceSpecifiers"]
|
9
|
-
|
10
8
|
podfile["PODS"].map do |pod|
|
11
9
|
pod = pod.keys.first if pod.is_a?(Hash)
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
CocoaPodsPackage.new(
|
11
|
+
name, version = pod.scan(/(.*)\s\((.*)\)/).flatten
|
12
|
+
|
13
|
+
CocoaPodsPackage.new(
|
14
|
+
name,
|
15
|
+
version,
|
16
|
+
license_texts[name],
|
17
|
+
logger: logger
|
18
|
+
)
|
16
19
|
end
|
17
20
|
end
|
18
21
|
|
@@ -26,6 +29,12 @@ module LicenseFinder
|
|
26
29
|
project_path.join("Podfile.lock")
|
27
30
|
end
|
28
31
|
|
32
|
+
def license_texts
|
33
|
+
# package name => license text
|
34
|
+
@license_texts ||= read_plist(acknowledgements_path)["PreferenceSpecifiers"]
|
35
|
+
.each_with_object({}) { |hash, memo| memo[hash["Title"]] = hash["FooterText"] }
|
36
|
+
end
|
37
|
+
|
29
38
|
def acknowledgements_path
|
30
39
|
filename = 'Pods-acknowledgements.plist'
|
31
40
|
directories = [
|
@@ -33,7 +42,9 @@ module LicenseFinder
|
|
33
42
|
'Pods/Target Support Files/Pods' # cocoapods >= 0.34
|
34
43
|
]
|
35
44
|
|
36
|
-
directories
|
45
|
+
directories
|
46
|
+
.map { |dir| project_path.join(dir, filename) }
|
47
|
+
.find(&:exist?)
|
37
48
|
end
|
38
49
|
|
39
50
|
def read_plist pathname
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module LicenseFinder
|
2
2
|
class CocoaPodsPackage < Package
|
3
|
-
def initialize(name, version, license_text)
|
4
|
-
super(name, version)
|
3
|
+
def initialize(name, version, license_text, options={})
|
4
|
+
super(name, version, options)
|
5
5
|
@license = License.find_by_text(license_text.to_s)
|
6
6
|
end
|
7
7
|
|
@@ -15,7 +15,9 @@ module LicenseFinder
|
|
15
15
|
options = {
|
16
16
|
'GroupTags' => { 'dependencies' => 'dependency' }
|
17
17
|
}
|
18
|
-
XmlSimple.xml_in(xml, options).fetch('dependency', [])
|
18
|
+
dependencies = XmlSimple.xml_in(xml, options).fetch('dependency', [])
|
19
|
+
|
20
|
+
dependencies.map do |dep|
|
19
21
|
GradlePackage.new(dep, logger: logger)
|
20
22
|
end
|
21
23
|
end
|
@@ -6,7 +6,10 @@ module LicenseFinder
|
|
6
6
|
|
7
7
|
def current_packages
|
8
8
|
json = npm_json
|
9
|
-
dependencies = DEPENDENCY_GROUPS
|
9
|
+
dependencies = DEPENDENCY_GROUPS
|
10
|
+
.map { |g| (json[g] || {}).values }
|
11
|
+
.flatten(1)
|
12
|
+
.reject{ |d| d.is_a?(String) }
|
10
13
|
|
11
14
|
pkgs = {} # name => spec
|
12
15
|
dependencies.each { |d| recursive_dependencies(d, pkgs) }
|
@@ -4,14 +4,14 @@ require 'httparty'
|
|
4
4
|
module LicenseFinder
|
5
5
|
class Pip < PackageManager
|
6
6
|
def current_packages
|
7
|
-
|
8
|
-
JSON(output).map do |package|
|
7
|
+
pip_output.map do |name, version, children, location|
|
9
8
|
PipPackage.new(
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
name,
|
10
|
+
version,
|
11
|
+
pypi_def(name, version),
|
12
|
+
logger: logger,
|
13
|
+
children: children,
|
14
|
+
install_path: Pathname(location).join(name),
|
15
15
|
)
|
16
16
|
end
|
17
17
|
end
|
@@ -22,6 +22,13 @@ module LicenseFinder
|
|
22
22
|
project_path.join('requirements.txt')
|
23
23
|
end
|
24
24
|
|
25
|
+
def pip_output
|
26
|
+
output = `#{LicenseFinder::BIN_PATH.join("license_finder_pip.py")}`
|
27
|
+
JSON(output).map do |package|
|
28
|
+
package.values_at(*%w[name version dependencies location])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
25
32
|
def pypi_def(name, version)
|
26
33
|
response = HTTParty.get("https://pypi.python.org/pypi/#{name}/#{version}/json")
|
27
34
|
if response.code == 200
|
@@ -1,11 +1,12 @@
|
|
1
1
|
module LicenseFinder
|
2
2
|
class PipPackage < Package
|
3
3
|
LICENSE_FORMAT = /^License.*::\s*(.*)$/
|
4
|
+
INVALID_LICENSES = ["", "UNKNOWN"].to_set
|
4
5
|
|
5
6
|
def self.license_names_from_spec(spec)
|
6
|
-
license = spec["license"]
|
7
|
+
license = spec["license"].to_s.strip
|
7
8
|
|
8
|
-
return [license]
|
9
|
+
return [license] unless INVALID_LICENSES.include?(license)
|
9
10
|
|
10
11
|
spec
|
11
12
|
.fetch("classifiers", [])
|
@@ -13,7 +14,7 @@ module LicenseFinder
|
|
13
14
|
.map { |c| c.gsub(LICENSE_FORMAT, '\1') }
|
14
15
|
end
|
15
16
|
|
16
|
-
def initialize(name, version,
|
17
|
+
def initialize(name, version, spec, options={})
|
17
18
|
super(
|
18
19
|
name,
|
19
20
|
version,
|
@@ -21,8 +22,7 @@ module LicenseFinder
|
|
21
22
|
summary: spec["summary"],
|
22
23
|
description: spec["description"],
|
23
24
|
homepage: spec["home_page"],
|
24
|
-
spec_licenses: self.class.license_names_from_spec(spec)
|
25
|
-
install_path: install_path
|
25
|
+
spec_licenses: self.class.license_names_from_spec(spec)
|
26
26
|
)
|
27
27
|
)
|
28
28
|
end
|
@@ -2,19 +2,7 @@ module LicenseFinder
|
|
2
2
|
module Activation
|
3
3
|
# An Activation reports that a license has been activated for a package, and
|
4
4
|
# tracks the source of that information
|
5
|
-
Basic = Struct.new(:package, :license)
|
6
|
-
def log(logger)
|
7
|
-
sources.each do |source|
|
8
|
-
log_package(logger, "found license '#{license.name}' #{source}")
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
private
|
13
|
-
|
14
|
-
def log_package(logger, text)
|
15
|
-
logger.log(package.class, "package #{package.name}: #{text}")
|
16
|
-
end
|
17
|
-
end
|
5
|
+
Basic = Struct.new(:package, :license)
|
18
6
|
|
19
7
|
class FromDecision < Basic
|
20
8
|
def sources
|
@@ -45,10 +33,6 @@ module LicenseFinder
|
|
45
33
|
def sources
|
46
34
|
[]
|
47
35
|
end
|
48
|
-
|
49
|
-
def log(logger)
|
50
|
-
log_package(logger, "no licenses found")
|
51
|
-
end
|
52
36
|
end
|
53
37
|
end
|
54
38
|
end
|
@@ -7,30 +7,26 @@ module LicenseFinder
|
|
7
7
|
# among the various sources of licenses we know about. In order of
|
8
8
|
# priority, licenses come from decisions, package specs, or package files.
|
9
9
|
def activations
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
aff = activations_from_files
|
17
|
-
return aff if aff.any?
|
18
|
-
|
19
|
-
[default_activation]
|
10
|
+
case
|
11
|
+
when activations_from_decisions.any? then activations_from_decisions
|
12
|
+
when activations_from_spec.any? then activations_from_spec
|
13
|
+
when activations_from_files.any? then activations_from_files
|
14
|
+
else [default_activation]
|
15
|
+
end
|
20
16
|
end
|
21
17
|
|
22
18
|
def activations_from_decisions
|
23
|
-
decided_licenses
|
19
|
+
@afd ||= decided_licenses
|
24
20
|
.map { |license| Activation::FromDecision.new(package, license) }
|
25
21
|
end
|
26
22
|
|
27
23
|
def activations_from_spec
|
28
|
-
licenses_from_spec
|
24
|
+
@afs ||= licenses_from_spec
|
29
25
|
.map { |license| Activation::FromSpec.new(package, license) }
|
30
26
|
end
|
31
27
|
|
32
28
|
def activations_from_files
|
33
|
-
license_files
|
29
|
+
@aff ||= license_files
|
34
30
|
.group_by(&:license)
|
35
31
|
.map { |license, files| Activation::FromFiles.new(package, license, files) }
|
36
32
|
end
|
@@ -3,7 +3,7 @@ require 'csv'
|
|
3
3
|
module LicenseFinder
|
4
4
|
class CsvReport < Report
|
5
5
|
COMMA_SEP = ","
|
6
|
-
AVAILABLE_COLUMNS = %w[name version licenses approved summary description]
|
6
|
+
AVAILABLE_COLUMNS = %w[name version licenses approved summary description homepage]
|
7
7
|
MISSING_DEPENDENCY_TEXT = "This package is not installed. Please install to determine licenses."
|
8
8
|
|
9
9
|
def initialize(dependencies, options)
|
@@ -35,6 +35,10 @@ module LicenseFinder
|
|
35
35
|
dep.version
|
36
36
|
end
|
37
37
|
|
38
|
+
def format_homepage(dep)
|
39
|
+
dep.homepage
|
40
|
+
end
|
41
|
+
|
38
42
|
def format_licenses(dep)
|
39
43
|
if dep.missing?
|
40
44
|
MISSING_DEPENDENCY_TEXT
|
data/license_finder.gemspec
CHANGED
@@ -42,7 +42,6 @@ Gem::Specification.new do |s|
|
|
42
42
|
|
43
43
|
s.add_development_dependency "rake"
|
44
44
|
s.add_development_dependency "rspec-its"
|
45
|
-
s.add_development_dependency "xpath"
|
46
45
|
s.add_development_dependency "pry"
|
47
46
|
s.add_development_dependency "rspec", "~> 3"
|
48
47
|
s.add_development_dependency "capybara", "~> 2.0.0"
|
@@ -28,6 +28,12 @@ describe LicenseFinder::License, "GPLv2" do
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
describe LicenseFinder::License, "GPLv3" do
|
32
|
+
it "should be recognized" do
|
33
|
+
expect(described_class.find_by_name("GPLv3").url).to be
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
31
37
|
describe LicenseFinder::License, "ISC" do
|
32
38
|
it "should be recognized" do
|
33
39
|
expect(described_class.find_by_name("ISC").url).to be
|
@@ -11,7 +11,7 @@ module LicenseFinder
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def make_package(pypi_def)
|
14
|
-
described_class.new('jasmine', '1.3.1', "jasmine/install/path",
|
14
|
+
described_class.new('jasmine', '1.3.1', pypi_def, install_path: "jasmine/install/path", children: ["achild"])
|
15
15
|
end
|
16
16
|
|
17
17
|
its(:name) { should == "jasmine" }
|
@@ -20,7 +20,7 @@ module LicenseFinder
|
|
20
20
|
its(:description) { should == "description" }
|
21
21
|
its(:homepage) { should == "homepage" }
|
22
22
|
its(:groups) { should == [] } # TODO: any way to extract install_requires and tests_require from `pip list` or `pip show`?
|
23
|
-
its(:children) { should == [] }
|
23
|
+
its(:children) { should == ["achild"] }
|
24
24
|
its(:install_path) { should eq "jasmine/install/path" }
|
25
25
|
|
26
26
|
|
@@ -60,6 +60,16 @@ module LicenseFinder
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
+
context "with blank license" do
|
64
|
+
it "returns the license from the classifier if it exists" do
|
65
|
+
data = { "license" => "", "classifiers" => [ 'License :: OSI Approved :: Apache 2.0 License' ] }
|
66
|
+
|
67
|
+
subject = make_package(data)
|
68
|
+
|
69
|
+
expect(subject.license_names_from_spec).to eq ['Apache 2.0 License']
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
63
73
|
context "with UNKNOWN license" do
|
64
74
|
it "returns the license from the classifier if it exists" do
|
65
75
|
data = { "license" => "UNKNOWN", "classifiers" => [ 'License :: OSI Approved :: Apache 2.0 License' ] }
|
@@ -17,15 +17,15 @@ module LicenseFinder
|
|
17
17
|
|
18
18
|
it 'fetches data from pip' do
|
19
19
|
stub_pip [
|
20
|
-
{"name" => "jasmine", "version" => "1.3.1", "location" => "jasmine/path"},
|
20
|
+
{"name" => "jasmine", "version" => "1.3.1", "location" => "jasmine/path", "dependencies" => ["jasmine-core"]},
|
21
21
|
{"name" => "jasmine-core", "version" => "1.3.1", "location" => "jasmine-core/path"}
|
22
22
|
].to_json
|
23
23
|
stub_pypi("jasmine", "1.3.1", status: 200, body: '{}')
|
24
24
|
stub_pypi("jasmine-core", "1.3.1", status: 200, body: '{}')
|
25
25
|
|
26
|
-
expect(pip.current_packages.map { |p| [p.name, p.version, p.install_path] }).to eq [
|
27
|
-
["jasmine", "1.3.1", "jasmine/path/jasmine"],
|
28
|
-
["jasmine-core", "1.3.1", "jasmine-core/path/jasmine-core"]
|
26
|
+
expect(pip.current_packages.map { |p| [p.name, p.version, p.install_path.to_s, p.children] }).to eq [
|
27
|
+
["jasmine", "1.3.1", "jasmine/path/jasmine", ["jasmine-core"]],
|
28
|
+
["jasmine-core", "1.3.1", "jasmine-core/path/jasmine-core", []]
|
29
29
|
]
|
30
30
|
end
|
31
31
|
|
@@ -4,6 +4,7 @@ module LicenseFinder
|
|
4
4
|
describe Activation do
|
5
5
|
let(:package) { Package.new("p", nil) }
|
6
6
|
let(:license) { License.find_by_name("l") }
|
7
|
+
let(:activation) { described_class.new(package, license) }
|
7
8
|
|
8
9
|
it "reports that a license has been activated for a package" do
|
9
10
|
subject = Activation::Basic.new(package, license)
|
@@ -12,35 +13,28 @@ module LicenseFinder
|
|
12
13
|
end
|
13
14
|
|
14
15
|
describe Activation::FromDecision do
|
15
|
-
it "
|
16
|
-
activation
|
17
|
-
subject = capture_stdout { activation.log(Logger::Verbose.new) }
|
18
|
-
expect(subject).to eq "LicenseFinder::Package: package p: found license 'l' from decision\n"
|
16
|
+
it "reports that it came from a decision" do
|
17
|
+
expect(activation.sources).to eq ["from decision"]
|
19
18
|
end
|
20
19
|
end
|
21
20
|
|
22
21
|
describe Activation::FromSpec do
|
23
|
-
it "
|
24
|
-
activation
|
25
|
-
subject = capture_stdout { activation.log(Logger::Verbose.new) }
|
26
|
-
expect(subject).to eq "LicenseFinder::Package: package p: found license 'l' from spec\n"
|
22
|
+
it "reports that it came from a spec" do
|
23
|
+
expect(activation.sources).to eq ["from spec"]
|
27
24
|
end
|
28
25
|
end
|
29
26
|
|
30
27
|
describe Activation::FromFiles do
|
31
|
-
it "
|
28
|
+
it "reports that it came from some files" do
|
32
29
|
files = [double(:file, path: "x"), double(:file, path: "y")]
|
33
30
|
activation = described_class.new(package, license, files)
|
34
|
-
|
35
|
-
expect(subject).to eq "LicenseFinder::Package: package p: found license 'l' from file 'x'\nLicenseFinder::Package: package p: found license 'l' from file 'y'\n"
|
31
|
+
expect(activation.sources).to eq ["from file 'x'", "from file 'y'"]
|
36
32
|
end
|
37
33
|
end
|
38
34
|
|
39
35
|
describe Activation::None do
|
40
|
-
it "
|
41
|
-
activation
|
42
|
-
subject = capture_stdout { activation.log(Logger::Verbose.new) }
|
43
|
-
expect(subject).to eq "LicenseFinder::Package: package p: no licenses found\n"
|
36
|
+
it "reports that has no source" do
|
37
|
+
expect(activation.sources).to eq []
|
44
38
|
end
|
45
39
|
end
|
46
40
|
end
|
@@ -9,12 +9,12 @@ module LicenseFinder
|
|
9
9
|
end
|
10
10
|
|
11
11
|
it "understands many columns" do
|
12
|
-
dep = Package.new('gem_a', '1.0', description: "A description", summary: "A summary")
|
12
|
+
dep = Package.new('gem_a', '1.0', description: "A description", summary: "A summary", homepage: "http://homepage.example.com")
|
13
13
|
dep.decide_on_license(License.find_by_name("MIT"))
|
14
14
|
dep.decide_on_license(License.find_by_name("GPL"))
|
15
15
|
dep.whitelisted!
|
16
|
-
subject = described_class.new([dep], columns: %w[name version licenses approved summary description])
|
17
|
-
expect(subject.to_s).to eq("gem_a,1.0,\"MIT,GPL\",Approved,A summary,A description\n")
|
16
|
+
subject = described_class.new([dep], columns: %w[name version licenses approved summary description homepage])
|
17
|
+
expect(subject.to_s).to eq("gem_a,1.0,\"MIT,GPL\",Approved,A summary,A description,http://homepage.example.com\n")
|
18
18
|
end
|
19
19
|
|
20
20
|
it "ignores unknown columns" do
|