license_finder 5.11.1 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +25 -0
  4. data/CONTRIBUTING.md +1 -1
  5. data/Dockerfile +9 -14
  6. data/LICENSE +1 -1
  7. data/README.md +18 -18
  8. data/Rakefile +1 -1
  9. data/VERSION +1 -1
  10. data/bin/license_finder_pip.py +6 -2
  11. data/ci/pipelines/pull-request.yml.erb +14 -0
  12. data/ci/pipelines/release.yml.erb +24 -0
  13. data/lib/license_finder/cli.rb +2 -2
  14. data/lib/license_finder/cli/approvals.rb +1 -0
  15. data/lib/license_finder/cli/dependencies.rb +3 -2
  16. data/lib/license_finder/cli/main.rb +8 -8
  17. data/lib/license_finder/cli/makes_decisions.rb +3 -0
  18. data/lib/license_finder/cli/permitted_licenses.rb +32 -0
  19. data/lib/license_finder/cli/restricted_licenses.rb +32 -0
  20. data/lib/license_finder/core.rb +1 -1
  21. data/lib/license_finder/decision_applier.rb +6 -6
  22. data/lib/license_finder/decisions.rb +19 -19
  23. data/lib/license_finder/license_aggregator.rb +2 -2
  24. data/lib/license_finder/package.rb +15 -15
  25. data/lib/license_finder/package_manager.rb +1 -0
  26. data/lib/license_finder/package_managers/bundler.rb +3 -1
  27. data/lib/license_finder/package_managers/composer.rb +1 -1
  28. data/lib/license_finder/package_managers/pip.rb +11 -19
  29. data/lib/license_finder/package_managers/pipenv.rb +63 -0
  30. data/lib/license_finder/package_utils/pypi.rb +41 -0
  31. data/lib/license_finder/packages/merged_package.rb +2 -2
  32. data/lib/license_finder/reports/templates/html_report.erb +2 -2
  33. data/lib/license_finder/reports/templates/markdown_report.erb +2 -2
  34. data/lib/license_finder/scanner.rb +4 -2
  35. data/lib/license_finder/shared_helpers/common_path.rb +3 -1
  36. data/license_finder.gemspec +5 -6
  37. metadata +15 -14
  38. data/lib/license_finder/cli/blacklist.rb +0 -32
  39. data/lib/license_finder/cli/whitelist.rb +0 -32
@@ -7,6 +7,7 @@ module LicenseFinder
7
7
  include MakesDecisions
8
8
 
9
9
  auditable
10
+ approvable
10
11
  desc 'add DEPENDENCY...', 'Approve one or more dependencies by name'
11
12
  def add(*names)
12
13
  assert_some names
@@ -8,9 +8,10 @@ module LicenseFinder
8
8
 
9
9
  method_option :approve, type: :boolean, desc: 'Approve the added dependency'
10
10
  method_option :homepage, type: :string, desc: 'Source of the added dependency'
11
+
11
12
  auditable
12
- desc 'add DEPENDENCY LICENSE [VERSION] [--homepage=HOMEPAGE] [--approve]', 'Add a dependency that is not managed by a package manager, optionally approving it at the same time'
13
- def add(name, license, version = nil)
13
+ desc 'add DEPENDENCY LICENSE VERSION [--homepage=HOMEPAGE] [--approve]', 'Add a dependency that is not managed by a package manager, optionally approving it at the same time'
14
+ def add(name, license, version)
14
15
  modifying do
15
16
  decisions
16
17
  .add_package(name, version, txn)
@@ -107,7 +107,7 @@ module LicenseFinder
107
107
  finder = LicenseAggregator.new(config, aggregate_paths)
108
108
  any_packages = finder.any_packages?
109
109
  unapproved = finder.unapproved
110
- blacklisted = finder.blacklisted
110
+ restricted = finder.restricted
111
111
 
112
112
  # Ensure to start output on a new line even with dot progress indicators.
113
113
  say "\n"
@@ -120,12 +120,12 @@ module LicenseFinder
120
120
  if unapproved.empty?
121
121
  say 'All dependencies are approved for use', :green
