license_finder 1.0.0.1 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +21 -1
- data/features/configure_ignore_dependencies.feature +16 -0
- data/features/step_definitions/cli_steps.rb +1 -1
- data/features/step_definitions/configure_ignore_dependencies.rb +35 -0
- data/files/license_finder.yml +2 -0
- data/lib/license_finder/cli.rb +72 -30
- data/lib/license_finder/configuration.rb +17 -1
- data/lib/license_finder/dependency_manager.rb +11 -4
- data/lib/license_finder/package.rb +28 -18
- data/lib/license_finder/reports/reporter.rb +1 -1
- data/lib/license_finder/tables/dependency.rb +8 -1
- data/lib/templates/html_report.erb +2 -2
- data/license_finder.gemspec +2 -2
- data/readme.md +17 -13
- data/spec/lib/license_finder/cli_spec.rb +46 -4
- data/spec/lib/license_finder/configuration_spec.rb +52 -0
- data/spec/lib/license_finder/dependency_manager_spec.rb +25 -9
- data/spec/lib/license_finder/package_managers/bower_package_spec.rb +2 -2
- data/spec/lib/license_finder/package_managers/bundler_package_spec.rb +13 -9
- data/spec/lib/license_finder/package_managers/gradle_package_spec.rb +1 -1
- data/spec/lib/license_finder/package_managers/maven_package_spec.rb +1 -1
- data/spec/lib/license_finder/package_managers/npm_package_spec.rb +2 -2
- data/spec/lib/license_finder/package_managers/pip_package_spec.rb +1 -1
- data/spec/lib/license_finder/reports/html_report_spec.rb +6 -5
- data/spec/lib/license_finder/reports/reporter_spec.rb +1 -1
- data/spec/lib/license_finder/tables/dependency_spec.rb +14 -1
- data/spec/support/stdout_helpers.rb +25 -0
- metadata +82 -47
- checksums.yaml +0 -7
- data/spec/support/silence_stdout.rb +0 -13
data/readme.md
CHANGED
@@ -152,16 +152,24 @@ whitelist:
|
|
152
152
|
ignore_groups:
|
153
153
|
#- test
|
154
154
|
#- development
|
155
|
+
ignore_dependencies:
|
156
|
+
#- bundler
|
155
157
|
dependencies_file_dir: './doc/'
|
156
158
|
project_name: My Project Name
|
157
159
|
```
|
158
160
|
|
159
|
-
By modifying this file, you can configure license_finder's behavior.
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
161
|
+
By modifying this file, you can configure license_finder's behavior.
|
162
|
+
|
163
|
+
- Licenses in the `whitelist` will be automatically approved.
|
164
|
+
- You can exclude test or development dependencies by setting `ignore_groups`. (Currently this only
|
165
|
+
works for Bundler.)
|
166
|
+
- You can exclude specific dependencies by setting `ignore_dependencies`.
|
167
|
+
(Think carefully before adding dependencies to this list. A likely item to exclude is
|
168
|
+
bundler itself, to avoid noisy changes to the doc files when different people run
|
169
|
+
license_finder with different versions of bundler.)
|
170
|
+
- You can store the license database and text files in another directory by changing
|
171
|
+
`dependencies_file_dir`. And the `project_name`, which defaults to your working
|
172
|
+
directory, appears in the [HTML report](#html-report).
|
165
173
|
|
166
174
|
You can also configure license_finder through the command line. See
|
167
175
|
`license_finder whitelist help`, `license_finder ignored_bundler_groups help`
|
@@ -169,13 +177,9 @@ and `license_finder project_name help` for more details.
|
|
169
177
|
|
170
178
|
## HTML Report
|
171
179
|
|
172
|
-
The HTML report generated by license_finder
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
The individual dependency summary follows a pattern like this:
|
177
|
-
|
178
|
-
![HTML Report](files/dependency_breakdown.png)
|
180
|
+
The HTML report generated by license_finder shows a summary of the project's dependencies
|
181
|
+
and dependencies which need to be approved. The project name at the top of the report can
|
182
|
+
be set in `config/license_finder.yml`.
|
179
183
|
|
180
184
|
## Upgrade for pre 0.8.0 users
|
181
185
|
|
@@ -124,11 +124,9 @@ module LicenseFinder
|
|
124
124
|
|
125
125
|
describe "list" do
|
126
126
|
it "shows the ignored groups in the standard output" do
|
127
|
-
config.should_receive(:ignore_groups).and_return([])
|
127
|
+
config.should_receive(:ignore_groups).and_return(['development'])
|
128
128
|
|
129
|
-
|
130
|
-
subject.list
|
131
|
-
end
|
129
|
+
expect(capture_stdout { subject.list }).to match /development/
|
132
130
|
end
|
133
131
|
end
|
134
132
|
|
@@ -157,6 +155,50 @@ module LicenseFinder
|
|
157
155
|
end
|
158
156
|
end
|
159
157
|
|
158
|
+
describe IgnoredDependencies do
|
159
|
+
let(:config) { LicenseFinder.config }
|
160
|
+
|
161
|
+
describe "list" do
|
162
|
+
context "when there is at least one ignored dependency" do
|
163
|
+
it "shows the ignored dependencies" do
|
164
|
+
expect(config).to receive(:ignore_dependencies).and_return(['bundler'])
|
165
|
+
expect(capture_stdout { subject.list }).to match /bundler/
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context "when there are no ignored dependencies" do
|
170
|
+
it "prints '(none)'" do
|
171
|
+
expect(config).to receive(:ignore_dependencies).and_return([])
|
172
|
+
expect(capture_stdout { subject.list }).to match /\(none\)/
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "add" do
|
178
|
+
it "adds the specified group to the ignored groups list" do
|
179
|
+
config.ignore_dependencies.should_receive(:push).with("test")
|
180
|
+
config.should_receive(:save)
|
181
|
+
Reporter.should_receive(:write_reports)
|
182
|
+
|
183
|
+
silence_stdout do
|
184
|
+
subject.add("test")
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe "remove" do
|
190
|
+
it "removes the specified group from the ignored groups list" do
|
191
|
+
config.ignore_dependencies.should_receive(:delete).with("test")
|
192
|
+
config.should_receive(:save)
|
193
|
+
Reporter.should_receive(:write_reports)
|
194
|
+
|
195
|
+
silence_stdout do
|
196
|
+
subject.remove("test")
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
160
202
|
describe Main do
|
161
203
|
describe "default" do
|
162
204
|
it "checks for action items" do
|
@@ -11,11 +11,23 @@ module LicenseFinder
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
describe "#last_modified" do
|
15
|
+
let(:time) { double :time }
|
16
|
+
before do
|
17
|
+
allow(Configuration::Persistence).to receive(:last_modified) { time }
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'returns the last modified date of the config file' do
|
21
|
+
expect(LicenseFinder::Configuration.new({}).last_modified).to eq time
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
14
25
|
describe '.new' do
|
15
26
|
it "should default missing attributes" do
|
16
27
|
subject = described_class.new({})
|
17
28
|
subject.whitelist.should == []
|
18
29
|
subject.ignore_groups.should == []
|
30
|
+
subject.ignore_dependencies.should == []
|
19
31
|
subject.artifacts.dir.should == Pathname('./doc/')
|
20
32
|
end
|
21
33
|
|
@@ -23,12 +35,14 @@ module LicenseFinder
|
|
23
35
|
attributes = {
|
24
36
|
"whitelist" => nil,
|
25
37
|
"ignore_groups" => nil,
|
38
|
+
"ignore_dependencies" => nil,
|
26
39
|
"dependencies_file_dir" => nil,
|
27
40
|
"project_name" => nil
|
28
41
|
}
|
29
42
|
subject = described_class.new(attributes)
|
30
43
|
subject.whitelist.should == []
|
31
44
|
subject.ignore_groups.should == []
|
45
|
+
subject.ignore_dependencies.should == []
|
32
46
|
subject.artifacts.dir.should == Pathname('./doc/')
|
33
47
|
subject.project_name.should_not be_nil
|
34
48
|
end
|
@@ -37,12 +51,14 @@ module LicenseFinder
|
|
37
51
|
attributes = {
|
38
52
|
"whitelist" => %w{a whitelist},
|
39
53
|
"ignore_groups" => %w{test development},
|
54
|
+
"ignore_dependencies" => %w{bundler},
|
40
55
|
"dependencies_file_dir" => "some/path",
|
41
56
|
"project_name" => "my_app"
|
42
57
|
}
|
43
58
|
subject = described_class.new(attributes)
|
44
59
|
subject.whitelist.should == %w{a whitelist}
|
45
60
|
subject.ignore_groups.should == %w{test development}
|
61
|
+
subject.ignore_dependencies.should == %w{bundler}
|
46
62
|
subject.artifacts.dir.should == Pathname("some/path")
|
47
63
|
subject.project_name.should == "my_app"
|
48
64
|
end
|
@@ -77,6 +93,7 @@ module LicenseFinder
|
|
77
93
|
{
|
78
94
|
'whitelist' => ['my_gem'],
|
79
95
|
'ignore_groups' => ['other_group', 'test'],
|
96
|
+
'ignore_dependencies' => ['bundler'],
|
80
97
|
'project_name' => "New Project Name",
|
81
98
|
'dependencies_file_dir' => "./deps"
|
82
99
|
}
|
@@ -91,6 +108,7 @@ module LicenseFinder
|
|
91
108
|
config = described_class.new(attributes)
|
92
109
|
config.whitelist << 'my_gem'
|
93
110
|
config.ignore_groups << 'test'
|
111
|
+
config.ignore_dependencies << 'bundler'
|
94
112
|
|
95
113
|
Configuration::Persistence.should_receive(:set).with(attributes)
|
96
114
|
config.save
|
@@ -98,6 +116,28 @@ module LicenseFinder
|
|
98
116
|
end
|
99
117
|
end
|
100
118
|
|
119
|
+
describe Configuration::Artifacts do
|
120
|
+
describe "#last_refreshed" do
|
121
|
+
let(:database_modified_time) { 1 }
|
122
|
+
let(:text_modified_time) { 2 }
|
123
|
+
let(:detailed_text_modified_time) { 3 }
|
124
|
+
let(:html_modified_time) { 4 }
|
125
|
+
let(:markdown_modified_time) { 5 }
|
126
|
+
|
127
|
+
before do
|
128
|
+
allow(File).to receive(:mtime).with(Pathname('./doc/dependencies.db')) { database_modified_time }
|
129
|
+
allow(File).to receive(:mtime).with(Pathname('./doc/dependencies.csv')) { text_modified_time }
|
130
|
+
allow(File).to receive(:mtime).with(Pathname('./doc/dependencies_detailed.csv')) { detailed_text_modified_time }
|
131
|
+
allow(File).to receive(:mtime).with(Pathname('./doc/dependencies.html')) { html_modified_time }
|
132
|
+
allow(File).to receive(:mtime).with(Pathname('./doc/dependencies.md')) { markdown_modified_time }
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'returns the earliest modified date of the config file' do
|
136
|
+
expect(described_class.new(Pathname('./doc')).last_refreshed).to eq database_modified_time
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
101
141
|
describe Configuration::Persistence do
|
102
142
|
describe ".get" do
|
103
143
|
it "should use saved configuration" do
|
@@ -150,5 +190,17 @@ module LicenseFinder
|
|
150
190
|
described_class.init
|
151
191
|
end
|
152
192
|
end
|
193
|
+
|
194
|
+
describe ".last_modified" do
|
195
|
+
let(:time) { double :time }
|
196
|
+
let(:config_path) { Pathname.new('.').join('config').join('license_finder.yml') }
|
197
|
+
before do
|
198
|
+
allow(File).to receive(:mtime).with(config_path) { time }
|
199
|
+
end
|
200
|
+
|
201
|
+
it "returns the last time the yml file was modified" do
|
202
|
+
expect(described_class.last_modified).to eq time
|
203
|
+
end
|
204
|
+
end
|
153
205
|
end
|
154
206
|
end
|
@@ -148,27 +148,43 @@ module LicenseFinder
|
|
148
148
|
context "when the database has not changed" do
|
149
149
|
before do
|
150
150
|
Digest::SHA2.stub_chain(:file, :hexdigest) { 5 }
|
151
|
+
allow(config).to receive(:last_modified) { config_last_update }
|
152
|
+
allow(config.artifacts).to receive(:last_refreshed) { artifacts_last_update }
|
151
153
|
end
|
152
154
|
|
153
|
-
context "
|
155
|
+
context "and the reports do not exist" do
|
154
156
|
before do
|
155
|
-
config.artifacts.stub(:html_file).and_return(
|
157
|
+
config.artifacts.stub(:html_file).and_return(file_does_not_exist)
|
156
158
|
end
|
157
159
|
|
158
|
-
it "
|
159
|
-
Reporter.
|
160
|
+
it "writes reports" do
|
161
|
+
Reporter.should_receive(:write_reports)
|
160
162
|
DependencyManager.modifying {}
|
161
163
|
end
|
162
164
|
end
|
163
165
|
|
164
|
-
context "
|
166
|
+
context "and the reports exist" do
|
165
167
|
before do
|
166
|
-
config.artifacts.stub(:html_file).and_return(
|
168
|
+
config.artifacts.stub(:html_file).and_return(file_exists)
|
167
169
|
end
|
168
170
|
|
169
|
-
|
170
|
-
|
171
|
-
|
171
|
+
context "and configs are newer than the reports" do
|
172
|
+
let(:config_last_update) { 4 }
|
173
|
+
let(:artifacts_last_update) { 1 }
|
174
|
+
it "writes reports" do
|
175
|
+
expect(Reporter).to receive(:write_reports)
|
176
|
+
DependencyManager.modifying {}
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
context "and configs are older than the reports" do
|
181
|
+
let(:config_last_update) { 4 }
|
182
|
+
let(:artifacts_last_update) { 6 }
|
183
|
+
|
184
|
+
it "does not write reports" do
|
185
|
+
expect(Reporter).not_to receive(:write_reports)
|
186
|
+
DependencyManager.modifying {}
|
187
|
+
end
|
172
188
|
end
|
173
189
|
end
|
174
190
|
end
|
@@ -56,7 +56,7 @@ module LicenseFinder
|
|
56
56
|
|
57
57
|
it "returns 'multiple licenses' if there's more than one license" do
|
58
58
|
package = BowerPackage.new({ "pkgMeta" => {"licenses" => ["MIT", "BSD"]}, "canonicalDir" => "/some/path" })
|
59
|
-
expect(package.license.name).to eq("multiple licenses")
|
59
|
+
expect(package.license.name).to eq("multiple licenses: MIT, BSD")
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -82,7 +82,7 @@ module LicenseFinder
|
|
82
82
|
double(:second_file, license: License.find_by_name('Second Detected License'))
|
83
83
|
])
|
84
84
|
|
85
|
-
subject.license.name.should == "
|
85
|
+
subject.license.name.should == "multiple licenses: First Detected License, Second Detected License"
|
86
86
|
end
|
87
87
|
end
|
88
88
|
end
|
@@ -36,21 +36,25 @@ module LicenseFinder
|
|
36
36
|
stub_license_files [double(:file, license: License.find_by_name('Detected License'))]
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
|
-
gemspec.licenses = ['MIT', 'Expat']
|
39
|
+
context 'if the gemspec provides two synonymous licenses' do
|
40
|
+
before { gemspec.licenses = ['MIT', 'Expat'] }
|
41
41
|
|
42
|
-
|
42
|
+
it 'returns the license only once' do
|
43
|
+
subject.license.name.should == "MIT"
|
44
|
+
end
|
43
45
|
end
|
44
46
|
|
45
|
-
|
46
|
-
gemspec.licenses = ['First Gemspec License', 'Second Gemspec License']
|
47
|
+
context 'if the gemspec provides many licenses' do
|
48
|
+
before { gemspec.licenses = ['First Gemspec License', 'Second Gemspec License'] }
|
47
49
|
|
48
|
-
|
50
|
+
it "returns 'multiple licenses' with the names of the licenses from the gemspec (but not those from detected files)" do
|
51
|
+
subject.license.name.should == "multiple licenses: First Gemspec License, Second Gemspec License"
|
52
|
+
end
|
49
53
|
end
|
50
54
|
end
|
51
55
|
|
52
56
|
context "when there is nothing in the spec" do
|
53
|
-
it "returns a license in a file if only one unique license detected" do
|
57
|
+
it "returns a license in a file if there is only one unique license detected" do
|
54
58
|
stub_license_files([
|
55
59
|
double(:first_file, license: License.find_by_name('MIT')),
|
56
60
|
double(:second_file, license: License.find_by_name('Expat'))
|
@@ -65,13 +69,13 @@ module LicenseFinder
|
|
65
69
|
subject.license.name.should == "other"
|
66
70
|
end
|
67
71
|
|
68
|
-
it "returns '
|
72
|
+
it "returns 'multiple licenses' if there are many licenses in files" do
|
69
73
|
stub_license_files([
|
70
74
|
double(:first_file, license: License.find_by_name('First Detected License')),
|
71
75
|
double(:second_file, license: License.find_by_name('Second Detected License'))
|
72
76
|
])
|
73
77
|
|
74
|
-
subject.license.name.should == "
|
78
|
+
subject.license.name.should == "multiple licenses: First Detected License, Second Detected License"
|
75
79
|
end
|
76
80
|
end
|
77
81
|
end
|
@@ -40,7 +40,7 @@ module LicenseFinder
|
|
40
40
|
end
|
41
41
|
|
42
42
|
it "returns 'multiple licenses'" do
|
43
|
-
subject.license.name.should == 'multiple licenses'
|
43
|
+
subject.license.name.should == 'multiple licenses: Eclipse Public License - v 1.0, GNU Lesser General Public License'
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
@@ -52,7 +52,7 @@ module LicenseFinder
|
|
52
52
|
|
53
53
|
it "returns 'multiple licenses' if there's more than one license" do
|
54
54
|
package = NpmPackage.new({ "licenses" => ["MIT", "BSD"], "path" => "/some/path" })
|
55
|
-
expect(package.license.name).to eq("multiple licenses")
|
55
|
+
expect(package.license.name).to eq("multiple licenses: MIT, BSD")
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
@@ -78,7 +78,7 @@ module LicenseFinder
|
|
78
78
|
double(:second_file, license: License.find_by_name('Second Detected License'))
|
79
79
|
])
|
80
80
|
|
81
|
-
subject.license.name.should == "
|
81
|
+
subject.license.name.should == "multiple licenses: First Detected License, Second Detected License"
|
82
82
|
end
|
83
83
|
end
|
84
84
|
end
|
@@ -4,8 +4,9 @@ require "capybara"
|
|
4
4
|
module LicenseFinder
|
5
5
|
describe HtmlReport do
|
6
6
|
describe "#to_s" do
|
7
|
+
let(:dependency_name) { "the-name" }
|
7
8
|
let(:dependency) do
|
8
|
-
dep = Dependency.create name:
|
9
|
+
dep = Dependency.create name: dependency_name
|
9
10
|
dep.apply_better_license License.find_by_name("MIT")
|
10
11
|
dep
|
11
12
|
end
|
@@ -64,9 +65,9 @@ module LicenseFinder
|
|
64
65
|
|
65
66
|
it "should show the relationships" do
|
66
67
|
should have_text "(foo group)"
|
67
|
-
should have_text "
|
68
|
+
should have_text "#{dependency_name} is required by:"
|
68
69
|
should have_text "foo parent"
|
69
|
-
should have_text "
|
70
|
+
should have_text "#{dependency_name} relies on:"
|
70
71
|
should have_text "foo child"
|
71
72
|
end
|
72
73
|
end
|
@@ -74,8 +75,8 @@ module LicenseFinder
|
|
74
75
|
context "when the gem has no relationships" do
|
75
76
|
it "should not show any relationships" do
|
76
77
|
should_not have_text "()"
|
77
|
-
should_not have_text "
|
78
|
-
should_not have_text "
|
78
|
+
should_not have_text "#{dependency_name} is required by:"
|
79
|
+
should_not have_text "#{dependency_name} relies on:"
|
79
80
|
end
|
80
81
|
end
|
81
82
|
end
|
@@ -6,7 +6,7 @@ module LicenseFinder
|
|
6
6
|
subject { Reporter.write_reports }
|
7
7
|
|
8
8
|
before do
|
9
|
-
Dependency.stub(:
|
9
|
+
Dependency.stub(:acknowledged) { [double(:dep)] }
|
10
10
|
|
11
11
|
MarkdownReport.stub(:of) { 'markdown report' }
|
12
12
|
DetailedTextReport.stub(:of) { 'detailed csv report' }
|
@@ -5,11 +5,14 @@ module LicenseFinder
|
|
5
5
|
describe '.unapproved' do
|
6
6
|
before do
|
7
7
|
License.find_by_name('MIT').stub(:whitelisted? => true)
|
8
|
+
allow(LicenseFinder.config).to receive(:ignore_dependencies) { ['this ignored dependency', 'that ignored dependency'] }
|
8
9
|
end
|
9
10
|
|
10
|
-
it "should return all unapproved dependencies" do
|
11
|
+
it "should return all unapproved dependencies that are not ignored" do
|
11
12
|
dependency = Dependency.create(name: "unapproved dependency", version: '0.0.1')
|
12
13
|
approved = Dependency.create(name: "approved dependency", version: '0.0.1')
|
14
|
+
this_ignored = Dependency.create(name: "this ignored dependency", version: '0.0.1')
|
15
|
+
that_ignored = Dependency.create(name: "that ignored dependency", version: '0.0.1')
|
13
16
|
approved.approve!
|
14
17
|
whitelisted = Dependency.create(name: "approved dependency", version: '0.0.1')
|
15
18
|
whitelisted.license = License.find_by_name('MIT')
|
@@ -37,6 +40,16 @@ module LicenseFinder
|
|
37
40
|
end
|
38
41
|
end
|
39
42
|
|
43
|
+
describe ".acknowledged" do
|
44
|
+
it "returns all dependencies that are not ignored" do
|
45
|
+
acknowledged_dependency = Dependency.create(name: "acknowledged dependency", version: '0.0.1')
|
46
|
+
ignored_dependency = Dependency.create(name: "ignored dependency", version: '0.0.1')
|
47
|
+
allow(LicenseFinder.config).to receive(:ignore_dependencies) { [ignored_dependency.name] }
|
48
|
+
|
49
|
+
expect(Dependency.acknowledged).to match_array [acknowledged_dependency]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
40
53
|
describe '#approve!' do
|
41
54
|
it "should update the database to show the dependency is approved" do
|
42
55
|
dependency = Dependency.named("foo")
|