bibliothecary 7.3.4 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +1 -1
  3. data/README.md +8 -0
  4. data/bibliothecary.gemspec +1 -0
  5. data/lib/bibliothecary/analyser/analysis.rb +110 -0
  6. data/lib/bibliothecary/analyser/determinations.rb +27 -0
  7. data/lib/bibliothecary/analyser/matchers.rb +64 -0
  8. data/lib/bibliothecary/analyser.rb +32 -188
  9. data/lib/bibliothecary/cli.rb +3 -3
  10. data/lib/bibliothecary/file_info.rb +2 -0
  11. data/lib/bibliothecary/multi_parsers/bundler_like_manifest.rb +22 -0
  12. data/lib/bibliothecary/multi_parsers/cyclonedx.rb +138 -0
  13. data/lib/bibliothecary/multi_parsers/json_runtime.rb +16 -0
  14. data/lib/bibliothecary/parsers/bower.rb +2 -2
  15. data/lib/bibliothecary/parsers/cargo.rb +4 -2
  16. data/lib/bibliothecary/parsers/carthage.rb +6 -6
  17. data/lib/bibliothecary/parsers/clojars.rb +2 -2
  18. data/lib/bibliothecary/parsers/cocoapods.rb +5 -4
  19. data/lib/bibliothecary/parsers/conda.rb +11 -5
  20. data/lib/bibliothecary/parsers/cpan.rb +2 -2
  21. data/lib/bibliothecary/parsers/cran.rb +3 -1
  22. data/lib/bibliothecary/parsers/dub.rb +3 -2
  23. data/lib/bibliothecary/parsers/elm.rb +2 -1
  24. data/lib/bibliothecary/parsers/generic.rb +3 -3
  25. data/lib/bibliothecary/parsers/go.rb +13 -11
  26. data/lib/bibliothecary/parsers/hackage.rb +4 -2
  27. data/lib/bibliothecary/parsers/haxelib.rb +1 -0
  28. data/lib/bibliothecary/parsers/hex.rb +6 -4
  29. data/lib/bibliothecary/parsers/julia.rb +2 -2
  30. data/lib/bibliothecary/parsers/maven.rb +27 -10
  31. data/lib/bibliothecary/parsers/meteor.rb +1 -0
  32. data/lib/bibliothecary/parsers/npm.rb +7 -5
  33. data/lib/bibliothecary/parsers/nuget.rb +10 -7
  34. data/lib/bibliothecary/parsers/packagist.rb +4 -2
  35. data/lib/bibliothecary/parsers/pub.rb +2 -2
  36. data/lib/bibliothecary/parsers/pypi.rb +11 -9
  37. data/lib/bibliothecary/parsers/rubygems.rb +7 -4
  38. data/lib/bibliothecary/parsers/shard.rb +2 -2
  39. data/lib/bibliothecary/parsers/swift_pm.rb +4 -2
  40. data/lib/bibliothecary/runner.rb +8 -3
  41. data/lib/bibliothecary/version.rb +1 -1
  42. data/lib/bibliothecary.rb +3 -0
  43. metadata +26 -6
