ecosystems-bibliothecary 14.2.0 → 15.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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -0
  3. data/README.md +9 -24
  4. data/bibliothecary.gemspec +5 -9
  5. data/lib/bibliothecary/analyser/analysis.rb +10 -5
  6. data/lib/bibliothecary/analyser/matchers.rb +7 -5
  7. data/lib/bibliothecary/analyser.rb +0 -30
  8. data/lib/bibliothecary/cli.rb +35 -26
  9. data/lib/bibliothecary/configuration.rb +1 -6
  10. data/lib/bibliothecary/dependency.rb +1 -4
  11. data/lib/bibliothecary/file_info.rb +7 -0
  12. data/lib/bibliothecary/parsers/bentoml.rb +0 -2
  13. data/lib/bibliothecary/parsers/bower.rb +0 -1
  14. data/lib/bibliothecary/parsers/cargo.rb +12 -10
  15. data/lib/bibliothecary/parsers/carthage.rb +51 -15
  16. data/lib/bibliothecary/parsers/clojars.rb +14 -18
  17. data/lib/bibliothecary/parsers/cocoapods.rb +100 -19
  18. data/lib/bibliothecary/parsers/cog.rb +0 -2
  19. data/lib/bibliothecary/parsers/conan.rb +156 -0
  20. data/lib/bibliothecary/parsers/conda.rb +0 -3
  21. data/lib/bibliothecary/parsers/cpan.rb +0 -2
  22. data/lib/bibliothecary/parsers/cran.rb +40 -19
  23. data/lib/bibliothecary/parsers/docker.rb +0 -2
  24. data/lib/bibliothecary/parsers/dub.rb +33 -8
  25. data/lib/bibliothecary/parsers/dvc.rb +0 -2
  26. data/lib/bibliothecary/parsers/elm.rb +13 -3
  27. data/lib/bibliothecary/parsers/go.rb +14 -5
  28. data/lib/bibliothecary/parsers/hackage.rb +132 -24
  29. data/lib/bibliothecary/parsers/haxelib.rb +14 -4
  30. data/lib/bibliothecary/parsers/hex.rb +37 -20
  31. data/lib/bibliothecary/parsers/homebrew.rb +0 -2
  32. data/lib/bibliothecary/parsers/julia.rb +0 -2
  33. data/lib/bibliothecary/parsers/maven.rb +35 -25
  34. data/lib/bibliothecary/parsers/meteor.rb +14 -4
  35. data/lib/bibliothecary/parsers/mlflow.rb +0 -2
  36. data/lib/bibliothecary/parsers/npm.rb +47 -59
  37. data/lib/bibliothecary/parsers/nuget.rb +23 -22
  38. data/lib/bibliothecary/parsers/ollama.rb +0 -2
  39. data/lib/bibliothecary/parsers/packagist.rb +0 -3
  40. data/lib/bibliothecary/parsers/pub.rb +0 -2
  41. data/lib/bibliothecary/parsers/pypi.rb +54 -35
  42. data/lib/bibliothecary/parsers/rubygems.rb +92 -27
  43. data/lib/bibliothecary/parsers/shard.rb +0 -1
  44. data/lib/bibliothecary/parsers/swift_pm.rb +77 -29
  45. data/lib/bibliothecary/parsers/vcpkg.rb +68 -17
  46. data/lib/bibliothecary/runner.rb +169 -22
  47. data/lib/bibliothecary/version.rb +1 -1
  48. data/lib/bibliothecary.rb +3 -10
  49. data/lib/dockerfile_parser.rb +1 -1
  50. data/lib/modelfile_parser.rb +8 -8
  51. metadata +2 -108
  52. data/.codeclimate.yml +0 -25
  53. data/.github/CONTRIBUTING.md +0 -195
  54. data/.github/workflows/ci.yml +0 -25
  55. data/.gitignore +0 -10
  56. data/.rspec +0 -2
  57. data/.rubocop.yml +0 -69
  58. data/.ruby-version +0 -1
  59. data/.tidelift +0 -1
  60. data/CODE_OF_CONDUCT.md +0 -74
  61. data/Gemfile +0 -34
  62. data/Rakefile +0 -18
  63. data/bin/console +0 -15
  64. data/bin/setup +0 -8
  65. data/lib/bibliothecary/multi_parsers/bundler_like_manifest.rb +0 -26
  66. data/lib/bibliothecary/multi_parsers/cyclonedx.rb +0 -170
  67. data/lib/bibliothecary/multi_parsers/dependencies_csv.rb +0 -155
  68. data/lib/bibliothecary/multi_parsers/json_runtime.rb +0 -22
  69. data/lib/bibliothecary/multi_parsers/spdx.rb +0 -149
  70. data/lib/bibliothecary/purl_util.rb +0 -37
  71. data/lib/bibliothecary/runner/multi_manifest_filter.rb +0 -92
  72. data/lib/sdl_parser.rb +0 -30
