license_finder 1.1.1 → 1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -2
  3. data/CHANGELOG.rdoc +10 -0
  4. data/Gemfile +1 -1
  5. data/README.md +363 -0
  6. data/Rakefile +30 -1
  7. data/TODO.md +28 -0
  8. data/bin/license_finder_pip.py +18 -0
  9. data/db/migrate/201410031451_rename_dependency_license_name.rb +6 -0
  10. data/features/multiple_licenses.feature +9 -0
  11. data/features/step_definitions/cli_steps.rb +9 -9
  12. data/features/step_definitions/cocoapod_steps.rb +1 -1
  13. data/features/step_definitions/configure_bundler_groups_steps.rb +3 -3
  14. data/features/step_definitions/configure_whitelist_steps.rb +4 -4
  15. data/features/step_definitions/gradle_steps.rb +1 -1
  16. data/features/step_definitions/manually_added_steps.rb +3 -3
  17. data/features/step_definitions/manually_approved_steps.rb +5 -5
  18. data/features/step_definitions/manually_assigned_license_steps.rb +4 -4
  19. data/features/step_definitions/maven_steps.rb +1 -1
  20. data/features/step_definitions/multiple_licenses_steps.rb +14 -0
  21. data/features/step_definitions/node_steps.rb +1 -1
  22. data/features/step_definitions/python_steps.rb +1 -1
  23. data/features/step_definitions/report_csv_steps.rb +3 -3
  24. data/features/step_definitions/report_html_steps.rb +5 -5
  25. data/features/step_definitions/shared_steps.rb +23 -6
  26. data/lib/license_finder.rb +3 -0
  27. data/lib/license_finder/cli.rb +13 -34
  28. data/lib/license_finder/configuration.rb +8 -4
  29. data/lib/license_finder/dependency_manager.rb +25 -15
  30. data/lib/license_finder/license.rb +8 -0
  31. data/lib/license_finder/logger.rb +59 -0
  32. data/lib/license_finder/package.rb +37 -30
  33. data/lib/license_finder/package_manager.rb +20 -0
  34. data/lib/license_finder/package_managers/bower.rb +4 -9
  35. data/lib/license_finder/package_managers/bower_package.rb +2 -1
  36. data/lib/license_finder/package_managers/bundler.rb +26 -41
  37. data/lib/license_finder/package_managers/bundler_package.rb +6 -3
  38. data/lib/license_finder/package_managers/cocoa_pods.rb +18 -10
  39. data/lib/license_finder/package_managers/cocoa_pods_package.rb +4 -3
  40. data/lib/license_finder/package_managers/gradle.rb +7 -11
  41. data/lib/license_finder/package_managers/gradle_package.rb +2 -7
  42. data/lib/license_finder/package_managers/maven.rb +5 -9
  43. data/lib/license_finder/package_managers/maven_package.rb +4 -8
  44. data/lib/license_finder/package_managers/npm.rb +6 -10
  45. data/lib/license_finder/package_managers/npm_package.rb +2 -1
  46. data/lib/license_finder/package_managers/pip.rb +11 -24
  47. data/lib/license_finder/package_managers/pip_package.rb +2 -1
  48. data/lib/license_finder/package_saver.rb +2 -2
  49. data/lib/license_finder/platform.rb +4 -0
  50. data/lib/license_finder/possible_license_file.rb +4 -0
  51. data/lib/license_finder/possible_license_files.rb +2 -1
  52. data/lib/license_finder/reports/detailed_text_report.rb +1 -1
  53. data/lib/license_finder/reports/formatted_report.rb +1 -1
  54. data/lib/license_finder/tables/dependency.rb +22 -12
  55. data/lib/license_finder/yml_to_sql.rb +1 -1
  56. data/lib/templates/html_report.erb +4 -4
  57. data/lib/templates/markdown_report.erb +4 -4
  58. data/lib/templates/text_report.erb +1 -1
  59. data/license_finder.gemspec +28 -12
  60. data/spec/lib/license_finder/cli_spec.rb +193 -185
  61. data/spec/lib/license_finder/configuration_spec.rb +46 -47
  62. data/spec/lib/license_finder/dependency_manager_spec.rb +48 -44
  63. data/spec/lib/license_finder/license/definitions_spec.rb +26 -26
  64. data/spec/lib/license_finder/license_spec.rb +25 -25
  65. data/spec/lib/license_finder/package_managers/bower_package_spec.rb +33 -17
  66. data/spec/lib/license_finder/package_managers/bower_spec.rb +35 -35
  67. data/spec/lib/license_finder/package_managers/bundler_package_spec.rb +20 -15
  68. data/spec/lib/license_finder/package_managers/bundler_spec.rb +12 -19
  69. data/spec/lib/license_finder/package_managers/cocoa_pods_package_spec.rb +8 -5
  70. data/spec/lib/license_finder/package_managers/cocoa_pods_spec.rb +20 -22
  71. data/spec/lib/license_finder/package_managers/gradle_package_spec.rb +8 -5
  72. data/spec/lib/license_finder/package_managers/gradle_spec.rb +20 -20
  73. data/spec/lib/license_finder/package_managers/maven_package_spec.rb +8 -5
  74. data/spec/lib/license_finder/package_managers/maven_spec.rb +18 -18
  75. data/spec/lib/license_finder/package_managers/npm_package_spec.rb +36 -17
  76. data/spec/lib/license_finder/package_managers/npm_spec.rb +17 -17
  77. data/spec/lib/license_finder/package_managers/pip_package_spec.rb +16 -10
  78. data/spec/lib/license_finder/package_managers/pip_spec.rb +21 -18
  79. data/spec/lib/license_finder/package_saver_spec.rb +15 -25
  80. data/spec/lib/license_finder/possible_license_file_spec.rb +5 -4
  81. data/spec/lib/license_finder/possible_license_files_spec.rb +11 -5
  82. data/spec/lib/license_finder/reports/detailed_text_report_spec.rb +3 -3
  83. data/spec/lib/license_finder/reports/html_report_spec.rb +23 -23
  84. data/spec/lib/license_finder/reports/markdown_report_spec.rb +12 -12
  85. data/spec/lib/license_finder/reports/reporter_spec.rb +11 -11
  86. data/spec/lib/license_finder/reports/text_report_spec.rb +3 -3
  87. data/spec/lib/license_finder/tables/dependency_spec.rb +59 -41
  88. data/spec/lib/license_finder/yml_to_sql_spec.rb +21 -21
  89. data/spec/lib/license_finder_spec.rb +1 -1
  90. data/spec/spec_helper.rb +0 -13
  91. data/spec/support/shared_examples_for_package.rb +46 -0
  92. data/spec/support/shared_examples_for_package_manager.rb +15 -0
  93. metadata +19 -114
  94. data/readme.md +0 -259
