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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +7 -13
  3. data/.travis/install_bower.sh +5 -0
  4. data/.travis/install_gradle.sh +12 -0
  5. data/.travis/install_rebar.sh +12 -0
  6. data/.travis/install_virtualenv.sh +9 -0
  7. data/CHANGELOG.rdoc +15 -0
  8. data/CONTRIBUTING.md +21 -0
  9. data/Rakefile +2 -2
  10. data/TODO.md +0 -3
  11. data/bin/license_finder_pip.py +15 -11
  12. data/lib/license_finder/core.rb +3 -1
  13. data/lib/license_finder/license/definitions.rb +9 -0
  14. data/lib/license_finder/license/templates/GPLv3.txt +674 -0
  15. data/lib/license_finder/logger.rb +11 -0
  16. data/lib/license_finder/package.rb +1 -1
  17. data/lib/license_finder/package_managers/bower.rb +10 -6
  18. data/lib/license_finder/package_managers/cocoa_pods.rb +17 -6
  19. data/lib/license_finder/package_managers/cocoa_pods_package.rb +2 -2
  20. data/lib/license_finder/package_managers/gradle.rb +3 -1
  21. data/lib/license_finder/package_managers/npm.rb +4 -1
  22. data/lib/license_finder/package_managers/pip.rb +14 -7
  23. data/lib/license_finder/package_managers/pip_package.rb +5 -5
  24. data/lib/license_finder/packages/activation.rb +1 -17
  25. data/lib/license_finder/packages/licensing.rb +9 -13
  26. data/lib/license_finder/reports/csv_report.rb +5 -1
  27. data/lib/license_finder/version.rb +1 -1
  28. data/license_finder.gemspec +0 -1
  29. data/spec/lib/license_finder/license/definitions_spec.rb +6 -0
  30. data/spec/lib/license_finder/package_managers/pip_package_spec.rb +12 -2
  31. data/spec/lib/license_finder/package_managers/pip_spec.rb +4 -4
  32. data/spec/lib/license_finder/packages/activation_spec.rb +9 -15
  33. data/spec/lib/license_finder/reports/csv_report_spec.rb +3 -3
  34. data/spec/spec_helper.rb +3 -4
  35. 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
@@ -91,7 +91,7 @@ module LicenseFinder
91
91
 
92
92
  def activations
93
93
  licensing.activations.tap do |activations|
94
- activations.each { |activation| activation.log(logger) }
94
+ activations.each { |activation| logger.activation(activation) }
95
95
  end
96
96
  end
97
97
 
@@ -3,17 +3,21 @@ require 'json'
3
3
  module LicenseFinder
4
4
  class Bower < PackageManager
5
5
  def current_packages
6
- output = `bower list --json -l action`
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
- pod_name, pod_version = pod.scan(/(.*)\s\((.*)\)/).flatten
14
- pod_acknowledgment = acknowledgements.detect { |hash| hash["Title"] == pod_name } || {}
15
- CocoaPodsPackage.new(pod_name, pod_version, pod_acknowledgment["FooterText"])
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.map { |dir| project_path.join(dir, filename) }.find(&:exist?)
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', []).map do |dep|
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.map { |g| (json[g] || {}).values }.flatten(1).reject{ |d| d.is_a?(String) }
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
- output = `#{LicenseFinder::BIN_PATH.join("license_finder_pip.py")}`
8
- JSON(output).map do |package|
7
+ pip_output.map do |name, version, children, location|
9
8
  PipPackage.new(
10
- package["name"],
11
- package["version"],
12
- File.join(package["location"], package["name"]),
13
- pypi_def(package["name"], package["version"]),
14
- logger: logger
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] if license && license != "UNKNOWN"
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, install_path, spec, options={})
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) do
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
- afd = activations_from_decisions
11
- return afd if afd.any?
12
-
13
- afs = activations_from_spec
14
- return afs if afs.any?
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
@@ -1,3 +1,3 @@
1
1
  module LicenseFinder
2
- VERSION = "2.0.1"
2
+ VERSION = "2.0.2"
3
3
  end
@@ -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", pypi_def)
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 == [] } # TODO: use pipdeptree or something like it
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 "logs that it came from a decision" do
16
- activation = described_class.new(package, license)
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 "logs that it came from a spec" do
24
- activation = described_class.new(package, license)
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 "logs that it came from some files" do
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
- subject = capture_stdout { activation.log(Logger::Verbose.new) }
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 "logs that no licenses could be found" do
41
- activation = described_class.new(package, license)
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