122
122
  else
123
- unless blacklisted.empty?
124
- say 'Blacklisted dependencies:', :red
125
- say report_of(blacklisted)
123
+ unless restricted.empty?
124
+ say 'Restricted dependencies:', :red
125
+ say report_of(restricted)
126
126
  end
127
127
 
128
- other_unapproved = unapproved - blacklisted
128
+ other_unapproved = unapproved - restricted
129
129
  unless other_unapproved.empty?
130
130
  say 'Dependencies that need approval:', :yellow
131
131
  say report_of(other_unapproved)
@@ -165,11 +165,11 @@ module LicenseFinder
165
165
 
166
166
  subcommand 'dependencies', Dependencies, 'Add or remove dependencies that your package managers are not aware of'
167
167
  subcommand 'licenses', Licenses, "Set a dependency's licenses, if the licenses found by license_finder are missing or wrong"
168
- subcommand 'approvals', Approvals, 'Manually approve dependencies, even if their licenses are not whitelisted'
168
+ subcommand 'approvals', Approvals, 'Manually approve dependencies, even if their licenses are not permitted'
169
169
  subcommand 'ignored_groups', IgnoredGroups, 'Exclude test and development dependencies from action items and reports'
170
170
  subcommand 'ignored_dependencies', IgnoredDependencies, 'Exclude individual dependencies from action items and reports'
171
- subcommand 'whitelist', Whitelist, 'Automatically approve any dependency that has a whitelisted license'
172
- subcommand 'blacklist', Blacklist, 'Forbid approval of any dependency whose licenses are all blacklisted'
171
+ subcommand 'permitted_licenses', PermittedLicenses, 'Automatically approve any dependency that has a permitted license'
172
+ subcommand 'restricted_licenses', RestrictedLicenses, 'Forbid approval of any dependency whose licenses are all restricted'
173
173
  subcommand 'project_name', ProjectName, 'Set the project name, for display in reports'
174
174
 
175
175
  private
@@ -11,6 +11,9 @@ module LicenseFinder
11
11
  def auditable
12
12
  method_option :who, desc: 'The person making this decision'
13
13
  method_option :why, desc: 'The reason for making this decision'
14
+ end
15
+
16
+ def approvable
14
17
  method_option :version, desc: 'The version that will be approved'
15
18
  end
16
19
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LicenseFinder
4
+ module CLI
5
+ class PermittedLicenses < Base
6
+ extend Subcommand
7
+ include MakesDecisions
8
+
9
+ desc 'list', 'List all the permitted licenses'
10
+ def list
11
+ say 'Permitted Licenses:', :blue
12
+ say_each(decisions.permitted, &:name)
13
+ end
14
+
15
+ auditable
16
+ desc 'add LICENSE...', 'Add one or more licenses to the permitted licenses'
17
+ def add(*licenses)
18
+ assert_some licenses
19
+ modifying { licenses.each { |l| decisions.permit(l, txn) } }
20
+ say "Added #{licenses.join(', ')} to the permitted licenses"
21
+ end
22
+
23
+ auditable
24
+ desc 'remove LICENSE...', 'Remove one or more licenses from the permitted licenses'
25
+ def remove(*licenses)
26
+ assert_some licenses
27
+ modifying { licenses.each { |l| decisions.unpermit(l, txn) } }
28
+ say "Removed #{licenses.join(', ')} from the license permitted licenses"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LicenseFinder
4
+ module CLI
5
+ class RestrictedLicenses < Base
6
+ extend Subcommand
7
+ include MakesDecisions
8
+
9
+ desc 'list', 'List all the restricted licenses'
10
+ def list
11
+ say 'Restricted Licenses:', :blue
12
+ say_each(decisions.restricted, &:name)
13
+ end
14
+
15
+ auditable
16
+ desc 'add LICENSE...', 'Add one or more licenses to the restricted licenses'
17
+ def add(*licenses)
18
+ assert_some licenses
19
+ modifying { licenses.each { |l| decisions.restrict(l, txn) } }
20
+ say "Added #{licenses.join(', ')} to the restricted licenses"
21
+ end
22
+
23
+ auditable
24
+ desc 'remove LICENSE...', 'Remove one or more licenses from the restricted licenses'
25
+ def remove(*licenses)
26
+ assert_some licenses
27
+ modifying { licenses.each { |l| decisions.unrestrict(l, txn) } }
28
+ say "Removed #{licenses.join(', ')} from the restricted licenses"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -42,7 +42,7 @@ module LicenseFinder
42
42
  end