@@ -0,0 +1,138 @@
1
+ require 'json'
2
+ require 'ox'
3
+
4
+ # packageurl-ruby uses pattern-matching (https://docs.ruby-lang.org/en/2.7.0/NEWS.html#label-Pattern+matching)
5
+ # which warns a whole bunch in Ruby 2.7 as being an experimental feature, but has
6
+ # been accepted in Ruby 3.0 (https://rubyreferences.github.io/rubychanges/3.0.html#pattern-matching).
7
+ Warning[:experimental] = false
8
+ require 'package_url'
9
+ Warning[:experimental] = true
10
+
11
+ module Bibliothecary
12
+ module MultiParsers
13
+ module CycloneDX
14
+ include Bibliothecary::Analyser
15
+ include Bibliothecary::Analyser::TryCache
16
+
17
+ NoComponents = Class.new(StandardError)
18
+
19
+ class ManifestEntries
20
+ # If a purl type (key) exists, it will be used in a manifest for
21
+ # the key's value. If not, it's ignored.
22
+ #
23
+ # https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst
24
+ PURL_TYPE_MAPPING = {
25
+ "golang" => :go,
26
+ "maven" => :maven,
27
+ "npm" => :npm,
28
+ "cargo" => :cargo,
29
+ "composer" => :packagist,
30
+ "conda" => :conda,
31
+ "cran" => :cran,
32
+ "gem" => :rubygems,
33
+ "hackage" => :hackage,
34
+ "hex" => :hex,
35
+ "nuget" => :nuget,
36
+ "pypi" => :pypi,
37
+ "swift" => :swift_pm
38
+ }
39
+
40
+ attr_reader :manifests
41
+
42
+ def initialize
43
+ @manifests = {}
44
+ end
45
+
46
+ def <<(purl)
47
+ mapping = PURL_TYPE_MAPPING[purl.type]
48
+ return unless mapping
49
+
50
+ @manifests[mapping] ||= Set.new
51
+ @manifests[mapping] << {
52
+ name: self.class.full_name_for_purl(purl),
53
+ requirement: purl.version,
54
+ type: 'lockfile'
55
+ }
56
+ end
57
+
58
+ def [](key)
59
+ @manifests[key]&.to_a
60
+ end
61
+
62
+ # @return [String] The properly namespaced package name
63
+ def self.full_name_for_purl(purl)
64
+ parts = [purl.namespace, purl.name].compact
65
+
66
+ case purl.type
67
+ when "maven"
68
+ parts.join(':')
69
+ else
70
+ parts.join('/')
71
+ end
72
+ end
73
+ end
74
+
75
+ def self.mapping
76
+ {
77
+ match_filename('cyclonedx.json') => {
78
+ kind: 'lockfile',
79
+ parser: :parse_cyclonedx_json
80
+ },
81
+ match_filename('cyclonedx.xml') => {
82
+ kind: 'lockfile',
83
+ parser: :parse_cyclonedx_xml
84
+ }
85
+ }
86
+ end
87
+
88
+ def parse_cyclonedx_json(file_contents, options: {})
89
+ manifest = nil
90
+
91
+ manifest = try_cache(options, options[:filename]) do
92
+ JSON.parse(file_contents)
93
+ end
94
+
95
+ raise NoComponents unless manifest["components"]
96
+
97
+ entries = ManifestEntries.new
98
+
99
+ manifest["components"].each_with_object(entries) do |component, obj|
100
+ next unless component["purl"]
101
+
102
+ purl = PackageURL.parse(component["purl"])
103
+
104
+ obj << purl
105
+ end
106
+
107
+ entries[platform_name.to_sym]
108
+ end
109
+
110
+ def parse_cyclonedx_xml(file_contents, options: {})
111
+ manifest = try_cache(options, options[:filename]) do
112
+ Ox.parse(file_contents)
113
+ end
114
+
115
+ root = manifest
116
+ if root.respond_to?(:bom)
117
+ root = root.bom
118
+ end
119
+
120
+ raise NoComponents unless root.locate('components').first
121
+
122
+ entries = ManifestEntries.new
123
+
124
+ root.locate('components/*').each_with_object(entries) do |component, obj|
125
+ purl_node = component.locate("purl").first
126
+
127
+ next unless purl_node
128
+
129
+ purl = PackageURL.parse(purl_node.text)
130
+
131
+ obj << purl
132
+ end
133
+
134
+ entries[platform_name.to_sym]
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,16 @@
1
+ module Bibliothecary
2
+ module MultiParsers
3
+ # Provide JSON Runtime Manifest parsing
4
+ module JSONRuntime
5
+ def parse_json_runtime_manifest(file_contents, options: {})
6
+ JSON.parse(file_contents).fetch('dependencies',[]).map do |name, requirement|
7
+ {
8
+ name: name,
9
+ requirement: requirement,
10
+ type: 'runtime'
11
+ }
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -14,8 +14,8 @@ module Bibliothecary
14
14
  }
15
15
  end
16
16
 
17
- def self.parse_manifest(manifest)
18
- json = JSON.parse(manifest)
17
+ def self.parse_manifest(file_contents, options: {})
18
+ json = JSON.parse(file_contents)
19
19
  map_dependencies(json, 'dependencies', 'runtime') +
20
20
  map_dependencies(json, 'devDependencies', 'development')
21
21
  end
@@ -16,7 +16,9 @@ module Bibliothecary
16
16
  }
17
17
  end
18
18
 
19
- def self.parse_manifest(file_contents)
19
+ add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
20
+
21
+ def self.parse_manifest(file_contents, options: {})
20
22
  manifest = Tomlrb.parse(file_contents)
