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
@@ -102,9 +102,13 @@ module LicenseFinder
102
102
  end
103
103
 
104
104
  def last_refreshed
105
- [database_file, text_file, detailed_text_file, html_file, markdown_file].map do |path|
106
- File.mtime(path)
107
- end.min
105
+ [
106
+ database_file,
107
+ text_file,
108
+ detailed_text_file,
109
+ html_file,
110
+ markdown_file
111
+ ].map(&:mtime).min
108
112
  end
109
113
  end
110
114
 
@@ -126,7 +130,7 @@ module LicenseFinder
126
130
  end
127
131
 
128
132
  def last_modified
129
- File.mtime(file)
133
+ file.mtime
130
134
  end
131
135
 
132
136
  private
@@ -1,8 +1,14 @@
1
1
  require 'digest'
2
2
 
3
3
  module LicenseFinder
4
- module DependencyManager
5
- def self.sync_with_package_managers
4
+ class DependencyManager
5
+ attr_reader :logger
6
+
7
+ def initialize options={}
8
+ @logger = options[:logger] || LicenseFinder::Logger::Default.new
9
+ end
10
+
11
+ def sync_with_package_managers options={}
6
12
  modifying {
7
13
  current_dependencies = PackageSaver.save_all(current_packages)
8
14
 
@@ -10,30 +16,30 @@ module LicenseFinder
10
16
  }
11
17
  end
12
18
 
13
- def self.manually_add(license, name, version)
19
+ def manually_add(license, name, version)
14
20
  raise Error.new("#{name} dependency already exists") unless Dependency.where(name: name).empty?
15
21
 
16
22
  modifying {
17
23
  dependency = Dependency.new(added_manually: true, name: name, version: version)
18
- dependency.license = License.find_by_name(license)
24
+ dependency.licenses = [License.find_by_name(license)].to_set
19
25
  dependency.save
20
26
  }
21
27
  end
22
28
 
23
- def self.manually_remove(name)
29
+ def manually_remove(name)
24
30
  modifying { find_by_name(name, Dependency.added_manually).destroy }
25
31
  end
26
32
 
27
- def self.license!(name, license_name)
33
+ def license!(name, license_name)
28
34
  license = License.find_by_name(license_name)
29
35
  modifying { find_by_name(name).set_license_manually!(license) }
30
36
  end
31
37
 
32
- def self.approve!(name, approver = nil, notes = nil)
38
+ def approve!(name, approver = nil, notes = nil)
33
39
  modifying { find_by_name(name).approve!(approver, notes) }
34
40
  end
35
41
 
36
- def self.modifying
42
+ def modifying
37
43
  checksum_before = checksum
38
44
  result = DB.transaction { yield }
39
45
  checksum_after = checksum
@@ -49,29 +55,33 @@ module LicenseFinder
49
55
 
50
56
  private # not really private, but it looks like it is!
51
57
 
52
- def self.reports_do_not_exist
58
+ def reports_do_not_exist
53
59
  !(LicenseFinder.config.artifacts.html_file.exist?)
54
60
  end
55
61
 
56
- def self.reports_are_stale
62
+ def reports_are_stale
57
63
  LicenseFinder.config.last_modified > LicenseFinder.config.artifacts.last_refreshed
58
64
  end
59
65
 
60
- def self.current_packages
61
- package_managers.select(&:active?).map(&:current_packages).flatten
66
+ def current_packages
67
+ package_managers.
68
+ map { |pm| pm.new(logger: logger) }.
69
+ select(&:active?).
70
+ map(&:current_packages).
71
+ flatten
62
72
  end
63
73
 
64
- def self.package_managers
74
+ def package_managers
65
75
  [Bundler, NPM, Pip, Bower, Maven, Gradle, CocoaPods]
66
76
  end
67
77
 
68
- def self.find_by_name(name, scope = Dependency)
78
+ def find_by_name(name, scope = Dependency)
69
79
  dep = scope.first(name: name)
70
80
  raise Error.new("could not find dependency named #{name}") unless dep
71
81
  dep
72
82
  end
73
83
 
74
- def self.checksum
84
+ def checksum
75
85
  database_file = LicenseFinder.config.artifacts.database_file
76
86
  if database_file.exist?
77
87
  Digest::SHA2.file(database_file).hexdigest
@@ -54,6 +54,14 @@ module LicenseFinder
54
54
  copy(whitelisted: true)
55
55
  end
56
56
 
57
+ def eql?(other)
58
+ name == other.name
59
+ end
60
+
61
+ def hash
62
+ name.hash
63
+ end
64
+
57
65
  private
58
66
 
59
67
  attr_reader :short_name, :pretty_name, :other_names
