license_finder 0.9.4 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -7
  3. data/.travis.yml +1 -3
  4. data/CHANGELOG.rdoc +13 -0
  5. data/db/migrate/201307251004_data_fix_manual_licenses.rb +2 -2
  6. data/db/migrate/201307251107_reassociate_license.rb +18 -18
  7. data/db/migrate/201311192002_add_manually_approved_to_dependencies.rb +7 -0
  8. data/db/migrate/201311192003_reassociate_manual_approval.rb +14 -0
  9. data/db/migrate/201311192010_drop_approvals.rb +5 -0
  10. data/features/cli.feature +1 -1
  11. data/features/html_report.feature +1 -1
  12. data/features/{non_bundler_dependencies.feature → manually_managed_dependencies.feature} +6 -6
  13. data/features/step_definitions/html_report_steps.rb +2 -9
  14. data/features/step_definitions/{non_bundler_steps.rb → manually_managed_steps.rb} +0 -0
  15. data/features/step_definitions/shared_steps.rb +4 -8
  16. data/lib/license_finder.rb +21 -17
  17. data/lib/license_finder/bower.rb +3 -34
  18. data/lib/license_finder/bower_package.rb +63 -0
  19. data/lib/license_finder/bundler.rb +73 -0
  20. data/lib/license_finder/bundler_package.rb +33 -0
  21. data/lib/license_finder/cli.rb +33 -35
  22. data/lib/license_finder/dependency_manager.rb +14 -23
  23. data/lib/license_finder/license/apache2.rb +1 -1
  24. data/lib/license_finder/license/lgpl.rb +1 -0
  25. data/lib/license_finder/npm.rb +22 -39
  26. data/lib/license_finder/npm_package.rb +61 -0
  27. data/lib/license_finder/package.rb +14 -80
  28. data/lib/license_finder/package_saver.rb +13 -75
  29. data/lib/license_finder/pip.rb +21 -33
  30. data/lib/license_finder/pip_package.rb +51 -0
  31. data/lib/license_finder/platform.rb +3 -15
  32. data/lib/license_finder/possible_license_file.rb +0 -4
  33. data/lib/license_finder/possible_license_files.rb +4 -0
  34. data/lib/license_finder/tables.rb +2 -2
  35. data/lib/license_finder/tables/bundler_group.rb +3 -0
  36. data/lib/license_finder/tables/dependency.rb +43 -18
  37. data/lib/license_finder/tables/license_alias.rb +4 -0
  38. data/lib/license_finder/yml_to_sql.rb +22 -30
  39. data/license_finder.gemspec +3 -3
  40. data/readme.md +5 -5
  41. data/spec/lib/license_finder/bower_package_spec.rb +56 -0
  42. data/spec/lib/license_finder/bower_spec.rb +3 -24
  43. data/spec/lib/license_finder/bundler_package_spec.rb +62 -0
  44. data/spec/lib/license_finder/{bundle_spec.rb → bundler_spec.rb} +7 -7
  45. data/spec/lib/license_finder/cli_spec.rb +6 -6
  46. data/spec/lib/license_finder/dependency_manager_spec.rb +14 -15
  47. data/spec/lib/license_finder/html_report_spec.rb +2 -3
  48. data/spec/lib/license_finder/markdown_report_spec.rb +4 -4
  49. data/spec/lib/license_finder/npm_package_spec.rb +51 -0
  50. data/spec/lib/license_finder/npm_spec.rb +25 -25
  51. data/spec/lib/license_finder/package_saver_spec.rb +50 -190
  52. data/spec/lib/license_finder/pip_package_spec.rb +74 -0
  53. data/spec/lib/license_finder/pip_spec.rb +33 -55
  54. data/spec/lib/license_finder/tables/dependency_spec.rb +83 -32
  55. data/spec/lib/license_finder/yml_to_sql_spec.rb +5 -12
  56. data/spec/spec_helper.rb +22 -2
  57. metadata +30 -18
  58. data/lib/license_finder/bundle.rb +0 -74
  59. data/lib/license_finder/tables/approval.rb +0 -4
  60. data/spec/lib/license_finder/package_spec.rb +0 -98