@@ -1,17 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "gemnasium/parser"
4
3
  require "yaml"
5
4
 
6
5
  module Bibliothecary
7
6
  module Parsers
8
7
  class CocoaPods
9
8
  include Bibliothecary::Analyser
10
- extend Bibliothecary::MultiParsers::BundlerLikeManifest
11
9
 
12
10
  NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'
13
11
  NAME_VERSION_4 = /^ {4}#{NAME_VERSION}$/
14
12
 
13
+ # Podfile pattern: pod "Name", "version" or pod "Name"
14
+ # Matches: pod 'name' or pod "name" with optional version
15
+ POD_REGEXP = /^\s*pod\s+['"]([^'"]+)['"]\s*(?:,\s*['"]([^'"]+)['"])?/
16
+
17
+ # Podspec pattern: .dependency "Name", "version"
18
+ PODSPEC_DEPENDENCY = /\.dependency\s+['"]([^'"]+)['"]\s*(?:,\s*['"]([^'"]+)['"])?/
19
+
15
20
  def self.mapping
16
21
  {
17
22
  match_filename("Podfile") => {
@@ -35,35 +40,111 @@ module Bibliothecary
35
40
  }
36
41
  end
37
42
 
38
- add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
39
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
40
43
 
41
44
  def self.parse_podfile_lock(file_contents, options: {})
42
- manifest = YAML.load file_contents
43
- dependencies = manifest["PODS"].map do |row|
44
- pod = row.is_a?(String) ? row : row.keys.first
45
- match = pod.match(/(.+?)\s\((.+?)\)/i)
46
- Dependency.new(
45
+ source = options.fetch(:filename, nil)
46
+ dependencies = []
47
+
48
+ # Match pod entries: " - Name (version)" or " - Name/Subspec (version)"
49
+ # Only process lines in PODS section (before DEPENDENCIES section)
50
+ pods_section = file_contents.split(/^DEPENDENCIES:/)[0]
51
+ pods_section.scan(/^ - ([^\s(]+(?:\/[^\s(]+)?)\s+\(([^)]+)\)/) do |name, version|
52
+ # Take only the base package name (before any /)
53
+ base_name = name.split("/").first
54
+ dependencies << Dependency.new(
47
55
  platform: platform_name,
48
- name: match[1].split("/").first,
49
- requirement: match[2],
56
+ name: base_name,
57
+ requirement: version,
50
58
  type: "runtime",
51
- source: options.fetch(:filename, nil)
59
+ source: source
52
60
  )
53
- end.compact
61
+ end
62
+
54
63
  ParserResult.new(dependencies: dependencies)
55
64
  end
56
65
 
57
66
  def self.parse_podspec(file_contents, options: {})
58
- manifest = Gemnasium::Parser.send(:podspec, file_contents)
59
- dependencies = parse_ruby_manifest(manifest, platform_name, options.fetch(:filename, nil))
60
- ParserResult.new(dependencies: dependencies)
67
+ source = options.fetch(:filename, nil)
68
+ deps = []
69
+ seen_names = Set.new
70
+
71
+ file_contents.each_line do |line|
72
+ match = line.match(PODSPEC_DEPENDENCY)
73
+ next unless match
74
+
75
+ name = match[1]
76
+ # Strip subspec path (e.g., "Foo/Bar" -> "Foo")
77
+ base_name = name.split("/").first
78
+
79
+ # Deduplicate by base name
80
+ next if seen_names.include?(base_name)
81
+ seen_names.add(base_name)
82
+
83
+ deps << Dependency.new(
84
+ platform: platform_name,
85
+ name: base_name,
86
+ requirement: ">= 0",
87
+ type: "runtime",
88
+ source: source
89
+ )
90
+ end
91
+
92
+ ParserResult.new(dependencies: deps)
61
93
  end
62
94
 
63
95
  def self.parse_podfile(file_contents, options: {})
64
- manifest = Gemnasium::Parser.send(:podfile, file_contents)
65
- dependencies = parse_ruby_manifest(manifest, platform_name, options.fetch(:filename, nil))
66
- ParserResult.new(dependencies: dependencies)
96
+ source = options.fetch(:filename, nil)
97
+ deps = []
98
+ in_conditional = false
99
+ conditional_depth = 0
100
+
101
+ file_contents.each_line do |line|
102
+ # Track if/else/elsif blocks to skip pods inside them
103
+ if line =~ /^\s*if\s+/
104
+ in_conditional = true
105
+ conditional_depth += 1
106
+ next
107
+ end
108
+ if line =~ /^\s*(elsif|else)\s*/
109
+ next
110
+ end
111
+ if line =~ /^\s*end\s*$/ && in_conditional
112
+ conditional_depth -= 1
113
+ in_conditional = false if conditional_depth == 0
114
+ next
115
+ end
116
+
117
+ # Skip pods inside conditionals
118
+ next if in_conditional
119
+
120
+ match = line.match(POD_REGEXP)
121
+ next unless match
122
+
123
+ name = match[1]
124
+ version = match[2]
125
+
126
+ # Skip pods with special characters like + or subspecs with /
127
+ next if name.include?("+") || name.include?("/")
128
+
129
+ requirement = version ? normalize_version(version) : ">= 0"
130
+
131
+ deps << Dependency.new(
132
+ platform: platform_name,
133
+ name: name,
134
+ requirement: requirement,
135
+ type: "runtime",
136
+ source: source
137
+ )
138
+ end
139
+
140
+ ParserResult.new(dependencies: deps)
141
+ end
142
+
143
+ def self.normalize_version(version)
144
+ # If version already has an operator, use as-is
145
+ return version if version =~ /^[~>=<]/
146
+ # Otherwise treat as exact version
147
+ "= #{version}"
67
148
  end
68
149
 
69
150
  def self.parse_json_manifest(file_contents, options: {})
@@ -15,8 +15,6 @@ module Bibliothecary
15
15
  }
16
16
  end
17
17
 
18
- add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
19
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
20
18
 
21
19
  def self.parse_cog_yaml(file_contents, options: {})
22
20
  source = options.fetch(:filename, 'cog.yaml')
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bibliothecary
4
+ module Parsers
5
+ class Conan
6
+ include Bibliothecary::Analyser
7
+
8
+ def self.mapping
9
+ {
10
+ match_filename("conanfile.py") => {
11
+ kind: "manifest",
12
+ parser: :parse_conanfile_py,
13
+ },
14
+ match_filename("conanfile.txt") => {
15
+ kind: "manifest",
16
+ parser: :parse_conanfile_txt,
17
+ },
18
+ match_filename("conan.lock") => {
19
+ kind: "lockfile",
20
+ parser: :parse_lockfile,
21
+ },
22
+ }
23
+ end
24
+
25
+
26
+ REQUIRES_PATTERN = /self\.requires\(\s*["']([^"']+)["']/
27
+
28
+ def self.parse_conanfile_py(file_contents, options: {})
29
+ dependencies = []
30
+
31
+ file_contents.scan(REQUIRES_PATTERN).each do |match|
32
+ name, version = parse_conan_reference(match[0])
33
+ next if name.nil? || name.empty?
34
+
35
+ dependencies << Dependency.new(
36
+ name: name,
37
+ requirement: version || "*",
38
+ type: "runtime",
39
+ source: options.fetch(:filename, nil),
40
+ platform: platform_name
41
+ )
42
+ end
43
+
44
+ ParserResult.new(dependencies: dependencies)
45
+ end
46
+
47
+ def self.parse_conanfile_txt(file_contents, options: {})
48
+ dependencies = []
49
+ current_section = nil
50
+
51
+ file_contents.each_line do |line|
52
+ line = line.strip
53
+ next if line.empty? || line.start_with?("#")
54
+
55
+ if line.match?(/^\[([^\]]+)\]$/)
56
+ current_section = line[1..-2]
57
+ next
58
+ end
59
+
60
+ next unless %w[requires build_requires].include?(current_section)
61
+
62
+ name, version = parse_conan_reference(line)
63
+ next if name.nil? || name.empty?
64
+
65
+ dependencies << Dependency.new(
66
+ name: name,
67
+ requirement: version || "*",
68
+ type: current_section == "requires" ? "runtime" : "development",
69
+ source: options.fetch(:filename, nil),
70
+ platform: platform_name
71
+ )
72
+ end
73
+
74
+ ParserResult.new(dependencies: dependencies)
75
+ end
76
+
77
+ def self.parse_lockfile(file_contents, options: {})
78
+ manifest = JSON.parse(file_contents)
79
+
80
+ if manifest.dig("graph_lock", "nodes")
81
+ parse_v1_lockfile(manifest, options: options)
82
+ else
83
+ parse_v2_lockfile(manifest, options: options)
84
+ end
85
+ end
86
+
87
+ def self.parse_v1_lockfile(lockfile, options: {})
88
+ dependencies = []
89
+
90
+ lockfile["graph_lock"]["nodes"].each_value do |node|
91
+ next if node["path"] && !node["path"].empty?
92
+
93
+ ref = node["pref"] || node["ref"]
94
+ next unless ref
95
+
96
+ name, version = parse_conan_reference(ref)
97
+ next if name.nil? || name.empty?
98
+
99
+ type = node["context"] == "build" ? "development" : "runtime"
100
+
101
+ dependencies << Dependency.new(
102
+ name: name,
103
+ requirement: version || "*",
104
+ type: type,
105
+ source: options.fetch(:filename, nil),
106
+ platform: platform_name
107
+ )
108
+ end
109
+
110
+ ParserResult.new(dependencies: dependencies)
111
+ end
112
+
113
+ def self.parse_v2_lockfile(lockfile, options: {})
114
+ dependencies = []
115
+
116
+ parse_requires(dependencies, lockfile["requires"], "runtime", options)
117
+ parse_requires(dependencies, lockfile["build_requires"], "development", options)
118
+ parse_requires(dependencies, lockfile["python_requires"], "development", options)
119
+
120
+ ParserResult.new(dependencies: dependencies)
121
+ end
122
+
123
+ def self.parse_requires(dependencies, requires, type, options)
124
+ return unless requires&.any?
125
+
126
+ requires.each do |ref|
127
+ name, version = parse_conan_reference(ref)
128
+ next if name.nil? || name.empty?
129
+
130
+ dependencies << Dependency.new(
131
+ name: name,
132
+ requirement: version || "*",
133
+ type: type,
134
+ source: options.fetch(:filename, nil),
135
+ platform: platform_name
136
+ )
137
+ end
138
+ end
139
+
140
+ # Parse Conan reference format:
141
+ # name/version[@username[/channel]][#recipe_revision][:package_id[#package_revision]][%timestamp]
142
+ def self.parse_conan_reference(ref)
143
+ return [nil, nil] if ref.nil? || ref.empty? || !ref.include?("/")
144
+
145
+ # Strip timestamp, package info, recipe revision, and user/channel
146
+ ref = ref.split("%", 2)[0]
147
+ ref = ref.split(":", 2)[0]
148
+ ref = ref.split("#", 2)[0]
149
+ ref = ref.split("@", 2)[0]
150
+
151
+ parts = ref.split("/", 2)
152
+ [parts[0], parts[1]]
153
+ end
154
+ end
155
+ end
156
+ end
@@ -20,9 +20,6 @@ module Bibliothecary
20
20
  }
21
21
  end
22
22
 
23
- add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
24
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
25
- add_multi_parser(Bibliothecary::MultiParsers::Spdx)
26
23
 
27
24
  def self.parse_conda(file_contents, options: {})
28
25
  manifest = YAML.load(file_contents)
@@ -21,8 +21,6 @@ module Bibliothecary
21
21
  }
