ecosystems-bibliothecary 15.0.1 → 15.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 508531974284b48df7e084f6d0c7c3a4762f7cf5f1af7ed624c86f469a413bfb
4
- data.tar.gz: 2442b323ba7169decede7da3c68284402440613dd1091fec7ff1dee8c5bdbe56
3
+ metadata.gz: 78f959cefcb80f16e6e56d087449ec23576dc7177b5f4a2f0448654f9d7dd022
4
+ data.tar.gz: fe94e344c4d43098281d613bfaf6168ec4464766fddedc61a97a9028f16f7174
5
5
  SHA512:
6
- metadata.gz: bf3b5d65d9d02cbafa1e86f04652cdb7f00ffcefd04f55410850bd904e4e1f1e09bf95cd4893270cd93b5219af6fe14c1cc4e7954fe21bd1deb0979b450a37b4
7
- data.tar.gz: 24d881e490fcd2dc28647a3936015d552ded40a0567a11f28fea160e277a3b70f736d9a640b3f3773d7aec5a6c8622d8a10349b2d0c820a0338d31cce9d0ba07
6
+ metadata.gz: 51e844390f10db2667ef885ad5a0ce300d33b8845808b4f2511869c03512176ce196f8de51c04d11fcfc429ec106849aa5e090800a3ae3aa151f989e8cfe0270
7
+ data.tar.gz: 0f3a788b617189160bb45f9d73afc4e78ded47a010112a7369c28853a1dd842c3922e1dd7e22c2a62cf494ebaa6a550cf8e009c387e1c58fa27fe6f9bfedc724
data/CHANGELOG.md CHANGED
@@ -13,6 +13,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13
13
 
14
14
  ### Removed
15
15
 
16
+ ## [15.1.0]
17
+
18
+ ### Added
19
+
20
+ - pdm.lock parser for Python PDM package manager
21
+ - renv.lock parser for R package management
22
+ - stack.yaml.lock parser for Haskell Stack
23
+ - gradle.lockfile parser for Gradle dependency locking
24
+ - .deps.json parser for .NET runtime dependencies
25
+ - verification-metadata.xml parser for Gradle dependency verification
26
+ - Nimble parser for Nim package manager (.nimble files)
27
+ - LuaRocks parser for Lua package manager (.rockspec files)
28
+
16
29
  ## [15.0.1]
17
30
 
18
31
  ### Changed
data/README.md CHANGED
@@ -62,6 +62,8 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
62
62
  - sbt-update-full.txt
63
63
  - maven-dependency-tree.txt
64
64
  - maven-dependency-tree.dot
65
+ - gradle.lockfile
66
+ - verification-metadata.xml
65
67
  - RubyGems
66
68
  - Gemfile
67
69
  - Gemfile.lock
@@ -84,6 +86,7 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
84
86
  - poetry.lock
85
87
  - uv.lock
86
88
  - pylock.toml
89
+ - pdm.lock
87
90
  - pip-resolved-dependencies.txt
88
91
  - pip-dependency-graph.json
89
92
  - Nuget
@@ -95,6 +98,7 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
95
98
  - paket.lock
96
99
  - *.csproj
97
100
  - project.assets.json
101
+ - \*.deps.json
98
102
  - Bower
99
103
  - bower.json
100
104
  - BentoML
@@ -124,6 +128,7 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
124
128
  - MLmodel
125
129
  - CRAN
126
130
  - DESCRIPTION
131
+ - renv.lock
127
132
  - Cargo
128
133
  - Cargo.toml
129
134
  - Cargo.lock
@@ -169,6 +174,7 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
169
174
  - Hackage
170
175
  - \*.cabal
171
176
  - cabal.config
177
+ - stack.yaml.lock
172
178
  - Actions
173
179
  - action.yml
174
180
  - action.yaml
@@ -188,6 +194,10 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
188
194
  - Brewfile.lock.json
189
195
  - Ollama
190
196
  - Modelfile
197
+ - Nimble
198
+ - \*.nimble
199
+ - LuaRocks
200
+ - \*.rockspec
191
201
 
192
202
  ## Development
193
203
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "json"
4
+
3
5
  module Bibliothecary
4
6
  module Parsers
5
7
  class CRAN
@@ -13,6 +15,10 @@ module Bibliothecary
13
15
  kind: "manifest",
14
16
  parser: :parse_description,
15
17
  },
18
+ match_filename("renv.lock") => {
19
+ kind: "lockfile",
20
+ parser: :parse_renv_lock,
21
+ },
16
22
  }
17
23
  end
18
24
 
@@ -67,6 +73,29 @@ module Bibliothecary
67
73
  )
68
74
  end.compact
69
75
  end
