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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +25 -0
- data/CONTRIBUTING.md +1 -1
- data/Dockerfile +9 -14
- data/LICENSE +1 -1
- data/README.md +18 -18
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/bin/license_finder_pip.py +6 -2
- data/ci/pipelines/pull-request.yml.erb +14 -0
- data/ci/pipelines/release.yml.erb +24 -0
- data/lib/license_finder/cli.rb +2 -2
- data/lib/license_finder/cli/approvals.rb +1 -0
- data/lib/license_finder/cli/dependencies.rb +3 -2
- data/lib/license_finder/cli/main.rb +8 -8
- data/lib/license_finder/cli/makes_decisions.rb +3 -0
- data/lib/license_finder/cli/permitted_licenses.rb +32 -0
- data/lib/license_finder/cli/restricted_licenses.rb +32 -0
- data/lib/license_finder/core.rb +1 -1
- data/lib/license_finder/decision_applier.rb +6 -6
- data/lib/license_finder/decisions.rb +19 -19
- data/lib/license_finder/license_aggregator.rb +2 -2
- data/lib/license_finder/package.rb +15 -15
- data/lib/license_finder/package_manager.rb +1 -0
- data/lib/license_finder/package_managers/bundler.rb +3 -1
- data/lib/license_finder/package_managers/composer.rb +1 -1
- data/lib/license_finder/package_managers/pip.rb +11 -19
- data/lib/license_finder/package_managers/pipenv.rb +63 -0
- data/lib/license_finder/package_utils/pypi.rb +41 -0
- data/lib/license_finder/packages/merged_package.rb +2 -2
- data/lib/license_finder/reports/templates/html_report.erb +2 -2
- data/lib/license_finder/reports/templates/markdown_report.erb +2 -2
- data/lib/license_finder/scanner.rb +4 -2
- data/lib/license_finder/shared_helpers/common_path.rb +3 -1
- data/license_finder.gemspec +5 -6
- metadata +15 -14
- data/lib/license_finder/cli/blacklist.rb +0 -32
- data/lib/license_finder/cli/whitelist.rb +0 -32
@@ -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
|
13
|
-
def add(name, license, version
|
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
|
-
|
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
|
124
|
-
say '
|
125
|
-
say report_of(
|
123
|
+
unless restricted.empty?
|
124
|
+
say 'Restricted dependencies:', :red
|
125
|
+
say report_of(restricted)
|
126
126
|
end
|
127
127
|
|
128
|
-
other_unapproved = unapproved -
|
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
|
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 '
|
172
|
-
subcommand '
|
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
|
data/lib/license_finder/core.rb
CHANGED
@@ -42,7 +42,7 @@ module LicenseFinder
|
|
42
42
|
end
|
43
43
|
|
44
44
|
extend Forwardable
|
45
|
-
def_delegators :decision_applier, :acknowledged, :unapproved, :
|
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
|
18
|
-
acknowledged.select(&:
|
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.
|
57
|
-
package.
|
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.
|
61
|
-
package.
|
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, :
|
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
|
40
|
-
@
|
39
|
+
def permitted?(lic)
|
40
|
+
@permitted.include?(lic)
|
41
41
|
end
|
42
42
|
|
43
|
-
def
|
44
|
-
@
|
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
|
-
@
|
72
|
-
@
|
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
|
124
|
-
@decisions << [:
|
125
|
-
@
|
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
|
130
|
-
@decisions << [:
|
131
|
-
@
|
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
|
136
|
-
@decisions << [:
|
137
|
-
@
|
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
|
142
|
-
@decisions << [:
|
143
|
-
@
|
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
|
|
@@ -48,8 +48,8 @@ module LicenseFinder
|
|
48
48
|
@groups = options[:groups] || []
|
49
49
|
|
50
50
|
## APPROVAL
|
51
|
-
@
|
52
|
-
@
|
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 `!
|
82
|
-
# DecisionApplier does not call `
|
83
|
-
# if a Package has been
|
84
|
-
(approved_manually? ||
|
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
|
88
|
-
@
|
87
|
+
def permitted!
|
88
|
+
@permitted = true
|
89
89
|
end
|
90
90
|
|
91
|
-
def
|
92
|
-
@
|
91
|
+
def permitted?
|
92
|
+
@permitted
|
93
93
|
end
|
94
94
|
|
95
|
-
def
|
96
|
-
@
|
95
|
+
def restricted!
|
96
|
+
@restricted = true
|
97
97
|
end
|
98
98
|
|
99
|
-
def
|
100
|
-
@
|
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
|
-
'
|
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
|
@@ -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
|
-
|
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
|
-
|
61
|
-
|
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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|