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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -1
  3. data/CHANGELOG.md +17 -0
  4. data/Dockerfile +80 -90
  5. data/README.md +6 -7
  6. data/Rakefile +1 -1
  7. data/VERSION +1 -1
  8. data/ci/pipelines/pull-request.yml.erb +29 -32
  9. data/ci/pipelines/release.yml.erb +17 -41
  10. data/ci/scripts/run-tests.sh +20 -4
  11. data/ci/tasks/rubocop.yml +3 -3
  12. data/ci/tasks/update-changelog.yml +2 -2
  13. data/lib/license_finder/core.rb +2 -2
  14. data/lib/license_finder/license/definitions.rb +127 -19
  15. data/lib/license_finder/license/templates/AGPL3.txt +661 -0
  16. data/lib/license_finder/license/templates/Artistic.txt +128 -0
  17. data/lib/license_finder/license/templates/CC01_alt.txt +31 -0
  18. data/lib/license_finder/license/templates/CDDL1_1.txt +123 -0
  19. data/lib/license_finder/license/templates/CPL1.txt +217 -0
  20. data/lib/license_finder/license/templates/EPL2.txt +80 -0
  21. data/lib/license_finder/license/templates/Unlicense.txt +24 -0
  22. data/lib/license_finder/license/text.rb +4 -0
  23. data/lib/license_finder/license.rb +1 -1
  24. data/lib/license_finder/manual_licenses.rb +1 -1
  25. data/lib/license_finder/package_manager.rb +1 -1
  26. data/lib/license_finder/package_managers/cargo.rb +1 -1
  27. data/lib/license_finder/package_managers/conan.rb +50 -8
  28. data/lib/license_finder/package_managers/dep.rb +43 -41
  29. data/lib/license_finder/package_managers/go_dep.rb +1 -1
  30. data/lib/license_finder/package_managers/go_workspace.rb +3 -2
  31. data/lib/license_finder/package_managers/maven.rb +18 -10
  32. data/lib/license_finder/package_managers/npm.rb +14 -1
  33. data/lib/license_finder/package_managers/pip.rb +1 -1
  34. data/lib/license_finder/package_managers/pnpm.rb +7 -1
  35. data/lib/license_finder/package_managers/yarn.rb +9 -9
  36. data/lib/license_finder/package_utils/conan_info_parser.rb +2 -2
  37. data/lib/license_finder/package_utils/conan_info_parser_v2.rb +82 -0
  38. data/lib/license_finder/package_utils/license_files.rb +12 -2
  39. data/lib/license_finder/package_utils/licensing.rb +2 -1
  40. data/lib/license_finder/package_utils/maven_dependency_finder.rb +43 -1
  41. data/lib/license_finder/package_utils/notice_files.rb +14 -3
  42. data/lib/license_finder/package_utils/possible_license_file.rb +8 -2
  43. data/lib/license_finder/packages/maven_package.rb +13 -1
  44. data/lib/license_finder/packages/npm_package.rb +37 -11
  45. data/lib/license_finder/printer.rb +2 -2
  46. data/lib/license_finder/scanner.rb +3 -3
  47. data/license_finder.gemspec +11 -10
  48. 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
@@ -85,7 +85,7 @@ module LicenseFinder
85
85
  attr_reader :short_name, :pretty_name, :other_names, :spdx_id, :matcher
86
86
 
87
87
  def names
88
- ([short_name, pretty_name] + other_names).uniq
88
+ ([short_name, pretty_name, spdx_id] + other_names).uniq
89
89
  end
90
90
  end
91
91
 
@@ -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]&.each do |_version, licenses|
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/, '_')&.gsub(%r{/}, '')
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/**/#{package['name']}-#{package['version']}").first
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 current_packages
12
- install_command = 'conan install .'
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
- url = dep['URL']
23
- license_file_path = Dir.glob("#{project_path}/licenses/#{name}/**/LICENSE*").first
24
- ConanPackage.new(name, version, File.open(license_file_path).read, url) unless name == 'conanfile.txt'
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
- require 'tomlrb'
4
-
5
- module LicenseFinder
6
- class Dep < PackageManager
7
- def possible_package_paths
8
- [project_path.join('Gopkg.lock')]
9
- end
10
-
11
- def current_packages
12
- toml = Tomlrb.load_file(detected_package_path)
13
- projects = toml['projects']
14
-
15
- return [] if projects.nil?
16
-
17
- projects.map do |project|
18
- GoPackage.from_dependency({
19
- 'ImportPath' => project['name'],
20
- 'InstallPath' => project_path.join('vendor', project['name']),
21
- 'Rev' => project['revision'],
22
- 'Homepage' => repo_name(project['name'])
23
- }, nil, true)
24
- end
25
- end
26
-
27
- def repo_name(name)
28
- name.split('/')[0..2].join('/')
29
- end
30
-
31
- def self.takes_priority_over
32
- Go15VendorExperiment
33
- end
34
-
35
- def prepare_command
36
- 'dep ensure -vendor-only'
37
- end
38
-
39
- def package_management_command
40
- 'dep'
41
- end
42
- end
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.each do |_sha, packages_in_group|
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.flat_map do |xml|
24
- options = {
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
@@ -4,7 +4,7 @@ require 'json'
4
4
 
5
5
  module LicenseFinder
6
6
  class Pip < PackageManager
7
- DEFAULT_VERSION = '2'
7
+ DEFAULT_VERSION = '3'
8
8
 
9
9
  def initialize(options = {})
10
10
  super
@@ -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
- version = pkg['version']
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>[@,\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')
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: ['unknown'])
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 = /(?<name>[\w,\-]+)@(?<version>(\d+\.?)+)/ =~ json_object['data'].to_s
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
- Pathname.glob(install_path.join('**', CANDIDATE_PATH_WILDCARD))
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 [default_activation]
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
- CANDIDATE_PATH_WILDCARD = "*{#{CANDIDATE_FILE_NAMES.join(',')}}*"
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
- Pathname.glob(install_path.join('**', CANDIDATE_PATH_WILDCARD))
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