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
|
@@ -15,6 +15,10 @@ module Bibliothecary
|
|
|
15
15
|
# Group 4: unquoted requirement (e.g., >= 1.0, ~> 2.0)
|
|
16
16
|
CARTFILE_REGEXP = /^(github|git|binary)\s+"([^"]+)"(?:\s+(?:"([^"]+)"|((?:>=|<=|~>|==|>|<)\s*[\d.]+)))?/
|
|
17
17
|
|
|
18
|
+
def self.file_patterns
|
|
19
|
+
["Cartfile", "Cartfile.private", "Cartfile.resolved"]
|
|
20
|
+
end
|
|
21
|
+
|
|
18
22
|
def self.mapping
|
|
19
23
|
{
|
|
20
24
|
match_filename("Cartfile") => {
|
|
@@ -7,11 +7,16 @@ module Bibliothecary
|
|
|
7
7
|
# Name can be like: org.clojure/clojure, cheshire, ring/ring-defaults
|
|
8
8
|
DEPENDENCY_REGEXP = %r{\[([a-zA-Z0-9_./\-]+)\s+"([^"]+)"\]}
|
|
9
9
|
|
|
10
|
+
def self.file_patterns
|
|
11
|
+
["project.clj"]
|
|
12
|
+
end
|
|
13
|
+
|
|
10
14
|
def self.mapping
|
|
11
15
|
{
|
|
12
16
|
match_filename("project.clj") => {
|
|
13
17
|
kind: "manifest",
|
|
14
18
|
parser: :parse_manifest,
|
|
19
|
+
can_have_lockfile: false,
|
|
15
20
|
},
|
|
16
21
|
}
|
|
17
22
|
end
|
|
@@ -17,6 +17,10 @@ module Bibliothecary
|
|
|
17
17
|
# Podspec pattern: .dependency "Name", "version"
|
|
18
18
|
PODSPEC_DEPENDENCY = /\.dependency\s+['"]([^'"]+)['"]\s*(?:,\s*['"]([^'"]+)['"])?/
|
|
19
19
|
|
|
20
|
+
def self.file_patterns
|
|
21
|
+
["Podfile", "*.podspec", "Podfile.lock", "*.podspec.json"]
|
|
22
|
+
end
|
|
23
|
+
|
|
20
24
|
def self.mapping
|
|
21
25
|
{
|
|
22
26
|
match_filename("Podfile") => {
|
|
@@ -45,6 +49,9 @@ module Bibliothecary
|
|
|
45
49
|
source = options.fetch(:filename, nil)
|
|
46
50
|
dependencies = []
|
|
47
51
|
|
|
52
|
+
# Parse SPEC CHECKSUMS section to build lookup table
|
|
53
|
+
checksums = parse_spec_checksums(file_contents)
|
|
54
|
+
|
|
48
55
|
# Match pod entries: " - Name (version)" or " - Name/Subspec (version)"
|
|
49
56
|
# Only process lines in PODS section (before DEPENDENCIES section)
|
|
50
57
|
pods_section = file_contents.split(/^DEPENDENCIES:/)[0]
|
|
@@ -56,13 +63,38 @@ module Bibliothecary
|
|
|
56
63
|
name: base_name,
|
|
57
64
|
requirement: version,
|
|
58
65
|
type: "runtime",
|
|
59
|
-
source: source
|
|
66
|
+
source: source,
|
|
67
|
+
integrity: checksums[base_name]
|
|
60
68
|
)
|
|
61
69
|
end
|
|
62
70
|
|
|
63
71
|
ParserResult.new(dependencies: dependencies)
|
|
64
72
|
end
|
|
65
73
|
|
|
74
|
+
def self.parse_spec_checksums(file_contents)
|
|
75
|
+
checksums = {}
|
|
76
|
+
in_checksums = false
|
|
77
|
+
|
|
78
|
+
file_contents.each_line do |line|
|
|
79
|
+
if line.start_with?("SPEC CHECKSUMS:")
|
|
80
|
+
in_checksums = true
|
|
81
|
+
next
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
next unless in_checksums
|
|
85
|
+
|
|
86
|
+
# End of section (blank line or new section)
|
|
87
|
+
break if line.strip.empty? || (line !~ /^\s/ && line.strip != "")
|
|
88
|
+
|
|
89
|
+
# Match " Name: sha1hash"
|
|
90
|
+
if (match = line.match(/^\s+([^:]+):\s*([a-f0-9]+)\s*$/))
|
|
91
|
+
checksums[match[1]] = "sha1=#{match[2]}"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
checksums
|
|
96
|
+
end
|
|
97
|
+
|
|
66
98
|
def self.parse_podspec(file_contents, options: {})
|
|
67
99
|
source = options.fetch(:filename, nil)
|
|
68
100
|
deps = []
|
|
@@ -5,12 +5,16 @@ module Bibliothecary
|
|
|
5
5
|
class Cog
|
|
6
6
|
include Bibliothecary::Analyser
|
|
7
7
|
|
|
8
|
+
def self.file_patterns
|
|
9
|
+
["cog.yaml"]
|
|
10
|
+
end
|
|
11
|
+
|
|
8
12
|
def self.mapping
|
|
9
13
|
{
|
|
10
14
|
match_filename("cog.yaml") => {
|
|
11
15
|
kind: 'manifest',
|
|
12
16
|
parser: :parse_cog_yaml,
|
|
13
|
-
|
|
17
|
+
can_have_lockfile: false,
|
|
14
18
|
}
|
|
15
19
|
}
|
|
16
20
|
end
|
|
@@ -7,15 +7,21 @@ module Bibliothecary
|
|
|
7
7
|
class Conda
|
|
8
8
|
include Bibliothecary::Analyser
|
|
9
9
|
|
|
10
|
+
def self.file_patterns
|
|
11
|
+
["environment.yml", "environment.yaml"]
|
|
12
|
+
end
|
|
13
|
+
|
|
10
14
|
def self.mapping
|
|
11
15
|
{
|
|
12
16
|
match_filename("environment.yml") => {
|
|
13
17
|
parser: :parse_conda,
|
|
14
18
|
kind: "manifest",
|
|
19
|
+
can_have_lockfile: false,
|
|
15
20
|
},
|
|
16
21
|
match_filename("environment.yaml") => {
|
|
17
22
|
parser: :parse_conda,
|
|
18
23
|
kind: "manifest",
|
|
24
|
+
can_have_lockfile: false,
|
|
19
25
|
},
|
|
20
26
|
}
|
|
21
27
|
end
|
|
@@ -8,6 +8,10 @@ module Bibliothecary
|
|
|
8
8
|
class CPAN
|
|
9
9
|
include Bibliothecary::Analyser
|
|
10
10
|
|
|
11
|
+
def self.file_patterns
|
|
12
|
+
["META.json", "META.yml", "cpanfile", "cpanfile.snapshot", "Makefile.PL", "Build.PL"]
|
|
13
|
+
end
|
|
14
|
+
|
|
11
15
|
def self.mapping
|
|
12
16
|
{
|
|
13
17
|
match_filename("META.json", case_insensitive: true) => {
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bibliothecary
|
|
4
|
+
module Parsers
|
|
5
|
+
class Deb
|
|
6
|
+
include Bibliothecary::Analyser
|
|
7
|
+
|
|
8
|
+
def self.file_patterns
|
|
9
|
+
["debian/control", "control"]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.mapping
|
|
13
|
+
{
|
|
14
|
+
match_filename("debian/control") => {
|
|
15
|
+
kind: "manifest",
|
|
16
|
+
parser: :parse_control,
|
|
17
|
+
can_have_lockfile: false,
|
|
18
|
+
},
|
|
19
|
+
match_filename("control") => {
|
|
20
|
+
kind: "manifest",
|
|
21
|
+
parser: :parse_control,
|
|
22
|
+
can_have_lockfile: false,
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
BUILD_DEP_FIELDS = %w[Build-Depends Build-Depends-Indep Build-Depends-Arch].freeze
|
|
28
|
+
RUNTIME_DEP_FIELDS = %w[Depends Pre-Depends Recommends Suggests].freeze
|
|
29
|
+
|
|
30
|
+
def self.parse_control(file_contents, options: {})
|
|
31
|
+
source = options.fetch(:filename, nil)
|
|
32
|
+
dependencies = []
|
|
33
|
+
|
|
34
|
+
# Parse the control file - it's in RFC 822 format with continuation lines
|
|
35
|
+
# Fields can span multiple lines if continuation lines start with whitespace
|
|
36
|
+
fields = parse_fields(file_contents)
|
|
37
|
+
|
|
38
|
+
# Build dependencies
|
|
39
|
+
BUILD_DEP_FIELDS.each do |field_name|
|
|
40
|
+
next unless fields[field_name.downcase]
|
|
41
|
+
|
|
42
|
+
parse_dependency_list(fields[field_name.downcase]).each do |dep|
|
|
43
|
+
dependencies << Dependency.new(
|
|
44
|
+
name: dep[:name],
|
|
45
|
+
requirement: dep[:requirement] || "*",
|
|
46
|
+
type: "build",
|
|
47
|
+
source: source,
|
|
48
|
+
platform: platform_name
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Runtime dependencies
|
|
54
|
+
RUNTIME_DEP_FIELDS.each do |field_name|
|
|
55
|
+
next unless fields[field_name.downcase]
|
|
56
|
+
|
|
57
|
+
parse_dependency_list(fields[field_name.downcase]).each do |dep|
|
|
58
|
+
dependencies << Dependency.new(
|
|
59
|
+
name: dep[:name],
|
|
60
|
+
requirement: dep[:requirement] || "*",
|
|
61
|
+
type: "runtime",
|
|
62
|
+
source: source,
|
|
63
|
+
platform: platform_name
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
ParserResult.new(dependencies: dependencies)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.parse_fields(contents)
|
|
72
|
+
fields = {}
|
|
73
|
+
current_field = nil
|
|
74
|
+
current_value = []
|
|
75
|
+
|
|
76
|
+
contents.each_line do |line|
|
|
77
|
+
if line =~ /^(\S+):\s*(.*)$/
|
|
78
|
+
# Save previous field
|
|
79
|
+
if current_field
|
|
80
|
+
fields[current_field] = current_value.join(" ").strip
|
|
81
|
+
end
|
|
82
|
+
current_field = $1.downcase
|
|
83
|
+
current_value = [$2]
|
|
84
|
+
elsif line =~ /^\s+(.*)$/ && current_field
|
|
85
|
+
# Continuation line
|
|
86
|
+
current_value << $1
|
|
87
|
+
elsif line.strip.empty?
|
|
88
|
+
# Empty line - save current field and reset
|
|
89
|
+
if current_field
|
|
90
|
+
fields[current_field] = current_value.join(" ").strip
|
|
91
|
+
end
|
|
92
|
+
current_field = nil
|
|
93
|
+
current_value = []
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Save last field
|
|
98
|
+
if current_field
|
|
99
|
+
fields[current_field] = current_value.join(" ").strip
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
fields
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def self.parse_dependency_list(dep_string)
|
|
106
|
+
deps = []
|
|
107
|
+
|
|
108
|
+
# Dependencies are comma-separated
|
|
109
|
+
dep_string.split(/,/).each do |dep|
|
|
110
|
+
dep = dep.strip
|
|
111
|
+
next if dep.empty?
|
|
112
|
+
next if dep.start_with?("$") # Skip substitution variables like ${shlibs:Depends}
|
|
113
|
+
|
|
114
|
+
# Handle alternatives (pkg1 | pkg2) - just take the first one
|
|
115
|
+
dep = dep.split("|").first.strip
|
|
116
|
+
|
|
117
|
+
# Parse package name and optional version constraint
|
|
118
|
+
# Format: package (>= 1.0) or just package
|
|
119
|
+
if dep =~ /^(\S+)\s*\(([^)]+)\)$/
|
|
120
|
+
name = $1
|
|
121
|
+
constraint = $2.strip
|
|
122
|
+
deps << { name: name, requirement: constraint }
|
|
123
|
+
elsif dep =~ /^(\S+)$/
|
|
124
|
+
deps << { name: $1, requirement: nil }
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
deps
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
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
|
+
# Build integrity lookup from jsr and npm sections
|
|
56
|
+
integrity_map = {}
|
|
57
|
+
manifest.fetch("jsr", {}).each do |key, value|
|
|
58
|
+
integrity_map["jsr:#{key}"] = value["integrity"] if value.is_a?(Hash) && value["integrity"]
|
|
59
|
+
end
|
|
60
|
+
manifest.fetch("npm", {}).each do |key, value|
|
|
61
|
+
integrity_map["npm:#{key}"] = value["integrity"] if value.is_a?(Hash) && value["integrity"]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
dependencies = manifest.fetch("specifiers", {}).map do |specifier, resolved_version|
|
|
65
|
+
name, _requirement = parse_specifier(specifier)
|
|
66
|
+
next unless name
|
|
67
|
+
|
|
68
|
+
# Determine protocol (npm: or jsr:) and build lookup key
|
|
69
|
+
protocol = specifier.start_with?("jsr:") ? "jsr" : "npm"
|
|
70
|
+
integrity_key = "#{protocol}:#{name}@#{resolved_version}"
|
|
71
|
+
|
|
72
|
+
Dependency.new(
|
|
73
|
+
name: name,
|
|
74
|
+
requirement: resolved_version,
|
|
75
|
+
type: "runtime",
|
|
76
|
+
source: source,
|
|
77
|
+
platform: platform_name,
|
|
78
|
+
integrity: integrity_map[integrity_key]
|
|
79
|
+
)
|
|
80
|
+
end.compact
|
|
81
|
+
|
|
82
|
+
ParserResult.new(dependencies: dependencies)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Parses specifiers like:
|
|
86
|
+
# "npm:chalk@1" => ["chalk", "1"]
|
|
87
|
+
# "npm:chalk" => ["chalk", "*"]
|
|
88
|
+
# "jsr:@std/path@^1" => ["@std/path", "^1"]
|
|
89
|
+
# "jsr:@std/path" => ["@std/path", "*"]
|
|
90
|
+
def self.parse_specifier(specifier)
|
|
91
|
+
return nil unless specifier.start_with?("npm:", "jsr:")
|
|
92
|
+
|
|
93
|
+
# Remove the protocol prefix
|
|
94
|
+
without_protocol = specifier.sub(/^(npm|jsr):/, "")
|
|
95
|
+
|
|
96
|
+
# Handle scoped packages (@scope/name@version)
|
|
97
|
+
if without_protocol.start_with?("@")
|
|
98
|
+
# Split on @ but keep the first @ for the scope
|
|
99
|
+
parts = without_protocol[1..].split("@", 2)
|
|
100
|
+
name = "@#{parts[0]}"
|
|
101
|
+
requirement = parts[1] || "*"
|
|
102
|
+
else
|
|
103
|
+
# Regular package (name@version)
|
|
104
|
+
name, requirement = without_protocol.split("@", 2)
|
|
105
|
+
requirement ||= "*"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
[name, requirement]
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -9,15 +9,21 @@ module Bibliothecary
|
|
|
9
9
|
|
|
10
10
|
SDL_DEPENDENCY_REGEXP = /^dependency\s+"([^"]+)"(?:\s+version="([^"]+)")?/
|
|
11
11
|
|
|
12
|
+
def self.file_patterns
|
|
13
|
+
["dub.json", "dub.sdl"]
|
|
14
|
+
end
|
|
15
|
+
|
|
12
16
|
def self.mapping
|
|
13
17
|
{
|
|
14
18
|
match_filename("dub.json") => {
|
|
15
19
|
kind: "manifest",
|
|
16
20
|
parser: :parse_json_manifest,
|
|
21
|
+
can_have_lockfile: false,
|
|
17
22
|
},
|
|
18
23
|
match_filename("dub.sdl") => {
|
|
19
24
|
kind: "manifest",
|
|
20
25
|
parser: :parse_sdl_manifest,
|
|
26
|
+
can_have_lockfile: false,
|
|
21
27
|
},
|
|
22
28
|
}
|
|
23
29
|
end
|
|
@@ -5,12 +5,16 @@ module Bibliothecary
|
|
|
5
5
|
class DVC
|
|
6
6
|
include Bibliothecary::Analyser
|
|
7
7
|
|
|
8
|
+
def self.file_patterns
|
|
9
|
+
["dvc.yaml"]
|
|
10
|
+
end
|
|
11
|
+
|
|
8
12
|
def self.mapping
|
|
9
13
|
{
|
|
10
14
|
match_filename("dvc.yaml") => {
|
|
11
15
|
kind: 'manifest',
|
|
12
16
|
parser: :parse_dvc_yaml,
|
|
13
|
-
|
|
17
|
+
can_have_lockfile: false,
|
|
14
18
|
}
|
|
15
19
|
}
|
|
16
20
|
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)
|
|
@@ -205,10 +209,12 @@ module Bibliothecary
|
|
|
205
209
|
requirement: match[2].strip.split("/").first,
|
|
206
210
|
type: "runtime",
|
|
207
211
|
source: options.fetch(:filename, nil),
|
|
208
|
-
platform: platform_name
|
|
212
|
+
platform: platform_name,
|
|
213
|
+
integrity: match[3].strip
|
|
209
214
|
)
|
|
210
215
|
end
|
|
211
|
-
|
|
216
|
+
# Dedupe by name+requirement, keeping the first occurrence (h1 hash, not go.mod hash)
|
|
217
|
+
dependencies = deps.uniq { |d| [d.name, d.requirement] }
|
|
212
218
|
ParserResult.new(dependencies: dependencies)
|
|
213
219
|
end
|
|
214
220
|
|
|
@@ -12,8 +12,12 @@ 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.]+)
|
|
15
|
+
# Matches stack.yaml.lock hackage entries like: hackage: fuzzyset-0.2.4@sha256:hash,size
|
|
16
|
+
STACK_LOCK_REGEXP = /hackage:\s*([a-zA-Z0-9-]+)-([0-9.]+)@sha256:([a-f0-9]+)/
|
|
17
|
+
|
|
18
|
+
def self.file_patterns
|
|
19
|
+
["*.cabal", "*cabal.config", "stack.yaml.lock", "cabal.project.freeze"]
|
|
20
|
+
end
|
|
17
21
|
|
|
18
22
|
def self.mapping
|
|
19
23
|
{
|
|
@@ -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
|
|
|
@@ -185,6 +193,54 @@ module Bibliothecary
|
|
|
185
193
|
match = line.match(STACK_LOCK_REGEXP)
|
|
186
194
|
next unless match
|
|
187
195
|
|
|
196
|
+
deps << Dependency.new(
|
|
197
|
+
platform: platform_name,
|
|
198
|
+
name: match[1],
|
|
199
|
+
requirement: match[2],
|
|
200
|
+
type: "runtime",
|
|
201
|
+
source: source,
|
|
202
|
+
integrity: "sha256=#{match[3]}"
|
|
203
|
+
)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
ParserResult.new(dependencies: deps)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def self.parse_cabal_project_freeze(file_contents, options: {})
|
|
210
|
+
source = options.fetch(:filename, "cabal.project.freeze")
|
|
211
|
+
deps = []
|
|
212
|
+
|
|
213
|
+
# Parse constraints field which can span multiple lines
|
|
214
|
+
# Format: constraints: any.pkg ==version, any.pkg2 ==version2, ...
|
|
215
|
+
# Also handles flag constraints like: any.pkg +flag -flag2
|
|
216
|
+
constraints = nil
|
|
217
|
+
file_contents.each_line do |line|
|
|
218
|
+
if line =~ /^constraints:\s*(.*)/i
|
|
219
|
+
constraints = $1.strip
|
|
220
|
+
elsif line =~ /^\s+(.*)/ && constraints
|
|
221
|
+
constraints += " " + $1.strip
|
|
222
|
+
elsif line =~ /^[a-z]/i && constraints
|
|
223
|
+
break
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
return ParserResult.new(dependencies: []) unless constraints
|
|
228
|
+
|
|
229
|
+
constraints.split(",").each do |dep_str|
|
|
230
|
+
dep_str = dep_str.strip
|
|
231
|
+
next if dep_str.empty?
|
|
232
|
+
|
|
233
|
+
# Format: any.package ==version or any.package +flag -flag
|
|
234
|
+
# Skip flag-only entries (no version constraint)
|
|
235
|
+
next unless dep_str.include?("==")
|
|
236
|
+
|
|
237
|
+
# Remove "any." prefix and parse
|
|
238
|
+
dep_str = dep_str.sub(/^any\./, "")
|
|
239
|
+
|
|
240
|
+
# Extract name and version: "package ==version" or "package ==version +flag"
|
|
241
|
+
match = dep_str.match(/^([a-zA-Z][a-zA-Z0-9-]*)\s*==\s*([\d.]+)/)
|
|
242
|
+
next unless match
|
|
243
|
+
|
|
188
244
|
deps << Dependency.new(
|
|
189
245
|
platform: platform_name,
|
|
190
246
|
name: match[1],
|
|
@@ -7,11 +7,16 @@ module Bibliothecary
|
|
|
7
7
|
class Haxelib
|
|
8
8
|
include Bibliothecary::Analyser
|
|
9
9
|
|
|
10
|
+
def self.file_patterns
|
|
11
|
+
["haxelib.json"]
|
|
12
|
+
end
|
|
13
|
+
|
|
10
14
|
def self.mapping
|
|
11
15
|
{
|
|
12
16
|
match_filename("haxelib.json") => {
|
|
13
17
|
kind: "manifest",
|
|
14
18
|
parser: :parse_manifest,
|
|
19
|
+
can_have_lockfile: false,
|
|
15
20
|
},
|
|
16
21
|
}
|
|
17
22
|
end
|