ecosystems-bibliothecary 15.1.1 → 15.3.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 +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +189 -124
- data/lib/bibliothecary/analyser.rb +4 -0
- data/lib/bibliothecary/dependency.rb +6 -1
- data/lib/bibliothecary/parsers/actions.rb +4 -0
- data/lib/bibliothecary/parsers/alpm.rb +89 -0
- data/lib/bibliothecary/parsers/apk.rb +91 -0
- data/lib/bibliothecary/parsers/bentoml.rb +5 -1
- data/lib/bibliothecary/parsers/bower.rb +5 -0
- data/lib/bibliothecary/parsers/cargo.rb +7 -1
- data/lib/bibliothecary/parsers/carthage.rb +4 -0
- data/lib/bibliothecary/parsers/clojars.rb +5 -0
- data/lib/bibliothecary/parsers/cocoapods.rb +33 -1
- data/lib/bibliothecary/parsers/cog.rb +5 -1
- data/lib/bibliothecary/parsers/conan.rb +4 -0
- data/lib/bibliothecary/parsers/conda.rb +6 -0
- data/lib/bibliothecary/parsers/cpan.rb +4 -0
- data/lib/bibliothecary/parsers/cran.rb +4 -0
- data/lib/bibliothecary/parsers/deb.rb +132 -0
- data/lib/bibliothecary/parsers/deno.rb +112 -0
- data/lib/bibliothecary/parsers/docker.rb +4 -0
- data/lib/bibliothecary/parsers/dub.rb +6 -0
- data/lib/bibliothecary/parsers/dvc.rb +5 -1
- data/lib/bibliothecary/parsers/elm.rb +4 -0
- data/lib/bibliothecary/parsers/go.rb +8 -2
- data/lib/bibliothecary/parsers/hackage.rb +58 -2
- data/lib/bibliothecary/parsers/haxelib.rb +5 -0
- data/lib/bibliothecary/parsers/hex.rb +109 -5
- data/lib/bibliothecary/parsers/homebrew.rb +4 -0
- data/lib/bibliothecary/parsers/julia.rb +55 -0
- data/lib/bibliothecary/parsers/luarocks.rb +5 -0
- data/lib/bibliothecary/parsers/maven.rb +4 -0
- data/lib/bibliothecary/parsers/meteor.rb +5 -0
- data/lib/bibliothecary/parsers/mlflow.rb +5 -1
- data/lib/bibliothecary/parsers/nimble.rb +5 -0
- data/lib/bibliothecary/parsers/nix.rb +205 -0
- data/lib/bibliothecary/parsers/npm.rb +84 -11
- data/lib/bibliothecary/parsers/nuget.rb +4 -0
- data/lib/bibliothecary/parsers/ollama.rb +5 -1
- data/lib/bibliothecary/parsers/packagist.rb +32 -31
- data/lib/bibliothecary/parsers/pub.rb +4 -0
- data/lib/bibliothecary/parsers/pypi.rb +25 -2
- data/lib/bibliothecary/parsers/rpm.rb +80 -0
- data/lib/bibliothecary/parsers/rubygems.rb +38 -4
- data/lib/bibliothecary/parsers/shard.rb +4 -0
- data/lib/bibliothecary/parsers/swift_pm.rb +4 -0
- data/lib/bibliothecary/parsers/vcpkg.rb +4 -0
- data/lib/bibliothecary/version.rb +1 -1
- data/lib/bibliothecary.rb +7 -0
- metadata +7 -1
|
@@ -10,6 +10,10 @@ module Bibliothecary
|
|
|
10
10
|
# Max depth to recurse into the "dependencies" property of package-lock.json
|
|
11
11
|
PACKAGE_LOCK_JSON_MAX_DEPTH = 10
|
|
12
12
|
|
|
13
|
+
def self.file_patterns
|
|
14
|
+
["package.json", "package-lock.json", "npm-shrinkwrap.json", "yarn.lock", "pnpm-lock.yaml", "pnpm-workspace.yaml", "bun.lock", "npm-ls.json"]
|
|
15
|
+
end
|
|
16
|
+
|
|
13
17
|
def self.mapping
|
|
14
18
|
{
|
|
15
19
|
match_filename("package.json") => {
|
|
@@ -28,6 +32,11 @@ module Bibliothecary
|
|
|
28
32
|
kind: "lockfile",
|
|
29
33
|
parser: :parse_pnpm_lock,
|
|
30
34
|
},
|
|
35
|
+
match_filename("pnpm-workspace.yaml") => {
|
|
36
|
+
kind: "manifest",
|
|
37
|
+
parser: :parse_pnpm_workspace,
|
|
38
|
+
related_to: ["lockfile"],
|
|
39
|
+
},
|
|
31
40
|
match_filename("npm-ls.json") => {
|
|
32
41
|
kind: "lockfile",
|
|
33
42
|
parser: :parse_ls,
|
|
@@ -97,7 +106,8 @@ module Bibliothecary
|
|
|
97
106
|
type: dep.fetch("dev", false) || dep.fetch("devOptional", false) ? "development" : "runtime",
|
|
98
107
|
local: dep.fetch("link", false),
|
|
99
108
|
source: source,
|
|
100
|
-
platform: platform_name
|
|
109
|
+
platform: platform_name,
|
|
110
|
+
integrity: dep["integrity"]
|
|
101
111
|
)
|
|
102
112
|
end
|
|
103
113
|
end
|
|
@@ -118,7 +128,8 @@ module Bibliothecary
|
|
|
118
128
|
requirement: version,
|
|
119
129
|
type: type,
|
|
120
130
|
source: source,
|
|
121
|
-
platform: platform_name
|
|
131
|
+
platform: platform_name,
|
|
132
|
+
integrity: requirement["integrity"]
|
|
122
133
|
)] + child_dependencies
|
|
123
134
|
end
|
|
124
135
|
end
|
|
@@ -187,7 +198,8 @@ module Bibliothecary
|
|
|
187
198
|
type: nil, # yarn.lock doesn't report on the type of dependency
|
|
188
199
|
local: dep[:requirements]&.first&.start_with?("file:"),
|
|
189
200
|
source: options.fetch(:filename, nil),
|
|
190
|
-
platform: platform_name
|
|
201
|
+
platform: platform_name,
|
|
202
|
+
integrity: dep[:integrity]
|
|
191
203
|
)
|
|
192
204
|
end
|
|
193
205
|
ParserResult.new(dependencies: dependencies)
|
|
@@ -228,11 +240,23 @@ module Bibliothecary
|
|
|
228
240
|
|
|
229
241
|
def self.parse_v2_yarn_lock(contents, source = nil)
|
|
230
242
|
deps = []
|
|
231
|
-
#
|
|
232
|
-
#
|
|
233
|
-
contents.
|
|
243
|
+
# Split into blocks by double newlines or by unquoted key lines
|
|
244
|
+
# Each block starts with "package@npm:req": and continues until the next package
|
|
245
|
+
blocks = contents.split(/\n\n+/)
|
|
246
|
+
|
|
247
|
+
blocks.each do |block|
|
|
248
|
+
# Match the package header: "package@npm:...":\n version: ...
|
|
249
|
+
match = block.match(/^"([^"]+)":\s*\n(.+)/m)
|
|
250
|
+
next unless match
|
|
251
|
+
|
|
252
|
+
packages_str = match[1]
|
|
253
|
+
body = match[2]
|
|
254
|
+
|
|
255
|
+
version = body[/version:\s*([^\n]+)/, 1]
|
|
256
|
+
checksum = body[/checksum:\s*([^\n]+)/, 1]
|
|
257
|
+
|
|
234
258
|
# Skip workspace/local packages and patches
|
|
235
|
-
next if version
|
|
259
|
+
next if version&.include?("use.local") && packages_str.include?("workspace")
|
|
236
260
|
next if packages_str.include?("@patch:")
|
|
237
261
|
|
|
238
262
|
packages = packages_str.split(", ")
|
|
@@ -247,6 +271,7 @@ module Bibliothecary
|
|
|
247
271
|
original_requirement: alias_name.nil? ? nil : version.to_s,
|
|
248
272
|
version: version.to_s,
|
|
249
273
|
source: source,
|
|
274
|
+
integrity: checksum,
|
|
250
275
|
}
|
|
251
276
|
end
|
|
252
277
|
deps
|
|
@@ -281,7 +306,8 @@ module Bibliothecary
|
|
|
281
306
|
original_requirement: original_requirement,
|
|
282
307
|
type: is_dev ? "development" : "runtime",
|
|
283
308
|
source: source,
|
|
284
|
-
platform: platform_name
|
|
309
|
+
platform: platform_name,
|
|
310
|
+
integrity: details.dig("resolution", "integrity")
|
|
285
311
|
)
|
|
286
312
|
end
|
|
287
313
|
end
|
|
@@ -318,7 +344,8 @@ module Bibliothecary
|
|
|
318
344
|
original_requirement: original_requirement,
|
|
319
345
|
type: is_dev ? "development" : "runtime",
|
|
320
346
|
source: source,
|
|
321
|
-
platform: platform_name
|
|
347
|
+
platform: platform_name,
|
|
348
|
+
integrity: details.dig("resolution", "integrity")
|
|
322
349
|
)
|
|
323
350
|
end
|
|
324
351
|
end
|
|
@@ -327,6 +354,7 @@ module Bibliothecary
|
|
|
327
354
|
dependencies = parsed_contents.fetch("importers", {}).fetch(".", {}).fetch("dependencies", {})
|
|
328
355
|
dev_dependencies = parsed_contents.fetch("importers", {}).fetch(".", {}).fetch("devDependencies", {})
|
|
329
356
|
dependency_mapping = dependencies.merge(dev_dependencies)
|
|
357
|
+
packages = parsed_contents.fetch("packages", {})
|
|
330
358
|
|
|
331
359
|
# "dependencies" is in "packages" for < v9 and in "snapshots" for >= v9
|
|
332
360
|
# as of https://github.com/pnpm/pnpm/pull/7700.
|
|
@@ -360,6 +388,10 @@ module Bibliothecary
|
|
|
360
388
|
dev_name == name && dev_details["version"] == version
|
|
361
389
|
end
|
|
362
390
|
|
|
391
|
+
# In v9, integrity is stored in packages section, not snapshots
|
|
392
|
+
package_key = "#{name}@#{version}"
|
|
393
|
+
integrity = packages.dig(package_key, "resolution", "integrity")
|
|
394
|
+
|
|
363
395
|
Dependency.new(
|
|
364
396
|
name: name,
|
|
365
397
|
requirement: version,
|
|
@@ -367,7 +399,8 @@ module Bibliothecary
|
|
|
367
399
|
original_requirement: original_requirement,
|
|
368
400
|
type: is_dev ? "development" : "runtime",
|
|
369
401
|
source: source,
|
|
370
|
-
platform: platform_name
|
|
402
|
+
platform: platform_name,
|
|
403
|
+
integrity: integrity
|
|
371
404
|
)
|
|
372
405
|
end
|
|
373
406
|
end
|
|
@@ -391,6 +424,45 @@ module Bibliothecary
|
|
|
391
424
|
ParserResult.new(dependencies: dependencies)
|
|
392
425
|
end
|
|
393
426
|
|
|
427
|
+
def self.parse_pnpm_workspace(contents, options: {})
|
|
428
|
+
parsed = YAML.load(contents)
|
|
429
|
+
source = options.fetch(:filename, nil)
|
|
430
|
+
|
|
431
|
+
dependencies = []
|
|
432
|
+
|
|
433
|
+
# Parse the default catalog (pnpm 9+)
|
|
434
|
+
if parsed["catalog"].is_a?(Hash)
|
|
435
|
+
parsed["catalog"].each do |name, requirement|
|
|
436
|
+
dependencies << Dependency.new(
|
|
437
|
+
name: name,
|
|
438
|
+
requirement: requirement,
|
|
439
|
+
type: "runtime",
|
|
440
|
+
source: source,
|
|
441
|
+
platform: platform_name
|
|
442
|
+
)
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
# Parse named catalogs (pnpm 9+)
|
|
447
|
+
if parsed["catalogs"].is_a?(Hash)
|
|
448
|
+
parsed["catalogs"].each do |_catalog_name, catalog_deps|
|
|
449
|
+
next unless catalog_deps.is_a?(Hash)
|
|
450
|
+
|
|
451
|
+
catalog_deps.each do |name, requirement|
|
|
452
|
+
dependencies << Dependency.new(
|
|
453
|
+
name: name,
|
|
454
|
+
requirement: requirement,
|
|
455
|
+
type: "runtime",
|
|
456
|
+
source: source,
|
|
457
|
+
platform: platform_name
|
|
458
|
+
)
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
ParserResult.new(dependencies: dependencies)
|
|
464
|
+
end
|
|
465
|
+
|
|
394
466
|
def self.parse_ls(file_contents, options: {})
|
|
395
467
|
manifest = JSON.parse(file_contents)
|
|
396
468
|
|
|
@@ -421,7 +493,8 @@ module Bibliothecary
|
|
|
421
493
|
type: dev_deps&.include?(name) ? "development" : "runtime",
|
|
422
494
|
local: is_local,
|
|
423
495
|
source: source,
|
|
424
|
-
platform: platform_name
|
|
496
|
+
platform: platform_name,
|
|
497
|
+
integrity: info[3]
|
|
425
498
|
)
|
|
426
499
|
end
|
|
427
500
|
ParserResult.new(dependencies: dependencies)
|
|
@@ -8,6 +8,10 @@ module Bibliothecary
|
|
|
8
8
|
class Nuget
|
|
9
9
|
include Bibliothecary::Analyser
|
|
10
10
|
|
|
11
|
+
def self.file_patterns
|
|
12
|
+
["Project.json", "Project.lock.json", "packages.lock.json", "packages.config", "*.nuspec", "*.csproj", "paket.lock", "project.assets.json", "*.deps.json"]
|
|
13
|
+
end
|
|
14
|
+
|
|
11
15
|
def self.mapping
|
|
12
16
|
{
|
|
13
17
|
match_filename("Project.json") => {
|
|
@@ -5,12 +5,16 @@ module Bibliothecary
|
|
|
5
5
|
class Ollama
|
|
6
6
|
include Bibliothecary::Analyser
|
|
7
7
|
|
|
8
|
+
def self.file_patterns
|
|
9
|
+
["Modelfile"]
|
|
10
|
+
end
|
|
11
|
+
|
|
8
12
|
def self.mapping
|
|
9
13
|
{
|
|
10
14
|
match_filename("Modelfile") => {
|
|
11
15
|
kind: 'manifest',
|
|
12
16
|
parser: :parse_modelfile,
|
|
13
|
-
|
|
17
|
+
can_have_lockfile: false,
|
|
14
18
|
}
|
|
15
19
|
}
|
|
16
20
|
end
|
|
@@ -7,6 +7,10 @@ module Bibliothecary
|
|
|
7
7
|
class Packagist
|
|
8
8
|
include Bibliothecary::Analyser
|
|
9
9
|
|
|
10
|
+
def self.file_patterns
|
|
11
|
+
["composer.json", "composer.lock"]
|
|
12
|
+
end
|
|
13
|
+
|
|
10
14
|
def self.mapping
|
|
11
15
|
{
|
|
12
16
|
match_filename("composer.json") => {
|
|
@@ -23,42 +27,39 @@ module Bibliothecary
|
|
|
23
27
|
|
|
24
28
|
def self.parse_lockfile(file_contents, options: {})
|
|
25
29
|
manifest = JSON.parse file_contents
|
|
26
|
-
|
|
27
|
-
requirement = dependency["version"]
|
|
30
|
+
source = options.fetch(:filename, nil)
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
original_requirement = requirement
|
|
32
|
-
requirement = dependency.dig("source", "reference")
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
Dependency.new(
|
|
36
|
-
name: dependency["name"],
|
|
37
|
-
requirement: requirement,
|
|
38
|
-
type: "runtime",
|
|
39
|
-
original_requirement: original_requirement,
|
|
40
|
-
source: options.fetch(:filename, nil),
|
|
41
|
-
platform: platform_name
|
|
42
|
-
)
|
|
32
|
+
dependencies = manifest.fetch("packages", []).map do |dependency|
|
|
33
|
+
parse_composer_dependency(dependency, "runtime", source)
|
|
43
34
|
end + manifest.fetch("packages-dev", []).map do |dependency|
|
|
44
|
-
|
|
35
|
+
parse_composer_dependency(dependency, "development", source)
|
|
36
|
+
end
|
|
37
|
+
ParserResult.new(dependencies: dependencies)
|
|
38
|
+
end
|
|
45
39
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
requirement = dependency.dig("source", "reference")
|
|
50
|
-
end
|
|
40
|
+
def self.parse_composer_dependency(dependency, type, source)
|
|
41
|
+
requirement = dependency["version"]
|
|
42
|
+
original_requirement = nil
|
|
51
43
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
original_requirement: original_requirement,
|
|
57
|
-
source: options.fetch(:filename, nil),
|
|
58
|
-
platform: platform_name
|
|
59
|
-
)
|
|
44
|
+
# Store Drupal version if Drupal, but include the original manifest version for reference
|
|
45
|
+
if drupal_module?(dependency)
|
|
46
|
+
original_requirement = requirement
|
|
47
|
+
requirement = dependency.dig("source", "reference")
|
|
60
48
|
end
|
|
61
|
-
|
|
49
|
+
|
|
50
|
+
# Extract shasum from dist if present and non-empty
|
|
51
|
+
shasum = dependency.dig("dist", "shasum")
|
|
52
|
+
integrity = shasum && !shasum.empty? ? "sha1=#{shasum}" : nil
|
|
53
|
+
|
|
54
|
+
Dependency.new(
|
|
55
|
+
name: dependency["name"],
|
|
56
|
+
requirement: requirement,
|
|
57
|
+
type: type,
|
|
58
|
+
original_requirement: original_requirement,
|
|
59
|
+
source: source,
|
|
60
|
+
platform: platform_name,
|
|
61
|
+
integrity: integrity
|
|
62
|
+
)
|
|
62
63
|
end
|
|
63
64
|
|
|
64
65
|
def self.parse_manifest(file_contents, options: {})
|
|
@@ -24,6 +24,15 @@ module Bibliothecary
|
|
|
24
24
|
# https://packaging.python.org/en/latest/specifications/pylock-toml/
|
|
25
25
|
PEP_751_LOCKFILE_REGEXP = /^pylock(\.[^.]+)?\.toml$/
|
|
26
26
|
|
|
27
|
+
def self.file_patterns
|
|
28
|
+
[
|
|
29
|
+
"setup.py", "requirements*.txt", "requirements*.pip", "requirements*.in",
|
|
30
|
+
"requirements.frozen", "Pipfile", "Pipfile.lock", "pyproject.toml",
|
|
31
|
+
"poetry.lock", "uv.lock", "pylock.toml", "pdm.lock",
|
|
32
|
+
"pip-resolved-dependencies.txt", "pip-dependency-graph.json"
|
|
33
|
+
]
|
|
34
|
+
end
|
|
35
|
+
|
|
27
36
|
def self.mapping
|
|
28
37
|
{
|
|
29
38
|
match_filenames("requirements-dev.txt", "requirements/dev.txt",
|
|
@@ -122,12 +131,16 @@ module Bibliothecary
|
|
|
122
131
|
name = block[/name\s*=\s*"([^"]+)"/, 1]
|
|
123
132
|
version = block[/version\s*=\s*"([^"]+)"/, 1]
|
|
124
133
|
|
|
134
|
+
# Extract sdist hash: sdist = { url = "...", hash = "sha256:...", size = ... }
|
|
135
|
+
integrity = block[/^sdist\s*=\s*\{[^}]*hash\s*=\s*"([^"]+)"/m, 1]
|
|
136
|
+
|
|
125
137
|
dependencies << Dependency.new(
|
|
126
138
|
platform: platform_name,
|
|
127
139
|
name: name,
|
|
128
140
|
requirement: version,
|
|
129
141
|
type: "runtime", # All dependencies are considered runtime
|
|
130
|
-
source: source
|
|
142
|
+
source: source,
|
|
143
|
+
integrity: integrity
|
|
131
144
|
)
|
|
132
145
|
end
|
|
133
146
|
ParserResult.new(dependencies: dependencies)
|
|
@@ -297,6 +310,15 @@ module Bibliothecary
|
|
|
297
310
|
|
|
298
311
|
groups = ["runtime"] if groups.empty?
|
|
299
312
|
|
|
313
|
+
# Extract sdist hash from files array (look for .tar.gz entry)
|
|
314
|
+
integrity = nil
|
|
315
|
+
if (files_match = block[/^files\s*=\s*\[(.*?)\]/m, 1])
|
|
316
|
+
# Match .tar.gz file entry and extract hash
|
|
317
|
+
if (sdist_match = files_match.match(/\{file\s*=\s*"[^"]+\.tar\.gz",\s*hash\s*=\s*"([^"]+)"\}/))
|
|
318
|
+
integrity = sdist_match[1]
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
300
322
|
groups.each do |group|
|
|
301
323
|
# Poetry lockfiles should already contain normalized names, but we'll
|
|
302
324
|
# apply it here as well just to be consistent with pyproject.toml parsing.
|
|
@@ -307,7 +329,8 @@ module Bibliothecary
|
|
|
307
329
|
requirement: version,
|
|
308
330
|
type: group,
|
|
309
331
|
source: options.fetch(:filename, nil),
|
|
310
|
-
platform: platform_name
|
|
332
|
+
platform: platform_name,
|
|
333
|
+
integrity: integrity
|
|
311
334
|
)
|
|
312
335
|
end
|
|
313
336
|
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bibliothecary
|
|
4
|
+
module Parsers
|
|
5
|
+
class Rpm
|
|
6
|
+
include Bibliothecary::Analyser
|
|
7
|
+
|
|
8
|
+
def self.file_patterns
|
|
9
|
+
["*.spec"]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.mapping
|
|
13
|
+
{
|
|
14
|
+
match_extension(".spec") => {
|
|
15
|
+
kind: "manifest",
|
|
16
|
+
parser: :parse_spec,
|
|
17
|
+
can_have_lockfile: false,
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.parse_spec(file_contents, options: {})
|
|
23
|
+
source = options.fetch(:filename, nil)
|
|
24
|
+
dependencies = []
|
|
25
|
+
|
|
26
|
+
# Parse BuildRequires (build dependencies)
|
|
27
|
+
file_contents.scan(/^BuildRequires:\s*(.+)$/i) do |match|
|
|
28
|
+
parse_dependency_line(match[0]).each do |dep|
|
|
29
|
+
dependencies << Dependency.new(
|
|
30
|
+
name: dep[:name],
|
|
31
|
+
requirement: dep[:requirement] || "*",
|
|
32
|
+
type: "build",
|
|
33
|
+
source: source,
|
|
34
|
+
platform: platform_name
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Parse Requires (runtime dependencies), including Requires(pre), Requires(post), etc.
|
|
40
|
+
file_contents.scan(/^Requires(?:\([^)]+\))?:\s*(.+)$/i) do |match|
|
|
41
|
+
parse_dependency_line(match[0]).each do |dep|
|
|
42
|
+
dependencies << Dependency.new(
|
|
43
|
+
name: dep[:name],
|
|
44
|
+
requirement: dep[:requirement] || "*",
|
|
45
|
+
type: "runtime",
|
|
46
|
+
source: source,
|
|
47
|
+
platform: platform_name
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
ParserResult.new(dependencies: dependencies)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.parse_dependency_line(line)
|
|
56
|
+
# Dependencies can be comma or whitespace separated
|
|
57
|
+
# Each dependency can have version constraints like: pkg >= 1.0
|
|
58
|
+
# Also filter out RPM macros like %{name}
|
|
59
|
+
deps = []
|
|
60
|
+
|
|
61
|
+
# Split on commas first, then handle each part
|
|
62
|
+
line.split(/,/).each do |part|
|
|
63
|
+
part = part.strip
|
|
64
|
+
next if part.empty?
|
|
65
|
+
next if part.start_with?("%") # Skip RPM macros
|
|
66
|
+
next if part.start_with?("/") # Skip file paths like /bin/sh
|
|
67
|
+
|
|
68
|
+
# Check for version constraint (pkg >= 1.0, pkg < 2.0, etc.)
|
|
69
|
+
if part =~ /^(\S+)\s+([<>=]+)\s*(\S+)$/
|
|
70
|
+
deps << { name: $1, requirement: "#{$2} #{$3}" }
|
|
71
|
+
elsif part =~ /^(\S+)$/
|
|
72
|
+
deps << { name: $1, requirement: nil }
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
deps
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -10,6 +10,8 @@ module Bibliothecary
|
|
|
10
10
|
NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'
|
|
11
11
|
NAME_VERSION_4 = /^ {4}#{NAME_VERSION}$/
|
|
12
12
|
BUNDLED_WITH = /BUNDLED WITH/
|
|
13
|
+
CHECKSUMS_START = /^CHECKSUMS$/
|
|
14
|
+
CHECKSUM_LINE = /^ (.+) \(([^)]+)\) sha256=([a-f0-9]+)$/
|
|
13
15
|
|
|
14
16
|
# Gemfile patterns
|
|
15
17
|
GEM_REGEXP = /^\s*gem\s+['"]([^'"]+)['"]\s*(?:,\s*['"]([^'"]+)['"])?/
|
|
@@ -19,6 +21,10 @@ module Bibliothecary
|
|
|
19
21
|
# Gemspec pattern - captures type in first group
|
|
20
22
|
GEMSPEC_DEPENDENCY = /\.add_(development_|runtime_)?dependency\s*\(?\s*['"]([^'"]+)['"]\s*(?:,\s*['"]([^'"]+)['"])?(?:\s*,\s*['"]([^'"]+)['"])?\s*\)?/
|
|
21
23
|
|
|
24
|
+
def self.file_patterns
|
|
25
|
+
["Gemfile", "Gemfile.lock", "gems.rb", "gems.locked", "*.gemspec"]
|
|
26
|
+
end
|
|
27
|
+
|
|
22
28
|
def self.mapping
|
|
23
29
|
{
|
|
24
30
|
match_filenames("Gemfile", "gems.rb") => {
|
|
@@ -43,6 +49,7 @@ module Bibliothecary
|
|
|
43
49
|
def self.parse_gemfile_lock(file_contents, options: {})
|
|
44
50
|
source = options.fetch(:filename, nil)
|
|
45
51
|
dependencies = []
|
|
52
|
+
checksums = parse_checksums(file_contents)
|
|
46
53
|
|
|
47
54
|
file_contents.each_line do |line|
|
|
48
55
|
line = line.chomp.gsub(/\r$/, "")
|
|
@@ -56,17 +63,43 @@ module Bibliothecary
|
|
|
56
63
|
name: name,
|
|
57
64
|
requirement: version,
|
|
58
65
|
type: "runtime",
|
|
59
|
-
source: source
|
|
66
|
+
source: source,
|
|
67
|
+
integrity: checksums["#{name}-#{version}"]
|
|
60
68
|
)
|
|
61
69
|
end
|
|
62
70
|
|
|
63
|
-
if (bundler_dep = parse_bundler(file_contents, source))
|
|
71
|
+
if (bundler_dep = parse_bundler(file_contents, source, checksums))
|
|
64
72
|
dependencies << bundler_dep
|
|
65
73
|
end
|
|
66
74
|
|
|
67
75
|
ParserResult.new(dependencies: dependencies)
|
|
68
76
|
end
|
|
69
77
|
|
|
78
|
+
def self.parse_checksums(file_contents)
|
|
79
|
+
checksums = {}
|
|
80
|
+
in_checksums = false
|
|
81
|
+
|
|
82
|
+
file_contents.each_line do |line|
|
|
83
|
+
line = line.chomp
|
|
84
|
+
if line.match?(CHECKSUMS_START)
|
|
85
|
+
in_checksums = true
|
|
86
|
+
next
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
next unless in_checksums
|
|
90
|
+
|
|
91
|
+
# End of CHECKSUMS section (blank line or new section)
|
|
92
|
+
break if line.empty? || line.match?(/^[A-Z]/)
|
|
93
|
+
|
|
94
|
+
if (match = line.match(CHECKSUM_LINE))
|
|
95
|
+
name, version, sha256 = match.captures
|
|
96
|
+
checksums["#{name}-#{version}"] = "sha256=#{sha256}"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
checksums
|
|
101
|
+
end
|
|
102
|
+
|
|
70
103
|
def self.parse_gemfile(file_contents, options: {})
|
|
71
104
|
source = options.fetch(:filename, nil)
|
|
72
105
|
deps = []
|
|
@@ -147,7 +180,7 @@ module Bibliothecary
|
|
|
147
180
|
end
|
|
148
181
|
end
|
|
149
182
|
|
|
150
|
-
def self.parse_bundler(file_contents, source = nil)
|
|
183
|
+
def self.parse_bundler(file_contents, source = nil, checksums = {})
|
|
151
184
|
bundled_with_index = file_contents.lines(chomp: true).find_index { |line| line.match(BUNDLED_WITH) }
|
|
152
185
|
return nil unless bundled_with_index
|
|
153
186
|
|
|
@@ -159,7 +192,8 @@ module Bibliothecary
|
|
|
159
192
|
requirement: version,
|
|
160
193
|
type: "runtime",
|
|
161
194
|
source: source,
|
|
162
|
-
platform: platform_name
|
|
195
|
+
platform: platform_name,
|
|
196
|
+
integrity: checksums["bundler-#{version}"]
|
|
163
197
|
)
|
|
164
198
|
end
|
|
165
199
|
end
|
|
@@ -12,6 +12,10 @@ module Bibliothecary
|
|
|
12
12
|
PACKAGE_REGEXP_EXACT = /\.package\s*\(\s*(?:name:\s*"[^"]+",\s*)?url:\s*"([^"]+)"[^)]*(?:\.exact|exact)\s*\(\s*"([^"]+)"\s*\)/i
|
|
13
13
|
PACKAGE_REGEXP_RANGE = /\.package\s*\(\s*(?:name:\s*"[^"]+",\s*)?url:\s*"([^"]+)"[^)]*"([^"]+)"\s*(?:\.\.|\.\.\.)\s*"([^"]+)"/i
|
|
14
14
|
|
|
15
|
+
def self.file_patterns
|
|
16
|
+
["Package.swift", "Package.resolved"]
|
|
17
|
+
end
|
|
18
|
+
|
|
15
19
|
def self.mapping
|
|
16
20
|
{
|
|
17
21
|
match_filename("Package.swift", case_insensitive: true) => {
|
data/lib/bibliothecary.rb
CHANGED
|
@@ -57,6 +57,13 @@ module Bibliothecary
|
|
|
57
57
|
runner.package_managers
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
def self.supported_files
|
|
61
|
+
package_managers.each_with_object({}) do |pm, hash|
|
|
62
|
+
patterns = pm.file_patterns
|
|
63
|
+
hash[pm.platform_name] = patterns unless patterns.empty?
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
60
67
|
def self.find_manifests(path)
|
|
61
68
|
runner.find_manifests(path)
|
|
62
69
|
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.
|
|
4
|
+
version: 15.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Nesbitt
|
|
@@ -117,6 +117,8 @@ files:
|
|
|
117
117
|
- lib/bibliothecary/file_info.rb
|
|
118
118
|
- lib/bibliothecary/parser_result.rb
|
|
119
119
|
- lib/bibliothecary/parsers/actions.rb
|
|
120
|
+
- lib/bibliothecary/parsers/alpm.rb
|
|
121
|
+
- lib/bibliothecary/parsers/apk.rb
|
|
120
122
|
- lib/bibliothecary/parsers/bentoml.rb
|
|
121
123
|
- lib/bibliothecary/parsers/bower.rb
|
|
122
124
|
- lib/bibliothecary/parsers/cargo.rb
|
|
@@ -128,6 +130,8 @@ files:
|
|
|
128
130
|
- lib/bibliothecary/parsers/conda.rb
|
|
129
131
|
- lib/bibliothecary/parsers/cpan.rb
|
|
130
132
|
- lib/bibliothecary/parsers/cran.rb
|
|
133
|
+
- lib/bibliothecary/parsers/deb.rb
|
|
134
|
+
- lib/bibliothecary/parsers/deno.rb
|
|
131
135
|
- lib/bibliothecary/parsers/docker.rb
|
|
132
136
|
- lib/bibliothecary/parsers/dub.rb
|
|
133
137
|
- lib/bibliothecary/parsers/dvc.rb
|
|
@@ -143,12 +147,14 @@ files:
|
|
|
143
147
|
- lib/bibliothecary/parsers/meteor.rb
|
|
144
148
|
- lib/bibliothecary/parsers/mlflow.rb
|
|
145
149
|
- lib/bibliothecary/parsers/nimble.rb
|
|
150
|
+
- lib/bibliothecary/parsers/nix.rb
|
|
146
151
|
- lib/bibliothecary/parsers/npm.rb
|
|
147
152
|
- lib/bibliothecary/parsers/nuget.rb
|
|
148
153
|
- lib/bibliothecary/parsers/ollama.rb
|
|
149
154
|
- lib/bibliothecary/parsers/packagist.rb
|
|
150
155
|
- lib/bibliothecary/parsers/pub.rb
|
|
151
156
|
- lib/bibliothecary/parsers/pypi.rb
|
|
157
|
+
- lib/bibliothecary/parsers/rpm.rb
|
|
152
158
|
- lib/bibliothecary/parsers/rubygems.rb
|
|
153
159
|
- lib/bibliothecary/parsers/shard.rb
|
|
154
160
|
- lib/bibliothecary/parsers/swift_pm.rb
|