76
+
77
+ def self.parse_renv_lock(file_contents, options: {})
78
+ source = options.fetch(:filename, nil)
79
+ manifest = JSON.parse(file_contents)
80
+ packages = manifest.fetch("Packages", {})
81
+
82
+ dependencies = packages.map do |_key, pkg|
83
+ # Only include packages from CRAN repository
84
+ # Skip local packages and packages from other sources like Bioconductor
85
+ repository = pkg["Repository"]
86
+ next unless repository == "CRAN"
87
+
88
+ Dependency.new(
89
+ name: pkg["Package"],
90
+ requirement: pkg["Version"],
91
+ type: "runtime",
92
+ source: source,
93
+ platform: platform_name
94
+ )
95
+ end.compact
96
+
97
+ ParserResult.new(dependencies: dependencies)
98
+ end
70
99
  end
71
100
  end
72
101
  end
@@ -12,6 +12,9 @@ module Bibliothecary
12
12
  # Matches build-tool-depends format: package:tool == version
13
13
  BUILD_TOOL_REGEXP = /^\s*([a-zA-Z][a-zA-Z0-9-]*):[a-zA-Z][a-zA-Z0-9-]*\s*((?:[<>=!]+\s*[\d.*]+(?:\s*&&\s*[<>=!]+\s*[\d.*]+)*)?)/
14
14
 
15
+ # Matches stack.yaml.lock hackage entries like: hackage: fuzzyset-0.2.4@sha256:...
16
+ STACK_LOCK_REGEXP = /hackage:\s*([a-zA-Z0-9-]+)-([0-9.]+)@/
17
+
15
18
  def self.mapping
16
19
  {
17
20
  match_extension(".cabal") => {
@@ -22,6 +25,10 @@ module Bibliothecary
22
25
  kind: "lockfile",
23
26
  parser: :parse_cabal_config,
24
27
  },
28
+ match_filename("stack.yaml.lock") => {
29
+ kind: "lockfile",
30
+ parser: :parse_stack_yaml_lock,
31
+ },
25
32
  }
26
33
  end
27
34
 
@@ -169,6 +176,26 @@ module Bibliothecary
169
176
 
170
177
  ParserResult.new(dependencies: deps)
171
178
  end
179
+
180
+ def self.parse_stack_yaml_lock(file_contents, options: {})
181
+ source = options.fetch(:filename, "stack.yaml.lock")
182
+ deps = []
183
+
184
+ file_contents.each_line do |line|
185
+ match = line.match(STACK_LOCK_REGEXP)
186
+ next unless match
187
+
188
+ deps << Dependency.new(
189
+ platform: platform_name,
190
+ name: match[1],
191
+ requirement: match[2],
192
+ type: "runtime",
193
+ source: source
194
+ )
195
+ end
196
+
197
+ ParserResult.new(dependencies: deps)
198
+ end
172
199
  end
173
200
  end
174
201
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bibliothecary
4
+ module Parsers
5
+ class LuaRocks
6
+ include Bibliothecary::Analyser
7
+
8
+ def self.mapping
9
+ {
10
+ match_extension(".rockspec") => {
11
+ kind: "manifest",
12
+ parser: :parse_rockspec,
13
+ },
14
+ }
15
+ end
16
+
17
+ def self.parse_rockspec(file_contents, options: {})
18
+ source = options.fetch(:filename, nil)
19
+ deps = []
20
+
21
+ # Find dependencies table in Lua format
22
+ # dependencies = { "lua >= 5.1", "package ~> 1.0" }
23
+ if file_contents =~ /dependencies\s*=\s*\{([^}]*)\}/m
24
+ deps_block = Regexp.last_match(1)
25
+
26
+ # Extract quoted strings from the dependencies table
27
+ deps_block.scan(/"([^"]+)"/).flatten.each do |spec|
28
+ spec = spec.strip
29
+ next if spec.empty?
30
+
31
+ # Parse "packagename" or "packagename >= version" or "packagename ~> version"
32
+ # Format: name [operator version]
33
+ if spec =~ /^([a-zA-Z0-9_-]+)\s*(.*)$/
34
+ name = Regexp.last_match(1)
35
+ requirement = Regexp.last_match(2).strip
36
+ requirement = "*" if requirement.empty?
37
+
38
+ deps << Dependency.new(
39
+ name: name,
40
+ requirement: requirement,
41
+ type: "runtime",
42
+ source: source,
43
+ platform: platform_name
44
+ )
45
+ end
46
+ end
47
+ end
48
+
49
+ ParserResult.new(dependencies: deps)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -130,6 +130,18 @@ module Bibliothecary
130
130
  kind: "lockfile",
