license_finder 1.0.0.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. data/CHANGELOG.rdoc +21 -1
  2. data/features/configure_ignore_dependencies.feature +16 -0
  3. data/features/step_definitions/cli_steps.rb +1 -1
  4. data/features/step_definitions/configure_ignore_dependencies.rb +35 -0
  5. data/files/license_finder.yml +2 -0
  6. data/lib/license_finder/cli.rb +72 -30
  7. data/lib/license_finder/configuration.rb +17 -1
  8. data/lib/license_finder/dependency_manager.rb +11 -4
  9. data/lib/license_finder/package.rb +28 -18
  10. data/lib/license_finder/reports/reporter.rb +1 -1
  11. data/lib/license_finder/tables/dependency.rb +8 -1
  12. data/lib/templates/html_report.erb +2 -2
  13. data/license_finder.gemspec +2 -2
  14. data/readme.md +17 -13
  15. data/spec/lib/license_finder/cli_spec.rb +46 -4
  16. data/spec/lib/license_finder/configuration_spec.rb +52 -0
  17. data/spec/lib/license_finder/dependency_manager_spec.rb +25 -9
  18. data/spec/lib/license_finder/package_managers/bower_package_spec.rb +2 -2
  19. data/spec/lib/license_finder/package_managers/bundler_package_spec.rb +13 -9
  20. data/spec/lib/license_finder/package_managers/gradle_package_spec.rb +1 -1
  21. data/spec/lib/license_finder/package_managers/maven_package_spec.rb +1 -1
  22. data/spec/lib/license_finder/package_managers/npm_package_spec.rb +2 -2
  23. data/spec/lib/license_finder/package_managers/pip_package_spec.rb +1 -1
  24. data/spec/lib/license_finder/reports/html_report_spec.rb +6 -5
  25. data/spec/lib/license_finder/reports/reporter_spec.rb +1 -1
  26. data/spec/lib/license_finder/tables/dependency_spec.rb +14 -1
  27. data/spec/support/stdout_helpers.rb +25 -0
  28. metadata +82 -47
  29. checksums.yaml +0 -7
  30. 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. Licenses
160
- in the `whitelist` will be automatically approved. You can exclude test or
161
- development dependencies by setting `ignore_groups`. (Currently this only
162
- works for Bundler.) You can store the license database and text files in
163
- another directory by changing `dependencies_file_dir`. And the `project_name`,
164
- which defaults to your working directory, appears in the [HTML report](#html-report).
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 has two sections, an overview at the top, and then a series of dependency summaries afterwards.
173
-
174
- ![HTML Report](files/report_breakdown.png)
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
- silence_stdout do
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 "when the reports exist" do
155
+ context "and the reports do not exist" do
154
156
  before do
155
- config.artifacts.stub(:html_file).and_return(file_exists)
157
+ config.artifacts.stub(:html_file).and_return(file_does_not_exist)
156
158
  end
157
159
 
158
- it "does not write reports" do
159
- Reporter.should_not_receive(:write_reports)
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 "when the reports do not exist" do
166
+ context "and the reports exist" do
165
167
  before do
166
- config.artifacts.stub(:html_file).and_return(file_does_not_exist)
168
+ config.artifacts.stub(:html_file).and_return(file_exists)
167
169
  end
168
170
 
169
- it "writes reports" do
170
- Reporter.should_receive(:write_reports)
171
- DependencyManager.modifying {}
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 == "other"
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
- it "returns the license from the gemspec if only one unique license provided" do
40
- gemspec.licenses = ['MIT', 'Expat']
39
+ context 'if the gemspec provides two synonymous licenses' do
40
+ before { gemspec.licenses = ['MIT', 'Expat'] }
41
41
 
42
- subject.license.name.should == "MIT"
42
+ it 'returns the license only once' do
43
+ subject.license.name.should == "MIT"
44
+ end
43
45
  end
44
46
 
45
- it "returns 'other' if the gemspec provides many" do
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
- subject.license.name.should == "multiple licenses"
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 'other' if there are many licenses in files" do
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 == "other"
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
 
@@ -46,7 +46,7 @@ module LicenseFinder
46
46
  end
47
47
 
48
48
  it "returns 'multiple licenses'" do
49
- subject.license.name.should == 'multiple licenses'
49
+ subject.license.name.should == 'multiple licenses: Common Public License Version 1.0, Apache 2'
50
50
  end
51
51
  end
52
52
 
@@ -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 == "other"
81
+ subject.license.name.should == "multiple licenses: First Detected License, Second Detected License"
82
82
  end
83
83
  end
84
84
  end
@@ -73,7 +73,7 @@ module LicenseFinder
73
73
 
74
74
  subject = make_package(data)
75
75
 
76
- expect(subject.license.name).to eq('multiple licenses')
76
+ expect(subject.license.name).to eq('multiple licenses: Apache 2.0 License, GPL')
77
77
  end
78
78
  end
79
79
 
@@ -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: "the-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 "Parents"
68
+ should have_text "#{dependency_name} is required by:"
68
69
  should have_text "foo parent"
69
- should have_text "Children"
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 "Parents"
78
- should_not have_text "Children"
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(:all) { [double(:dep)] }
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")