@@ -0,0 +1,59 @@
1
+ module LicenseFinder
2
+ module Logger
3
+ def self.new options={}
4
+ klass = if options[:quiet]
5
+ Quiet
6
+ elsif options[:debug]
7
+ Verbose
8
+ else
9
+ Progress
10
+ end
11
+ klass.new
12
+ end
13
+
14
+ class Base
15
+ def active package_manager, is_active
16
+ log package_manager, sprintf("%s active", (is_active ? "is" : "not"))
17
+ end
18
+
19
+ def package package_manager, package
20
+ dependencies = package.children
21
+ if dependencies.empty?
22
+ log package_manager, sprintf("package '%s' has no dependencies", package.name)
23
+ else
24
+ log package_manager, sprintf("package '%s' has dependencies:", package.name)
25
+ dependencies.each do |dep|
26
+ log package_manager, sprintf("- %s", dep)
27
+ end
28
+ end
29
+ end
30
+
31
+ def license package, package_name, license, how
32
+ log package, sprintf("package %s: found license '%s' %s", package_name, license, how)
33
+ end
34
+
35
+ def log prefix, string
36
+ raise NotImplementedError, "#log must be implemented"
37
+ end
38
+ end
39
+
40
+ class Quiet < Base
41
+ def log prefix, string
42
+ end
43
+ end
44
+
45
+ class Progress < Base
46
+ def log prefix, string
47
+ STDOUT.print(".") && STDOUT.flush
48
+ end
49
+ end
50
+
51
+ class Verbose < Base
52
+ def log prefix, string
53
+ STDOUT.printf("%s: %s\n", prefix, string)
54
+ end
55
+ end
56
+
57
+ Default = Quiet
58
+ end
59
+ end
@@ -3,9 +3,20 @@ module LicenseFinder
3
3
  # systems (gems, npm, pip, etc.) to a common interface.
4
4
  #
5
5
  # For guidance on adding a new system use the shared behavior
6
- # it_behaves_like "it conforms to interface required by PackageSaver"
7
- # and see BundlerPackage, PipPackage and NpmPackage
6
+ #
7
+ # it_behaves_like "a Package"
8
+ #
9
+ # Additional guidelines are:
10
+ #
11
+ # - if you're going to use Package#licenses ...
12
+ # - implement #licenses_names_from_spec
13
+ # - implement #install_path
14
+ # - else
15
+ # - implement #licenses
16
+ #
8
17
  class Package
18
+ attr_reader :logger
19
+
9
20
  def self.license_names_from_standard_spec(spec)
10
21
  licenses = spec["licenses"] || [spec["license"]].compact
11
22
  licenses = [licenses] unless licenses.is_a?(Array)
@@ -18,54 +29,50 @@ module LicenseFinder
18
29
  end
19
30
  end
20
31
 
21
- def license
22
- @license ||= determine_license
32
+ def initialize options={}
33
+ @logger = options[:logger] || LicenseFinder::Logger::Default.new
23
34
  end
24
35
 
25
- private
36
+ def licenses
37
+ @licenses ||= determine_license.to_set
38
+ end
26
39
 
27
40
  def determine_license
28
- if licenses_from_spec.any?
29
- choose_license_from licenses_from_spec
30
- elsif licenses_from_files.any?
31
- choose_license_from licenses_from_files
32
- else
33
- default_license
34
- end
35
- end
41
+ lfs = licenses_from_spec
42
+ return lfs if lfs.any?
36
43
 
37
- def choose_license_from licenses
38
- if ( licenses.uniq.size > 1 )
39
- License.find_by_name "multiple licenses: #{(licenses).map(&:name).uniq.join(', ')}"
40
- else
41
- licenses.first
42
- end
44
+ lff = licenses_from_files
45
+ return lff if lff.any?
46
+
47
+ [default_license].to_set
43
48
  end
44
49
 
45
50
  def licenses_from_spec
46
51
  license_names_from_spec.map do |name|
47
- License.find_by_name(name)
48
- end
52
+ License.find_by_name(name).tap do |license|
53
+ logger.license self.class, self.name, license.name, "from spec" if license
54
+ end
55
+ end.compact.to_set
49
56
  end
50
57
 
51
58
  def licenses_from_files
52
- license_files.map(&:license).compact
59
+ license_files.map do |license_file|
60
+ license_file.license.tap do |license|
61
+ logger.license self.class, self.name, license.name, "from file '#{license_file.path}'" if license
62
+ end
63
+ end.compact.to_set
53
64
  end
54
65
 
55
66
  def license_files
56
67
  PossibleLicenseFiles.find(install_path)
57
68
  end
58
69
 
59
- def multiple_licenses
60
- if ( licenses_from_spec.uniq.size > 1 )
61
- License.find_by_name "multiple licenses: #{(licenses_from_spec).map(&:name).uniq.join(', ')}"
62
- else
63
- License.find_by_name "multiple licenses: #{(licenses_from_files).map(&:name).uniq.join(', ')}"
64
- end
65
- end
66
-
67
70
  def default_license
68
71
  License.find_by_name nil
69
72
  end
73
+
74
+ def install_path
75
+ nil
76
+ end
70
77
  end