22
22
  end
23
23
 
24
- add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
25
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
26
24
 
27
25
  def self.parse_json_manifest(file_contents, options: {})
28
26
  manifest = JSON.parse file_contents
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "deb_control"
4
-
5
3
  module Bibliothecary
6
4
  module Parsers
7
5
  class CRAN
@@ -18,33 +16,56 @@ module Bibliothecary
18
16
  }
19
17
  end
20
18
 
21
- add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
22
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
23
- add_multi_parser(Bibliothecary::MultiParsers::Spdx)
24
19
 
25
20
  def self.parse_description(file_contents, options: {})
26
- manifest = DebControl::ControlFileBase.parse(file_contents)
27
- dependencies = parse_section(manifest, "Depends", options.fetch(:filename, nil)) +
28
- parse_section(manifest, "Imports", options.fetch(:filename, nil)) +
29
- parse_section(manifest, "Suggests", options.fetch(:filename, nil)) +
30
- parse_section(manifest, "Enhances", options.fetch(:filename, nil))
21
+ source = options.fetch(:filename, nil)
22
+ fields = parse_rfc822(file_contents)
23
+
24
+ dependencies = parse_deps(fields["Depends"], "depends", source) +
25
+ parse_deps(fields["Imports"], "imports", source) +
26
+ parse_deps(fields["Suggests"], "suggests", source) +
27
+ parse_deps(fields["Enhances"], "enhances", source)
28
+
31
29
  ParserResult.new(dependencies: dependencies)