@@ -0,0 +1,33 @@
1
+ module LicenseFinder
2
+ class BundlerPackage < Package
3
+ extend Forwardable
4
+ def_delegators :gem_def, :summary, :description, :name, :homepage
5
+
6
+ attr_reader :gem_def
7
+ attr_accessor :children
8
+
9
+ def initialize(gem_def, bundler_def)
10
+ @gem_def = gem_def
11
+ @bundler_def = bundler_def
12
+ @children = []
13
+ end
14
+
15
+ def groups
16
+ Array(@bundler_def && @bundler_def.groups)
17
+ end
18
+
19
+ def version
20
+ gem_def.version.to_s
21
+ end
22
+
23
+ private
24
+
25
+ def install_path
26
+ gem_def.full_gem_path
27
+ end
28
+
29
+ def license_from_spec
30
+ gem_def.license
31
+ end
32
+ end
33
+ end
@@ -40,10 +40,10 @@ module LicenseFinder
40
40
 
41
41
  class Dependencies < Subcommand
42
42
  method_option :approve, type: :boolean, desc: "Approve the added dependency"
43
- desc "add LICENSE DEPENDENCY_NAME [VERSION] [--approve]", "Add a dependency that is not managed by Bundler"
43
+ desc "Add LICENSE DEPENDENCY_NAME [VERSION] [--approve]", "Add a dependency that is not managed by Bundler, NPM, etc"
44
44
  def add(license, name, version = nil)
45
45
  die_on_error {
46
- DependencyManager.create_non_bundler(license, name, version)
46
+ DependencyManager.create_manually_managed(license, name, version)
47
47
  DependencyManager.approve!(name) if options[:approve]
48
48
  }
49
49
  if options[:approve]
@@ -53,17 +53,30 @@ module LicenseFinder
53
53
  end
54
54
  end
55
55
 
56
- desc "remove DEPENDENCY_NAME", "Remove a dependency that is not managed by Bundler"
56
+ desc "Remove DEPENDENCY_NAME", "Remove a dependency that is not managed by Bundler, NPM, etc"
57
57
  def remove(name)
58
58
  die_on_error {
59
- DependencyManager.destroy_non_bundler(name)
59
+ DependencyManager.destroy_manually_managed(name)
60
60
  }
61
61
 
62
62
  say "The #{name} dependency has been removed.", :green
63
63
  end
64
64
  end
65
65
 
66
- class Whitelist < Subcommand
66
+ class ConfigSubcommand < Subcommand
67
+ private
68
+
69
+ def modifying
70
+ die_on_error {
71
+ yield
72
+
73
+ LicenseFinder.config.save
74
+ Reporter.write_reports
75
+ }
76
+ end
77
+ end
78
+
79
+ class Whitelist < ConfigSubcommand
67
80
  desc "list", "List all the whitelisted licenses"
68
81
  def list
69
82
  whitelist = LicenseFinder.config.whitelist
@@ -76,45 +89,36 @@ module LicenseFinder
76
89
 
77
90
  desc "add LICENSE", "Add one ore more licenses to the whitelist"
78
91
  def add(*licenses)