@@ -4,12 +4,11 @@ module LicenseFinder
4
4
  def_delegators :gem_def, :summary, :description, :name, :homepage
5
5
 
6
6
  attr_reader :gem_def
7
- attr_accessor :children
8
7
 
9
- def initialize(gem_def, bundler_def)
8
+ def initialize(gem_def, bundler_def, options={})
9
+ super options
10
10
  @gem_def = gem_def
11
11
  @bundler_def = bundler_def
12
- @children = []
13
12
  end
14
13
 
15
14
  def groups
@@ -20,6 +19,10 @@ module LicenseFinder
20
19
  gem_def.version.to_s
21
20
  end
22
21
 
22
+ def children
23
+ gem_def.dependencies.map(&:name)
24
+ end
25
+
23
26
  private
24
27
 
25
28
  def install_path
@@ -1,12 +1,11 @@
1
1
  require "json"
2
2
 
3
3
  module LicenseFinder
4
- class CocoaPods
5
-
6
- def self.current_packages
4
+ class CocoaPods < PackageManager
5
+ def current_packages
7
6
  podfile = YAML.load_file(lockfile_path)
8
7
 
9
- acknowledgements = JSON.parse(`plutil -convert json -o - #{Pathname.new('Pods/Pods-acknowledgements.plist').expand_path}`)["PreferenceSpecifiers"]
8
+ acknowledgements = read_plist(acknowledgements_path)["PreferenceSpecifiers"]
10
9
 
