license_finder 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +22 -0
- data/README.markdown +84 -0
- data/Rakefile +12 -0
- data/bin/license_finder +7 -0
- data/files/license_finder.yml +7 -0
- data/lib/license_finder.rb +15 -0
- data/lib/license_finder/dependency.rb +63 -0
- data/lib/license_finder/dependency_list.rb +55 -0
- data/lib/license_finder/file_parser.rb +32 -0
- data/lib/license_finder/finder.rb +48 -0
- data/lib/license_finder/gem_spec_details.rb +101 -0
- data/lib/license_finder/license_file.rb +77 -0
- data/lib/license_finder/railtie.rb +9 -0
- data/lib/license_finder/version.rb +3 -0
- data/lib/tasks/license_finder.rake +17 -0
- data/lib/templates/Apache-2.0-body +172 -0
- data/lib/templates/GPL-2.0-body +339 -0
- data/lib/templates/MIT-body +9 -0
- data/license_finder.gemspec +23 -0
- data/spec/dependency_list_spec.rb +202 -0
- data/spec/dependency_spec.rb +57 -0
- data/spec/file_parser_spec.rb +16 -0
- data/spec/finder_spec.rb +44 -0
- data/spec/fixtures/APACHE-2-LICENSE +202 -0
- data/spec/fixtures/GPLv2 +339 -0
- data/spec/fixtures/MIT-LICENSE +22 -0
- data/spec/fixtures/MIT-LICENSE-with-varied-disclaimer +22 -0
- data/spec/fixtures/README-with-MIT-LICENSE +222 -0
- data/spec/fixtures/apache_licensed_gem/LICENSE +191 -0
- data/spec/fixtures/gplv2_licensed_gem/LICENSE +339 -0
- data/spec/fixtures/license_directory/COPYING +0 -0
- data/spec/fixtures/license_directory/LICENSE/BSD-2-Clause.txt +25 -0
- data/spec/fixtures/license_directory/LICENSE/GPL-2.0.txt +339 -0
- data/spec/fixtures/license_directory/LICENSE/LICENSE +191 -0
- data/spec/fixtures/license_directory/LICENSE/MIT.txt +21 -0
- data/spec/fixtures/license_directory/LICENSE/RUBY.txt +60 -0
- data/spec/fixtures/license_names/COPYING.txt +0 -0
- data/spec/fixtures/license_names/LICENSE +0 -0
- data/spec/fixtures/license_names/Mit-License +0 -0
- data/spec/fixtures/license_names/README.rdoc +0 -0
- data/spec/fixtures/mit_licensed_gem/LICENSE +22 -0
- data/spec/fixtures/mit_licensed_gem_in_README/README.rdoc +222 -0
- data/spec/fixtures/mit_licensed_gem_via_url/README +210 -0
- data/spec/fixtures/nested_gem/vendor/LICENSE +0 -0
- data/spec/fixtures/nested_readme/vendor/README +0 -0
- data/spec/fixtures/no_license/.gitkeep +0 -0
- data/spec/fixtures/other_licensed_gem/LICENSE +3 -0
- data/spec/fixtures/readme/Project ReadMe b/data/spec/fixtures/readme/Project → ReadMe +0 -0
- data/spec/fixtures/readme/README +0 -0
- data/spec/fixtures/readme/Readme.markdown +0 -0
- data/spec/fixtures/utf8_gem/README +210 -0
- data/spec/gem_spec_details_spec.rb +167 -0
- data/spec/license_file_spec.rb +129 -0
- data/spec/spec_helper.rb +10 -0
- metadata +159 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2010 Jacob Maine
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
22
|
+
|
data/README.markdown
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
Goal
|
2
|
+
====
|
3
|
+
|
4
|
+
With bundler it's easy for your project to depend on many gems. This decomposition is nice, but managing licenses becomes difficult. This tool gathers info about the licenses of the gems in your project.
|
5
|
+
|
6
|
+
Usage
|
7
|
+
=====
|
8
|
+
|
9
|
+
For a Rails project add license_finder to your Gemfile:
|
10
|
+
gem 'license_finder', :git => "https://github.com/pivotal/LicenseFinder.git"
|
11
|
+
|
12
|
+
Run 'rake license:init'
|
13
|
+
This will create a config/license_finder.yml file that lets you configure license finder.
|
14
|
+
|
15
|
+
Run 'rake license:generate_dependencies'
|
16
|
+
This will write out a dependencies.yml and dependencies.txt file in the root of your project.
|
17
|
+
It will also merge in an existing dependencies.yml file, if one exists.
|
18
|
+
|
19
|
+
Run 'rake license:action_items'
|
20
|
+
This will output a list of unapproved dependencies to the console
|
21
|
+
|
22
|
+
As a standalone script:
|
23
|
+
cd ~
|
24
|
+
git clone http://github.com/pivotal/LicenseFinder.git license_finder
|
25
|
+
cd your/project
|
26
|
+
~/license_finder/bin/license_finder
|
27
|
+
|
28
|
+
Optionally add `--with-licenses` to include the full text of the licenses in the output.
|
29
|
+
|
30
|
+
Sample Output
|
31
|
+
=============
|
32
|
+
|
33
|
+
dependencies.yml:
|
34
|
+
|
35
|
+
---
|
36
|
+
- name: "json_pure"
|
37
|
+
version: "1.5.1"
|
38
|
+
license: "other"
|
39
|
+
approved: false
|
40
|
+
|
41
|
+
- name: "rake"
|
42
|
+
version: "0.8.7"
|
43
|
+
license: "MIT"
|
44
|
+
approved: true
|
45
|
+
|
46
|
+
dependencies.txt:
|
47
|
+
|
48
|
+
json_pure 1.5.1, other
|
49
|
+
rake 0.8.7, MIT
|
50
|
+
|
51
|
+
bin/license_finder:
|
52
|
+
|
53
|
+
---
|
54
|
+
json_pure 1.5.1:
|
55
|
+
dependency_name: json_pure
|
56
|
+
dependency_version: 1.5.1
|
57
|
+
install_path: /some/path/.rvm/gems/ruby-1.9.2-p180/gems/json_pure-1.5.1
|
58
|
+
license_files:
|
59
|
+
- file_name: COPYING
|
60
|
+
header_type: other
|
61
|
+
body_type: other
|
62
|
+
disclaimer_of_liability: other
|
63
|
+
- file_name: COPYING-json-jruby
|
64
|
+
header_type: other
|
65
|
+
body_type: other
|
66
|
+
disclaimer_of_liability: other
|
67
|
+
readme_files:
|
68
|
+
- file_name: README
|
69
|
+
mentions_license: true
|
70
|
+
- file_name: README-json-jruby.markdown
|
71
|
+
mentions_license: false
|
72
|
+
---
|
73
|
+
rake 0.8.7:
|
74
|
+
dependency_name: rake
|
75
|
+
dependency_version: 0.8.7
|
76
|
+
install_path: /some/path/.rvm/gems/ruby-1.9.2-p180/gems/rake-0.8.7
|
77
|
+
license_files:
|
78
|
+
- file_name: MIT-LICENSE
|
79
|
+
header_type: other
|
80
|
+
body_type: mit
|
81
|
+
disclaimer_of_liability: "mit: THE AUTHORS OR COPYRIGHT HOLDERS"
|
82
|
+
readme_files:
|
83
|
+
- file_name: README
|
84
|
+
mentions_license: true
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
desc "Run all specs in spec/"
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
8
|
+
t.fail_on_error = true
|
9
|
+
t.pattern = "./spec/**/*_spec.rb"
|
10
|
+
t.rspec_opts = %w[--color]
|
11
|
+
end
|
12
|
+
|
data/bin/license_finder
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module LicenseFinder
|
5
|
+
ROOT_PATH = Pathname.new(__FILE__).dirname
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'license_finder/railtie' if defined?(Rails)
|
9
|
+
require 'license_finder/finder'
|
10
|
+
require 'license_finder/gem_spec_details'
|
11
|
+
require 'license_finder/file_parser'
|
12
|
+
require 'license_finder/license_file'
|
13
|
+
|
14
|
+
require 'license_finder/dependency'
|
15
|
+
require 'license_finder/dependency_list'
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module LicenseFinder
|
2
|
+
class Dependency
|
3
|
+
|
4
|
+
attr_reader :name, :version, :license, :approved, :license_url, :notes, :license_files, :readme_files
|
5
|
+
|
6
|
+
def self.from_hash(attrs)
|
7
|
+
lfs = attrs['license_files'] ? attrs['license_files'].map { |lf| lf['path'] } : []
|
8
|
+
rfs = attrs['readme_files'] ? attrs['readme_files'].map { |rf| rf['path'] } : []
|
9
|
+
new(attrs['name'], attrs['version'], attrs['license'], attrs['approved'], attrs['license_url'], attrs['notes'], lfs, rfs)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(name, version, license, approved, license_url = '', notes = '', license_files = [], readme_files = [])
|
13
|
+
@name = name
|
14
|
+
@version = version
|
15
|
+
@license = license
|
16
|
+
@approved = approved
|
17
|
+
@license_url = license_url
|
18
|
+
@notes = notes
|
19
|
+
@license_files = license_files
|
20
|
+
@readme_files = readme_files
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_yaml_entry
|
24
|
+
attrs = "- name: \"#{name}\"\n version: \"#{version}\"\n license: \"#{license}\"\n approved: #{approved}\n license_url: \"#{license_url}\"\n notes: \"#{notes}\"\n"
|
25
|
+
attrs << " license_files:\n"
|
26
|
+
if !self.license_files.empty?
|
27
|
+
self.license_files.each do |lf|
|
28
|
+
attrs << " - path: \"#{lf}\"\n"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
attrs << " readme_files:\n"
|
32
|
+
if !self.readme_files.empty?
|
33
|
+
self.readme_files.each do |rf|
|
34
|
+
attrs << " - path: \"#{rf}\"\n"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
attrs
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
url = ", #{license_url}" if license_url != ''
|
42
|
+
str = "#{name} #{version}, #{license}#{url}"
|
43
|
+
if license == 'other'
|
44
|
+
str << "\n license files:"
|
45
|
+
unless self.license_files.empty?
|
46
|
+
self.license_files.each do |lf|
|
47
|
+
str << "\n #{lf}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
str << "\n readme files:"
|
51
|
+
unless self.readme_files.empty?
|
52
|
+
self.readme_files.each do |lf|
|
53
|
+
str << "\n #{lf}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
str
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module LicenseFinder
|
2
|
+
class DependencyList
|
3
|
+
|
4
|
+
attr_reader :dependencies
|
5
|
+
|
6
|
+
def self.from_bundler(whitelist = [], ignore_groups = [])
|
7
|
+
gemfile = Pathname.new("Gemfile").expand_path
|
8
|
+
root = gemfile.dirname
|
9
|
+
lockfile = root.join('Gemfile.lock')
|
10
|
+
definition = Bundler::Definition.build(gemfile, lockfile, nil)
|
11
|
+
|
12
|
+
groups = definition.groups - ignore_groups
|
13
|
+
|
14
|
+
new(definition.specs_for(groups).map { |spec| GemSpecDetails.new(spec, whitelist).dependency })
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(dependencies)
|
18
|
+
@dependencies = dependencies
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.from_yaml(yml)
|
22
|
+
deps = YAML.load(yml)
|
23
|
+
new(deps.map { |dhash| Dependency.from_hash(dhash) })
|
24
|
+
end
|
25
|
+
|
26
|
+
def merge(new_list)
|
27
|
+
deps = new_list.dependencies.map do |new_dep|
|
28
|
+
old_dep = self.dependencies.detect { |d| d.name == new_dep.name }
|
29
|
+
if old_dep && old_dep.license == new_dep.license
|
30
|
+
Dependency.new(new_dep.name, new_dep.version, new_dep.license, old_dep.approved || new_dep.approved, old_dep.license_url, old_dep.notes, new_dep.license_files, new_dep.readme_files)
|
31
|
+
elsif old_dep && new_dep.license == 'other'
|
32
|
+
Dependency.new(new_dep.name, new_dep.version, old_dep.license, old_dep.approved, old_dep.license_url, old_dep.notes, new_dep.license_files, new_dep.readme_files)
|
33
|
+
else
|
34
|
+
new_dep
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
self.class.new(deps)
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_yaml
|
42
|
+
result = "--- \n"
|
43
|
+
dependencies.sort_by(&:name).inject(result) { |r, d| r << d.to_yaml_entry; r }
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
dependencies.sort_by(&:name).map(&:to_s).join("\n")
|
48
|
+
end
|
49
|
+
|
50
|
+
def action_items
|
51
|
+
dependencies.sort_by(&:name).reject(&:approved).map(&:to_s).join("\n")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module LicenseFinder
|
2
|
+
class FileParser
|
3
|
+
def initialize(install_path, file_path)
|
4
|
+
@install_path = Pathname.new(install_path)
|
5
|
+
@file_path = Pathname.new(file_path)
|
6
|
+
end
|
7
|
+
|
8
|
+
def file_path
|
9
|
+
@file_path.relative_path_from(@install_path).to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
def full_file_path
|
13
|
+
@file_path.realpath.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def file_name
|
17
|
+
@file_path.basename.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def text
|
21
|
+
@text ||= @file_path.send((@file_path.respond_to? :binread) ? :binread : :read)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def on_single_line(text)
|
27
|
+
text.gsub(/\s+/, ' ').gsub("'", "\"")
|
28
|
+
rescue
|
29
|
+
''
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module LicenseFinder
|
2
|
+
class Finder
|
3
|
+
|
4
|
+
attr_reader :whitelist, :ignore_groups
|
5
|
+
def initialize
|
6
|
+
if File.exists?('./config/license_finder.yml')
|
7
|
+
config = YAML.load(File.open('./config/license_finder.yml').readlines.join)
|
8
|
+
@whitelist = config['whitelist'] || []
|
9
|
+
@ignore_groups = config['ignore_groups'] ? config['ignore_groups'].map{|g| g.to_sym} : []
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def from_bundler
|
14
|
+
require 'bundler'
|
15
|
+
Bundler.load.specs.map { |spec| GemSpecDetails.new(spec) }.sort_by &:sort_order
|
16
|
+
end
|
17
|
+
|
18
|
+
def write_files
|
19
|
+
new_list = generate_list
|
20
|
+
|
21
|
+
File.open('./dependencies.yml', 'w+') do |f|
|
22
|
+
f.puts new_list.to_yaml
|
23
|
+
end
|
24
|
+
File.open('./dependencies.txt', 'w+') do |f|
|
25
|
+
f.puts new_list.to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def action_items
|
31
|
+
new_list = generate_list
|
32
|
+
new_list.action_items
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def generate_list
|
37
|
+
bundler_list = DependencyList.from_bundler(whitelist, ignore_groups)
|
38
|
+
|
39
|
+
if (File.exists?('./dependencies.yml'))
|
40
|
+
yml = File.open('./dependencies.yml').readlines.join
|
41
|
+
existing_list = DependencyList.from_yaml(yml)
|
42
|
+
existing_list.merge(bundler_list)
|
43
|
+
else
|
44
|
+
bundler_list
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module LicenseFinder
|
2
|
+
class GemSpecDetails
|
3
|
+
LICENSE_FILE_NAMES = '*{LICENSE,License,COPYING,README,Readme,ReadMe}*' # follows Dir.glob format
|
4
|
+
README_FILE_NAMES = '*{README,Readme,ReadMe}*' # follows Dir.glob format
|
5
|
+
|
6
|
+
def initialize(spec, whitelist = [])
|
7
|
+
@spec = spec
|
8
|
+
@whitelist = whitelist
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :spec
|
12
|
+
|
13
|
+
def name
|
14
|
+
"#{dependency_name} #{dependency_version}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def dependency_name
|
18
|
+
spec.name
|
19
|
+
end
|
20
|
+
|
21
|
+
def dependency_version
|
22
|
+
spec.version.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def dependency
|
26
|
+
license = determine_license
|
27
|
+
@dependency ||= Dependency.new(@spec.name, @spec.version, license, @whitelist.include?(license), '', '', license_files.map(&:full_file_path), readme_files.map(&:full_file_path))
|
28
|
+
end
|
29
|
+
|
30
|
+
def determine_license
|
31
|
+
return 'MIT' if license_files.any?{|f| f.mit_license_body? || f.mit_license_header?}
|
32
|
+
return 'Apache 2.0' if license_files.any?(&:apache_license_body?)
|
33
|
+
return 'GPLv2' if license_files.any?(&:gplv2_license_body?)
|
34
|
+
'other'
|
35
|
+
end
|
36
|
+
|
37
|
+
def license_files
|
38
|
+
paths_with_license_names = Dir.glob(File.join(install_path, '**', LICENSE_FILE_NAMES))
|
39
|
+
paths_for_license_files = paths_with_license_names.map do |path|
|
40
|
+
File.directory?(path) ? paths_for_files_in_license_directory(path) : path
|
41
|
+
end.flatten.uniq
|
42
|
+
get_files_for_paths(paths_for_license_files)
|
43
|
+
end
|
44
|
+
|
45
|
+
def readme_files
|
46
|
+
Dir.glob(File.join(install_path, '**', README_FILE_NAMES)).map do |path|
|
47
|
+
file = LicenseFile.new(install_path, path)
|
48
|
+
file.include_license_text = include_license_text?
|
49
|
+
file
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def install_path
|
54
|
+
spec.full_gem_path
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_s(include_license_text = true)
|
58
|
+
self.include_license_text = include_license_text
|
59
|
+
|
60
|
+
{ name => to_hash }.to_yaml
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_hash
|
64
|
+
{
|
65
|
+
'dependency_name' => dependency_name,
|
66
|
+
'dependency_version' => dependency_version,
|
67
|
+
'install_path' => install_path,
|
68
|
+
'license_files' => license_files.map { |file| file.to_hash }
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def sort_order
|
73
|
+
dependency_name.downcase
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
attr_writer :include_license_text
|
79
|
+
|
80
|
+
def include_license_text?
|
81
|
+
@include_license_text
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_file_for_path(path)
|
85
|
+
file = LicenseFile.new(install_path, path)
|
86
|
+
file.include_license_text = include_license_text?
|
87
|
+
file
|
88
|
+
end
|
89
|
+
|
90
|
+
def paths_for_files_in_license_directory(path)
|
91
|
+
entries_in_directory = Dir::entries(path).reject { |p| p.match(/^(\.){1,2}$/) }
|
92
|
+
entries_in_directory.map { |entry_name| File.join(path, entry_name) }
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_files_for_paths(paths_for_license_files)
|
96
|
+
paths_for_license_files.map do |path|
|
97
|
+
get_file_for_path(path)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|