79
- die_on_error {
92
+ modifying {
80
93
  licenses.each do |license|
81
94
  LicenseFinder.config.whitelist.push(license)
82
95
  end
83
- LicenseFinder.config.save
84
-
85
- Reporter.write_reports
86
96
  }
87
97
  say "Added #{licenses.join(", ")} to the license whitelist"
88
98
  end
89
99
 
90
100
  desc "remove LICENSE", "Remove one ore more licenses from the whitelist"
91
101
  def remove(*licenses)
92
- die_on_error {
102
+ modifying {
93
103
  licenses.each do |license|
94
104
  LicenseFinder.config.whitelist.delete(license)
95
105
  end
96
- LicenseFinder.config.save
97
-
98
- Reporter.write_reports
99
106
  }
100
107
  say "Removed #{licenses.join(", ")} from the license whitelist"
101
108
  end
102
109
  end
103
110
 
104
- class ProjectName < Subcommand
111
+ class ProjectName < ConfigSubcommand
105
112
  desc "set NAME", "Set the project name"
106
113
  def set(name)
107
- die_on_error {
114
+ modifying {
108
115
  LicenseFinder.config.project_name = name
109
- LicenseFinder.config.save
110
-
111
- Reporter.write_reports
112
116
  }
113
117
  say "Set the project name to #{name}", :green
114
118
  end
115
119
  end
116
120
 
117
- class IgnoredBundlerGroups < Subcommand
121
+ class IgnoredBundlerGroups < ConfigSubcommand
118
122
  desc "list", "List all the ignored bundler groups"
119
123
  def list
120
124
  ignored = LicenseFinder.config.ignore_groups
@@ -127,22 +131,16 @@ module LicenseFinder
127
131
 
128
132
  desc "add GROUP", "Add a bundler group to be ignored"
129
133
  def add(group)
130
- die_on_error {
134
+ modifying {
131
135
  LicenseFinder.config.ignore_groups.push(group)
132
- LicenseFinder.config.save
133
-
134
- Reporter.write_reports
135
136
  }
136
137
  say "Added #{group} to the ignored bundler groups"
137
138
  end
138
139
 
139
140
  desc "remove GROUP", "Remove a bundler group from the ignored bundler groups"
140
141
  def remove(group)
141
- die_on_error {
142
+ modifying {
142
143
  LicenseFinder.config.ignore_groups.delete(group)
143
- LicenseFinder.config.save
144
-
145
- Reporter.write_reports
146
144
  }
147
145
  say "Removed #{group} from the ignored bundler groups"
148
146
  end
@@ -154,7 +152,7 @@ module LicenseFinder
154
152
  def rescan
155
153
  die_on_error {
156
154
  spinner {
157
- DependencyManager.sync_with_bundler
155
+ DependencyManager.sync_with_package_managers
158
156
  }
159
157
  }
160
158
 
@@ -162,7 +160,7 @@ module LicenseFinder
162
160
  end
163
161
  default_task :rescan
164
162
 
165
- desc "approve DEPENDENCY_NAME", "Approve one ore more dependencies by name."
163
+ desc "approve DEPENDENCY_NAME", "Approve one ore more dependencies by name"
166
164
  def approve(*names)
167
165
  die_on_error {
168
166
  names.each { |name| DependencyManager.approve!(name) }
@@ -171,7 +169,7 @@ module LicenseFinder
171
169
  say "The #{names.join(", ")} dependency has been approved!", :green
172
170
  end
173
171
 
174
- desc "license LICENSE DEPENDENCY_NAME", "Update a dependency's license."
172
+ desc "license LICENSE DEPENDENCY_NAME", "Update a dependency's license"
175
173
  def license(license, name)
176
174
  die_on_error {
177
175
  DependencyManager.license!(name, license)
@@ -180,7 +178,7 @@ module LicenseFinder
180
178
  say "The #{name} dependency has been marked as using #{license} license!", :green
181
179
  end
182
180
 
183
- desc "move", "Move dependency.* files from root directory to doc/."
181
+ desc "move", "Move dependency.* files from root directory to doc/"
184
182
  def move
185
183
  Configuration.move!
186
184
  say "Congratulations, you have cleaned up your root directory!'", :green
@@ -199,10 +197,10 @@ module LicenseFinder
199
197
  end
200
198
  end
201
199
 
202
- subcommand "dependencies", Dependencies, "manage non-Bundler dependencies"
203
- subcommand "ignored_bundler_groups", IgnoredBundlerGroups, "manage ignored bundler groups"
204
- subcommand "whitelist", Whitelist, "manage whitelisted licenses"
205
- subcommand "project_name", ProjectName, "manage the project name"
200
+ subcommand "dependencies", Dependencies, "Manually manage dependencies outside of Bundler, NPM, pip, etc"
201
+ subcommand "ignored_bundler_groups", IgnoredBundlerGroups, "Manage ignored bundler groups"
202
+ subcommand "whitelist", Whitelist, "Manage whitelisted licenses"
203
+ subcommand "project_name", ProjectName, "Manage the project name"
206
204
 
207
205
  private
208
206
 
@@ -2,43 +2,26 @@ require 'digest'
2
2
 
3
3
  module LicenseFinder
4
4
  module DependencyManager
5
- def self.sync_with_bundler
5
+ def self.sync_with_package_managers
6
6
  modifying {
7
- current_dependencies = []
7
+ current_dependencies = PackageSaver.save_all(current_packages)
8
8
 
9
- if Bundle.has_gemfile?
10
- current_dependencies += PackageSaver.save_packages(Bundle.current_gems(LicenseFinder.config))
11
- end
12
-
13
- if Pip.has_requirements?
14
- current_dependencies += PackageSaver.save_packages(Pip.current_dists())
15
- end
16
-
17
- if NPM.has_package?
18
- current_dependencies += PackageSaver.save_packages(NPM.current_modules())
19
- end
20
-
21
- if Bower.has_package_file?
22
- current_dependencies += PackageSaver.save_packages(Bower.current_packages())
23
- end
24
-
25
- Dependency.bundler.obsolete(current_dependencies).each(&:destroy)
9
+ Dependency.managed.obsolete(current_dependencies).each(&:destroy)
26
10
  }
27
11
  end
28
12
 
29
- def self.create_non_bundler(license, name, version)
13
+ def self.create_manually_managed(license, name, version)
30
14
  raise Error.new("#{name} dependency already exists") unless Dependency.where(name: name).empty?
31
15
 
32
16
  modifying {
33
17
  dependency = Dependency.new(manual: true, name: name, version: version)
34
18
  dependency.license = LicenseAlias.create(name: license)
35
- dependency.approval = Approval.create
36
19
  dependency.save
37
20
  }
38
21
  end
39
22
 
40
- def self.destroy_non_bundler(name)
41
- modifying { find_by_name(name, Dependency.non_bundler).destroy }
23
+ def self.destroy_manually_managed(name)
24
+ modifying { find_by_name(name, Dependency.manually_managed).destroy }
42
25
  end
43
26
 
44
27
  def self.license!(name, license)
@@ -68,6 +51,14 @@ module LicenseFinder
68
51
 
69
52
  private # not really private, but it looks like it is!
70
53
 
54
+ def self.current_packages
55
+ package_managers.select(&:active?).map(&:current_packages).flatten
56
+ end
57
+
58
+ def self.package_managers
59
+ [Bundler, NPM, Pip, Bower]
60
+ end
61
+
71
62
  def self.find_by_name(name, scope = Dependency)
72
63
  dep = scope.first(name: name)
73
64
  raise Error.new("could not find dependency named #{name}") unless dep
@@ -1,5 +1,5 @@
1
1
  class LicenseFinder::License::Apache2 < LicenseFinder::License::Base
2
- self.alternative_names = ["Apache 2.0", "Apache2", "Apache-2.0", "Apache Software License", "Apache License 2.0", "Apache License Version 2.0"]
2
+ self.alternative_names = ["Apache 2.0", "Apache2", "Apache-2.0", "Apache Software License", "Apache License 2.0", "Apache License Version 2.0", "Apache Public License 2.0"]
3
3
  self.license_url = "http://www.apache.org/licenses/LICENSE-2.0.txt"
4
4
 
5
5
  def self.pretty_name
@@ -1,3 +1,4 @@
1
1
  class LicenseFinder::License::LGPL < LicenseFinder::License::Base
2
+ self.alternative_names = ["LGPL-3", "LGPLv3", "LGPL-3.0"]
2
3
  self.license_url = "http://www.gnu.org/licenses/lgpl.txt"
3
4
  end
@@ -1,39 +1,40 @@
1
1
  require 'json'
2
- require 'license_finder/package'
3
2
 
4
3
  module LicenseFinder
5
4
  class NPM
6
-
7
5
  DEPENDENCY_GROUPS = ["dependencies", "devDependencies", "bundleDependencies", "bundledDependencies"]
8
6
 
9
- def self.current_modules
10
- return @modules if @modules
7
+ def self.current_packages
8
+ json = npm_json
9
+ dependencies = DEPENDENCY_GROUPS.map { |g| (json[g] || {}).values }.flatten(1).reject{ |d| d.is_a?(String) }
11
10
 
12
- command = "npm list --json --long"
13
- output, success = capture(command)
14
- raise "Command #{command} failed to execute: #{output}" unless success
15
-
16
- json = JSON(output)
17
- dependencies = DEPENDENCY_GROUPS.map { |g| (json[g] || {}).values }.flatten(1)
18
-
19
- @modules = dependencies.map do |node_module|
20
- Package.new(OpenStruct.new(
21
- :name => node_module.fetch("name", nil),
22
- :version => node_module.fetch("version", nil),
23
- :full_gem_path => node_module.fetch("path", nil),
24
- :license => self.harvest_license(node_module),
25
- :summary => node_module.fetch("description", nil),
26
- :description => node_module.fetch("readme", nil)
27
- ))
11
+ dependencies.map do |node_module|
12
+ NpmPackage.new(node_module)
28
13
  end
29
14
  end
30
15
 
31
- def self.has_package?
16
+ def self.active?
32
17
  File.exists?(package_path)
33
18
  end
34
19
 
35
20
  private
36
21
 
22
+ def self.npm_json
23
+ command = "npm list --json --long"
24
+ output, success = capture(command)
25
+ if success
26
+ json = JSON(output)
27
+ else
28
+ json = JSON(output) rescue nil
29
+ if json
30
+ $stderr.puts "Command #{command} returned error but parsing succeeded." unless ENV['test_run']
31
+ else
32
+ raise "Command #{command} failed to execute: #{output}"
33
+ end
34
+ end
35
+ json
36
+ end
37
+
37
38
  def self.capture(command)
38
39
  [`#{command}`, $?.success?]
39
40
  end
@@ -41,23 +42,5 @@ module LicenseFinder
41
42
  def self.package_path
42
43
  Pathname.new('package.json').expand_path
43
44
  end
44
-
45
- def self.harvest_license(node_module)
46
- license = node_module.fetch("licenses", []).first
47
-
48
- if license.is_a? Hash
49
- license = license.fetch("type", nil)
50
- end
51
-
52
- if license.nil?
53
- license = node_module.fetch("license", nil)
54
-
55
- if license.is_a? Hash
56
- license = license.fetch("type", nil)
57
- end
58
- end
59
-
60
- license
61
- end
62
45
  end
63
46
  end
@@ -0,0 +1,61 @@
1
+ module LicenseFinder
2
+ class NpmPackage < Package
3
+ def initialize(node_module)
4
+ @node_module = node_module
5
+ end
6
+
7
+ def name
8
+ node_module["name"]
9
+ end
10
+
11
+ def version
12
+ node_module["version"]
13
+ end
14
+
15
+ def summary
16
+ node_module["description"]
17
+ end
18
+
19
+ def description
20
+ node_module["readme"]
21
+ end
22
+
23
+ def children
24
+ [] # no way to determine child deps from npm (maybe?)
25
+ end
26
+
27
+ def groups
28
+ [] # no concept of dev/test groups in npm (maybe?)
29
+ end
30
+
31
+ def homepage
32
+ nil # no way to extract homepage from npm (maybe?)
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :node_module
38
+
39
+ def install_path
40
+ node_module["path"]
41
+ end
42
+
43
+ def license_from_spec
44
+ license = node_module.fetch("licenses", []).first
45
+
46
+ if license
47
+ license = license.fetch("type", nil)
48
+ end
49
+
50
+ if license.nil?
51
+ license = node_module.fetch("license", nil)
52
+
53
+ if license.is_a? Hash
54
+ license = license.fetch("type", nil)
55
+ end
56
+ end
57
+
58
+ license
59
+ end
60
+ end
61
+ end
@@ -1,97 +1,31 @@
1
1
  module LicenseFinder
2
+ # Super-class that adapts data from different package management
3
+ # systems (gems, npm, pip, etc.) to a common interface.
4
+ #
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
2
8
  class Package
3
- attr_reader :parents, :spec, :bundler_dependency, :children
4
-
5
- def initialize(spec, bundler_dependency = nil)
6
- @spec = spec
7
- @bundler_dependency = bundler_dependency
8
- @children = []
9
- end
10
-
11
- def name
12
- "#{dependency_name} #{dependency_version}"
13
- end
14
-
15
- def parents
16
- @parents ||= []
17
- end
18
-
19
- def dependency_name
20
- @spec.name
21
- end
22
-
23
- def dependency_version
24
- @spec.version.to_s
25
- end
26
-
27
- def summary
28
- @spec.summary
29
- end
30
-
31
- def description
32
- @spec.description
33
- end
34
-
35
- def groups
36
- @groups ||= bundler_dependency ? bundler_dependency.groups : []
37
- end
38
-
39
9
  def license
40
10
  @license ||= determine_license
41
11
  end
42
12
 
43
- def sort_order
44
- dependency_name.downcase
45
- end
46
-
47
- def license_files
48
- PossibleLicenseFiles.new(@spec.full_gem_path).find
49
- end
50
-
51
- def children=(childs)
52
- @children = childs
53
- end
54
-
55
13
  private
56
14
 
57
15
  def determine_license
58
- return @spec.license if @spec.license
59
-
60
- license = license_files.map(&:license).compact.first
61
- license || "other"
16
+ license_from_spec || license_from_files || default_license
62
17
  end
63
- end
64
18
 
65
- class PythonPackage < Package
66
- def determine_license
67
- return @spec.license if @spec.license
68
-
69
- license = super
70
-
71
- if !license || license == "other"
72
- license = Pip.license_for self
73
- end
74
-
75
- license
76
- end
77
-
78
- def summary
79
- json.fetch("summary", "")
19
+ def license_from_files
20
+ license_files.map(&:license).compact.first
80
21
  end
81
22
 
82
- def description
83
- json.fetch("description", "")
23
+ def license_files
24
+ PossibleLicenseFiles.find(install_path)
84
25
  end
85
26
 
86
- def json
87
- return @json if @json
88
-
89
- response = HTTParty.get("https://pypi.python.org/pypi/#{dependency_name}/#{dependency_version}/json")
90
- if response.code == 200
91
- @json = JSON.parse(response.body).fetch("info", {})
92
- end
93
-
94
- @json ||= {}
27
+ def default_license
28
+ "other"
95
29
  end
96
30
  end
97
- end
31
+ end