license_finder 7.1.0 → 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/.rubocop.yml +5 -1
- data/CHANGELOG.md +17 -0
- data/Dockerfile +80 -90
- data/README.md +6 -7
- 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/lib/license_finder/core.rb +2 -2
- data/lib/license_finder/license/definitions.rb +127 -19
- data/lib/license_finder/license/templates/AGPL3.txt +661 -0
- 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 +1 -1
- data/lib/license_finder/package_manager.rb +1 -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/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/pip.rb +1 -1
- data/lib/license_finder/package_managers/pnpm.rb +7 -1
- data/lib/license_finder/package_managers/yarn.rb +9 -9
- 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/packages/maven_package.rb +13 -1
- data/lib/license_finder/packages/npm_package.rb +37 -11
- data/lib/license_finder/printer.rb +2 -2
- data/lib/license_finder/scanner.rb +3 -3
- data/license_finder.gemspec +11 -10
- metadata +44 -22
@@ -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
|
@@ -43,7 +43,7 @@ module LicenseFinder
|
|
43
43
|
# Ex: licenses remove foo_gem MIT => Removes MIT at all versions for this gem
|
44
44
|
@all_versions[name]&.delete(to_license(lic))
|
45
45
|
|
46
|
-
@specific_versions[name]&.
|
46
|
+
@specific_versions[name]&.each_value do |licenses|
|
47
47
|
licenses.delete(to_license(lic))
|
48
48
|
end
|
49
49
|
else
|
@@ -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|
|
@@ -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
|
@@ -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
|
@@ -45,7 +45,13 @@ module LicenseFinder
|
|
45
45
|
json_objects.map do |_, value|
|
46
46
|
value.each do |pkg|
|
47
47
|
name = pkg['name']
|
48
|
-
|
48
|
+
|
49
|
+
if pkg['version']
|
50
|
+
version = pkg['version']
|
51
|
+
elsif pkg['versions']
|
52
|
+
version = pkg['versions'][0]
|
53
|
+
end
|
54
|
+
|
49
55
|
license = pkg['license']
|
50
56
|
homepage = pkg['vendorUrl']
|
51
57
|
author = pkg['vendorName']
|
@@ -22,7 +22,7 @@ module LicenseFinder
|
|
22
22
|
cmd += " #{@yarn_options}" unless @yarn_options.nil?
|
23
23
|
end
|
24
24
|
|
25
|
-
stdout, stderr, status = Cmd.run(cmd)
|
25
|
+
stdout, stderr, status = Dir.chdir(project_path) { Cmd.run(cmd) }
|
26
26
|
raise "Command '#{cmd}' failed to execute: #{stderr}" unless status.success?
|
27
27
|
|
28
28
|
json_strings = stdout.encode('ASCII', invalid: :replace, undef: :replace, replace: '?').split("\n")
|
@@ -80,7 +80,7 @@ module LicenseFinder
|
|
80
80
|
|
81
81
|
def yarn_version
|
82
82
|
Dir.chdir(project_path) do
|
83
|
-
version_string, stderr_str, status = Cmd.run('yarn -v')
|
83
|
+
version_string, stderr_str, status = Dir.chdir(project_path) { Cmd.run('yarn -v') }
|
84
84
|
raise "Command 'yarn -v' failed to execute: #{stderr_str}" unless status.success?
|
85
85
|
|
86
86
|
version = version_string.split('.').map(&:to_i)
|
@@ -96,8 +96,8 @@ module LicenseFinder
|
|
96
96
|
body = json_object['children']
|
97
97
|
|
98
98
|
body.each do |package_name, vendor_info|
|
99
|
-
valid_match = %r{(?<name
|
100
|
-
valid_match = %r{(?<name
|
99
|
+
valid_match = %r{(?<name>@?[\w/.-]+)@(?<manager>\D*):\D*(?<version>(\d+\.?)+)} =~ package_name.to_s
|
100
|
+
valid_match = %r{(?<name>@?[\w/.-]+)@virtual:.+#(\D*):\D*(?<version>(\d+\.?)+)} =~ package_name.to_s if manager.eql?('virtual')
|
101
101
|
|
102
102
|
if valid_match
|
103
103
|
homepage = vendor_info['children']['vendorUrl']
|
@@ -112,10 +112,10 @@ module LicenseFinder
|
|
112
112
|
)
|
113
113
|
packages << package
|
114
114
|
end
|
115
|
-
incompatible_match = /(?<name>[\w,\-]+)@[a-z]*:(?<version>(\.))/ =~ package_name.to_s
|
116
115
|
|
116
|
+
incompatible_match = %r{(?<name>@?[\w/.-]+)@[a-z]*:(?<version>(\.))} =~ package_name.to_s
|
117
117
|
if incompatible_match
|
118
|
-
package = YarnPackage.new(name, version, spec_licenses: [
|
118
|
+
package = YarnPackage.new(name, version, spec_licenses: [license])
|
119
119
|
incompatible_packages.push(package)
|
120
120
|
end
|
121
121
|
end
|
@@ -126,14 +126,14 @@ module LicenseFinder
|
|
126
126
|
|
127
127
|
def get_yarn1_packages(json_objects)
|
128
128
|
packages = []
|
129
|
-
incompatible_packages = []
|
130
129
|
if json_objects.last['type'] == 'table'
|
131
130
|
license_json = json_objects.pop['data']
|
132
131
|
packages = packages_from_json(license_json)
|
133
132
|
end
|
134
133
|
|
134
|
+
incompatible_packages = []
|
135
135
|
json_objects.each do |json_object|
|
136
|
-
match =
|
136
|
+
match = %r{(?<name>@?[\w/.-]+)@(?<version>(\d+\.?)+)} =~ json_object['data'].to_s
|
137
137
|
if match
|
138
138
|
package = YarnPackage.new(name, version, spec_licenses: ['unknown'])
|
139
139
|
incompatible_packages.push(package)
|
@@ -168,7 +168,7 @@ module LicenseFinder
|
|
168
168
|
def modules_folder
|
169
169
|
return @modules_folder if @modules_folder
|
170
170
|
|
171
|
-
stdout, _stderr, status = Cmd.run('yarn config get modules-folder')
|
171
|
+
stdout, _stderr, status = Dir.chdir(project_path) { Cmd.run('yarn config get modules-folder') }
|
172
172
|
@modules_folder = 'node_modules' if !status.success? || stdout.strip == 'undefined'
|
173
173
|
@modules_folder ||= stdout.strip
|
174
174
|
end
|
@@ -31,9 +31,9 @@ module LicenseFinder
|
|
31
31
|
def parse_key_val(line)
|
32
32
|
key, val = key_val(line)
|
33
33
|
if val
|
34
|
-
@current_project[key] = val
|
34
|
+
@current_project[key.downcase] = val
|
35
35
|
elsif line.start_with?(' ')
|
36
|
-
@current_key = key
|
36
|
+
@current_key = key.downcase
|
37
37
|
@current_vals = []
|
38
38
|
@state = :val_list
|
39
39
|
else
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LicenseFinder
|
4
|
+
class ConanInfoParserV2
|
5
|
+
def parse(info)
|
6
|
+
@lines = info.lines.map(&:chomp)
|
7
|
+
@state = :project_level # state of the state machine
|
8
|
+
@projects = [] # list of projects
|
9
|
+
@current_project = nil # current project being populated in the SM
|
10
|
+
@current_vals = [] # current val list being populate in the SM
|
11
|
+
@current_key = nil # current key to be associated with the current val
|
12
|
+
|
13
|
+
line = @lines.shift
|
14
|
+
line = @lines.shift while line != '======== Basic graph information ========'
|
15
|
+
|
16
|
+
while (line = @lines.shift)
|
17
|
+
next if line == ''
|
18
|
+
|
19
|
+
case @state
|
20
|
+
when :project_level
|
21
|
+
@current_project = {}
|
22
|
+
name, _id = line.strip.split('#')
|
23
|
+
@current_project['name'] = name
|
24
|
+
@state = :key_val
|
25
|
+
when :key_val
|
26
|
+
parse_key_val(line)
|
27
|
+
when :val_list
|
28
|
+
parse_val_list(line)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
wrap_up
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def parse_key_val(line)
|
37
|
+
key, val = key_val(line)
|
38
|
+
if val
|
39
|
+
@current_project[key.downcase] = val
|
40
|
+
elsif line.start_with?(' ')
|
41
|
+
@current_key = key.downcase
|
42
|
+
@current_vals = []
|
43
|
+
@state = :val_list
|
44
|
+
else
|
45
|
+
change_to_new_project_state line
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_val_list(line)
|
50
|
+
if val_list_level(line)
|
51
|
+
@current_vals << line.strip
|
52
|
+
else
|
53
|
+
@current_project[@current_key] = @current_vals
|
54
|
+
if line.start_with?(' ')
|
55
|
+
@state = :key_val
|
56
|
+
@lines.unshift(line)
|
57
|
+
else
|
58
|
+
change_to_new_project_state line
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def wrap_up
|
64
|
+
@current_project[@current_key] = @current_vals if @current_vals.count && @current_key
|
65
|
+
@projects << @current_project
|
66
|
+
end
|
67
|
+
|
68
|
+
def val_list_level(line)
|
69
|
+
line.start_with?(' ')
|
70
|
+
end
|
71
|
+
|
72
|
+
def change_to_new_project_state(line)
|
73
|
+
@state = :project_level
|
74
|
+
@projects << @current_project
|
75
|
+
@lines.unshift(line)
|
76
|
+
end
|
77
|
+
|
78
|
+
def key_val(info)
|
79
|
+
info.split(':', 2).map(&:strip!)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -27,7 +27,7 @@ module LicenseFinder
|
|
27
27
|
|
28
28
|
def paths_of_candidate_files
|
29
29
|
candidate_files_and_dirs
|
30
|
-
.flat_map { |path| path.directory? ? path.children : path }
|
30
|
+
.flat_map { |path| !path.is_a?(Zip::Entry) && path.directory? ? path.children : path }
|
31
31
|
.reject(&:directory?)
|
32
32
|
.uniq
|
33
33
|
end
|
@@ -35,7 +35,17 @@ module LicenseFinder
|
|
35
35
|
def candidate_files_and_dirs
|
36
36
|
return [] if install_path.nil?
|
37
37
|
|
38
|
-
|
38
|
+
if !install_path.extname.casecmp('.jar').zero?
|
39
|
+
Pathname.glob(install_path.join('**', CANDIDATE_PATH_WILDCARD))
|
40
|
+
else
|
41
|
+
candidates_from_zip
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def candidates_from_zip
|
46
|
+
Zip::File.open(install_path.to_s) do |zip_file|
|
47
|
+
zip_file.glob(CANDIDATE_PATH_WILDCARD, File::FNM_EXTGLOB)
|
48
|
+
end
|
39
49
|
end
|
40
50
|
end
|
41
51
|
end
|
@@ -11,7 +11,8 @@ module LicenseFinder
|
|
11
11
|
if activations_from_decisions.any? then activations_from_decisions
|
12
12
|
elsif activations_from_spec.any? then activations_from_spec
|
13
13
|
elsif activations_from_files.any? then activations_from_files
|
14
|
-
else
|
14
|
+
else
|
15
|
+
[default_activation]
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
@@ -2,14 +2,56 @@
|
|
2
2
|
|
3
3
|
module LicenseFinder
|
4
4
|
class MavenDependencyFinder
|
5
|
-
def initialize(project_path)
|
5
|
+
def initialize(project_path, m2_path)
|
6
6
|
@project_path = project_path
|
7
|
+
@m2_path = m2_path
|
7
8
|
end
|
8
9
|
|
9
10
|
def dependencies
|
11
|
+
options = {
|
12
|
+
'GroupTags' => { 'licenses' => 'license', 'dependencies' => 'dependency' },
|
13
|
+
'ForceArray' => %w[license dependency]
|
14
|
+
}
|
15
|
+
|
10
16
|
Pathname
|
11
17
|
.glob(@project_path.join('**', 'target', 'generated-resources', 'licenses.xml'))
|
12
18
|
.map(&:read)
|
19
|
+
.flat_map { |xml| XmlSimple.xml_in(xml, options)['dependencies'] }
|
20
|
+
.reject(&:empty?)
|
21
|
+
.each { |dep| add_info_from_m2(dep) }
|
22
|
+
end
|
23
|
+
|
24
|
+
# Add the name of the JAR file to allow later retrieval of license and notice files,
|
25
|
+
# and add the name, description and URL from the POM XML file.
|
26
|
+
def add_info_from_m2(dep)
|
27
|
+
m2_artifact_dir = @m2_path
|
28
|
+
.join(dep['groupId'].tr('.', '/'))
|
29
|
+
.join(dep['artifactId'])
|
30
|
+
.join(dep['version'])
|
31
|
+
artifact_basename = "#{dep['artifactId']}-#{dep['version']}"
|
32
|
+
|
33
|
+
# Basic support for Maven classifiers. So far, only "jakarta" is supported. Unfortunately,
|
34
|
+
# we do not have access to the "classifier" field here (licenses.xml does not have it).
|
35
|
+
jar_file = m2_artifact_dir.join("#{artifact_basename}.jar")
|
36
|
+
jar_file = m2_artifact_dir.join("#{artifact_basename}-jakarta.jar") unless File.exist?(jar_file)
|
37
|
+
|
38
|
+
dep.store('jarFile', jar_file)
|
39
|
+
|
40
|
+
add_info_from_pom(m2_artifact_dir.join("#{artifact_basename}.pom"), dep)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Extract name, description and URL from pom.xml
|
44
|
+
def add_info_from_pom(pom_file, dep)
|
45
|
+
pom = XmlSimple.xml_in(pom_file.read, { 'ForceArray' => false })
|
46
|
+
|
47
|
+
name = pom['name']
|
48
|
+
dep.store('summary', name) unless name.nil?
|
49
|
+
|
50
|
+
description = pom['description']
|
51
|
+
dep.store('description', description) unless description.nil?
|
52
|
+
|
53
|
+
url = pom['url']
|
54
|
+
dep.store('homepage', url) unless url.nil?
|
13
55
|
end
|
14
56
|
end
|
15
57
|
end
|
@@ -5,7 +5,8 @@ require 'license_finder/package_utils/possible_license_file'
|
|
5
5
|
module LicenseFinder
|
6
6
|
class NoticeFiles
|
7
7
|
CANDIDATE_FILE_NAMES = %w[NOTICE Notice].freeze
|
8
|
-
|
8
|
+
CANDIDATE_PATH_WILDCARD_STRICT = "{#{CANDIDATE_FILE_NAMES.join(',')}}*"
|
9
|
+
CANDIDATE_PATH_WILDCARD = "*#{CANDIDATE_PATH_WILDCARD_STRICT}"
|
9
10
|
|
10
11
|
def self.find(install_path, options = {})
|
11
12
|
new(install_path).find(options)
|
@@ -26,7 +27,7 @@ module LicenseFinder
|
|
26
27
|
|
27
28
|
def paths_of_candidate_files
|
28
29
|
candidate_files_and_dirs
|
29
|
-
.flat_map { |path| path.directory? ? path.children : path }
|
30
|
+
.flat_map { |path| !path.is_a?(Zip::Entry) && path.directory? ? path.children : path }
|
30
31
|
.reject(&:directory?)
|
31
32
|
.uniq
|
32
33
|
end
|
@@ -34,7 +35,17 @@ module LicenseFinder
|
|
34
35
|
def candidate_files_and_dirs
|
35
36
|
return [] if install_path.nil?
|
36
37
|
|
37
|
-
|
38
|
+
if !install_path.extname.casecmp('.jar').zero?
|
39
|
+
Pathname.glob(install_path.join('**', CANDIDATE_PATH_WILDCARD))
|
40
|
+
else
|
41
|
+
candidates_from_zip
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def candidates_from_zip
|
46
|
+
Zip::File.open(install_path.to_s) do |zip_file|
|
47
|
+
zip_file.glob("*/#{CANDIDATE_PATH_WILDCARD_STRICT}", File::FNM_EXTGLOB)
|
48
|
+
end
|
38
49
|
end
|
39
50
|
end
|
40
51
|
end
|