21
23
  manifest.fetch('dependencies', []).map do |name, requirement|
22
24
  if requirement.respond_to?(:fetch)
@@ -31,7 +33,7 @@ module Bibliothecary
31
33
  .compact
32
34
  end
33
35
 
34
- def self.parse_lockfile(file_contents)
36
+ def self.parse_lockfile(file_contents, options: {})
35
37
  manifest = Tomlrb.parse(file_contents)
36
38
  manifest.fetch('package',[]).map do |dependency|
37
39
  next if not dependency['source'] or not dependency['source'].start_with?('registry+')
@@ -20,16 +20,16 @@ module Bibliothecary
20
20
  }
21
21
  end
22
22
 
23
- def self.parse_cartfile(manifest)
24
- map_dependencies(manifest, 'cartfile')
23
+ def self.parse_cartfile(file_contents, options: {})
24
+ map_dependencies(file_contents, 'cartfile')
25
25
  end
26
26
 
27
- def self.parse_cartfile_private(manifest)
28
- map_dependencies(manifest, 'cartfile.private')
27
+ def self.parse_cartfile_private(file_contents, options: {})
28
+ map_dependencies(file_contents, 'cartfile.private')
29
29
  end
30
30
 
31
- def self.parse_cartfile_resolved(manifest)
32
- map_dependencies(manifest, 'cartfile.resolved')
31
+ def self.parse_cartfile_resolved(file_contents, options: {})
32
+ map_dependencies(file_contents, 'cartfile.resolved')
33
33
  end
34
34
 
35
35
  def self.map_dependencies(manifest, path)
@@ -15,8 +15,8 @@ module Bibliothecary
15
15
  }
16
16
  end
17
17
 
18
- def self.parse_manifest(manifest)
19
- response = Typhoeus.post("#{Bibliothecary.configuration.clojars_parser_host}/project.clj", body: manifest)
18
+ def self.parse_manifest(file_contents, options: {})
19
+ response = Typhoeus.post("#{Bibliothecary.configuration.clojars_parser_host}/project.clj", body: file_contents)
20
20
  raise Bibliothecary::RemoteParsingError.new("Http Error #{response.response_code} when contacting: #{Bibliothecary.configuration.clojars_parser_host}/project.clj", response.response_code) unless response.success?
21
21
  json = JSON.parse response.body
22
22
  index = json.index("dependencies")
@@ -5,6 +5,7 @@ module Bibliothecary
5
5
  module Parsers
6
6
  class CocoaPods
7
7
  include Bibliothecary::Analyser
8
+ extend Bibliothecary::MultiParsers::BundlerLikeManifest
8
9
 
9
10
  NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'.freeze
10
11
  NAME_VERSION_4 = /^ {4}#{NAME_VERSION}$/
@@ -32,7 +33,7 @@ module Bibliothecary
32
33
  }
33
34
  end
34
35
 
35
- def self.parse_podfile_lock(file_contents)
36
+ def self.parse_podfile_lock(file_contents, options: {})
36
37
  manifest = YAML.load file_contents
37
38
  manifest['PODS'].map do |row|
38
39
  pod = row.is_a?(String) ? row : row.keys.first
@@ -45,17 +46,17 @@ module Bibliothecary
45
46
  end.compact
46
47
  end
47
48
 
48
- def self.parse_podspec(file_contents)
49
+ def self.parse_podspec(file_contents, options: {})
49
50
  manifest = Gemnasium::Parser.send(:podspec, file_contents)
50
51
  parse_ruby_manifest(manifest)
51
52
  end
52
53
 
53
- def self.parse_podfile(file_contents)
54
+ def self.parse_podfile(file_contents, options: {})
54
55
  manifest = Gemnasium::Parser.send(:podfile, file_contents)
55
56
  parse_ruby_manifest(manifest)
56
57
  end
57
58
 
58
- def self.parse_json_manifest(file_contents)
59
+ def self.parse_json_manifest(file_contents, options: {})
59
60
  manifest = JSON.parse(file_contents)
60
61
  manifest['dependencies'].inject([]) do |deps, dep|