32
30
  end
33
31
 
34
- def self.parse_section(manifest, name, source = nil)
35
- return [] unless manifest.first[name]
32
+ def self.parse_rfc822(contents)
33
+ fields = {}
34
+ current_field = nil
35
+
36
+ contents.each_line do |line|
37
+ if line =~ /^([A-Za-z][A-Za-z0-9-]*):\s*(.*)/
38
+ current_field = $1
39
+ fields[current_field] = $2.strip
40
+ elsif line =~ /^\s+(.*)/ && current_field
41
+ # Continuation line
42
+ fields[current_field] += " " + $1.strip
43
+ end
44
+ end
45
+
46
+ fields
47
+ end
48
+
49
+ def self.parse_deps(value, type, source)
50
+ return [] unless value
51
+
52
+ value.split(",").map(&:strip).map do |dep_str|
53
+ next if dep_str.empty?
54
+
55
+ match = dep_str.match(REQUIRE_REGEXP)
56
+ next unless match
57
+
58
+ # Normalize whitespace: collapse multiple spaces, but preserve single space after operator
59
+ requirement = match[2]&.gsub(/\s+/, " ")&.strip || "*"
36
60
 
37
- deps = manifest.first[name].delete("\n").split(",").map(&:strip)
38
- deps.map do |dependency|
39
- dep = dependency.match(REQUIRE_REGEXP)
40
61
  Dependency.new(
41
- name: dep[1],
42
- requirement: dep[2],
43
- type: name.downcase,
62
+ name: match[1],
63
+ requirement: requirement,
64
+ type: type,
44
65
  source: source,
45
66
  platform: platform_name
46
67
  )