11
10
  podfile["PODS"].map do |pod|
12
11
  pod = pod.keys.first if pod.is_a?(Hash)
@@ -17,19 +16,28 @@ module LicenseFinder
17
16
  end
18
17
  end
19
18
 
20
- def self.active?
21
- package_path.exist?
22
- end
23
-
24
19
  private
25
20
 
26
- def self.package_path
21
+ def package_path
27
22
  Pathname.new("Podfile")
28
23
  end
29
24
 
30
- def self.lockfile_path
25
+ def lockfile_path
31
26
  Pathname.new("Podfile.lock")
32
27
  end
33
28
 
29
+ def acknowledgements_path
30
+ filename = 'Pods-acknowledgements.plist'
31
+ directories = [
32
+ 'Pods', # cocoapods < 0.34
33
+ 'Pods/Target Support Files/Pods' # cocoapods >= 0.34
34
+ ]
35
+
36
+ directories.map { |dir| Pathname.new(File.join(dir, filename)) }.find(&:exist?)
37
+ end
38
+
39
+ def read_plist pathname
40
+ JSON.parse(`plutil -convert json -o - '#{pathname.expand_path}'`)
41
+ end
34
42
  end
35
43
  end
@@ -3,7 +3,8 @@ module LicenseFinder
3
3
  attr_reader :name, :version
4
4
  attr_reader :summary, :description, :homepage
5
5
 
6
- def initialize(name, version, license_text)
6
+ def initialize(name, version, license_text, options={})
7
+ super options
7
8
  @name = name
8
9
  @version = version
9
10
  @license_text = license_text
@@ -12,8 +13,8 @@ module LicenseFinder
12
13
  def groups; []; end
13
14
  def children; []; end
14
15
 
15
- def license
16
- License.find_by_text(@license_text.to_s) || default_license
16
+ def licenses
17
+ [License.find_by_text(@license_text.to_s) || default_license].to_set
17
18
  end
18
19
  end
19
20
  end
@@ -1,8 +1,8 @@
1
1
  require "xmlsimple"
2
2
 
3
3
  module LicenseFinder
4
- class Gradle
5
- def self.current_packages
4
+ class Gradle < PackageManager
5
+ def current_packages
6
6
  `#{LicenseFinder.config.gradle_command} downloadLicenses`
7
7
 
8
8
  xml = license_report.read
@@ -10,23 +10,19 @@ module LicenseFinder
10
10
  options = {
11
11
  'GroupTags' => { 'dependencies' => 'dependency' }
12
12
  }
13
- XmlSimple.xml_in(xml, options).fetch('dependency', []).map do |d|
14
- d["license"].reject! { |l| l["name"] == "No license found" }
15
- GradlePackage.new(d)
13
+ XmlSimple.xml_in(xml, options).fetch('dependency', []).map do |dep|
14
+ dep["license"].reject! { |l| l["name"] == "No license found" }
15
+ GradlePackage.new(dep, logger: logger)
16
16
  end
17
17
  end
18
18
 
19
- def self.active?
20
- package_path.exist?
21
- end
22
-
23
19
  private
24
20
 
25
- def self.license_report
21
+ def license_report
26
22
  Pathname.new('build/reports/license/dependency-license.xml')
27
23
  end
28
24
 
29
- def self.package_path
25
+ def package_path
30
26
  Pathname.new('build.gradle')