61
62
  deps.push({
@@ -26,13 +26,19 @@ module Bibliothecary
26
26
  }
27
27
  end
28
28
 
29
- def self.parse_conda(info, kind = "manifest")
30
- dependencies = call_conda_parser_web(info, kind)[kind.to_sym]
31
- dependencies.map { |dep| dep.merge(type: "runtime") }
29
+ add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
30
+
31
+ def self.parse_conda(file_contents, options: {})
32
+ parse_conda_with_kind(file_contents, "manifest")
32
33
  end
33
34
 
34
- def self.parse_conda_lockfile(info)
35
- parse_conda(info, "lockfile")
35
+ def self.parse_conda_lockfile(file_contents, options: {})
36
+ parse_conda_with_kind(file_contents, "lockfile")
37
+ end
38
+
39
+ def self.parse_conda_with_kind(info, kind)
40
+ dependencies = call_conda_parser_web(info, kind)[kind.to_sym]
41
+ dependencies.map { |dep| dep.merge(type: "runtime") }
36
42
  end
37
43
 
38
44
  private_class_method def self.call_conda_parser_web(file_contents, kind)
@@ -19,14 +19,14 @@ module Bibliothecary
19
19
  }
20
20
  end
21
21
 
22
- def self.parse_json_manifest(file_contents)
22
+ def self.parse_json_manifest(file_contents, options: {})
23
23
  manifest = JSON.parse file_contents
24
24
  manifest['prereqs'].map do |_group, deps|
25
25
  map_dependencies(deps, 'requires', 'runtime')
26
26
  end.flatten
27
27
  end
28
28
 
29
- def self.parse_yaml_manifest(file_contents)
29
+ def self.parse_yaml_manifest(file_contents, options: {})
30
30
  manifest = YAML.load file_contents
31
31
  map_dependencies(manifest, 'requires', 'runtime')
32
32
  end
@@ -16,7 +16,9 @@ module Bibliothecary
16
16
  }
17
17
  end
18
18
 
19
- def self.parse_description(file_contents)
19
+ add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
20
+
21
+ def self.parse_description(file_contents, options: {})
20
22
  manifest = DebControl::ControlFileBase.parse(file_contents)
21
23
  parse_section(manifest, 'Depends') +
22
24
  parse_section(manifest, 'Imports') +
@@ -5,6 +5,7 @@ module Bibliothecary
5
5
  module Parsers
6
6
  class Dub
7
7
  include Bibliothecary::Analyser
8
+ extend Bibliothecary::MultiParsers::JSONRuntime
8
9
 
9
10
  def self.mapping
10
11
  {
@@ -19,8 +20,8 @@ module Bibliothecary
19
20
  }
20
21
  end
21
22
 
22
- def self.parse_sdl_manifest(manifest)
23
- SdlParser.new(:runtime, manifest).dependencies
23
+ def self.parse_sdl_manifest(file_contents, options: {})
24
+ SdlParser.new(:runtime, file_contents).dependencies
24
25
  end
25
26
  end
26
27
  end
@@ -4,6 +4,7 @@ module Bibliothecary
4
4
  module Parsers
5
5
  class Elm
6
6
  include Bibliothecary::Analyser
7
+ extend Bibliothecary::MultiParsers::JSONRuntime
7
8
 
8
9
  def self.mapping
9
10
  {
@@ -18,7 +19,7 @@ module Bibliothecary
18
19
  }
19
20
  end
20
21
 
21
- def self.parse_json_lock(file_contents)
22
+ def self.parse_json_lock(file_contents, options: {})
22
23
  manifest = JSON.parse file_contents
23
24
  manifest.map do |name, requirement|
24
25
  {
@@ -14,7 +14,7 @@ module Bibliothecary
14
14
  }
15
15
  end
16
16
 
17
- def self.parse_lockfile(file_contents)
17
+ def self.parse_lockfile(file_contents, options: {})
18
18
  table = CSV.parse(file_contents, headers: true)
19
19
 
20
20
  required_headers = ["platform", "name", "requirement"]
@@ -22,9 +22,9 @@ module Bibliothecary
22
22
  raise "Missing headers #{missing_headers} in CSV" unless missing_headers.empty?
23
23
 
24
24
  table.map.with_index do |row, idx|
25
- line = idx + 1
25
+ line = idx + 2 # use 1-based index just like the 'csv' std lib, and count the headers as first row.
26
26
  required_headers.each do |h|