43
43
 
44
44
  extend Forwardable
45
- def_delegators :decision_applier, :acknowledged, :unapproved, :blacklisted, :any_packages?
45
+ def_delegators :decision_applier, :acknowledged, :unapproved, :restricted, :any_packages?
46
46
 
47
47
  def project_name
48
48
  decisions.project_name || config.project_path.basename.to_s
@@ -14,8 +14,8 @@ module LicenseFinder
14
14
  acknowledged.reject(&:approved?)
15
15
  end
16
16
 
17
- def blacklisted
18
- acknowledged.select(&:blacklisted?)
17
+ def restricted
18
+ acknowledged.select(&:restricted?)
19
19
  end
20
20
 
21
21
  def any_packages?
@@ -53,12 +53,12 @@ module LicenseFinder
53
53
  end
54
54
 
55
55
  def with_approval(package)
56
- if package.licenses.all? { |license| decisions.blacklisted?(license) }
57
- package.blacklisted!
56
+ if package.licenses.all? { |license| decisions.restricted?(license) }
57
+ package.restricted!
58
58
  elsif decisions.approved?(package.name, package.version)
59
59
  package.approved_manually!(decisions.approval_of(package.name, package.version))
60
- elsif package.licenses.any? { |license| decisions.whitelisted?(license) }
61
- package.whitelisted!
60
+ elsif package.licenses.any? { |license| decisions.permitted?(license) }
61
+ package.permitted!
62
62
  end
63
63
  package
64
64
  end
@@ -6,7 +6,7 @@ module LicenseFinder
6
6
  # READ
7
7
  ######
8
8
 
9
- attr_reader :packages, :whitelisted, :blacklisted, :ignored, :ignored_groups, :project_name
9
+ attr_reader :packages, :permitted, :restricted, :ignored, :ignored_groups, :project_name
10
10
 
11
11
  def licenses_of(name)
12
12
  @licenses[name]
@@ -36,12 +36,12 @@ module LicenseFinder
36
36
  end
37
37
  end
38
38
 
39
- def whitelisted?(lic)
40
- @whitelisted.include?(lic)
39
+ def permitted?(lic)
40
+ @permitted.include?(lic)
41
41
  end
42
42
 
43
- def blacklisted?(lic)
44
- @blacklisted.include?(lic)
43
+ def restricted?(lic)
44
+ @restricted.include?(lic)
45
45
  end
46
46
 
47
47
  def ignored?(name)
@@ -68,8 +68,8 @@ module LicenseFinder
68
68
  @licenses = Hash.new { |h, k| h[k] = Set.new }
69
69
  @homepages = {}
70
70
  @approvals = {}
71
- @whitelisted = Set.new
72
- @blacklisted = Set.new
71
+ @permitted = Set.new
72
+ @restricted = Set.new
73
73
  @ignored = Set.new
74
74
  @ignored_groups = Set.new
75
75
  end
@@ -120,27 +120,27 @@ module LicenseFinder
120
120
  self
121
121
  end
122
122
 
123
- def whitelist(lic, txn = {})
124
- @decisions << [:whitelist, lic, txn]
125
- @whitelisted << License.find_by_name(lic)
123
+ def permit(lic, txn = {})
124
+ @decisions << [:permit, lic, txn]
125
+ @permitted << License.find_by_name(lic)
126
126
  self
127
127
  end
128
128
 
129
- def unwhitelist(lic, txn = {})
130
- @decisions << [:unwhitelist, lic, txn]
131
- @whitelisted.delete(License.find_by_name(lic))
129
+ def unpermit(lic, txn = {})
130
+ @decisions << [:unpermit, lic, txn]
131
+ @permitted.delete(License.find_by_name(lic))
132
132
  self
