ecosystems-bibliothecary 15.0.1 → 15.1.1
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 +26 -0
- data/README.md +14 -0
- data/lib/bibliothecary/parsers/cpan.rb +186 -2
- data/lib/bibliothecary/parsers/cran.rb +29 -0
- data/lib/bibliothecary/parsers/hackage.rb +27 -0
- data/lib/bibliothecary/parsers/luarocks.rb +53 -0
- data/lib/bibliothecary/parsers/maven.rb +74 -0
- data/lib/bibliothecary/parsers/nimble.rb +55 -0
- data/lib/bibliothecary/parsers/nuget.rb +32 -0
- data/lib/bibliothecary/parsers/pypi.rb +32 -0
- data/lib/bibliothecary/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 49988f4a70c1d5dcf6b7c05bffba8d885b3e63f04545f0c524d32b46de0804f2
|
|
4
|
+
data.tar.gz: 121b4e75f934770b9967b0e3b2427ddc1dc6aaaa19f8ed0d72f1001e50d4d575
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 227adbf47ce52d6a9bb70be154f0cc204f5f327b68adeb8c91fd2e668036359eebd07d3fb940f0d4eef00128af04fb2073304147b9285589917efef1b1d5d5cf
|
|
7
|
+
data.tar.gz: ca3b36ddfa71702dae9737500dcf09ad6a9e6c4daf5758738c793614bcdcbd730a50083d081ee45a899536b0f01c1bd7d4eef90117cc3c1aeee6063a780e0b3f
|
data/CHANGELOG.md
CHANGED
|
@@ -13,6 +13,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
13
13
|
|
|
14
14
|
### Removed
|
|
15
15
|
|
|
16
|
+
## [15.1.1]
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- CPAN: cpanfile parser for Perl dependency declarations
|
|
21
|
+
- CPAN: cpanfile.snapshot parser for Carton lockfiles
|
|
22
|
+
- CPAN: Makefile.PL parser for ExtUtils::MakeMaker build scripts
|
|
23
|
+
- CPAN: Build.PL parser for Module::Build scripts
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- CPAN: META.json and META.yml are now classified as lockfiles (they are generated, not hand-written)
|
|
28
|
+
|
|
29
|
+
## [15.1.0]
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- pdm.lock parser for Python PDM package manager
|
|
34
|
+
- renv.lock parser for R package management
|
|
35
|
+
- stack.yaml.lock parser for Haskell Stack
|
|
36
|
+
- gradle.lockfile parser for Gradle dependency locking
|
|
37
|
+
- .deps.json parser for .NET runtime dependencies
|
|
38
|
+
- verification-metadata.xml parser for Gradle dependency verification
|
|
39
|
+
- Nimble parser for Nim package manager (.nimble files)
|
|
40
|
+
- LuaRocks parser for Lua package manager (.rockspec files)
|
|
41
|
+
|
|
16
42
|
## [15.0.1]
|
|
17
43
|
|
|
18
44
|
### 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
|
|
@@ -102,6 +106,10 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
|
|
|
102
106
|
- CPAN
|
|
103
107
|
- META.json
|
|
104
108
|
- META.yml
|
|
109
|
+
- cpanfile
|
|
110
|
+
- cpanfile.snapshot
|
|
111
|
+
- Makefile.PL
|
|
112
|
+
- Build.PL
|
|
105
113
|
- CocoaPods
|
|
106
114
|
- Podfile
|
|
107
115
|
- Podfile.lock
|
|
@@ -124,6 +132,7 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
|
|
|
124
132
|
- MLmodel
|
|
125
133
|
- CRAN
|
|
126
134
|
- DESCRIPTION
|
|
135
|
+
- renv.lock
|
|
127
136
|
- Cargo
|
|
128
137
|
- Cargo.toml
|
|
129
138
|
- Cargo.lock
|
|
@@ -169,6 +178,7 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
|
|
|
169
178
|
- Hackage
|
|
170
179
|
- \*.cabal
|
|
171
180
|
- cabal.config
|
|
181
|
+
- stack.yaml.lock
|
|
172
182
|
- Actions
|
|
173
183
|
- action.yml
|
|
174
184
|
- action.yaml
|
|
@@ -188,6 +198,10 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
|
|
|
188
198
|
- Brewfile.lock.json
|
|
189
199
|
- Ollama
|
|
190
200
|
- Modelfile
|
|
201
|
+
- Nimble
|
|
202
|
+
- \*.nimble
|
|
203
|
+
- LuaRocks
|
|
204
|
+
- \*.rockspec
|
|
191
205
|
|
|
192
206
|
## Development
|
|
193
207
|
|
|
@@ -11,13 +11,29 @@ module Bibliothecary
|
|
|
11
11
|
def self.mapping
|
|
12
12
|
{
|
|
13
13
|
match_filename("META.json", case_insensitive: true) => {
|
|
14
|
-
kind: "
|
|
14
|
+
kind: "lockfile",
|
|
15
15
|
parser: :parse_json_manifest,
|
|
16
16
|
},
|
|
17
17
|
match_filename("META.yml", case_insensitive: true) => {
|
|
18
|
-
kind: "
|
|
18
|
+
kind: "lockfile",
|
|
19
19
|
parser: :parse_yaml_manifest,
|
|
20
20
|
},
|
|
21
|
+
match_filename("cpanfile", case_insensitive: true) => {
|
|
22
|
+
kind: "manifest",
|
|
23
|
+
parser: :parse_cpanfile,
|
|
24
|
+
},
|
|
25
|
+
match_filename("cpanfile.snapshot", case_insensitive: true) => {
|
|
26
|
+
kind: "lockfile",
|
|
27
|
+
parser: :parse_cpanfile_snapshot,
|
|
28
|
+
},
|
|
29
|
+
match_filename("Makefile.PL", case_insensitive: true) => {
|
|
30
|
+
kind: "manifest",
|
|
31
|
+
parser: :parse_makefile_pl,
|
|
32
|
+
},
|
|
33
|
+
match_filename("Build.PL", case_insensitive: true) => {
|
|
34
|
+
kind: "manifest",
|
|
35
|
+
parser: :parse_build_pl,
|
|
36
|
+
},
|
|
21
37
|
}
|
|
22
38
|
end
|
|
23
39
|
|
|
@@ -35,6 +51,174 @@ module Bibliothecary
|
|
|
35
51
|
dependencies = map_dependencies(manifest, "requires", "runtime", options.fetch(:filename, nil))
|
|
36
52
|
ParserResult.new(dependencies: dependencies)
|
|
37
53
|
end
|
|
54
|
+
|
|
55
|
+
def self.parse_cpanfile(file_contents, options: {})
|
|
56
|
+
filename = options.fetch(:filename, nil)
|
|
57
|
+
dependencies = []
|
|
58
|
+
current_phase = "runtime"
|
|
59
|
+
current_feature = nil
|
|
60
|
+
|
|
61
|
+
file_contents.each_line do |line|
|
|
62
|
+
line = line.strip
|
|
63
|
+
|
|
64
|
+
# Track phase changes: on 'test' => sub {
|
|
65
|
+
if line =~ /\bon\s+['"](\w+)['"]\s*=>/
|
|
66
|
+
current_phase = $1
|
|
67
|
+
next
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Track feature blocks: feature 'name', 'desc' => sub {
|
|
71
|
+
if line =~ /\bfeature\s+['"]([\w-]+)['"]/
|
|
72
|
+
current_feature = $1
|
|
73
|
+
next
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# End of block - reset to defaults
|
|
77
|
+
if line =~ /^\s*\};\s*$/
|
|
78
|
+
current_phase = "runtime"
|
|
79
|
+
current_feature = nil
|
|
80
|
+
next
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Parse dependency declarations
|
|
84
|
+
# requires 'Module::Name', 'version';
|
|
85
|
+
# requires 'Module::Name';
|
|
86
|
+
# recommends 'Module::Name', 'version';
|
|
87
|
+
if line =~ /\b(requires|recommends|suggests|conflicts)\s+['"]([^'"]+)['"](?:\s*,\s*['"]?([^'";]+)['"]?)?/
|
|
88
|
+
dep_type = $1
|
|
89
|
+
name = $2
|
|
90
|
+
version = $3&.strip || "*"
|
|
91
|
+
|
|
92
|
+
# Map cpanfile phases to our types
|
|
93
|
+
type = case current_phase
|
|
94
|
+
when "test" then "test"
|
|
95
|
+
when "develop" then "develop"
|
|
96
|
+
when "build" then "build"
|
|
97
|
+
when "configure" then "build"
|
|
98
|
+
else dep_type == "requires" ? "runtime" : dep_type
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
dependencies << Dependency.new(
|
|
102
|
+
name: name,
|
|
103
|
+
requirement: version,
|
|
104
|
+
type: type,
|
|
105
|
+
platform: "cpan",
|
|
106
|
+
source: filename
|
|
107
|
+
)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
ParserResult.new(dependencies: dependencies)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def self.parse_cpanfile_snapshot(file_contents, options: {})
|
|
115
|
+
filename = options.fetch(:filename, nil)
|
|
116
|
+
dependencies = []
|
|
117
|
+
|
|
118
|
+
file_contents.each_line do |line|
|
|
119
|
+
# Match distribution header: Module-Name-1.23
|
|
120
|
+
if (match = line.match(/^ (\S+)-v?([\d._]+)$/))
|
|
121
|
+
dist_name = match[1].gsub("-", "::")
|
|
122
|
+
version = match[2]
|
|
123
|
+
dependencies << Dependency.new(
|
|
124
|
+
name: dist_name,
|
|
125
|
+
requirement: version,
|
|
126
|
+
type: "runtime",
|
|
127
|
+
platform: "cpan",
|
|
128
|
+
source: filename
|
|
129
|
+
)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
ParserResult.new(dependencies: dependencies)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Parse Makefile.PL (ExtUtils::MakeMaker format)
|
|
137
|
+
# Looks for PREREQ_PM, BUILD_REQUIRES, TEST_REQUIRES, CONFIGURE_REQUIRES
|
|
138
|
+
def self.parse_makefile_pl(file_contents, options: {})
|
|
139
|
+
filename = options.fetch(:filename, nil)
|
|
140
|
+
dependencies = []
|
|
141
|
+
|
|
142
|
+
# Map of hash key names to dependency types
|
|
143
|
+
type_mapping = {
|
|
144
|
+
"PREREQ_PM" => "runtime",
|
|
145
|
+
"BUILD_REQUIRES" => "build",
|
|
146
|
+
"TEST_REQUIRES" => "test",
|
|
147
|
+
"CONFIGURE_REQUIRES" => "build",
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
type_mapping.each do |key, type|
|
|
151
|
+
deps = extract_perl_hash(file_contents, key)
|
|
152
|
+
deps.each do |name, version|
|
|
153
|
+
dependencies << Dependency.new(
|
|
154
|
+
name: name,
|
|
155
|
+
requirement: version,
|
|
156
|
+
type: type,
|
|
157
|
+
platform: "cpan",
|
|
158
|
+
source: filename
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
ParserResult.new(dependencies: dependencies)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Parse Build.PL (Module::Build format)
|
|
167
|
+
# Looks for requires, build_requires, test_requires, configure_requires
|
|
168
|
+
def self.parse_build_pl(file_contents, options: {})
|
|
169
|
+
filename = options.fetch(:filename, nil)
|
|
170
|
+
dependencies = []
|
|
171
|
+
|
|
172
|
+
# Map of hash key names to dependency types
|
|
173
|
+
type_mapping = {
|
|
174
|
+
"requires" => "runtime",
|
|
175
|
+
"build_requires" => "build",
|
|
176
|
+
"test_requires" => "test",
|
|
177
|
+
"configure_requires" => "build",
|
|
178
|
+
"recommends" => "runtime",
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
type_mapping.each do |key, type|
|
|
182
|
+
deps = extract_perl_hash(file_contents, key)
|
|
183
|
+
deps.each do |name, version|
|
|
184
|
+
dependencies << Dependency.new(
|
|
185
|
+
name: name,
|
|
186
|
+
requirement: version,
|
|
187
|
+
type: type,
|
|
188
|
+
platform: "cpan",
|
|
189
|
+
source: filename
|
|
190
|
+
)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
ParserResult.new(dependencies: dependencies)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Extract a Perl hash from source code
|
|
198
|
+
# Handles patterns like: KEY => { 'Module' => '1.0', ... }
|
|
199
|
+
def self.extract_perl_hash(content, key)
|
|
200
|
+
deps = {}
|
|
201
|
+
|
|
202
|
+
# Match the hash assignment: KEY => { ... }
|
|
203
|
+
# Use word boundary to avoid matching configure_requires when looking for requires
|
|
204
|
+
pattern = /(?:^|[^\w])#{Regexp.escape(key)}\s*=>\s*\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}/m
|
|
205
|
+
|
|
206
|
+
if (match = content.match(pattern))
|
|
207
|
+
hash_content = match[1]
|
|
208
|
+
|
|
209
|
+
# Extract 'Module::Name' => 'version' or 'Module::Name' => version patterns
|
|
210
|
+
hash_content.scan(/['"]([^'"]+)['"]\s*=>\s*['"]?([^'",}\s]+)['"]?/) do |name, version|
|
|
211
|
+
# Skip perl version requirements and non-module entries
|
|
212
|
+
next if name == "perl"
|
|
213
|
+
|
|
214
|
+
# Normalize version: 0 means any version
|
|
215
|
+
version = "*" if version == "0"
|
|
216
|
+
deps[name] = version
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
deps
|
|
221
|
+
end
|
|
38
222
|
end
|
|
39
223
|
end
|
|
40
224
|
end
|
|
@@ -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)) +
|
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.1.1
|
|
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
|