license_finder 7.0.1 → 7.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +7 -0
- data/.pre-commit-hooks.yaml +10 -0
- data/.rubocop.yml +5 -1
- data/CHANGELOG.md +41 -0
- data/CONTRIBUTING.md +1 -0
- data/Dockerfile +129 -122
- data/README.md +53 -14
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/ci/pipelines/pull-request.yml.erb +29 -32
- data/ci/pipelines/release.yml.erb +17 -41
- data/ci/scripts/run-tests.sh +20 -4
- data/ci/tasks/rubocop.yml +3 -3
- data/ci/tasks/update-changelog.yml +2 -2
- data/dlf +6 -1
- data/lib/license_finder/cli/base.rb +2 -0
- data/lib/license_finder/cli/licenses.rb +8 -3
- data/lib/license_finder/cli/main.rb +3 -1
- data/lib/license_finder/configuration.rb +8 -0
- data/lib/license_finder/core.rb +4 -2
- data/lib/license_finder/decision_applier.rb +1 -1
- data/lib/license_finder/decisions.rb +24 -6
- data/lib/license_finder/license/definitions.rb +129 -19
- data/lib/license_finder/license/templates/AGPL3.txt +661 -0
- data/lib/license_finder/license/templates/Apache2.txt +0 -2
- data/lib/license_finder/license/templates/Artistic.txt +128 -0
- data/lib/license_finder/license/templates/CC01_alt.txt +31 -0
- data/lib/license_finder/license/templates/CDDL1_1.txt +123 -0
- data/lib/license_finder/license/templates/CPL1.txt +217 -0
- data/lib/license_finder/license/templates/EPL2.txt +80 -0
- data/lib/license_finder/license/templates/Unlicense.txt +24 -0
- data/lib/license_finder/license/text.rb +4 -0
- data/lib/license_finder/license.rb +1 -1
- data/lib/license_finder/manual_licenses.rb +79 -0
- data/lib/license_finder/package.rb +1 -0
- data/lib/license_finder/package_manager.rb +2 -1
- data/lib/license_finder/package_managers/cargo.rb +1 -1
- data/lib/license_finder/package_managers/conan.rb +50 -8
- data/lib/license_finder/package_managers/dep.rb +43 -41
- data/lib/license_finder/package_managers/dotnet.rb +5 -2
- data/lib/license_finder/package_managers/go_dep.rb +1 -1
- data/lib/license_finder/package_managers/go_workspace.rb +3 -2
- data/lib/license_finder/package_managers/maven.rb +18 -10
- data/lib/license_finder/package_managers/npm.rb +14 -1
- data/lib/license_finder/package_managers/nuget.rb +5 -0
- data/lib/license_finder/package_managers/pip.rb +1 -1
- data/lib/license_finder/package_managers/pnpm.rb +126 -0
- data/lib/license_finder/package_managers/yarn.rb +69 -20
- data/lib/license_finder/package_utils/conan_info_parser.rb +2 -2
- data/lib/license_finder/package_utils/conan_info_parser_v2.rb +82 -0
- data/lib/license_finder/package_utils/license_files.rb +12 -2
- data/lib/license_finder/package_utils/licensing.rb +2 -1
- data/lib/license_finder/package_utils/maven_dependency_finder.rb +43 -1
- data/lib/license_finder/package_utils/notice_files.rb +14 -3
- data/lib/license_finder/package_utils/possible_license_file.rb +8 -2
- data/lib/license_finder/package_utils/pypi.rb +3 -1
- data/lib/license_finder/packages/maven_package.rb +13 -1
- data/lib/license_finder/packages/npm_package.rb +56 -9
- data/lib/license_finder/packages/pnpm_package.rb +13 -0
- data/lib/license_finder/printer.rb +2 -2
- data/lib/license_finder/reports/csv_report.rb +10 -1
- data/lib/license_finder/scanner.rb +3 -3
- data/license_finder.gemspec +12 -11
- metadata +54 -28
@@ -16,6 +16,8 @@ module LicenseFinder
|
|
16
16
|
NEWLINE_CHARACTER = /\n+/.freeze
|
17
17
|
QUOTE_COMMENT_CHARACTER = /^\s*>+/.freeze
|
18
18
|
ESCAPED_QUOTES = /\\"/.freeze
|
19
|
+
SPECIAL_CHARACTERS = /§/.freeze
|
20
|
+
SPECIAL_DASHES = /–/.freeze
|
19
21
|
|
20
22
|
def self.normalize_punctuation(text)
|
21
23
|
text.dup.force_encoding('UTF-8')
|
@@ -26,6 +28,8 @@ module LicenseFinder
|
|
26
28
|
.gsub(NEWLINE_CHARACTER, ' ')
|
27
29
|
.gsub(ESCAPED_QUOTES, '"')
|
28
30
|
.gsub(QUOTES, '"')
|
31
|
+
.gsub(SPECIAL_CHARACTERS, '?')
|
32
|
+
.gsub(SPECIAL_DASHES, '-')
|
29
33
|
.strip
|
30
34
|
rescue ArgumentError => _e
|
31
35
|
text
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LicenseFinder
|
4
|
+
class ManualLicenses
|
5
|
+
def initialize
|
6
|
+
@all_versions = {}
|
7
|
+
@specific_versions = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def licenses_of(name, version = nil)
|
11
|
+
return @all_versions[name] if @all_versions[name]
|
12
|
+
|
13
|
+
if version && @specific_versions[name] && @specific_versions[name][version]
|
14
|
+
@specific_versions[name][version]
|
15
|
+
else
|
16
|
+
Set.new
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def assign_to_all_versions(name, lic)
|
21
|
+
# Ex: licenses add foo_gem MIT => Adds MIT at "all" versions for this gem
|
22
|
+
|
23
|
+
@all_versions[name] ||= Set.new
|
24
|
+
@all_versions[name] << to_license(lic)
|
25
|
+
|
26
|
+
@specific_versions.delete(name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def assign_to_specific_versions(name, lic, versions)
|
30
|
+
# Ex: licenses add foo_gem MIT --version=1.0 => Adds MIT at only 1.0 for this gem
|
31
|
+
|
32
|
+
@specific_versions[name] ||= {}
|
33
|
+
versions.each do |version|
|
34
|
+
@specific_versions[name][version] ||= Set.new
|
35
|
+
@specific_versions[name][version] << to_license(lic)
|
36
|
+
end
|
37
|
+
|
38
|
+
@all_versions.delete(name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def unassign_from_all_versions(name, lic = nil)
|
42
|
+
if lic
|
43
|
+
# Ex: licenses remove foo_gem MIT => Removes MIT at all versions for this gem
|
44
|
+
@all_versions[name]&.delete(to_license(lic))
|
45
|
+
|
46
|
+
@specific_versions[name]&.each_value do |licenses|
|
47
|
+
licenses.delete(to_license(lic))
|
48
|
+
end
|
49
|
+
else
|
50
|
+
# Ex: licenses remove foo_gem => Removes all licenses for all versions of the gem
|
51
|
+
@all_versions.delete(name)
|
52
|
+
@specific_versions.delete(name)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def unassign_from_specific_versions(name, lic, versions)
|
57
|
+
return unless @specific_versions[name]
|
58
|
+
|
59
|
+
versions.each do |version|
|
60
|
+
if @specific_versions[name][version]
|
61
|
+
if lic
|
62
|
+
# Ex: licenses remove foo_gem MIT --version=1.0 => Removes MIT at only 1.0 for this gem
|
63
|
+
@specific_versions[name][version].delete(to_license(lic))
|
64
|
+
@specific_versions[name].delete(version) if @specific_versions[name][version].empty?
|
65
|
+
else
|
66
|
+
# Ex: licenses remove foo_gem --version=1.0 => Removes all licenses at only 1.0 for the gem
|
67
|
+
@specific_versions[name].delete(version)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def to_license(lic)
|
76
|
+
License.find_by_name(lic)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -187,6 +187,7 @@ require 'license_finder/packages/merged_package'
|
|
187
187
|
require 'license_finder/packages/nuget_package'
|
188
188
|
require 'license_finder/packages/conan_package'
|
189
189
|
require 'license_finder/packages/yarn_package'
|
190
|
+
require 'license_finder/packages/pnpm_package'
|
190
191
|
require 'license_finder/packages/sbt_package'
|
191
192
|
require 'license_finder/packages/cargo_package'
|
192
193
|
require 'license_finder/packages/composer_package'
|
@@ -136,7 +136,7 @@ module LicenseFinder
|
|
136
136
|
FileUtils.mkdir_p @log_directory
|
137
137
|
|
138
138
|
# replace whitespace with underscores and remove slashes
|
139
|
-
log_file_name = package_management_command&.gsub(/\s/, '_')&.
|
139
|
+
log_file_name = package_management_command&.gsub(/\s/, '_')&.delete('/')
|
140
140
|
log_file = File.join(@log_directory, "prepare_#{log_file_name || 'errors'}.log")
|
141
141
|
|
142
142
|
File.open(log_file, 'w') do |f|
|
@@ -158,6 +158,7 @@ require 'license_finder/package_managers/go_modules'
|
|
158
158
|
require 'license_finder/package_managers/trash'
|
159
159
|
require 'license_finder/package_managers/bundler'
|
160
160
|
require 'license_finder/package_managers/npm'
|
161
|
+
require 'license_finder/package_managers/pnpm'
|
161
162
|
require 'license_finder/package_managers/yarn'
|
162
163
|
require 'license_finder/package_managers/pip'
|
163
164
|
require 'license_finder/package_managers/pipenv'
|
@@ -6,7 +6,7 @@ module LicenseFinder
|
|
6
6
|
class Cargo < PackageManager
|
7
7
|
def current_packages
|
8
8
|
cargo_output.map do |package|
|
9
|
-
path = Dir.glob("#{Dir.home}/.cargo/registry/src
|
9
|
+
path = Dir.glob("#{Dir.home}/.cargo/registry/src/*/#{package['name']}-#{package['version']}").first
|
10
10
|
CargoPackage.new(package, logger: logger, install_path: path)
|
11
11
|
end
|
12
12
|
end
|
@@ -1,27 +1,69 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'license_finder/package_utils/conan_info_parser'
|
4
|
+
require 'license_finder/package_utils/conan_info_parser_v2'
|
4
5
|
|
5
6
|
module LicenseFinder
|
6
7
|
class Conan < PackageManager
|
7
8
|
def possible_package_paths
|
8
|
-
[project_path.join('conanfile.txt')]
|
9
|
+
[project_path.join('conanfile.txt'), project_path.join('conanfile.py')]
|
9
10
|
end
|
10
11
|
|
11
|
-
def
|
12
|
-
|
12
|
+
def license_file_is_good?(license_file_path)
|
13
|
+
!license_file_path.nil? && File.file?(license_file_path)
|
14
|
+
end
|
15
|
+
|
16
|
+
def license_file(project_path, name)
|
17
|
+
candidates = Dir.glob("#{project_path}/licenses/#{name}/**/LICENSE*")
|
18
|
+
candidates.each do |candidate|
|
19
|
+
return candidate if license_file_is_good?(candidate)
|
20
|
+
end
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def deps_list_conan_v1(project_path)
|
13
25
|
info_command = 'conan info .'
|
14
|
-
Dir.chdir(project_path) { Cmd.run(install_command) }
|
15
26
|
info_output, _stderr, _status = Dir.chdir(project_path) { Cmd.run(info_command) }
|
27
|
+
return nil if info_output.empty?
|
16
28
|
|
17
29
|
info_parser = ConanInfoParser.new
|
30
|
+
info_parser.parse(info_output)
|
31
|
+
end
|
32
|
+
|
33
|
+
def deps_list_conan_v2(project_path)
|
34
|
+
info_command = 'conan graph info .'
|
35
|
+
info_output, stderr, _status = Dir.chdir(project_path) { Cmd.run(info_command) }
|
36
|
+
if info_output.empty?
|
37
|
+
return if stderr.empty?
|
38
|
+
|
39
|
+
info_output = stderr
|
40
|
+
end
|
41
|
+
info_parser = ConanInfoParserV2.new
|
42
|
+
info_parser.parse(info_output)
|
43
|
+
end
|
44
|
+
|
45
|
+
def deps_list(project_path)
|
46
|
+
deps = deps_list_conan_v1(project_path)
|
47
|
+
deps = deps_list_conan_v2(project_path) if deps.nil? || deps.empty?
|
48
|
+
deps
|
49
|
+
end
|
50
|
+
|
51
|
+
def current_packages
|
52
|
+
install_command = 'conan install .'
|
53
|
+
Dir.chdir(project_path) { Cmd.run(install_command) }
|
54
|
+
|
55
|
+
deps = deps_list(project_path)
|
56
|
+
return [] if deps.nil?
|
18
57
|
|
19
|
-
deps = info_parser.parse(info_output)
|
20
58
|
deps.map do |dep|
|
21
59
|
name, version = dep['name'].split('/')
|
22
|
-
|
23
|
-
|
24
|
-
|
60
|
+
license_file_path = license_file(project_path, name)
|
61
|
+
|
62
|
+
next unless license_file_is_good?(license_file_path)
|
63
|
+
|
64
|
+
url = dep['homepage']
|
65
|
+
url = dep['url'] if url.nil?
|
66
|
+
ConanPackage.new(name, version, File.open(license_file_path).read, url)
|
25
67
|
end.compact
|
26
68
|
end
|
27
69
|
end
|
@@ -1,43 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
3
|
+
# Dep has been deprecated since 2020
|
4
|
+
#
|
5
|
+
# require 'tomlrb'
|
6
|
+
#
|
7
|
+
# module LicenseFinder
|
8
|
+
# class Dep < PackageManager
|
9
|
+
# def possible_package_paths
|
10
|
+
# [project_path.join('Gopkg.lock')]
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# def current_packages
|
14
|
+
# toml = Tomlrb.load_file(detected_package_path)
|
15
|
+
# projects = toml['projects']
|
16
|
+
#
|
17
|
+
# return [] if projects.nil?
|
18
|
+
#
|
19
|
+
# projects.map do |project|
|
20
|
+
# GoPackage.from_dependency({
|
21
|
+
# 'ImportPath' => project['name'],
|
22
|
+
# 'InstallPath' => project_path.join('vendor', project['name']),
|
23
|
+
# 'Rev' => project['revision'],
|
24
|
+
# 'Homepage' => repo_name(project['name'])
|
25
|
+
# }, nil, true)
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# def repo_name(name)
|
30
|
+
# name.split('/')[0..2].join('/')
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# def self.takes_priority_over
|
34
|
+
# Go15VendorExperiment
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# def prepare_command
|
38
|
+
# 'dep ensure -vendor-only'
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# def package_management_command
|
42
|
+
# 'dep'
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
# end
|
@@ -42,9 +42,13 @@ module LicenseFinder
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def read_license_urls
|
45
|
-
possible_spec_paths.flat_map do |path|
|
45
|
+
raw_licenses = possible_spec_paths.flat_map do |path|
|
46
46
|
Nuget.nuspec_license_urls(File.read(path)) if File.exist? path
|
47
47
|
end.compact
|
48
|
+
|
49
|
+
raw_licenses&.map! do |license|
|
50
|
+
license.gsub('https://licenses.nuget.org/', '')
|
51
|
+
end
|
48
52
|
end
|
49
53
|
|
50
54
|
def ==(other)
|
@@ -61,7 +65,6 @@ module LicenseFinder
|
|
61
65
|
package_metadatas = asset_files
|
62
66
|
.flat_map { |path| AssetFile.new(path).dependencies }
|
63
67
|
.uniq { |d| [d.name, d.version] }
|
64
|
-
|
65
68
|
package_metadatas.map do |d|
|
66
69
|
path = Dir.glob("#{Dir.home}/.nuget/packages/#{d.name.downcase}/#{d.version}").first
|
67
70
|
NugetPackage.new(d.name, d.version, spec_licenses: d.read_license_urls, install_path: path)
|
@@ -56,7 +56,7 @@ module LicenseFinder
|
|
56
56
|
packages_grouped_by_revision = all_packages.group_by { |package| package['Rev'] }
|
57
57
|
result = []
|
58
58
|
|
59
|
-
packages_grouped_by_revision.
|
59
|
+
packages_grouped_by_revision.each_value do |packages_in_group|
|
60
60
|
all_paths_in_group = packages_in_group.map { |p| p['ImportPath'] }
|
61
61
|
common_paths = CommonPathHelper.longest_common_paths(all_paths_in_group)
|
62
62
|
package_info = packages_in_group.first
|
@@ -51,11 +51,12 @@ module LicenseFinder
|
|
51
51
|
def active?
|
52
52
|
return false if @strict_matching
|
53
53
|
|
54
|
+
# Dep has been deprecated since 2020
|
54
55
|
godep = LicenseFinder::GoDep.new(project_path: Pathname(project_path))
|
55
|
-
dep = LicenseFinder::Dep.new(project_path: Pathname(project_path))
|
56
56
|
# go workspace is only active if GoDep wasn't. There are some projects
|
57
57
|
# that will use the .envrc and have a Godep folder as well.
|
58
|
-
!!(!godep.active? && !dep.active? && envrc_path && ENVRC_REGEXP.match(IO.read(envrc_path)))
|
58
|
+
# !!(!godep.active? && !dep.active? && envrc_path && ENVRC_REGEXP.match(IO.read(envrc_path)))
|
59
|
+
!!(!godep.active? && envrc_path && ENVRC_REGEXP.match(IO.read(envrc_path)))
|
59
60
|
end
|
60
61
|
|
61
62
|
private
|
@@ -13,22 +13,20 @@ module LicenseFinder
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def current_packages
|
16
|
+
# Generate a file "target/generated-resources/licenses.xml" that contains a list of
|
17
|
+
# dependencies including their groupId, artifactId, version and license (name, file, url).
|
18
|
+
# The license file downloaded this way, however, is a generic one without author information.
|
19
|
+
# This file also does not contain further information about the package like its name,
|
20
|
+
# description or website URL.
|
16
21
|
command = "#{package_management_command} org.codehaus.mojo:license-maven-plugin:download-licenses"
|
17
22
|
command += " -Dlicense.excludedScopes=#{@ignored_groups.to_a.join(',')}" if @ignored_groups && !@ignored_groups.empty?
|
18
23
|
command += " #{@maven_options}" unless @maven_options.nil?
|
19
24
|
_stdout, stderr, status = Dir.chdir(project_path) { Cmd.run(command) }
|
20
25
|
raise "Command '#{command}' failed to execute: #{stderr}" unless status.success?
|
21
26
|
|
22
|
-
dependencies = MavenDependencyFinder.new(project_path).dependencies
|
23
|
-
packages = dependencies.
|
24
|
-
|
25
|
-
'GroupTags' => { 'licenses' => 'license', 'dependencies' => 'dependency' },
|
26
|
-
'ForceArray' => %w[license dependency]
|
27
|
-
}
|
28
|
-
contents = XmlSimple.xml_in(xml, options)['dependencies']
|
29
|
-
contents.map do |dep|
|
30
|
-
MavenPackage.new(dep, logger: logger, include_groups: @include_groups)
|
31
|
-
end
|
27
|
+
dependencies = MavenDependencyFinder.new(project_path, maven_repository_path).dependencies
|
28
|
+
packages = dependencies.map do |dep|
|
29
|
+
MavenPackage.new(dep, logger: logger, include_groups: @include_groups)
|
32
30
|
end
|
33
31
|
packages.uniq
|
34
32
|
end
|
@@ -57,5 +55,15 @@ module LicenseFinder
|
|
57
55
|
|
58
56
|
stdout.include?('null object or invalid expression')
|
59
57
|
end
|
58
|
+
|
59
|
+
# Look up the path of the Maven repository (e.g. ~/.m2)
|
60
|
+
def maven_repository_path
|
61
|
+
command = "#{package_management_command} help:evaluate -Dexpression=settings.localRepository -q -DforceStdout"
|
62
|
+
command += " #{@maven_options}" unless @maven_options.nil?
|
63
|
+
stdout, stderr, status = Dir.chdir(project_path) { Cmd.run(command) }
|
64
|
+
raise "Command '#{command}' failed to execute: #{stderr}" unless status.success?
|
65
|
+
|
66
|
+
Pathname(stdout)
|
67
|
+
end
|
60
68
|
end
|
61
69
|
end
|
@@ -39,7 +39,7 @@ module LicenseFinder
|
|
39
39
|
private
|
40
40
|
|
41
41
|
def npm_json
|
42
|
-
command = "#{package_management_command} list --json --long#{production_flag}"
|
42
|
+
command = "#{package_management_command} list --json --long#{all_flag}#{production_flag}"
|
43
43
|
command += " #{@npm_options}" unless @npm_options.nil?
|
44
44
|
stdout, stderr, status = Dir.chdir(project_path) { Cmd.run(command) }
|
45
45
|
# we can try and continue if we got an exit status 1 - unmet peer dependency
|
@@ -53,5 +53,18 @@ module LicenseFinder
|
|
53
53
|
|
54
54
|
@ignored_groups.include?('devDependencies') ? ' --production' : ''
|
55
55
|
end
|
56
|
+
|
57
|
+
def all_flag
|
58
|
+
npm_version >= 7 ? ' --all' : ''
|
59
|
+
end
|
60
|
+
|
61
|
+
def npm_version
|
62
|
+
command = "#{package_management_command} -v"
|
63
|
+
stdout, stderr, status = Dir.chdir(project_path) { Cmd.run(command) }
|
64
|
+
raise "Command '#{command}' failed to execute: #{stderr}" unless status.success?
|
65
|
+
|
66
|
+
version = stdout.split('.').map(&:to_i)
|
67
|
+
version[0]
|
68
|
+
end
|
56
69
|
end
|
57
70
|
end
|
@@ -51,6 +51,10 @@ module LicenseFinder
|
|
51
51
|
def current_packages
|
52
52
|
dependencies.each_with_object({}) do |dep, memo|
|
53
53
|
licenses = license_urls(dep)
|
54
|
+
licenses&.map! do |license|
|
55
|
+
license.gsub('https://licenses.nuget.org/', '')
|
56
|
+
end
|
57
|
+
|
54
58
|
path = Dir.glob("#{Dir.home}/.nuget/packages/#{dep.name.downcase}/#{dep.version}").first
|
55
59
|
|
56
60
|
memo[dep.name] ||= NugetPackage.new(dep.name, dep.version, spec_licenses: licenses, install_path: path)
|
@@ -60,6 +64,7 @@ module LicenseFinder
|
|
60
64
|
|
61
65
|
def license_urls(dep)
|
62
66
|
files = Dir["**/#{dep.name}.#{dep.version}.nupkg"]
|
67
|
+
|
63
68
|
return nil if files.empty?
|
64
69
|
|
65
70
|
file = files.first
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'tempfile'
|
5
|
+
|
6
|
+
module LicenseFinder
|
7
|
+
class PNPM < PackageManager
|
8
|
+
def initialize(options = {})
|
9
|
+
super
|
10
|
+
@pnpm_options = options[:pnpm_options]
|
11
|
+
end
|
12
|
+
|
13
|
+
SHELL_COMMAND = 'pnpm licenses list --json --long'
|
14
|
+
|
15
|
+
def possible_package_paths
|
16
|
+
[project_path.join('pnpm-lock.yaml')]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.takes_priority_over
|
20
|
+
NPM
|
21
|
+
end
|
22
|
+
|
23
|
+
def current_packages
|
24
|
+
# check if the minimum version of PNPM is met
|
25
|
+
raise 'The minimum PNPM version is not met, requires 7.17.0 or later' unless supported_pnpm?
|
26
|
+
|
27
|
+
# check if the project directory has workspace file
|
28
|
+
cmd = PNPM::SHELL_COMMAND.to_s
|
29
|
+
cmd += ' --no-color'
|
30
|
+
cmd += ' --recursive' unless project_has_workspaces == false
|
31
|
+
cmd += " --dir #{project_path}" unless project_path.nil?
|
32
|
+
cmd += " #{@pnpm_options}" unless @pnpm_options.nil?
|
33
|
+
|
34
|
+
stdout, stderr, status = Cmd.run(cmd)
|
35
|
+
raise "Command '#{cmd}' failed to execute: #{stderr}" unless status.success?
|
36
|
+
|
37
|
+
json_objects = JSON.parse(stdout)
|
38
|
+
get_pnpm_packages(json_objects)
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_pnpm_packages(json_objects)
|
42
|
+
packages = []
|
43
|
+
incompatible_packages = []
|
44
|
+
|
45
|
+
json_objects.map do |_, value|
|
46
|
+
value.each do |pkg|
|
47
|
+
name = pkg['name']
|
48
|
+
|
49
|
+
if pkg['version']
|
50
|
+
version = pkg['version']
|
51
|
+
elsif pkg['versions']
|
52
|
+
version = pkg['versions'][0]
|
53
|
+
end
|
54
|
+
|
55
|
+
license = pkg['license']
|
56
|
+
homepage = pkg['vendorUrl']
|
57
|
+
author = pkg['vendorName']
|
58
|
+
module_path = pkg['path']
|
59
|
+
|
60
|
+
package = PNPMPackage.new(
|
61
|
+
name,
|
62
|
+
version,
|
63
|
+
spec_licenses: [license],
|
64
|
+
homepage: homepage,
|
65
|
+
authors: author,
|
66
|
+
install_path: module_path
|
67
|
+
)
|
68
|
+
packages << package
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
packages + incompatible_packages.uniq
|
73
|
+
end
|
74
|
+
|
75
|
+
def package_management_command
|
76
|
+
'pnpm'
|
77
|
+
end
|
78
|
+
|
79
|
+
def prepare_command
|
80
|
+
'pnpm install --no-lockfile --ignore-scripts'
|
81
|
+
end
|
82
|
+
|
83
|
+
def prepare
|
84
|
+
prep_cmd = "#{prepare_command}#{production_flag}"
|
85
|
+
_stdout, stderr, status = Dir.chdir(project_path) { Cmd.run(prep_cmd) }
|
86
|
+
|
87
|
+
return if status.success?
|
88
|
+
|
89
|
+
log_errors stderr
|
90
|
+
raise "Prepare command '#{prep_cmd}' failed" unless @prepare_no_fail
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def project_has_workspaces
|
96
|
+
Dir.chdir(project_path) do
|
97
|
+
return File.file?('pnpm-workspace.yaml')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# PNPM introduced the licenses command in 7.17.0
|
102
|
+
def supported_pnpm?
|
103
|
+
Dir.chdir(project_path) do
|
104
|
+
version_string, stderr_str, status = Cmd.run('pnpm --version')
|
105
|
+
raise "Command 'pnpm -v' failed to execute: #{stderr_str}" unless status.success?
|
106
|
+
|
107
|
+
version = version_string.split('.').map(&:to_i)
|
108
|
+
major = version[0]
|
109
|
+
minor = version[1]
|
110
|
+
patch = version[1]
|
111
|
+
|
112
|
+
return true if major > 7
|
113
|
+
return true if major == 7 && minor > 17
|
114
|
+
return true if major == 7 && minor == 17 && patch >= 0
|
115
|
+
|
116
|
+
return false
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def production_flag
|
121
|
+
return '' if @ignored_groups.nil?
|
122
|
+
|
123
|
+
@ignored_groups.include?('devDependencies') ? ' --prod' : ''
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|