133
133
  end
134
134
 
135
- def blacklist(lic, txn = {})
136
- @decisions << [:blacklist, lic, txn]
137
- @blacklisted << License.find_by_name(lic)
135
+ def restrict(lic, txn = {})
136
+ @decisions << [:restrict, lic, txn]
137
+ @restricted << License.find_by_name(lic)
138
138
  self
139
139
  end
140
140
 
141
- def unblacklist(lic, txn = {})
142
- @decisions << [:unblacklist, lic, txn]
143
- @blacklisted.delete(License.find_by_name(lic))
141
+ def unrestrict(lic, txn = {})
142
+ @decisions << [:unrestrict, lic, txn]
143
+ @restricted.delete(License.find_by_name(lic))
144
144
  self
145
145
  end
146
146
 
@@ -22,8 +22,8 @@ module LicenseFinder
22
22
  aggregate_packages.reject(&:approved?)
23
23
  end
24
24
 
25
- def blacklisted
26
- aggregate_packages.select(&:blacklisted?)
25
+ def restricted
26
+ aggregate_packages.select(&:restricted?)
27
27
  end
28
28
 
29
29
  private
@@ -48,8 +48,8 @@ module LicenseFinder
48
48
  @groups = options[:groups] || []
49
49
 
50
50
  ## APPROVAL
51
- @whitelisted = false
52
- @blacklisted = false
51
+ @permitted = false
52
+ @restricted = false
53
53
  @manual_approval = nil
54
54
 
55
55
  ## LICENSING
@@ -78,26 +78,26 @@ module LicenseFinder
78
78
  end
79
79
 
80
80
  def approved?
81
- # Question: is `!blacklisted?` redundant?
82
- # DecisionApplier does not call `whitelisted!` or `approved_manually!`
83
- # if a Package has been blacklisted.
84
- (approved_manually? || whitelisted?) && !blacklisted?
81
+ # Question: is `!restricted?` redundant?
82
+ # DecisionApplier does not call `permitted!` or `approved_manually!`
83
+ # if a Package has been restricted.
84
+ (approved_manually? || permitted?) && !restricted?
85
85
  end
86
86
 
87
- def whitelisted!
88
- @whitelisted = true
87
+ def permitted!
88
+ @permitted = true
89
89
  end
90
90
 
91
- def whitelisted?
92
- @whitelisted
91
+ def permitted?
92
+ @permitted
93
93
  end
94
94
 
95
- def blacklisted!
96
- @blacklisted = true
95
+ def restricted!
96
+ @restricted = true
97
97
  end
98
98
 
99
- def blacklisted?
100
- @blacklisted
99
+ def restricted?
100
+ @restricted
101
101
  end
102
102
 
103
103
  attr_reader :manual_approval
@@ -125,7 +125,7 @@ module LicenseFinder
125
125
  attr_reader :install_path # checked in tests, otherwise private
126
126
 
127
127
  def licenses
128
- @licenses ||= activations.map(&:license).to_set
128
+ @licenses ||= activations.map(&:license).sort_by(&:name).to_set
129
129
  end
130
130
 
131
131
  def activations
@@ -152,6 +152,7 @@ require 'license_finder/package_managers/bundler'
152
152
  require 'license_finder/package_managers/npm'
153
153
  require 'license_finder/package_managers/yarn'
154
154
  require 'license_finder/package_managers/pip'
155
+ require 'license_finder/package_managers/pipenv'
155
156
  require 'license_finder/package_managers/maven'
156
157
  require 'license_finder/package_managers/mix'
157
158
  require 'license_finder/package_managers/cocoa_pods'
@@ -24,7 +24,9 @@ module LicenseFinder
24
24
  end
25
25
 
26
26
  def prepare_command
27
- 'bundle install'
27
+ ignored_groups_argument = !ignored_groups.empty? ? "--without #{ignored_groups.to_a.join(' ')}" : ''
28
+
29
+ "bundle install #{ignored_groups_argument}".strip
28
30
  end
29
31
 
30
32
  def possible_package_paths