131
131
  parser: :parse_maven_tree_dot,
132
132
  },
133
+ # gradle.lockfile is the output of Gradle dependency locking:
134
+ # https://docs.gradle.org/current/userguide/dependency_locking.html
135
+ match_filename("gradle.lockfile", case_insensitive: true) => {
136
+ kind: "lockfile",
137
+ parser: :parse_gradle_lockfile,
138
+ },
139
+ # gradle/verification-metadata.xml is used by Gradle for dependency verification
140
+ # https://docs.gradle.org/current/userguide/dependency_verification.html
141
+ match_filename("verification-metadata.xml", case_insensitive: true) => {
142
+ kind: "lockfile",
143
+ parser: :parse_gradle_verification_metadata,
144
+ },
133
145
  }
134
146
  end
135
147
 
@@ -413,6 +425,68 @@ module Bibliothecary
413
425
  )
414
426
  end
415
427
 
428
+ def self.parse_gradle_lockfile(file_contents, options: {})
429
+ source = options.fetch(:filename, nil)
430
+ deps = []
431
+
432
+ file_contents.each_line do |line|
433
+ line = line.strip
434
+ # Skip comments and empty lines
435
+ next if line.empty? || line.start_with?("#")
436
+
437
+ # Format: group:artifact:version=configurations
438
+ # Split on = first to separate the GAV from configurations
439
+ gav_part = line.split("=").first
440
+ next unless gav_part
441
+
442
+ parts = gav_part.split(":")
443
+ # Must have exactly 3 parts: group, artifact, version
444
+ next unless parts.length == 3
445
+
446
+ group, artifact, version = parts
447
+ # Skip empty entries (like "empty=")
448
+ next if version.nil? || version.empty?
449
+
450
+ deps << Dependency.new(
451
+ name: "#{group}:#{artifact}",
452
+ requirement: version,
453
+ type: "runtime",
454
+ source: source,
455
+ platform: platform_name
456
+ )
457
+ end
458
+
459
+ ParserResult.new(dependencies: deps)
460
+ end
461
+
462
+ def self.parse_gradle_verification_metadata(file_contents, options: {})
463
+ source = options.fetch(:filename, nil)
464
+ deps = []
465
+
466
+ doc = Ox.parse(file_contents)
467
+ components = doc.locate("verification-metadata/components/component")
468
+
469
+ components.each do |component|
470
+ attrs = component.attributes
471
+ group = attrs[:group]
472
+ name = attrs[:name]
473
+ version = attrs[:version]
474
+
475
+ next if group.nil? || name.nil? || version.nil?
476
+ next if group.empty? || name.empty? || version.empty?
477
+
478
+ deps << Dependency.new(
479
+ name: "#{group}:#{name}",
480
+ requirement: version,
481
+ type: "runtime",
482
+ source: source,
483
+ platform: platform_name
484
+ )
485
+ end
486
+
487
+ ParserResult.new(dependencies: deps)
488
+ end
489
+
416
490
  def self.parse_resolved_dep_line(line, options: {})
417
491
  # filter out anything that doesn't look like a
418
492
  # resolved dep line
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bibliothecary
4
+ module Parsers
5
+ class Nimble
6
+ include Bibliothecary::Analyser
7
+
8
+ # Matches requires statements like: requires "nim >= 1.0.0", "chronos >= 3.0.0"
9
+ # or: requires "packagename"
10
+ REQUIRES_REGEXP = /^\s*requires\s+"([^"]+)"/
11
+
12
+ def self.mapping
13
+ {
14
+ match_extension(".nimble") => {
15
+ kind: "manifest",
16
+ parser: :parse_nimble,
17
+ },
18
+ }
19
+ end
20
+
21
+ def self.parse_nimble(file_contents, options: {})
22
+ source = options.fetch(:filename, nil)
23
+ deps = []
24
+
25
+ file_contents.each_line do |line|
26
+ next unless line.strip.start_with?("requires")
27
+
28
+ # Extract all quoted strings from requires lines
29
+ # handles: requires "pkg1", "pkg2 >= 1.0"
30
+ line.scan(/"([^"]+)"/).flatten.each do |spec|
31
+ spec = spec.strip
32
+ next if spec.empty?
33
+
34
+ # Parse "packagename" or "packagename >= version" or "packagename == version"
35
+ if spec =~ /^([a-zA-Z0-9_]+)\s*(.*)$/
36
+ name = Regexp.last_match(1)
37
+ requirement = Regexp.last_match(2).strip
38
+ requirement = "*" if requirement.empty?
39
+
40
+ deps << Dependency.new(
41
+ name: name,
42
+ requirement: requirement,
43
+ type: "runtime",
44
+ source: source,
45
+ platform: platform_name
46
+ )
47
+ end
48
+ end
49
+ end
50
+
51
+ ParserResult.new(dependencies: deps)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -42,6 +42,11 @@ module Bibliothecary
42
42
  kind: "lockfile",