31
27
  end
32
28
  end
@@ -2,7 +2,8 @@ module LicenseFinder
2
2
  class GradlePackage < Package
3
3
  attr_reader :name, :version
4
4
 
5
- def initialize(gradle_dependency)
5
+ def initialize(gradle_dependency, options={})
6
+ super options
6
7
  @gradle_dependency = gradle_dependency
7
8
  @name = @gradle_dependency["name"].split(":")[1]
8
9
  @version = @gradle_dependency["name"].split(":")[2]
@@ -28,12 +29,6 @@ module LicenseFinder
28
29
  []
29
30
  end
30
31
 
31
- private
32
-
33
- def licenses_from_files
34
- []
35
- end
36
-
37
32
  def license_names_from_spec
38
33
  @gradle_dependency["license"].map { |l| l["name"] }
39
34
  end
@@ -1,8 +1,8 @@
1
1
  require "xmlsimple"
2
2
 
3
3
  module LicenseFinder
4
- class Maven
5
- def self.current_packages
4
+ class Maven < PackageManager
5
+ def current_packages
6
6
  `mvn license:download-licenses`
7
7
 
8
8
  xml = license_report.read
@@ -14,21 +14,17 @@ module LicenseFinder
14
14
  dependencies = XmlSimple.xml_in(xml, options)["dependencies"]
15
15
 
16
16
  dependencies.map do |dep|
17
- MavenPackage.new(dep)
17
+ MavenPackage.new(dep, logger: logger)
18
18
  end
19
19
  end
20
20
 
21
- def self.active?
22
- package_path.exist?
23
- end
24
-
25
21
  private
26
22
 
27
- def self.license_report
23
+ def license_report
28
24
  Pathname.new('target/generated-resources/licenses.xml')
29
25
  end
30
26
 
31
- def self.package_path
27
+ def package_path
32
28
  Pathname.new('pom.xml')
33
29
  end
34
30
  end
@@ -1,6 +1,9 @@
1
1
  module LicenseFinder
2
2
  class MavenPackage < Package
3
- def initialize(mvn_dependency)
3
+ attr_reader :mvn_dependency
4
+
5
+ def initialize(mvn_dependency, options={})
6
+ super options
4
7
  @mvn_dependency = mvn_dependency
5
8
  end
6
9
 
@@ -32,13 +35,6 @@ module LicenseFinder
32
35
  []
33
36
  end
34
37
 
35
- private
36
- attr_reader :mvn_dependency
37
-
38
- def licenses_from_files
39
- []
40
- end
41
-
42
38
  def license_names_from_spec
43
39
  mvn_dependency["licenses"].map { |l| l["name"] }
44
40
  end
@@ -1,25 +1,21 @@
1
1
  require 'json'
2
2
 
3
3
  module LicenseFinder
4
- class NPM
4
+ class NPM < PackageManager
5
5
  DEPENDENCY_GROUPS = ["dependencies", "devDependencies", "bundleDependencies", "bundledDependencies"]
6
6
 
7
- def self.current_packages
7
+ def current_packages
8
8
  json = npm_json
9
9
  dependencies = DEPENDENCY_GROUPS.map { |g| (json[g] || {}).values }.flatten(1).reject{ |d| d.is_a?(String) }
10
10
 
11
11
  dependencies.map do |node_module|
12
- NpmPackage.new(node_module)
12
+ NpmPackage.new(node_module, logger: logger)
13
13
  end
14
14
  end
15
15
 
16
- def self.active?
17
- package_path.exist?
18
- end
19
-
20
16
  private
21
17
 
22
- def self.npm_json
18
+ def npm_json
23
19
  command = "npm list --json --long"
24
20
  output, success = capture(command)
25
21
  if success
@@ -35,11 +31,11 @@ module LicenseFinder
35
31
  json
36
32
  end
37
33
 