@@ -29,7 +29,7 @@ module LicenseFinder
29
29
  end
30
30
 
31
31
  def prepare_command
32
- 'composer install'
32
+ 'composer install --no-plugins --ignore-platform-reqs --no-interaction'
33
33
  end
34
34
 
35
35
  def package_path
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
- require 'net/http'
5
4
 
6
5
  module LicenseFinder
7
6
  class Pip < PackageManager
@@ -17,7 +16,7 @@ module LicenseFinder
17
16
  PipPackage.new(
18
17
  name,
19
18
  version,
20
- pypi_def(name, version),
19
+ PyPI.definition(name, version),
21
20
  logger: logger,
22
21
  children: children,
23
22
  install_path: Pathname(location).join(name)
@@ -57,24 +56,17 @@ module LicenseFinder
57
56
  private
58
57
 
59
58
  def pip_output
60
- output = `python#{@python_version} #{LicenseFinder::BIN_PATH.join('license_finder_pip.py')} #{detected_package_path}`
61
- JSON(output).map do |package|
62
- package.values_at('name', 'version', 'dependencies', 'location')
63
- end
64
- end
65
-
66
- def pypi_def(name, version)
67
- response = pypi_request("https://pypi.org/pypi/#{name}/#{version}/json")
68
- response.is_a?(Net::HTTPSuccess) ? JSON.parse(response.body).fetch('info', {}) : {}
69
- end
59
+ command = "python#{@python_version == '2' ? '' : '3'} #{LicenseFinder::BIN_PATH.join('license_finder_pip.py')} #{detected_package_path}"
60
+ stdout, stderr, status = Cmd.run(command)
70
61
 
71
- def pypi_request(location, limit = 10)
72
- uri = URI(location)
73
- http = Net::HTTP.new(uri.host, uri.port)
74
- http.use_ssl = true
75
- response = http.get(uri.request_uri).response
76
-
77
- response.is_a?(Net::HTTPRedirection) && limit.positive? ? pypi_request(response['location'], limit - 1) : response
62
+ if status.success?
63
+ JSON(stdout).map do |package|
64
+ package.values_at('name', 'version', 'dependencies', 'location')
65
+ end
66
+ else
67
+ log_errors "LicenseFinder command '#{command}' failed:\n\t#{stderr}"
68
+ []
69
+ end
78
70
  end
79
71
  end
80
72
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'license_finder/package_utils/pypi'
5
+
6
+ module LicenseFinder
7
+ class Pipenv < PackageManager
8
+ def initialize(options = {})
9
+ super
10
+ @lockfile = Pathname('Pipfile.lock')
11
+ end
12
+
13
+ def current_packages
14
+ @current_packages ||=
15
+ begin
16
+ packages = {}
17
+ each_dependency(groups: allowed_groups) do |name, data, group|
18
+ version = canonicalize(data['version'])
19
+ package = packages.fetch(key_for(name, version)) do |key|
20
+ packages[key] = build_package_for(name, version)
21
+ end
22
+ package.groups << group
23
+ end
24
+ packages.values
25
+ end
26
+ end
27
+
28
+ def possible_package_paths
29
+ project_path ? [project_path.join(@lockfile)] : [@lockfile]
30
+ end
31
+
32
+ private
33
+
34
+ def each_dependency(groups: [])
35
+ dependencies = JSON.parse(IO.read(detected_package_path))
36
+ groups.each do |group|
37
+ dependencies[group].each do |name, data|
38
+ yield name, data, group
39
+ end
40
+ end
41
+ end
42
+
43
+ def canonicalize(version)
44
+ version.sub(/^==/, '')
45
+ end
46
+
47
+ def build_package_for(name, version)
48
+ PipPackage.new(name, version, PyPI.definition(name, version))
49
+ end
50
+
51
+ def key_for(name, version)
52
+ "#{name}-#{version}"
53
+ end
54
+
55
+ def allowed_groups
56
+ %w[default develop] - ignored_groups.to_a
57
+ end
58
+
59
+ def ignored_groups
60
+ @ignored_groups || []
61
+ end
62
+ end
63
+ end