27
- raise "missing field '#{h}' on line #{line}" if row[h].empty?
27
+ raise "missing field '#{h}' on line #{line}" if row[h].nil? || row[h].empty?
28
28
  end
29
29
  {
30
30
  platform: row['platform'],
@@ -65,12 +65,14 @@ module Bibliothecary
65
65
  }
66
66
  end
67
67
 
68
- def self.parse_godep_json(file_contents)
68
+ add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
69
+
70
+ def self.parse_godep_json(file_contents, options: {})
69
71
  manifest = JSON.parse file_contents
70
72
  map_dependencies(manifest, 'Deps', 'ImportPath', 'Rev', 'runtime')
71
73
  end
72
74
 
73
- def self.parse_gpm(file_contents)
75
+ def self.parse_gpm(file_contents, options: {})
74
76
  deps = []
75
77
  file_contents.split("\n").each do |line|
76
78
  match = line.gsub(/(\#(.*))/, '').match(GPM_REGEXP)
@@ -84,38 +86,38 @@ module Bibliothecary
84
86
  deps
85
87
  end
86
88
 
87
- def self.parse_govendor(file_contents)
89
+ def self.parse_govendor(file_contents, options: {})
88
90
  manifest = JSON.load file_contents
89
91
  map_dependencies(manifest, 'package', 'path', 'revision', 'runtime')
90
92
  end
91
93
 
92
- def self.parse_glide_yaml(file_contents)
94
+ def self.parse_glide_yaml(file_contents, options: {})
93
95
  manifest = YAML.load file_contents
94
96
  map_dependencies(manifest, 'import', 'package', 'version', 'runtime') +
95
97
  map_dependencies(manifest, 'devImports', 'package', 'version', 'development')
96
98
  end
97
99
 
98
- def self.parse_glide_lockfile(file_contents)
100
+ def self.parse_glide_lockfile(file_contents, options: {})
99
101
  manifest = YAML.load file_contents
100
102
  map_dependencies(manifest, 'imports', 'name', 'version', 'runtime')
101
103
  end
102
104
 
103
- def self.parse_gb_manifest(file_contents)
105
+ def self.parse_gb_manifest(file_contents, options: {})
104
106
  manifest = JSON.parse file_contents
105
107
  map_dependencies(manifest, 'dependencies', 'importpath', 'revision', 'runtime')
106
108
  end
107
109
 
108
- def self.parse_dep_toml(file_contents)
110
+ def self.parse_dep_toml(file_contents, options: {})
109
111
  manifest = Tomlrb.parse file_contents
110
112
  map_dependencies(manifest, 'constraint', 'name', 'version', 'runtime')
111
113
  end
112
114
 
113
- def self.parse_dep_lockfile(file_contents)
115
+ def self.parse_dep_lockfile(file_contents, options: {})
114
116
  manifest = Tomlrb.parse file_contents
115
117
  map_dependencies(manifest, 'projects', 'name', 'revision', 'runtime')
116
118
  end
117
119
 
118
- def self.parse_go_mod(file_contents)
120
+ def self.parse_go_mod(file_contents, options: {})
119
121
  deps = []
120
122
  file_contents.lines.map(&:strip).each do |line|
121
123
  next if line.match(GOMOD_IGNORABLE_REGEX)
@@ -130,7 +132,7 @@ module Bibliothecary
130
132
  deps
131
133
  end
132
134
 
133
- def self.parse_go_sum(file_contents)
135
+ def self.parse_go_sum(file_contents, options: {})
134
136
  deps = []
135
137
  file_contents.lines.map(&:strip).each do |line|
136
138
  if match = line.match(GOSUM_REGEX)
@@ -144,7 +146,7 @@ module Bibliothecary
144
146
  deps.uniq
145
147
  end
146
148
 
147
- def self.parse_go_resolved(file_contents)
149
+ def self.parse_go_resolved(file_contents, options: {})
148
150
  JSON.parse(file_contents)
149
151
  .select { |dep| dep["Main"] != "true" }
150
152
  .map { |dep| { name: dep["Path"], requirement: dep["Version"], type: 'runtime' } }
@@ -19,7 +19,9 @@ module Bibliothecary
19
19
  }
20
20
  end
21
21
 
22
- def self.parse_cabal(file_contents)
22
+ add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
23
+
24
+ def self.parse_cabal(file_contents, options: {})
23
25
  headers = {
24
26
  'Content-Type' => "text/plain;charset=utf-8"
25
27
  }
@@ -30,7 +32,7 @@ module Bibliothecary
30
32
  JSON.parse(response.body, symbolize_names: true)
31
33
  end
32
34
 
33
- def self.parse_cabal_config(file_contents)
35
+ def self.parse_cabal_config(file_contents, options: {})
34
36
  manifest = DebControl::ControlFileBase.parse(file_contents)
35
37
  deps = manifest.first['constraints'].delete("\n").split(',').map(&:strip)
36
38
  deps.map do |dependency|
@@ -4,6 +4,7 @@ module Bibliothecary
4
4
  module Parsers
5
5
  class Haxelib
6
6
  include Bibliothecary::Analyser
7
+ extend Bibliothecary::MultiParsers::JSONRuntime
7
8
 
8
9
  def self.mapping
9
10
  {
@@ -18,8 +18,10 @@ module Bibliothecary
18
18
  }
19
19
  end
20
20
 
21
- def self.parse_mix(manifest)
22
- response = Typhoeus.post("#{Bibliothecary.configuration.mix_parser_host}/", body: manifest)
21
+ add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
22
+
23
+ def self.parse_mix(file_contents, options: {})
24
+ response = Typhoeus.post("#{Bibliothecary.configuration.mix_parser_host}/", body: file_contents)
23
25
  raise Bibliothecary::RemoteParsingError.new("Http Error #{response.response_code} when contacting: #{Bibliothecary.configuration.mix_parser_host}/", response.response_code) unless response.success?
24
26
  json = JSON.parse response.body
25
27
 
@@ -32,8 +34,8 @@ module Bibliothecary
32
34
  end
33
35
  end
34
36
 
35
- def self.parse_mix_lock(manifest)
36
- response = Typhoeus.post("#{Bibliothecary.configuration.mix_parser_host}/lock", body: manifest)
37
+ def self.parse_mix_lock(file_contents, options: {})
38
+ response = Typhoeus.post("#{Bibliothecary.configuration.mix_parser_host}/lock", body: file_contents)
37
39
  raise Bibliothecary::RemoteParsingError.new("Http Error #{response.response_code} when contacting: #{Bibliothecary.configuration.mix_parser_host}/", response.response_code) unless response.success?
38
40
  json = JSON.parse response.body
39
41
 
@@ -12,9 +12,9 @@ module Bibliothecary
12
12
  }
