license_finder 5.11.1 → 6.0.0

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 (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