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
@@ -1,4 +1,24 @@
1
- === 1.0.0 / Unreleased
1
+ === 1.0.1 / 2014-05-28
2
+
3
+ * Features
4
+
5
+ * For dependencies with multiple licenses, the dependency is listed as
6
+ 'multiple licenses' along with the names of each license
7
+ * Added 'ignore_dependencies' config option to allow specific
8
+ dependencies to be excluded from reports.
9
+
10
+ * Bugfixes
11
+
12
+ * Dependency reports generate when license_finder.yml updates
13
+ * Dependency reports generate when config is changed through the command line
14
+
15
+ === 1.0.0.1 / 2014-05-23
16
+
17
+ * Bugfixes
18
+
19
+ * LicenseFinder detects its own license
20
+
21
+ === 1.0.0 / 2014-04-03
2
22
 
3
23
  * Features
4
24
 
@@ -0,0 +1,16 @@
1
+ Feature: Ignore Dependencies
2
+ As a developer
3
+ I want to ignore certain dependencies
4
+ To avoid noisy doc changes when there are safe dependencies with high version churn
5
+
6
+ Scenario: Select dependencies can be ignored
7
+ Given I have an app that depends on bundler
8
+ And I ignore the bundler dependency
9
+ When I get the ignored dependencies
10
+ Then I should see 'bundler' in the output
11
+ And I should not see 'bundler' in the dependency docs
12
+
13
+ Scenario: Ignored dependencies do not appear in the unapproved list
14
+ Given I have an app that depends on bundler
15
+ When I ignore the bundler dependency
16
+ Then the bundler dependency is not listed as an action item
@@ -22,7 +22,7 @@ end
22
22
 
23
23
  Then(/^it creates a config directory with the license_finder config$/) do
24
24
  @user.config_path.should be_exist
25
- text = "---\nwhitelist:\n#- MIT\n#- Apache 2.0\nignore_groups:\n#- test\n#- development\ndependencies_file_dir: './doc/'\nproject_name: # project name\n"
25
+ text = "---\nwhitelist:\n#- MIT\n#- Apache 2.0\nignore_groups:\n#- test\n#- development\nignore_dependencies:\n#- bundler\ndependencies_file_dir: './doc/'\nproject_name: # project name\n"
26
26
  @user.config_file.read.should == text.gsub(/^\s+/, "")
27
27
  end
28
28
 
@@ -0,0 +1,35 @@
1
+ Given(/^I have an app that depends on bundler$/) do
2
+ @user = ::DSL::User.new
3
+ @user.create_ruby_app
4
+ @user.create_gem 'bundler_faker', license: 'Whatever'
5
+ @user.depend_on_local_gem 'bundler_faker', groups: ['test', 'development', 'production']
6
+ @user.create_gem 'gpl_gem', license: 'GPL'
7
+ @user.depend_on_local_gem 'gpl_gem', groups: ['test']
8
+ end
9
+
10
+ Given(/^I ignore the bundler dependency$/) do
11
+ @user.execute_command('license_finder ignored_dependencies add bundler_faker')
12
+ end
13
+
14
+ When(/^I get the ignored dependencies$/) do
15
+ @user.execute_command('license_finder ignored_dependencies list')
16
+ end
17
+
18
+ Then(/^I should see 'bundler' in the output$/) do
19
+ expect(@user).to be_seeing 'bundler_faker'
20
+ end
21
+
22
+ Then(/^the bundler dependency is not listed as an action item$/) do
23
+ @user.execute_command('license_finder > /dev/null')
24
+ @user.execute_command('license_finder action_items')
25
+ expect(@user).not_to be_seeing 'bundler_faker'
26
+ end
27
+
28
+ Then(/^I should not see 'bundler' in the dependency docs$/)do
29
+ @user.execute_command('license_finder')
30
+ dependencies_csv_path = @user.app_path.join('doc', 'dependencies.csv')
31
+ dependencies_csv = File.open(dependencies_csv_path, 'r')
32
+
33
+ expect(dependencies_csv.read).not_to match /bundler_faker/
34
+ end
35
+
@@ -5,5 +5,7 @@ whitelist:
5
5
  ignore_groups:
6
6
  #- test
7
7
  #- development
8
+ ignore_dependencies:
9
+ #- bundler
8
10
  dependencies_file_dir: './doc/'
9
11
  project_name: # project name