38
- def self.capture(command)
34
+ def capture(command)
39
35
  [`#{command}`, $?.success?]
40
36
  end
41
37
 
42
- def self.package_path
38
+ def package_path
43
39
  Pathname.new('package.json')
44
40
  end
45
41
  end
@@ -1,6 +1,7 @@
1
1
  module LicenseFinder
2
2
  class NpmPackage < Package
3
- def initialize(node_module)
3
+ def initialize(node_module, options={})
4
+ super options
4
5
  @node_module = node_module
5
6
  end
6
7
 
@@ -2,40 +2,27 @@ require 'json'
2
2
  require 'httparty'
3
3
 
4
4
  module LicenseFinder
5
- class Pip
6
- GET_DEPENDENCIES_PY = <<-PYTHON.gsub(/\n+/, ";")
7
- from pip.util import get_installed_distributions
8
-
9
- dists = [(x.project_name, x.version, x.location) for x in get_installed_distributions()]
10
- dists = ["[\\\"{0}\\\", \\\"{1}\\\", \\\"{2}\\\"]".format(*dist) for dist in dists]
11
-
12
- print "[" + ",".join(dists) + "]"
13
- PYTHON
14
-
15
- def self.current_packages
16
- output = `python -c '#{GET_DEPENDENCIES_PY}'`
17
-
18
- JSON(output).map do |(name, version, install_dir)|
5
+ class Pip < PackageManager
6
+ def current_packages
7
+ output = `#{LicenseFinder::BIN_PATH.join("license_finder_pip.py")}`
8
+ JSON(output).map do |package|
19
9
  PipPackage.new(
20
- name,
21
- version,
22
- File.join(install_dir, name),
23
- pypi_def(name, version)
10
+ package["name"],
11
+ package["version"],
12
+ File.join(package["location"], package["name"]),
13
+ pypi_def(package["name"], package["version"]),
14
+ logger: logger
24
15
  )
25
16
  end
26
17
  end
27
18
 
28
- def self.active?
29
- requirements_path.exist?
30
- end
31
-
32
19
  private
33
20
 
34
- def self.requirements_path
21
+ def package_path
35
22
  Pathname.new('requirements.txt')
36
23
  end
37
24
 
38
- def self.pypi_def(name, version)
25
+ def pypi_def(name, version)
39
26
  response = HTTParty.get("https://pypi.python.org/pypi/#{name}/#{version}/json")
40
27
  if response.code == 200
41
28
  JSON.parse(response.body).fetch("info", {})
@@ -1,6 +1,7 @@
1
1
  module LicenseFinder
2
2
  class PipPackage < Package
3
- def initialize(name, version, install_path, pypi_def)
3
+ def initialize(name, version, install_path, pypi_def, options={})
4
+ super options
4
5
  @name = name
5
6
  @version = version
6
7
  @install_path = install_path
@@ -3,7 +3,7 @@ require 'forwardable'
3
3
  module LicenseFinder
4
4
  class PackageSaver
5
5
  extend Forwardable
6
- def_delegators :package, :license, :children, :groups, :summary, :description, :version, :homepage
6
+ def_delegators :package, :licenses, :children, :groups, :summary, :description, :version, :homepage
7
7
 
8
8
  attr_reader :dependency, :package
9
9
 
@@ -25,7 +25,7 @@ module LicenseFinder
25
25
  dependency.homepage = homepage
26
26
  dependency.bundler_group_names = groups.map(&:to_s)
27
27
  dependency.children_names = children
28
- dependency.apply_better_license license
28
+ dependency.set_licenses licenses
29
29
 
30
30
  # Only save *changed* dependencies. This ensures re-running
31
31
  # `license_finder` won't always update the DB, and therefore won't always
@@ -15,6 +15,10 @@ module LicenseFinder
15
15
  def self.java?
16
16
  RUBY_PLATFORM =~ /java/
17
17
  end