47
- end
68
+ end.compact
48
69
  end
49
70
  end
50
71
  end
@@ -23,8 +23,6 @@ module Bibliothecary
23
23
  }
24
24
  end
25
25
 
26
- add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
27
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
28
26
 
29
27
  def self.parse_docker_compose(file_contents, options: {})
30
28
  source = options.fetch(:filename, 'docker-compose.yml')
@@ -1,19 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
- require "sdl_parser"
5
4
 
6
5
  module Bibliothecary
7
6
  module Parsers
8
7
  class Dub
9
8
  include Bibliothecary::Analyser
10
- extend Bibliothecary::MultiParsers::JSONRuntime
9
+
10
+ SDL_DEPENDENCY_REGEXP = /^dependency\s+"([^"]+)"(?:\s+version="([^"]+)")?/
11
11
 
12
12
  def self.mapping
13
13
  {
14
14
  match_filename("dub.json") => {
15
15
  kind: "manifest",
16
- parser: :parse_json_runtime_manifest,
16
+ parser: :parse_json_manifest,
17
17
  },
18
18
  match_filename("dub.sdl") => {
19
19
  kind: "manifest",
@@ -22,13 +22,38 @@ module Bibliothecary
22
22
  }
23
23
  end
24
24
 
25
- add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
26
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
25
+ def self.parse_json_manifest(file_contents, options: {})
26
+ manifest = JSON.parse(file_contents)
27
+ dependencies = manifest.fetch("dependencies", {}).map do |name, requirement|
28
+ Dependency.new(
29
+ name: name,
30
+ requirement: requirement,
31
+ type: "runtime",
32
+ source: options.fetch(:filename, nil),
33
+ platform: platform_name
34
+ )
35
+ end
36
+ ParserResult.new(dependencies: dependencies)
37
+ end
27
38
 
28
39
  def self.parse_sdl_manifest(file_contents, options: {})
29
- ParserResult.new(
30
- dependencies: SdlParser.new(:runtime, file_contents, platform_name, options.fetch(:filename, nil)).dependencies
31
- )
40
+ source = options.fetch(:filename, nil)
41
+ deps = []
42
+
43
+ file_contents.each_line do |line|
44
+ match = line.match(SDL_DEPENDENCY_REGEXP)
45
+ next unless match
46
+
47
+ deps << Dependency.new(
48
+ platform: platform_name,
49
+ name: match[1],
50
+ requirement: match[2] || ">= 0",
51
+ type: :runtime,
52
+ source: source
53
+ )
54
+ end
55
+
56
+ ParserResult.new(dependencies: deps.uniq)
32
57
  end
33
58
  end
34
59
  end
@@ -15,8 +15,6 @@ module Bibliothecary
15
15
  }
16
16
  end
17
17
 
18
- add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
19
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
20
18
 
21
19
  def self.parse_dvc_yaml(file_contents, options: {})
22
20
  source = options.fetch(:filename, 'dvc.yaml')
@@ -6,7 +6,6 @@ module Bibliothecary
6
6
  module Parsers
7
7
  class Elm
8
8
  include Bibliothecary::Analyser
9
- extend Bibliothecary::MultiParsers::JSONRuntime
10
9
 
11
10
  def self.mapping
12
11
  {
@@ -21,8 +20,19 @@ module Bibliothecary
21
20
  }
22
21
  end
23
22
 
24
- add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
25
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
23
+ def self.parse_json_runtime_manifest(file_contents, options: {})
24
+ manifest = JSON.parse(file_contents)
25
+ dependencies = manifest.fetch("dependencies", {}).map do |name, requirement|
26
+ Dependency.new(
27
+ name: name,
28
+ requirement: requirement,
29
+ type: "runtime",
30
+ source: options.fetch(:filename, nil),
31
+ platform: platform_name
32
+ )
33
+ end
34
+ ParserResult.new(dependencies: dependencies)
35
+ end
26
36
 
27
37
  def self.parse_json_lock(file_contents, options: {})
28
38
  manifest = JSON.parse file_contents
@@ -72,9 +72,6 @@ module Bibliothecary
72
72
  }