71
78
  end
@@ -0,0 +1,20 @@
1
+ module LicenseFinder
2
+ class PackageManager
3
+ attr_reader :logger
4
+
5
+ def initialize options={}
6
+ @logger = options[:logger] || LicenseFinder::Logger::Default.new
7
+ @package_path = options[:package_path] # dependency injection for tests
8
+ end
9
+
10
+ def active?
11
+ injected_package_path.exist?.tap { |is_active| logger.active self.class, is_active }
12
+ end
13
+
14
+ private
15
+
16
+ def injected_package_path
17
+ @package_path || package_path
18
+ end
19
+ end
20
+ end
@@ -1,25 +1,20 @@
1
1
  require 'json'
2
2
 
3
3
  module LicenseFinder
4
- class Bower
5
-
6
- def self.current_packages
4
+ class Bower < PackageManager
5
+ def current_packages
7
6
  output = `bower list --json`
8
7
 
9
8
  json = JSON(output)
10
9
 
11
10
  json.fetch("dependencies",[]).map do |package|
12
- BowerPackage.new(package[1])
11
+ BowerPackage.new(package[1], logger: logger)
13
12
  end
14
13
  end
15
14
 
16
- def self.active?
17
- package_path.exist?
18
- end
19
-
20
15
  private
21
16
 
22
- def self.package_path
17
+ def package_path
23
18
  Pathname.new('bower.json')
24
19
  end
25
20
  end
@@ -1,6 +1,7 @@
1
1
  module LicenseFinder
2
2
  class BowerPackage < Package
3
- def initialize(bower_module)
3
+ def initialize(bower_module, options={})
4
+ super options
4
5
  @bower_module = bower_module
5
6
  @module_metadata = bower_module.fetch("pkgMeta", Hash.new)
6
7
  end
@@ -1,48 +1,41 @@
1
1
  require "bundler"
2
2
 
3
3
  module LicenseFinder
4
- class Bundler
5
- class << self
6
- def current_packages(ignore_groups = LicenseFinder.config.ignore_groups, bundler_definition=nil)
7
- new(ignore_groups, bundler_definition).packages
8
- end
9
-
10
- def active?
11
- gemfile_path.exist?
12
- end
13
-
14
- def gemfile_path
15
- Pathname.new("Gemfile").expand_path
16
- end
4
+ class Bundler < PackageManager
5
+ def initialize options={}
6
+ super
7
+ @ignore_groups = options[:ignore_groups] # dependency injection for tests
8
+ @definition = options[:definition] # dependency injection for tests
17
9
  end
18
10
 
19
- def initialize(ignore_groups, bundler_definition=nil)
20
- @definition = bundler_definition || ::Bundler::Definition.build(self.class.gemfile_path, lockfile_path, nil)
21
- @ignore_groups = ignore_groups
22
- end
23
-
24
- def packages
25
- top_level_gems = Set.new
26
-
27
- packages = definition.specs_for(included_groups).map do |gem_def|
11
+ def current_packages
12
+ logger.log self.class, "including groups #{included_groups.inspect}"
13
+ definition.specs_for(included_groups).map do |gem_def|
28
14
  bundler_def = bundler_defs.detect { |bundler_def| bundler_def.name == gem_def.name }
29
-
30
- top_level_gems << format_name(gem_def)
31
-
32
- BundlerPackage.new(gem_def, bundler_def)
15
+ BundlerPackage.new(gem_def, bundler_def, logger: logger).tap do |package|
16
+ logger.package self.class, package
17
+ end
33
18
  end
19
+ end
34
20
 
35
- packages.each do |gem|
36
- gem.children = children_for(gem, top_level_gems)
37
- end
21
+ private
38
22
 
39
- packages
23
+ def definition
24
+ # DI
25
+ @definition ||= ::Bundler::Definition.build(package_path, lockfile_path, nil)
40
26
  end
41
27
 
42
- private
43
- attr_reader :definition, :ignore_groups
28
+ def ignore_groups
29
+ # DI
30
+ @ignore_groups ||= LicenseFinder.config.ignore_groups
31
+ end
32
+
33
+ def package_path
34
+ Pathname.new("Gemfile")
35
+ end
44
36
 
45
37
  def bundler_defs
38
+ # memoized
46
39
  @bundler_defs ||= definition.dependencies
47
40
  end
48
41
 
@@ -51,15 +44,7 @@ module LicenseFinder
51
44
  end
52
45
 
53
46
  def lockfile_path
54
- self.class.gemfile_path.dirname.join('Gemfile.lock')
55
- end
56
-
57
- def children_for(gem, top_level_gems)
58
- gem.gem_def.dependencies.map(&:name).select { |name| top_level_gems.include? name }
59
- end
60
-
61
- def format_name(gem)
62
- gem.name.split(" ")[0]
47
+ package_path.dirname.join('Gemfile.lock')
63
48
  end
64
49
  end
65
50
  end