@@ -11,12 +11,43 @@ module LicenseFinder
11
11
 
12
12
  private
13
13
 
14
+ def sync_with_spinner
15
+ die_on_error {
16
+ spinner {
17
+ DependencyManager.sync_with_package_managers
18
+ }
19
+ }
20
+ end
21
+
14
22
  def die_on_error
15
23
  yield
16
24
  rescue LicenseFinder::Error => e
17
25
  say e.message, :red
18
26
  exit 1
19
27
  end
28
+
29
+ def spinner
30
+ if options[:quiet]
31
+ yield
32
+ else
33
+ begin
34
+ thread = Thread.new {
35
+ wheel = '\|/-'
36
+ i = 0
37
+ while true do
38
+ print "\r ---------- #{wheel[i]} ----------"
39
+ i = (i + 1) % 4
40
+ end
41
+ }
42
+ yield
43
+ ensure
44
+ if thread
45
+ thread.kill
46
+ puts "\r" + " "*24
47
+ end
48
+ end
49
+ end
50
+ end
20
51
  end
21
52
 
22
53
  # Thor fix for `license_finder <subcommand> help <action>`
@@ -73,7 +104,7 @@ module LicenseFinder
73
104
  yield
74
105
 
75
106
  LicenseFinder.config.save
76
- Reporter.write_reports
107
+ sync_with_spinner
77
108
  }
78
109
  end
79
110
  end
@@ -150,18 +181,52 @@ module LicenseFinder
150
181
  end
151
182
  end
152
183
 
184
+ class IgnoredDependencies < ConfigSubcommand
185
+ desc "list", "List all the ignored dependencies"
186
+ def list
187
+ ignored = LicenseFinder.config.ignore_dependencies
188
+
189
+ say "Ignored Dependencies:", :blue
190
+ if ignored.any?
191
+ ignored.each do |group|
192
+ say group
193
+ end
194
+ else
195
+ say '(none)'
196
+ end
197
+ end
198
+
199
+ desc "add DEPENDENCY", "Add a dependency to be ignored"
200
+ def add(group)
201
+ modifying {
202
+ LicenseFinder.config.ignore_dependencies.push(group)
203
+ }
204
+ say "Added #{group} to the ignored dependencies"
205
+ end
206
+
207
+ desc "remove DEPENDENCY", "Remove a dependency from the ignored dependencies"
208
+ def remove(group)
209
+ modifying {
210
+ LicenseFinder.config.ignore_dependencies.delete(group)
211
+ }
212
+ say "Removed #{group} from the ignored dependencies"
213
+ end
214
+ end
215
+
153
216
  class Main < Base
154
217
  method_option :quiet, type: :boolean, desc: "silences loading output"
155
218
  desc "rescan", "Find new dependencies. (Default action)"
156
219
  def rescan
157
- die_on_error {
158
- spinner {
159
- DependencyManager.sync_with_package_managers
160
- }
161
- }
220
+ sync_with_spinner
221
+ show_results
222
+ end
162
223
 
224
+ desc "show_results", "Display ignored dependencies and action items"
225
+ def show_results
226
+ IgnoredDependencies.new.list
163
227
  action_items
164
228
  end
229
+
165
230
  default_task :rescan
166
231
 
167
232
  method_option :approver, desc: "The person granting the approval"
@@ -206,33 +271,10 @@ module LicenseFinder
206
271
 
207
272
  subcommand "dependencies", Dependencies, "Manually manage dependencies that your package managers are not aware of"
208
273
  subcommand "ignored_bundler_groups", IgnoredBundlerGroups, "Manage ignored Bundler groups"
274
+ subcommand "ignored_dependencies", IgnoredDependencies, "Manage ignored dependencies"
209
275
  subcommand "whitelist", Whitelist, "Manage whitelisted licenses"
210
276
  subcommand "project_name", ProjectName, "Manage the project name"
211
277
 
212
- private
213
-
214
- def spinner
215
- if options[:quiet]
216
- yield
217
- else
218
- begin
219
- thread = Thread.new {
220
- wheel = '\|/-'
221
- i = 0
222
- while true do
223
- print "\r ---------- #{wheel[i]} ----------"
224
- i = (i + 1) % 4
225
- end
226
- }
227
- yield
228
- ensure
229
- if thread
230
- thread.kill
231
- puts "\r" + " "*24
232
- end
233
- end
234
- end
235
- end
236
278
  end