13
13
  end
14
14
 
15
- def self.parse_require(manifest)
15
+ def self.parse_require(file_contents, options: {})
16
16
  deps = []
17
- manifest.split("\n").each do |line|
17
+ file_contents.split("\n").each do |line|
18
18
  next if line.match(/^#/) || line.empty?
19
19
  split = line.split(/\s/)
20
20
  if line.match(/^@/)
@@ -38,12 +38,16 @@ module Bibliothecary
38
38
  },
39
39
  match_filename("pom.xml", case_insensitive: true) => {
40
40
  kind: 'manifest',
41
- parser: :parse_pom_manifest
41
+ parser: :parse_standalone_pom_manifest
42
42
  },
43
43
  match_filename("build.gradle", case_insensitive: true) => {
44
44
  kind: 'manifest',
45
45
  parser: :parse_gradle
46
46
  },
47
+ match_filename("build.gradle.kts", case_insensitive: true) => {
48
+ kind: 'manifest',
49
+ parser: :parse_gradle_kts
50
+ },
47
51
  match_extension(".xml", case_insensitive: true) => {
48
52
  content_matcher: :ivy_report?,
49
53
  kind: 'lockfile',
@@ -68,7 +72,9 @@ module Bibliothecary
68
72
  }
69
73
  end
70
74
 
71
- def self.parse_ivy_manifest(file_contents)
75
+ add_multi_parser Bibliothecary::MultiParsers::CycloneDX
76
+
77
+ def self.parse_ivy_manifest(file_contents, options: {})
72
78
  manifest = Ox.parse file_contents
73
79
  manifest.dependencies.locate('dependency').map do |dependency|
74
80
  attrs = dependency.attributes
@@ -91,7 +97,7 @@ module Bibliothecary
91
97
  false
92
98
  end
93
99
 
94
- def self.parse_ivy_report(file_contents)
100
+ def self.parse_ivy_report(file_contents, options: {})
95
101
  doc = Ox.parse file_contents
