ecosystems-bibliothecary 15.1.0 → 15.2.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 +24 -0
- data/README.md +137 -120
- data/lib/bibliothecary/analyser.rb +4 -0
- data/lib/bibliothecary/parsers/actions.rb +4 -0
- data/lib/bibliothecary/parsers/bentoml.rb +4 -0
- data/lib/bibliothecary/parsers/bower.rb +4 -0
- data/lib/bibliothecary/parsers/cargo.rb +4 -0
- data/lib/bibliothecary/parsers/carthage.rb +4 -0
- data/lib/bibliothecary/parsers/clojars.rb +4 -0
- data/lib/bibliothecary/parsers/cocoapods.rb +4 -0
- data/lib/bibliothecary/parsers/cog.rb +4 -0
- data/lib/bibliothecary/parsers/conan.rb +4 -0
- data/lib/bibliothecary/parsers/conda.rb +4 -0
- data/lib/bibliothecary/parsers/cpan.rb +190 -2
- data/lib/bibliothecary/parsers/cran.rb +4 -0
- data/lib/bibliothecary/parsers/deno.rb +98 -0
- data/lib/bibliothecary/parsers/docker.rb +4 -0
- data/lib/bibliothecary/parsers/dub.rb +4 -0
- data/lib/bibliothecary/parsers/dvc.rb +4 -0
- data/lib/bibliothecary/parsers/elm.rb +4 -0
- data/lib/bibliothecary/parsers/go.rb +4 -0
- data/lib/bibliothecary/parsers/hackage.rb +55 -0
- data/lib/bibliothecary/parsers/haxelib.rb +4 -0
- data/lib/bibliothecary/parsers/hex.rb +89 -0
- data/lib/bibliothecary/parsers/homebrew.rb +4 -0
- data/lib/bibliothecary/parsers/julia.rb +55 -0
- data/lib/bibliothecary/parsers/luarocks.rb +4 -0
- data/lib/bibliothecary/parsers/maven.rb +4 -0
- data/lib/bibliothecary/parsers/meteor.rb +4 -0
- data/lib/bibliothecary/parsers/mlflow.rb +4 -0
- data/lib/bibliothecary/parsers/nimble.rb +4 -0
- data/lib/bibliothecary/parsers/nix.rb +205 -0
- data/lib/bibliothecary/parsers/npm.rb +4 -0
- data/lib/bibliothecary/parsers/nuget.rb +4 -0
- data/lib/bibliothecary/parsers/ollama.rb +4 -0
- data/lib/bibliothecary/parsers/packagist.rb +4 -0
- data/lib/bibliothecary/parsers/pub.rb +4 -0
- data/lib/bibliothecary/parsers/pypi.rb +9 -0
- data/lib/bibliothecary/parsers/rubygems.rb +4 -0
- 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 +3 -1
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Bibliothecary
|
|
6
|
+
module Parsers
|
|
7
|
+
class Deno
|
|
8
|
+
include Bibliothecary::Analyser
|
|
9
|
+
|
|
10
|
+
def self.file_patterns
|
|
11
|
+
["deno.json", "deno.jsonc", "deno.lock"]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.mapping
|
|
15
|
+
{
|
|
16
|
+
match_filename("deno.json") => {
|
|
17
|
+
kind: "manifest",
|
|
18
|
+
parser: :parse_manifest,
|
|
19
|
+
},
|
|
20
|
+
match_filename("deno.jsonc") => {
|
|
21
|
+
kind: "manifest",
|
|
22
|
+
parser: :parse_manifest,
|
|
23
|
+
},
|
|
24
|
+
match_filename("deno.lock") => {
|
|
25
|
+
kind: "lockfile",
|
|
26
|
+
parser: :parse_lockfile,
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.parse_manifest(file_contents, options: {})
|
|
32
|
+
manifest = JSON.parse(file_contents)
|
|
33
|
+
source = options.fetch(:filename, nil)
|
|
34
|
+
|
|
35
|
+
dependencies = manifest.fetch("imports", {}).map do |alias_name, specifier|
|
|
36
|
+
name, requirement = parse_specifier(specifier)
|
|
37
|
+
next unless name
|
|
38
|
+
|
|
39
|
+
Dependency.new(
|
|
40
|
+
name: name,
|
|
41
|
+
requirement: requirement,
|
|
42
|
+
type: "runtime",
|
|
43
|
+
source: source,
|
|
44
|
+
platform: platform_name
|
|
45
|
+
)
|
|
46
|
+
end.compact
|
|
47
|
+
|
|
48
|
+
ParserResult.new(dependencies: dependencies)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.parse_lockfile(file_contents, options: {})
|
|
52
|
+
manifest = JSON.parse(file_contents)
|
|
53
|
+
source = options.fetch(:filename, nil)
|
|
54
|
+
|
|
55
|
+
dependencies = manifest.fetch("specifiers", {}).map do |specifier, resolved_version|
|
|
56
|
+
name, _requirement = parse_specifier(specifier)
|
|
57
|
+
next unless name
|
|
58
|
+
|
|
59
|
+
Dependency.new(
|
|
60
|
+
name: name,
|
|
61
|
+
requirement: resolved_version,
|
|
62
|
+
type: "runtime",
|
|
63
|
+
source: source,
|
|
64
|
+
platform: platform_name
|
|
65
|
+
)
|
|
66
|
+
end.compact
|
|
67
|
+
|
|
68
|
+
ParserResult.new(dependencies: dependencies)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Parses specifiers like:
|
|
72
|
+
# "npm:chalk@1" => ["chalk", "1"]
|
|
73
|
+
# "npm:chalk" => ["chalk", "*"]
|
|
74
|
+
# "jsr:@std/path@^1" => ["@std/path", "^1"]
|
|
75
|
+
# "jsr:@std/path" => ["@std/path", "*"]
|
|
76
|
+
def self.parse_specifier(specifier)
|
|
77
|
+
return nil unless specifier.start_with?("npm:", "jsr:")
|
|
78
|
+
|
|
79
|
+
# Remove the protocol prefix
|
|
80
|
+
without_protocol = specifier.sub(/^(npm|jsr):/, "")
|
|
81
|
+
|
|
82
|
+
# Handle scoped packages (@scope/name@version)
|
|
83
|
+
if without_protocol.start_with?("@")
|
|
84
|
+
# Split on @ but keep the first @ for the scope
|
|
85
|
+
parts = without_protocol[1..].split("@", 2)
|
|
86
|
+
name = "@#{parts[0]}"
|
|
87
|
+
requirement = parts[1] || "*"
|
|
88
|
+
else
|
|
89
|
+
# Regular package (name@version)
|
|
90
|
+
name, requirement = without_protocol.split("@", 2)
|
|
91
|
+
requirement ||= "*"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
[name, requirement]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -7,6 +7,10 @@ module Bibliothecary
|
|
|
7
7
|
class Elm
|
|
8
8
|
include Bibliothecary::Analyser
|
|
9
9
|
|
|
10
|
+
def self.file_patterns
|
|
11
|
+
["elm-package.json", "elm_dependencies.json", "elm-stuff/exact-dependencies.json"]
|
|
12
|
+
end
|
|
13
|
+
|
|
10
14
|
def self.mapping
|
|
11
15
|
{
|
|
12
16
|
match_filenames("elm-package.json", "elm_dependencies.json") => {
|
|
@@ -18,6 +18,10 @@ module Bibliothecary
|
|
|
18
18
|
GOMOD_MULTILINE_END_REGEXP = /^\)/
|
|
19
19
|
GOSUM_REGEXP = /^(.+)\s+(.+)\s+(.+)$/
|
|
20
20
|
|
|
21
|
+
def self.file_patterns
|
|
22
|
+
["go.mod", "go.sum", "glide.yaml", "glide.lock", "Godeps/Godeps.json", "Godeps", "vendor/manifest", "vendor/vendor.json", "Gopkg.toml", "Gopkg.lock", "go-resolved-dependencies.json"]
|
|
23
|
+
end
|
|
24
|
+
|
|
21
25
|
def self.mapping
|
|
22
26
|
{
|
|
23
27
|
# Go Modules (recommended)
|
|
@@ -15,6 +15,10 @@ module Bibliothecary
|
|
|
15
15
|
# Matches stack.yaml.lock hackage entries like: hackage: fuzzyset-0.2.4@sha256:...
|
|
16
16
|
STACK_LOCK_REGEXP = /hackage:\s*([a-zA-Z0-9-]+)-([0-9.]+)@/
|
|
17
17
|
|
|
18
|
+
def self.file_patterns
|
|
19
|
+
["*.cabal", "*cabal.config", "stack.yaml.lock", "cabal.project.freeze"]
|
|
20
|
+
end
|
|
21
|
+
|
|
18
22
|
def self.mapping
|
|
19
23
|
{
|
|
20
24
|
match_extension(".cabal") => {
|
|
@@ -29,6 +33,10 @@ module Bibliothecary
|
|
|
29
33
|
kind: "lockfile",
|
|
30
34
|
parser: :parse_stack_yaml_lock,
|
|
31
35
|
},
|
|
36
|
+
match_filename("cabal.project.freeze") => {
|
|
37
|
+
kind: "lockfile",
|
|
38
|
+
parser: :parse_cabal_project_freeze,
|
|
39
|
+
},
|
|
32
40
|
}
|
|
33
41
|
end
|
|
34
42
|
|
|
@@ -196,6 +204,53 @@ module Bibliothecary
|
|
|
196
204
|
|
|
197
205
|
ParserResult.new(dependencies: deps)
|
|
198
206
|
end
|
|
207
|
+
|
|
208
|
+
def self.parse_cabal_project_freeze(file_contents, options: {})
|
|
209
|
+
source = options.fetch(:filename, "cabal.project.freeze")
|
|
210
|
+
deps = []
|
|
211
|
+
|
|
212
|
+
# Parse constraints field which can span multiple lines
|
|
213
|
+
# Format: constraints: any.pkg ==version, any.pkg2 ==version2, ...
|
|
214
|
+
# Also handles flag constraints like: any.pkg +flag -flag2
|
|
215
|
+
constraints = nil
|
|
216
|
+
file_contents.each_line do |line|
|
|
217
|
+
if line =~ /^constraints:\s*(.*)/i
|
|
218
|
+
constraints = $1.strip
|
|
219
|
+
elsif line =~ /^\s+(.*)/ && constraints
|
|
220
|
+
constraints += " " + $1.strip
|
|
221
|
+
elsif line =~ /^[a-z]/i && constraints
|
|
222
|
+
break
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
return ParserResult.new(dependencies: []) unless constraints
|
|
227
|
+
|
|
228
|
+
constraints.split(",").each do |dep_str|
|
|
229
|
+
dep_str = dep_str.strip
|
|
230
|
+
next if dep_str.empty?
|
|
231
|
+
|
|
232
|
+
# Format: any.package ==version or any.package +flag -flag
|
|
233
|
+
# Skip flag-only entries (no version constraint)
|
|
234
|
+
next unless dep_str.include?("==")
|
|
235
|
+
|
|
236
|
+
# Remove "any." prefix and parse
|
|
237
|
+
dep_str = dep_str.sub(/^any\./, "")
|
|
238
|
+
|
|
239
|
+
# Extract name and version: "package ==version" or "package ==version +flag"
|
|
240
|
+
match = dep_str.match(/^([a-zA-Z][a-zA-Z0-9-]*)\s*==\s*([\d.]+)/)
|
|
241
|
+
next unless match
|
|
242
|
+
|
|
243
|
+
deps << Dependency.new(
|
|
244
|
+
platform: platform_name,
|
|
245
|
+
name: match[1],
|
|
246
|
+
requirement: match[2],
|
|
247
|
+
type: "runtime",
|
|
248
|
+
source: source
|
|
249
|
+
)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
ParserResult.new(dependencies: deps)
|
|
253
|
+
end
|
|
199
254
|
end
|
|
200
255
|
end
|
|
201
256
|
end
|
|
@@ -10,6 +10,13 @@ module Bibliothecary
|
|
|
10
10
|
HEX_LOCK_REGEXP = /"([^"]+)":\s*\{:hex,\s*:[^,]+,\s*"([^"]+)"/
|
|
11
11
|
GIT_LOCK_REGEXP = /"([^"]+)":\s*\{:git,\s*"([^"]+)",\s*"([^"]+)"/
|
|
12
12
|
|
|
13
|
+
# Matches rebar.lock entries: {<<"name">>,{pkg,<<"name">>,<<"version">>},N}
|
|
14
|
+
REBAR_LOCK_REGEXP = /\{<<"([^"]+)">>,\{pkg,<<"[^"]+">>,<<"([^"]+)">>},\d+\}/
|
|
15
|
+
|
|
16
|
+
def self.file_patterns
|
|
17
|
+
["mix.exs", "mix.lock", "gleam.toml", "manifest.toml", "rebar.lock"]
|
|
18
|
+
end
|
|
19
|
+
|
|
13
20
|
def self.mapping
|
|
14
21
|
{
|
|
15
22
|
match_filename("mix.exs") => {
|
|
@@ -20,9 +27,26 @@ module Bibliothecary
|
|
|
20
27
|
kind: "lockfile",
|
|
21
28
|
parser: :parse_mix_lock,
|
|
22
29
|
},
|
|
30
|
+
match_filename("gleam.toml") => {
|
|
31
|
+
kind: "manifest",
|
|
32
|
+
parser: :parse_gleam_toml,
|
|
33
|
+
},
|
|
34
|
+
match_filename("manifest.toml") => {
|
|
35
|
+
kind: "lockfile",
|
|
36
|
+
parser: :parse_gleam_manifest,
|
|
37
|
+
content_matcher: :gleam_manifest?,
|
|
38
|
+
},
|
|
39
|
+
match_filename("rebar.lock") => {
|
|
40
|
+
kind: "lockfile",
|
|
41
|
+
parser: :parse_rebar_lock,
|
|
42
|
+
},
|
|
23
43
|
}
|
|
24
44
|
end
|
|
25
45
|
|
|
46
|
+
def self.gleam_manifest?(file_contents)
|
|
47
|
+
file_contents.include?("# This file was generated by Gleam")
|
|
48
|
+
end
|
|
49
|
+
|
|
26
50
|
|
|
27
51
|
def self.parse_mix(file_contents, options: {})
|
|
28
52
|
source = options.fetch(:filename, "mix.exs")
|
|
@@ -74,6 +98,71 @@ module Bibliothecary
|
|
|
74
98
|
|
|
75
99
|
ParserResult.new(dependencies: deps)
|
|
76
100
|
end
|
|
101
|
+
|
|
102
|
+
def self.parse_gleam_toml(file_contents, options: {})
|
|
103
|
+
source = options.fetch(:filename, "gleam.toml")
|
|
104
|
+
manifest = Tomlrb.parse(file_contents)
|
|
105
|
+
deps = []
|
|
106
|
+
|
|
107
|
+
manifest.fetch("dependencies", {}).each do |name, requirement|
|
|
108
|
+
deps << Dependency.new(
|
|
109
|
+
platform: platform_name,
|
|
110
|
+
name: name,
|
|
111
|
+
requirement: requirement,
|
|
112
|
+
type: "runtime",
|
|
113
|
+
source: source
|
|
114
|
+
)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
manifest.fetch("dev-dependencies", {}).each do |name, requirement|
|
|
118
|
+
deps << Dependency.new(
|
|
119
|
+
platform: platform_name,
|
|
120
|
+
name: name,
|
|
121
|
+
requirement: requirement,
|
|
122
|
+
type: "development",
|
|
123
|
+
source: source
|
|
124
|
+
)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
ParserResult.new(dependencies: deps)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def self.parse_gleam_manifest(file_contents, options: {})
|
|
131
|
+
source = options.fetch(:filename, "manifest.toml")
|
|
132
|
+
manifest = Tomlrb.parse(file_contents)
|
|
133
|
+
deps = []
|
|
134
|
+
|
|
135
|
+
manifest.fetch("packages", []).each do |pkg|
|
|
136
|
+
next unless pkg["source"] == "hex"
|
|
137
|
+
|
|
138
|
+
deps << Dependency.new(
|
|
139
|
+
platform: platform_name,
|
|
140
|
+
name: pkg["name"],
|
|
141
|
+
requirement: pkg["version"],
|
|
142
|
+
type: "runtime",
|
|
143
|
+
source: source
|
|
144
|
+
)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
ParserResult.new(dependencies: deps)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def self.parse_rebar_lock(file_contents, options: {})
|
|
151
|
+
source = options.fetch(:filename, "rebar.lock")
|
|
152
|
+
deps = []
|
|
153
|
+
|
|
154
|
+
file_contents.scan(REBAR_LOCK_REGEXP) do |name, version|
|
|
155
|
+
deps << Dependency.new(
|
|
156
|
+
platform: platform_name,
|
|
157
|
+
name: name,
|
|
158
|
+
requirement: version,
|
|
159
|
+
type: "runtime",
|
|
160
|
+
source: source
|
|
161
|
+
)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
ParserResult.new(dependencies: deps)
|
|
165
|
+
end
|
|
77
166
|
end
|
|
78
167
|
end
|
|
79
168
|
end
|
|
@@ -5,12 +5,24 @@ module Bibliothecary
|
|
|
5
5
|
class Julia
|
|
6
6
|
include Bibliothecary::Analyser
|
|
7
7
|
|
|
8
|
+
def self.file_patterns
|
|
9
|
+
["REQUIRE", "Project.toml", "Manifest.toml"]
|
|
10
|
+
end
|
|
11
|
+
|
|
8
12
|
def self.mapping
|
|
9
13
|
{
|
|
10
14
|
match_filename("REQUIRE", case_insensitive: true) => {
|
|
11
15
|
kind: "manifest",
|
|
12
16
|
parser: :parse_require,
|
|
13
17
|
},
|
|
18
|
+
match_filename("Project.toml") => {
|
|
19
|
+
kind: "manifest",
|
|
20
|
+
parser: :parse_project_toml,
|
|
21
|
+
},
|
|
22
|
+
match_filename("Manifest.toml") => {
|
|
23
|
+
kind: "lockfile",
|
|
24
|
+
parser: :parse_manifest_toml,
|
|
25
|
+
},
|
|
14
26
|
}
|
|
15
27
|
end
|
|
16
28
|
|
|
@@ -41,6 +53,49 @@ module Bibliothecary
|
|
|
41
53
|
end
|
|
42
54
|
ParserResult.new(dependencies: deps)
|
|
43
55
|
end
|
|
56
|
+
|
|
57
|
+
def self.parse_project_toml(file_contents, options: {})
|
|
58
|
+
source = options.fetch(:filename, "Project.toml")
|
|
59
|
+
manifest = Tomlrb.parse(file_contents)
|
|
60
|
+
deps = []
|
|
61
|
+
|
|
62
|
+
manifest.fetch("deps", {}).each do |name, _uuid|
|
|
63
|
+
deps << Dependency.new(
|
|
64
|
+
name: name,
|
|
65
|
+
requirement: "*",
|
|
66
|
+
type: "runtime",
|
|
67
|
+
source: source,
|
|
68
|
+
platform: platform_name
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
ParserResult.new(dependencies: deps)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def self.parse_manifest_toml(file_contents, options: {})
|
|
76
|
+
source = options.fetch(:filename, "Manifest.toml")
|
|
77
|
+
manifest = Tomlrb.parse(file_contents)
|
|
78
|
+
deps = []
|
|
79
|
+
|
|
80
|
+
manifest.fetch("deps", {}).each do |name, entries|
|
|
81
|
+
# entries is an array of package entries (usually just one)
|
|
82
|
+
entry = entries.is_a?(Array) ? entries.first : entries
|
|
83
|
+
next unless entry.is_a?(Hash)
|
|
84
|
+
|
|
85
|
+
version = entry["version"]
|
|
86
|
+
next unless version
|
|
87
|
+
|
|
88
|
+
deps << Dependency.new(
|
|
89
|
+
name: name,
|
|
90
|
+
requirement: version,
|
|
91
|
+
type: "runtime",
|
|
92
|
+
source: source,
|
|
93
|
+
platform: platform_name
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
ParserResult.new(dependencies: deps)
|
|
98
|
+
end
|
|
44
99
|
end
|
|
45
100
|
end
|
|
46
101
|
end
|
|
@@ -62,6 +62,10 @@ module Bibliothecary
|
|
|
62
62
|
# e.g. "[info] "
|
|
63
63
|
SBT_IGNORE_REGEXP = /^\[info\]\s*$/
|
|
64
64
|
|
|
65
|
+
def self.file_patterns
|
|
66
|
+
["ivy.xml", "pom.xml", "build.gradle", "build.gradle.kts", "gradle-dependencies-q.txt", "maven-resolved-dependencies.txt", "sbt-update-full.txt", "maven-dependency-tree.txt", "maven-dependency-tree.dot", "gradle.lockfile", "verification-metadata.xml"]
|
|
67
|
+
end
|
|
68
|
+
|
|
65
69
|
# Copied from the "strings-ansi" gem, because it seems abandoned: https://github.com/piotrmurach/strings-ansi/pull/2
|
|
66
70
|
# From: https://github.com/piotrmurach/strings-ansi/blob/35d0c9430cf0a8022dc12bdab005bce296cb9f00/lib/strings/ansi.rb#L14-L29
|
|
67
71
|
# License: MIT
|