237
279
  end
238
280
  end
@@ -7,6 +7,10 @@ module LicenseFinder
7
7
  prepare(Persistence.get)
8
8
  end
9
9
 
10
+ def last_modified
11
+ Persistence.last_modified
12
+ end
13
+
10
14
  def self.move!
11
15
  config = prepare(Persistence.get.merge('dependencies_file_dir' => './doc/'))
12
16
  config.save
@@ -24,11 +28,12 @@ module LicenseFinder
24
28
  result
25
29
  end
26
30
 
27
- attr_accessor :whitelist, :ignore_groups, :artifacts, :project_name
31
+ attr_accessor :whitelist, :ignore_groups, :ignore_dependencies, :artifacts, :project_name
28
32
 
29
33
  def initialize(config)
30
34
  @whitelist = Array(config['whitelist'])
31
35
  @ignore_groups = Array(config["ignore_groups"])
36
+ @ignore_dependencies = Array(config["ignore_dependencies"])
32
37
  @artifacts = Artifacts.new(Pathname(config['dependencies_file_dir'] || './doc/'))
33
38
  @project_name = config['project_name'] || determine_project_name
34
39
  end
@@ -43,6 +48,7 @@ module LicenseFinder
43
48
  {
44
49
  'whitelist' => whitelist.uniq,
45
50
  'ignore_groups' => ignore_groups.uniq,
51
+ 'ignore_dependencies' => ignore_dependencies.uniq,
46
52
  'dependencies_file_dir' => artifacts.dir.to_s,
47
53
  'project_name' => project_name
48
54
  }
@@ -92,6 +98,12 @@ module LicenseFinder
92
98
  def legacy_text_file
93
99
  join("dependencies.txt")
94
100
  end
101
+
102
+ def last_refreshed
103
+ [database_file, text_file, detailed_text_file, html_file, markdown_file].map do |path|
104
+ File.mtime(path)
105
+ end.min
106
+ end
95
107
  end
96
108
 
97
109
  module Persistence
@@ -111,6 +123,10 @@ module LicenseFinder
111
123
  file.open('w') { |f| f.write(YAML.dump(hash)) }
112
124
  end
113
125
 
126
+ def last_modified
127
+ File.mtime(file)
128
+ end
129
+
114
130
  private
115
131
 
116
132
  def inited?
@@ -38,10 +38,9 @@ module LicenseFinder
38
38
  result = DB.transaction { yield }
39
39
  checksum_after = checksum
40
40
 
41
- unless checksum_before == checksum_after
42
- Reporter.write_reports
43
- end
44
- unless LicenseFinder.config.artifacts.html_file.exist?
41
+ database_changed = checksum_before != checksum_after
42
+
43
+ if database_changed || reports_do_not_exist || reports_are_stale
45
44
  Reporter.write_reports
46
45
  end
47
46
 
@@ -50,6 +49,14 @@ module LicenseFinder
50
49
 
51
50
  private # not really private, but it looks like it is!
52
51
 
52
+ def self.reports_do_not_exist
53
+ !(LicenseFinder.config.artifacts.html_file.exist?)
54
+ end
55
+
56
+ def self.reports_are_stale
57
+ LicenseFinder.config.last_modified > LicenseFinder.config.artifacts.last_refreshed
58
+ end
59
+
53
60
  def self.current_packages
54
61
  package_managers.select(&:active?).map(&:current_packages).flatten
55
62
  end
@@ -24,32 +24,38 @@ module LicenseFinder
24
24
  private
25
25
 
26
26
  def determine_license
27
- if one_license_from_spec?
28
- licenses_from_spec.first
29
- elsif no_licenses_from_spec? && one_license_from_files?
30
- licenses_from_files.first
31
- elsif multiple_licenses_from_spec_and_files?
32
- multiple_licenses
27
+ if licenses_from_spec.any?
28
+ choose_license_from licenses_from_spec
29
+ elsif licenses_from_files.any?
30
+ choose_license_from licenses_from_files
33
31
  else
34
32
  default_license
35
33
  end
36
34
  end
37
35
 
38
- def multiple_licenses_from_spec_and_files?
39
- (licenses_from_spec+licenses_from_spec).uniq.size > 1
36
+ def choose_license_from licenses
37
+ if ( licenses.uniq.size > 1 )
38
+ License.find_by_name "multiple licenses: #{(licenses).map(&:name).uniq.join(', ')}"
39
+ else
40
+ licenses.first
41
+ end
40
42
  end