96
102
  root = doc.locate("ivy-report").first
97
103
  raise "ivy-report document does not have ivy-report at the root" if root.nil?
@@ -116,7 +122,7 @@ module Bibliothecary
116
122
  end.compact
117
123
  end
118
124
 
119
- def self.parse_gradle_resolved(file_contents)
125
+ def self.parse_gradle_resolved(file_contents, options: {})
120
126
  type = nil
121
127
  file_contents.split("\n").map do |line|
122
128
  type_match = GRADLE_TYPE_REGEX.match(line)
@@ -147,7 +153,7 @@ module Bibliothecary
147
153
  end.compact.uniq {|item| [item[:name], item[:requirement], item[:type]]}
148
154
  end
149
155
 
150
- def self.parse_maven_resolved(file_contents)
156
+ def self.parse_maven_resolved(file_contents, options: {})
151
157
  Strings::ANSI.sanitize(file_contents)
152
158
  .split("\n")
153
159
  .map(&method(:parse_resolved_dep_line))
@@ -155,7 +161,7 @@ module Bibliothecary
155
161
  .uniq
156
162
  end
157
163
 
158
- def self.parse_maven_tree(file_contents)
164
+ def self.parse_maven_tree(file_contents, options: {})
159
165
  file_contents = file_contents.gsub(/\r\n?/, "\n")
160
166
  captures = file_contents.scan(/^\[INFO\](?:(?:\+-)|\||(?:\\-)|\s)+((?:[\w\.-]+:)+[\w\.\-${}]+)/).flatten.uniq
161
167
 
@@ -191,7 +197,13 @@ module Bibliothecary
191
197
  }
192
198
  end
193
199
 
194
- def self.parse_pom_manifest(file_contents, parent_properties = {})
200
+ def self.parse_standalone_pom_manifest(file_contents, options: {})
201
+ parse_pom_manifest(file_contents, {}, options: options)
202
+ end
203
+
204
+ # parent_properties is used by Libraries:
205
+ # https://github.com/librariesio/libraries.io/blob/e970925aade2596a03268b6e1be785eba8502c62/app/models/package_manager/maven.rb#L129
206
+ def self.parse_pom_manifest(file_contents, parent_properties = {}, options: {})
195
207
  manifest = Ox.parse file_contents
196
208
  xml = manifest.respond_to?('project') ? manifest.project : manifest
197
209
  [].tap do |deps|
@@ -207,8 +219,8 @@ module Bibliothecary
207
219
  end
208
220
  end
209
221
 
210
- def self.parse_gradle(manifest)
211
- response = Typhoeus.post("#{Bibliothecary.configuration.gradle_parser_host}/parse", body: manifest)
222
+ def self.parse_gradle(file_contents, options: {})
223
+ response = Typhoeus.post("#{Bibliothecary.configuration.gradle_parser_host}/parse", body: file_contents)
212
224
  raise Bibliothecary::RemoteParsingError.new("Http Error #{response.response_code} when contacting: #{Bibliothecary.configuration.gradle_parser_host}/parse", response.response_code) unless response.success?
213
225
  json = JSON.parse(response.body)
214
226
  return [] unless json['dependencies']
@@ -223,6 +235,11 @@ module Bibliothecary
223
235
  end.compact
224
236
  end
225
237
 
238
+ def self.parse_gradle_kts(file_contents, options: {})
239
+ # TODO: the gradle-parser side needs to be implemented for this, coming soon.
240
+ []
241
+ end
242
+
226
243
  def self.gradle_dependency_name(group, name)
227
244
  if group.empty? && name.include?(":")
228
245
  group, name = name.split(":", 2)
@@ -298,7 +315,7 @@ module Bibliothecary
298
315
  end
299
316
  end
300
317
 
301
- def self.parse_sbt_update_full(file_contents)
318
+ def self.parse_sbt_update_full(file_contents, options: {})
302
319
  all_deps = []
303
320
  type = nil
304
321
  lines = file_contents.split("\n")
@@ -4,6 +4,7 @@ module Bibliothecary
4
4
  module Parsers
5
5
  class Meteor
6
6
  include Bibliothecary::Analyser
7
+ extend Bibliothecary::MultiParsers::JSONRuntime
7
8
 
8
9
  def self.mapping
9
10
  {