license_finder 0.9.1 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.rdoc +11 -1
- data/features/node_dependencies.feature +9 -0
- data/features/python_dependencies.feature +9 -0
- data/features/step_definitions/html_report_steps.rb +3 -1
- data/features/step_definitions/node_steps.rb +8 -0
- data/features/step_definitions/python_steps.rb +8 -0
- data/features/step_definitions/shared_steps.rb +48 -0
- data/lib/data/licenses/Python.txt +47 -0
- data/lib/license_finder.rb +4 -2
- data/lib/license_finder/bundle.rb +15 -9
- data/lib/license_finder/dependency_manager.rb +14 -1
- data/lib/license_finder/license/apache2.rb +1 -1
- data/lib/license_finder/license/bsd.rb +1 -1
- data/lib/license_finder/license/mit.rb +1 -1
- data/lib/license_finder/license/python.rb +8 -0
- data/lib/license_finder/npm.rb +56 -0
- data/lib/license_finder/{bundled_gem.rb → package.rb} +45 -3
- data/lib/license_finder/{bundled_gem_saver.rb → package_saver.rb} +15 -13
- data/lib/license_finder/pip.rb +59 -0
- data/lib/license_finder/reports/html_report.rb +10 -1
- data/lib/templates/html_report.erb +14 -12
- data/license_finder.gemspec +3 -1
- data/readme.md +10 -4
- data/release/manual_instructions.md +7 -6
- data/spec/lib/license_finder/bundle_spec.rb +24 -0
- data/spec/lib/license_finder/dependency_manager_spec.rb +4 -4
- data/spec/lib/license_finder/license/python_spec.rb +7 -0
- data/spec/lib/license_finder/npm_spec.rb +79 -0
- data/spec/lib/license_finder/{bundled_gem_saver_spec.rb → package_saver_spec.rb} +17 -17
- data/spec/lib/license_finder/{bundled_gem_spec.rb → package_spec.rb} +20 -2
- data/spec/lib/license_finder/pip_spec.rb +89 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/license_examples.rb +1 -1
- metadata +55 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d836a5fd251af244f6b671af5f1f016426eb3837
|
4
|
+
data.tar.gz: 6677c0d9aa21ec99cf64c5e49f1f748883dc72c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2560fd0fe43bd76b35cb0277cea7b0bc60d30b6e4d4784525758a9cb2bbab80185b0e002c1d9518ca3a1bef8d7b1f778f86c95f8ccc23cff1c1c944a5f5af44f
|
7
|
+
data.tar.gz: fa9ba7eeddbe032146aadc626c28b339a2edb83949ff05c78e7762efe9e2f831980d1eef26bdf8c0e2e19c140c482e1e4b9d6a6059ad2d0c5c38ec37b3a33021
|
data/CHANGELOG.rdoc
CHANGED
@@ -0,0 +1,9 @@
|
|
1
|
+
Feature: Tracking Node Dependencies
|
2
|
+
So that I can track Node dependencies
|
3
|
+
As an application developer using license finder
|
4
|
+
I want to be able to manage Node dependencies
|
5
|
+
|
6
|
+
Scenario: See the dependencies from the package file
|
7
|
+
Given A package file with dependencies
|
8
|
+
When I run license_finder
|
9
|
+
Then I should see a Node dependency with a license
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Feature: Tracking Python Dependencies
|
2
|
+
So that I can track Python dependencies
|
3
|
+
As an application developer using license finder
|
4
|
+
I want to be able to manage Python dependencies
|
5
|
+
|
6
|
+
Scenario: See the dependencies from the requirements file
|
7
|
+
Given A requirements file with dependencies
|
8
|
+
When I run license_finder
|
9
|
+
Then I should see a Python dependency with a license
|
@@ -49,9 +49,11 @@ end
|
|
49
49
|
Then(/^I should see only see GPL liceneses as unapproved in the html$/) do
|
50
50
|
html = File.read(@user.dependencies_html_path)
|
51
51
|
page = Capybara.string(html)
|
52
|
-
page.should have_content '
|
52
|
+
page.should have_content '12 total'
|
53
53
|
page.should have_content '1 unapproved'
|
54
54
|
page.should have_content '1 GPL'
|
55
|
+
page.should have_content '1 other'
|
56
|
+
page.should have_content '9 MIT'
|
55
57
|
end
|
56
58
|
|
57
59
|
def is_html_status?(gem, approval)
|
@@ -30,6 +30,28 @@ end
|
|
30
30
|
|
31
31
|
module DSL
|
32
32
|
class User
|
33
|
+
def create_python_app
|
34
|
+
reset_projects!
|
35
|
+
|
36
|
+
`mkdir -p #{app_path}`
|
37
|
+
`cd #{app_path} && touch requirements.txt`
|
38
|
+
|
39
|
+
add_pip_dependency('argparse==1.2.1')
|
40
|
+
|
41
|
+
pip_install
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_node_app
|
45
|
+
reset_projects!
|
46
|
+
|
47
|
+
`mkdir -p #{app_path}`
|
48
|
+
`cd #{app_path} && touch package.json`
|
49
|
+
|
50
|
+
add_npm_dependency('jshint', '2.1.9')
|
51
|
+
|
52
|
+
npm_install
|
53
|
+
end
|
54
|
+
|
33
55
|
def create_nonrails_app
|
34
56
|
reset_projects!
|
35
57
|
|
@@ -160,12 +182,30 @@ module DSL
|
|
160
182
|
add_to_gemfile(line)
|
161
183
|
end
|
162
184
|
|
185
|
+
def add_pip_dependency(dependency)
|
186
|
+
add_to_requirements(dependency)
|
187
|
+
end
|
188
|
+
|
189
|
+
def add_npm_dependency(dependency, version)
|
190
|
+
line = "{\"dependencies\" : {\"#{dependency}\": \"#{version}\"}}"
|
191
|
+
|
192
|
+
add_to_package(line)
|
193
|
+
end
|
194
|
+
|
163
195
|
def bundle_app
|
164
196
|
Bundler.with_clean_env do
|
165
197
|
`bundle install --gemfile=#{File.join(app_path, "Gemfile")} --path=#{bundle_path}`
|
166
198
|
end
|
167
199
|
end
|
168
200
|
|
201
|
+
def pip_install
|
202
|
+
`cd #{app_path} && pip install -r requirements.txt`
|
203
|
+
end
|
204
|
+
|
205
|
+
def npm_install
|
206
|
+
`cd #{app_path} && npm install 2>/dev/null`
|
207
|
+
end
|
208
|
+
|
169
209
|
def modifying_dependencies_file
|
170
210
|
FileUtils.mkdir_p(File.dirname(dependencies_file_path))
|
171
211
|
File.open(dependencies_file_path, 'w+') { |f| yield f }
|
@@ -181,6 +221,14 @@ module DSL
|
|
181
221
|
`echo #{line.inspect} >> #{File.join(app_path, "Rakefile")}`
|
182
222
|
end
|
183
223
|
|
224
|
+
def add_to_requirements(line)
|
225
|
+
`echo #{line.inspect} >> #{File.join(app_path, "requirements.txt")}`
|
226
|
+
end
|
227
|
+
|
228
|
+
def add_to_package(line)
|
229
|
+
`echo #{line.inspect} >> #{File.join(app_path, "package.json")}`
|
230
|
+
end
|
231
|
+
|
184
232
|
def app_name
|
185
233
|
"my_app"
|
186
234
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
2
|
+
--------------------------------------------
|
3
|
+
|
4
|
+
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
5
|
+
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
6
|
+
otherwise using this software ("Python") in source or binary form and
|
7
|
+
its associated documentation.
|
8
|
+
|
9
|
+
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
10
|
+
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
11
|
+
analyze, test, perform and/or display publicly, prepare derivative works,
|
12
|
+
distribute, and otherwise use Python alone or in any derivative version,
|
13
|
+
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
14
|
+
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
15
|
+
2011, 2012, 2013 Python Software Foundation; All Rights Reserved" are retained
|
16
|
+
in Python alone or in any derivative version prepared by Licensee.
|
17
|
+
|
18
|
+
3. In the event Licensee prepares a derivative work that is based on
|
19
|
+
or incorporates Python or any part thereof, and wants to make
|
20
|
+
the derivative work available to others as provided herein, then
|
21
|
+
Licensee hereby agrees to include in any such work a brief summary of
|
22
|
+
the changes made to Python.
|
23
|
+
|
24
|
+
4. PSF is making Python available to Licensee on an "AS IS"
|
25
|
+
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
26
|
+
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
27
|
+
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
28
|
+
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
29
|
+
INFRINGE ANY THIRD PARTY RIGHTS.
|
30
|
+
|
31
|
+
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
32
|
+
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
33
|
+
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
34
|
+
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
35
|
+
|
36
|
+
6. This License Agreement will automatically terminate upon a material
|
37
|
+
breach of its terms and conditions.
|
38
|
+
|
39
|
+
7. Nothing in this License Agreement shall be deemed to create any
|
40
|
+
relationship of agency, partnership, or joint venture between PSF and
|
41
|
+
Licensee. This License Agreement does not grant permission to use PSF
|
42
|
+
trademarks or trade name in a trademark sense to endorse or promote
|
43
|
+
products or services of Licensee, or any third party.
|
44
|
+
|
45
|
+
8. By copying, installing or otherwise using Python, Licensee
|
46
|
+
agrees to be bound by the terms and conditions of this License
|
47
|
+
Agreement.
|
data/lib/license_finder.rb
CHANGED
@@ -8,13 +8,15 @@ module LicenseFinder
|
|
8
8
|
Error = Class.new(StandardError)
|
9
9
|
|
10
10
|
autoload :Bundle, 'license_finder/bundle'
|
11
|
-
autoload :
|
12
|
-
autoload :BundledGemSaver, 'license_finder/bundled_gem_saver'
|
11
|
+
autoload :PackageSaver, 'license_finder/package_saver'
|
13
12
|
autoload :CLI, 'license_finder/cli'
|
14
13
|
autoload :Configuration, 'license_finder/configuration'
|
15
14
|
autoload :DependencyManager, 'license_finder/dependency_manager'
|
16
15
|
autoload :License, 'license_finder/license'
|
17
16
|
autoload :LicenseUrl, 'license_finder/license_url'
|
17
|
+
autoload :NPM, 'license_finder/npm'
|
18
|
+
autoload :Pip, 'license_finder/pip'
|
19
|
+
autoload :Package, 'license_finder/package'
|
18
20
|
autoload :Platform, 'license_finder/platform'
|
19
21
|
autoload :PossibleLicenseFile, 'license_finder/possible_license_file'
|
20
22
|
autoload :PossibleLicenseFiles, 'license_finder/possible_license_files'
|
@@ -4,12 +4,22 @@ module LicenseFinder
|
|
4
4
|
class Bundle
|
5
5
|
attr_writer :ignore_groups
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
class << self
|
8
|
+
def current_gems(config, bundler_definition=nil)
|
9
|
+
new(config, bundler_definition).gems
|
10
|
+
end
|
11
|
+
|
12
|
+
def has_gemfile?
|
13
|
+
File.exists?(gemfile_path)
|
14
|
+
end
|
15
|
+
|
16
|
+
def gemfile_path
|
17
|
+
Pathname.new("Gemfile").expand_path
|
18
|
+
end
|
9
19
|
end
|
10
20
|
|
11
21
|
def initialize(config=nil, bundler_definition=nil)
|
12
|
-
@definition = bundler_definition || Bundler::Definition.build(gemfile_path, lockfile_path, nil)
|
22
|
+
@definition = bundler_definition || Bundler::Definition.build(self.class.gemfile_path, lockfile_path, nil)
|
13
23
|
@config ||= config
|
14
24
|
end
|
15
25
|
|
@@ -24,7 +34,7 @@ module LicenseFinder
|
|
24
34
|
formatted_name = format_name(spec)
|
25
35
|
gem_names_cache[format_name(spec)] = true
|
26
36
|
|
27
|
-
|
37
|
+
Package.new(spec, dependency)
|
28
38
|
end
|
29
39
|
|
30
40
|
@gems.each do |gem|
|
@@ -49,12 +59,8 @@ module LicenseFinder
|
|
49
59
|
definition.groups - ignore_groups.map(&:to_sym)
|
50
60
|
end
|
51
61
|
|
52
|
-
def gemfile_path
|
53
|
-
Pathname.new("Gemfile").expand_path
|
54
|
-
end
|
55
|
-
|
56
62
|
def lockfile_path
|
57
|
-
gemfile_path.dirname.join('Gemfile.lock')
|
63
|
+
self.class.gemfile_path.dirname.join('Gemfile.lock')
|
58
64
|
end
|
59
65
|
|
60
66
|
def children_for(gem, cache)
|
@@ -4,7 +4,20 @@ module LicenseFinder
|
|
4
4
|
module DependencyManager
|
5
5
|
def self.sync_with_bundler
|
6
6
|
modifying {
|
7
|
-
current_dependencies =
|
7
|
+
current_dependencies = []
|
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
|
+
|
8
21
|
Dependency.bundler.obsolete(current_dependencies).each(&:destroy)
|
9
22
|
}
|
10
23
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class LicenseFinder::License::Apache2 < LicenseFinder::License::Base
|
2
|
-
self.alternative_names = ["Apache 2.0", "Apache2", "Apache-2.0"]
|
2
|
+
self.alternative_names = ["Apache 2.0", "Apache2", "Apache-2.0", "Apache Software License"]
|
3
3
|
self.license_url = "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
4
4
|
|
5
5
|
def self.pretty_name
|
@@ -1,4 +1,4 @@
|
|
1
1
|
class LicenseFinder::License::BSD < LicenseFinder::License::Base
|
2
|
-
self.alternative_names = ["BSD4", "bsd-old", "4-clause BSD", "BSD-4-Clause"]
|
2
|
+
self.alternative_names = ["BSD4", "bsd-old", "4-clause BSD", "BSD-4-Clause", "BSD License"]
|
3
3
|
self.license_url = "http://en.wikipedia.org/wiki/BSD_licenses#4-clause_license_.28original_.22BSD_License.22.29"
|
4
4
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class LicenseFinder::License::MIT < LicenseFinder::License::Base
|
2
2
|
self.license_url = "http://opensource.org/licenses/mit-license"
|
3
|
-
self.alternative_names = ["Expat"]
|
3
|
+
self.alternative_names = ["Expat", "MIT license", "MIT License"]
|
4
4
|
|
5
5
|
HEADER_REGEX = /The MIT Licen[sc]e/
|
6
6
|
ONE_LINER_REGEX = /is released under the MIT licen[sc]e/
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class LicenseFinder::License::Python < LicenseFinder::License::Base
|
2
|
+
self.alternative_names = ["PSF", "Python Software Foundation License"]
|
3
|
+
self.license_url = "http://hg.python.org/cpython/raw-file/89ce323357db/LICENSE"
|
4
|
+
|
5
|
+
def self.pretty_name
|
6
|
+
'Python Software Foundation License'
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'license_finder/package'
|
3
|
+
|
4
|
+
module LicenseFinder
|
5
|
+
class NPM
|
6
|
+
|
7
|
+
def self.current_modules
|
8
|
+
return @modules if @modules
|
9
|
+
|
10
|
+
output = `npm list --json --long`
|
11
|
+
|
12
|
+
json = JSON(output)
|
13
|
+
|
14
|
+
@modules = json.fetch("dependencies",[]).map do |node_module|
|
15
|
+
node_module = node_module[1]
|
16
|
+
|
17
|
+
Package.new(OpenStruct.new(
|
18
|
+
:name => node_module.fetch("name", nil),
|
19
|
+
:version => node_module.fetch("version", nil),
|
20
|
+
:full_gem_path => node_module.fetch("path", nil),
|
21
|
+
:license => self.harvest_license(node_module),
|
22
|
+
:summary => node_module.fetch("description", nil),
|
23
|
+
:description => node_module.fetch("readme", nil)
|
24
|
+
))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.has_package?
|
29
|
+
File.exists?(package_path)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def self.package_path
|
35
|
+
Pathname.new('package.json').expand_path
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.harvest_license(node_module)
|
39
|
+
license = node_module.fetch("licenses", []).first
|
40
|
+
|
41
|
+
if license
|
42
|
+
license = license.fetch("type", nil)
|
43
|
+
end
|
44
|
+
|
45
|
+
if license.nil?
|
46
|
+
license = node_module.fetch("license", nil)
|
47
|
+
|
48
|
+
if license.is_a? Hash
|
49
|
+
license = license.fetch("type", nil)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
license
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module LicenseFinder
|
2
|
-
class
|
2
|
+
class Package
|
3
3
|
attr_reader :parents, :spec, :bundler_dependency, :children
|
4
4
|
|
5
5
|
def initialize(spec, bundler_dependency = nil)
|
@@ -24,6 +24,14 @@ module LicenseFinder
|
|
24
24
|
@spec.version.to_s
|
25
25
|
end
|
26
26
|
|
27
|
+
def summary
|
28
|
+
@spec.summary
|
29
|
+
end
|
30
|
+
|
31
|
+
def description
|
32
|
+
@spec.description
|
33
|
+
end
|
34
|
+
|
27
35
|
def groups
|
28
36
|
@groups ||= bundler_dependency ? bundler_dependency.groups : []
|
29
37
|
end
|
@@ -49,7 +57,41 @@ module LicenseFinder
|
|
49
57
|
def determine_license
|
50
58
|
return @spec.license if @spec.license
|
51
59
|
|
52
|
-
license_files.map(&:license).compact.first
|
60
|
+
license = license_files.map(&:license).compact.first
|
61
|
+
license || "other"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
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", "")
|
80
|
+
end
|
81
|
+
|
82
|
+
def description
|
83
|
+
json.fetch("description", "")
|
84
|
+
end
|
85
|
+
|
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 ||= {}
|
53
95
|
end
|
54
96
|
end
|
55
|
-
end
|
97
|
+
end
|