73
73
  end
74
74
 
75
- add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
76
- add_multi_parser(Bibliothecary::MultiParsers::Spdx)
77
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
78
75
 
79
76
  def self.parse_godep_json(file_contents, options: {})
80
77
  manifest = JSON.parse file_contents
@@ -132,8 +129,20 @@ module Bibliothecary
132
129
  end
133
130
 
134
131
  def self.parse_dep_lockfile(file_contents, options: {})
135
- manifest = Tomlrb.parse file_contents
136
- dependencies = map_dependencies(manifest, "projects", "name", "revision", "runtime", options.fetch(:filename, nil))
132
+ dependencies = []
133
+ # Split into [[projects]] blocks and extract fields from each
134
+ file_contents.split(/\[\[projects\]\]/).drop(1).each do |block|
135
+ name = block[/name\s*=\s*"([^"]+)"/, 1]
136
+ revision = block[/revision\s*=\s*"([^"]+)"/, 1]
137
+
138
+ dependencies << Dependency.new(
139
+ platform: platform_name,
140
+ name: name,
141
+ requirement: revision,
142
+ type: "runtime",
143
+ source: options.fetch(:filename, nil)
144
+ )
145
+ end
137
146
  ParserResult.new(dependencies: dependencies)
138
147
  end
139
148