license_finder 7.0.1 → 7.2.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/.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
|