18
+
19
+ def self.darwin?
20
+ RUBY_PLATFORM =~ /darwin/
21
+ end
18
22
  end
19
23
  end
20
24
 
@@ -15,6 +15,10 @@ module LicenseFinder
15
15
  @text ||= @file_path.send(@file_path.respond_to?(:binread) ? :binread : :read)
16
16
  end
17
17
 
18
+ def path
19
+ File.join(@install_path, file_path)
20
+ end
21
+
18
22
  def license
19
23
  License.find_by_text(text)
20
24
  end
@@ -8,7 +8,7 @@ module LicenseFinder
8
8
  end
9
9
 
10
10
  def initialize(install_path)
11
- @install_path = Pathname(install_path)
11
+ @install_path = install_path ? Pathname(install_path) : nil
12
12
  end
13
13
 
14
14
  def find
@@ -28,6 +28,7 @@ module LicenseFinder
28
28
  end
29
29
 
30
30
  def candidate_files_and_dirs
31
+ return [] if install_path.nil?
31
32
  Pathname.glob(install_path.join('**', CANDIDATE_PATH_WILDCARD))
32
33
  end
33
34
 
@@ -8,7 +8,7 @@ module LicenseFinder
8
8
  csv << [
9
9
  s.name,
10
10
  s.version,
11
- s.license.name,
11
+ s.licenses.map(&:name).join(','),
12
12
  s.summary ? s.summary.strip : "",
13
13
  s.description ? s.description.strip : ""
14
14
  ]
@@ -7,7 +7,7 @@ module LicenseFinder
7
7
 
8
8
  def grouped_dependencies
9
9
  find_name = lambda do |dep|
10
- dep.license.name
10
+ dep.licenses.map(&:name).sort.join ', '
11
11
  end
12
12
 
13
13
  dependencies.group_by(&find_name).sort_by { |_, group| group.size }.reverse
@@ -1,10 +1,19 @@
1
+ require 'json'
2
+
1
3
  module LicenseFinder
2
4
  class Dependency < Sequel::Model
3
5
  plugin :boolean_readers
4
6
  plugin :composition
5
- composition :license,
6
- composer: ->(d) { License.find_by_name(d.license_name) },
7
- decomposer: ->(d) { self.license_name = license.name }
7
+ composition :licenses,
8
+ composer: ->(d) do
9
+ if d.license_names.nil?
10
+ [License.find_by_name(nil)].to_set
11
+ else
12
+ names = JSON.parse(d.license_names)
13
+ names.map { |n| License.find_by_name(n) }.to_set
14
+ end
15
+ end,
16
+ decomposer: ->(d) { self.license_names = licenses.map(&:name).to_json }
8
17
 
9
18
  one_to_one :manual_approval
10
19
  many_to_many :children, join_table: :ancestries, left_key: :parent_dependency_id, right_key: :child_dependency_id, class: self
@@ -58,26 +67,27 @@ module LicenseFinder
58
67
  end
59
68
 
60
69
  def whitelisted?
61
- license.whitelisted?
70
+ licenses.any? &:whitelisted?
62
71
  end
63
72
 
64
73
  def approved_manually?
65
74
  !!manual_approval
66
75
  end
67
76
 
77
+ def set_licenses(other_licenses)
78
+ return if license_assigned_manually?
79
+ other_licenses = other_licenses.to_set
80
+ if licenses != other_licenses
81
+ self.licenses = other_licenses
82
+ end
83
+ end
84
+
68
85
  def set_license_manually!(license)
69
- self.license = license
86
+ self.licenses = [license].to_set
70
87
  self.license_assigned_manually = true
71
88
  save
72
89
  end
73
90
 
74
- def apply_better_license(other_license)
75
- return if license_assigned_manually?
76
- if license.name != other_license.name
77
- self.license = other_license
78
- end
79
- end
80
-
81
91
  private
82
92
 
83
93
  def update_association_collection(association_name, names)