41
43
 
42
- def one_license_from_spec?
43
- licenses_from_spec.uniq.size == 1
44
- end
44
+ # def multiple_licenses_from_spec_and_files?
45
+ # (licenses_from_spec+licenses_from_files).uniq.size > 1
46
+ # end
45
47
 
46
- def one_license_from_files?
47
- licenses_from_files.uniq.size == 1
48
- end
48
+ # def one_license_from_spec?
49
+ # licenses_from_spec.uniq.size == 1
50
+ # end
49
51
 
50
- def no_licenses_from_spec?
51
- licenses_from_spec.uniq.size == 0
52
- end
52
+ # def one_license_from_files?
53
+ # licenses_from_files.uniq.size == 1
54
+ # end
55
+
56
+ # def no_licenses_from_spec?
57
+ # licenses_from_spec.uniq.size == 0
58
+ # end
53
59
 
54
60
  def licenses_from_spec
55
61
  license_names_from_spec.map do |name|
@@ -66,7 +72,11 @@ module LicenseFinder
66
72
  end
67
73
 
68
74
  def multiple_licenses
69
- License.find_by_name 'multiple licenses'
75
+ if ( licenses_from_spec.uniq.size > 1 )
76
+ License.find_by_name "multiple licenses: #{(licenses_from_spec).map(&:name).uniq.join(', ')}"
77
+ else
78
+ License.find_by_name "multiple licenses: #{(licenses_from_files).map(&:name).uniq.join(', ')}"
79
+ end
70
80
  end
71
81
 
72
82
  def default_license
@@ -3,7 +3,7 @@ module LicenseFinder
3
3
  extend self
4
4
 
5
5
  def write_reports
6
- dependencies = Dependency.all
6
+ dependencies = Dependency.acknowledged
7
7
  artifacts = LicenseFinder.config.artifacts
8
8
 
9
9
  write_file artifacts.text_file, TextReport.of(dependencies)
@@ -26,7 +26,14 @@ module LicenseFinder
26
26
  end
27
27
 
28
28
  def self.unapproved
29
- all.reject(&:approved?)
29
+ acknowledged.reject(&:approved?)
30
+ end
31
+
32
+ def self.acknowledged
33
+ ignored_dependencies = LicenseFinder.config.ignore_dependencies
34
+ all.reject do |dependency|
35
+ ignored_dependencies.include? dependency.name
36
+ end
30
37
  end
31
38
 
32
39
  def self.named(name)
@@ -99,13 +99,13 @@
99
99
  <p><%= dependency.description %></p>
100
100
  <% if dependency.parents.any? -%>
101
101
  <dl>
102
- <dt>Parents</dt>
102
+ <dt><%=dependency.name%> is required by:</dt>
103
103
  <dd><%= dependency.parents.map(&:name).join(", ") -%></dd>
104
104
  </dl>
105
105
  <% end -%>
106
106
  <% if dependency.children.any? -%>
107
107
  <dl>
108
- <dt>Children</dt>
108
+ <dt><%=dependency.name%> relies on:</dt>
109
109
  <dd><%= dependency.children.map(&:name).join(", ") -%></dd>
110
110
  </dl>
111
111
  <% end -%>
@@ -3,8 +3,8 @@ require './lib/license_finder/platform'
3
3
  Gem::Specification.new do |s|
4
4
  s.required_ruby_version = '>= 1.9.3'
5
5
  s.name = "license_finder"
6
- s.version = "1.0.0.1"
7
- s.authors = ["Jacob Maine", "Matthew Kane Parker", "Ian Lesperance", "David Edwards", "Paul Meskers", "Brent Wheeldon", "Trevor John", "David Tengdin", "William Ramsey"]
6
+ s.version = "1.0.1"
7
+ s.authors = ["Jacob Maine", "Matthew Kane Parker", "Ian Lesperance", "David Edwards", "Paul Meskers", "Brent Wheeldon", "Trevor John", "David Tengdin", "William Ramsey", "David Dening"]
8
8
  s.email = ["commoncode@pivotalabs.com"]
9
9
  s.homepage = "https://github.com/pivotal/LicenseFinder"
10
10
  s.summary = "Audit the OSS licenses of your application's dependencies."