43
43
  parser: :parse_project_assets_json,
44
44
  },
45
+ # .deps.json files end with .deps.json (e.g. myapp.deps.json)
46
+ match_extension(".deps.json") => {
47
+ kind: "lockfile",
48
+ parser: :parse_deps_json,
49
+ },
45
50
  }
46
51
  end
47
52
 
@@ -246,6 +251,33 @@ module Bibliothecary
246
251
  end
247
252
  ParserResult.new(dependencies: [])
248
253
  end
254
+
255
+ def self.parse_deps_json(file_contents, options: {})
256
+ manifest = JSON.parse(file_contents)
257
+ libraries = manifest.fetch("libraries", {})
258
+
259
+ dependencies = libraries.map do |name_version, details|
260
+ # Skip project-type entries (these are the root/main package)
261
+ next if details["type"] == "project"
262
+
263
+ # Split name/version format
264
+ parts = name_version.split("/")
265
+ next unless parts.length == 2
266
+
267
+ name, version = parts
268
+ next if name.nil? || name.empty? || version.nil? || version.empty?
269
+
270
+ Dependency.new(
271
+ name: name,
272
+ requirement: version,
273
+ type: "runtime",
274
+ source: options.fetch(:filename, nil),
275
+ platform: platform_name
276
+ )
277
+ end.compact
278
+
279
+ ParserResult.new(dependencies: dependencies)
280
+ end
249
281
  end
250
282
  end
251
283
  end
@@ -85,6 +85,10 @@ module Bibliothecary
85
85
  kind: "lockfile",
86
86
  parser: :parser_pylock,
87
87
  },
88
+ match_filename("pdm.lock") => {
89
+ kind: "lockfile",
90
+ parser: :parse_pdm_lock,
91
+ },
88
92
  }
89
93
  end
90
94
 
@@ -129,6 +133,34 @@ module Bibliothecary
129
133
  ParserResult.new(dependencies: dependencies)
130
134
  end
131
135
 
136
+ def self.parse_pdm_lock(file_contents, options: {})
137
+ source = options.fetch(:filename, nil)
138
+ dependencies = []
139
+ # Split into [[package]] blocks and extract fields from each
140
+ file_contents.split(/\[\[package\]\]/).drop(1).each do |block|
141
+ name = block[/^name\s*=\s*"([^"]+)"/m, 1]
142
+ version = block[/^version\s*=\s*"([^"]+)"/m, 1]
143
+ # PDM stores groups as an array, e.g. groups = ["default"] or groups = ["dev"]
144
+ groups_match = block[/^groups\s*=\s*\[([^\]]+)\]/m, 1]
145
+ groups = groups_match ? groups_match.scan(/"([^"]+)"/).flatten : ["default"]
146
+
147
+ type = if groups.include?("dev")
148
+ "develop"
149
+ else
150
+ "runtime"
151
+ end
152
+
153
+ dependencies << Dependency.new(
154
+ platform: platform_name,
155
+ name: name,
156
+ requirement: version,
157
+ type: type,
158
+ source: source
159
+ )
160
+ end
161
+ ParserResult.new(dependencies: dependencies)
162
+ end
163
+
132
164
  def self.parse_pipfile(file_contents, options: {})
133
165
  manifest = Tomlrb.parse(file_contents)
134
166
  dependencies = map_dependencies(manifest["packages"], "runtime", options.fetch(:filename, nil)) +
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bibliothecary
4
- VERSION = "15.0.1"
4
+ VERSION = "15.1.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ecosystems-bibliothecary
3
3
  version: !ruby/object:Gem::Version
4
- version: 15.0.1
4
+ version: 15.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Nesbitt
@@ -138,9 +138,11 @@ files:
138
138
  - lib/bibliothecary/parsers/hex.rb
139
139
  - lib/bibliothecary/parsers/homebrew.rb
140
140
  - lib/bibliothecary/parsers/julia.rb
141
+ - lib/bibliothecary/parsers/luarocks.rb
141
142
  - lib/bibliothecary/parsers/maven.rb
142
143
  - lib/bibliothecary/parsers/meteor.rb
143
144
  - lib/bibliothecary/parsers/mlflow.rb
145
+ - lib/bibliothecary/parsers/nimble.rb
144
146
  - lib/bibliothecary/parsers/npm.rb
145
147
  - lib/bibliothecary/parsers/nuget.rb
146
148
  - lib/bibliothecary/parsers/ollama.rb