ecosystems-bibliothecary 15.2.0 → 15.4.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 +18 -1
- data/README.md +57 -1
- data/lib/bibliothecary/dependency.rb +6 -1
- data/lib/bibliothecary/parsers/alpm.rb +89 -0
- data/lib/bibliothecary/parsers/apk.rb +91 -0
- data/lib/bibliothecary/parsers/bazel.rb +65 -0
- data/lib/bibliothecary/parsers/bentoml.rb +1 -1
- data/lib/bibliothecary/parsers/bower.rb +1 -0
- data/lib/bibliothecary/parsers/cargo.rb +3 -1
- data/lib/bibliothecary/parsers/clojars.rb +1 -0
- data/lib/bibliothecary/parsers/cocoapods.rb +29 -1
- data/lib/bibliothecary/parsers/cog.rb +1 -1
- data/lib/bibliothecary/parsers/conda.rb +2 -0
- data/lib/bibliothecary/parsers/deb.rb +132 -0
- data/lib/bibliothecary/parsers/deno.rb +15 -1
- data/lib/bibliothecary/parsers/dub.rb +2 -0
- data/lib/bibliothecary/parsers/dvc.rb +1 -1
- data/lib/bibliothecary/parsers/go.rb +4 -2
- data/lib/bibliothecary/parsers/hackage.rb +4 -3
- data/lib/bibliothecary/parsers/haxelib.rb +1 -0
- data/lib/bibliothecary/parsers/hex.rb +22 -7
- data/lib/bibliothecary/parsers/luarocks.rb +1 -0
- data/lib/bibliothecary/parsers/meteor.rb +1 -0
- data/lib/bibliothecary/parsers/mlflow.rb +1 -1
- data/lib/bibliothecary/parsers/nimble.rb +1 -0
- data/lib/bibliothecary/parsers/npm.rb +81 -12
- data/lib/bibliothecary/parsers/ollama.rb +1 -1
- data/lib/bibliothecary/parsers/packagist.rb +28 -31
- data/lib/bibliothecary/parsers/pypi.rb +16 -2
- data/lib/bibliothecary/parsers/rpm.rb +80 -0
- data/lib/bibliothecary/parsers/rubygems.rb +34 -4
- data/lib/bibliothecary/version.rb +1 -1
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c9b737a0f2ae5bf0446ef878a8f50074dff5b40eaaff1fbf487e35a4dc61dab7
|
|
4
|
+
data.tar.gz: bd98a9d57a7fab6169b011b9782bdc403dafda174505184564745963dda8c5a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c4a8176935149d241d23abe6be2ed8566ff33ef16fb8ca4b08a2a053e4965fee3714f941b4da9adbf287e0ee87cc068c181e4b15f8197263c8988489e1acf39b
|
|
7
|
+
data.tar.gz: 63fd2a8f0c168af999f2ff785e3e34189b6afe09d8c66dc4974f365461b233ca52d396667647362b23cb17aea9c3bad7c48b0778dc731b4ce4162e290fd028c8
|
data/CHANGELOG.md
CHANGED
|
@@ -13,6 +13,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
13
13
|
|
|
14
14
|
### Removed
|
|
15
15
|
|
|
16
|
+
## [15.4.0]
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Parser for MODULE.bazel manifests
|
|
21
|
+
|
|
22
|
+
## [15.3.0]
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- npm: pnpm-workspace.yaml support for catalog dependencies (pnpm 9+)
|
|
27
|
+
- alpm: Arch Linux PKGBUILD parser for depends, makedepends, and checkdepends
|
|
28
|
+
- apk: Alpine Linux APKBUILD parser for depends, makedepends, and checkdepends
|
|
29
|
+
- deb: Debian control file parser for Build-Depends, Depends, Pre-Depends, Recommends, Suggests
|
|
30
|
+
- rpm: RPM spec file parser for BuildRequires and Requires
|
|
31
|
+
- integrity: Added `integrity` field to Dependency class for lockfile hashes (npm package-lock.json, pnpm-lock.yaml, yarn.lock v4, bun.lock, go.sum, Gemfile.lock, deno.lock, composer.lock, stack.yaml.lock, Cargo.lock, Podfile.lock, mix.lock, rebar.lock, manifest.toml, poetry.lock, uv.lock)
|
|
32
|
+
|
|
16
33
|
## [15.2.0]
|
|
17
34
|
|
|
18
35
|
### Added
|
|
@@ -147,7 +164,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
147
164
|
|
|
148
165
|
### Added
|
|
149
166
|
|
|
150
|
-
- Added Bibliothecary::ParserResult class, which wraps the parsed dependencies along with the project name, if it was found in the manifest.
|
|
167
|
+
- Added Bibliothecary::ParserResult class, which wraps the parsed dependencies along with the project name, if it was found in the manifest.
|
|
151
168
|
|
|
152
169
|
### Changed
|
|
153
170
|
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Bibliothecary
|
|
2
2
|
|
|
3
|
-
Dependency manifest parsing library for https://github.com/ecosyste-ms
|
|
3
|
+
Dependency manifest parsing library for https://github.com/ecosyste-ms
|
|
4
4
|
|
|
5
5
|
This is a maintained fork of the original [Bibliothecary](https://github.com/librariesio/bibliothecary) gem, with support for additional manifest formats and bug fixes.
|
|
6
6
|
|
|
@@ -42,6 +42,48 @@ Bibliothecary.analyse('./')
|
|
|
42
42
|
|
|
43
43
|
All available config options are in: https://github.com/ecosyste-ms/bibliothecary/blob/master/lib/bibliothecary/configuration.rb
|
|
44
44
|
|
|
45
|
+
## Dependency fields
|
|
46
|
+
|
|
47
|
+
Each parsed dependency is a `Bibliothecary::Dependency` with:
|
|
48
|
+
|
|
49
|
+
| Field | Type | Description |
|
|
50
|
+
|-------|------|-------------|
|
|
51
|
+
| `name` | String | Package name |
|
|
52
|
+
| `requirement` | String | Version requirement (defaults to `"*"`) |
|
|
53
|
+
| `platform` | String | Package manager platform (e.g. `"npm"`, `"maven"`) |
|
|
54
|
+
| `type` | String | Dependency scope: `"runtime"`, `"development"`, `"test"`, etc. |
|
|
55
|
+
| `direct` | Boolean | Direct dependency (vs transitive) |
|
|
56
|
+
| `deprecated` | Boolean | Deprecated dependency |
|
|
57
|
+
| `local` | Boolean | Local/file path dependency |
|
|
58
|
+
| `optional` | Boolean | Optional dependency |
|
|
59
|
+
| `original_name` | String | Original name before aliasing/normalization |
|
|
60
|
+
| `original_requirement` | String | Original requirement before resolution |
|
|
61
|
+
| `source` | String | Path to the manifest file |
|
|
62
|
+
| `integrity` | String | Lockfile integrity hash (see table below) |
|
|
63
|
+
|
|
64
|
+
## Integrity hash support
|
|
65
|
+
|
|
66
|
+
The `integrity` field is populated for lockfiles that include per-dependency hashes:
|
|
67
|
+
|
|
68
|
+
| Lockfile | Platform | Hash format |
|
|
69
|
+
|----------|----------|-------------|
|
|
70
|
+
| package-lock.json | npm | `sha512-...` |
|
|
71
|
+
| pnpm-lock.yaml | npm | `sha512-...` |
|
|
72
|
+
| yarn.lock (v2+) | npm | `sha512-...` |
|
|
73
|
+
| bun.lock | npm | `sha512-...` |
|
|
74
|
+
| deno.lock | deno | `sha512-...` |
|
|
75
|
+
| go.sum | go | `h1:...` |
|
|
76
|
+
| Gemfile.lock | rubygems | `sha256=...` |
|
|
77
|
+
| poetry.lock | pypi | `sha256:...` |
|
|
78
|
+
| uv.lock | pypi | `sha256:...` |
|
|
79
|
+
| composer.lock | packagist | `sha1=...` |
|
|
80
|
+
| Cargo.lock | cargo | `sha256=...` |
|
|
81
|
+
| Podfile.lock | cocoapods | `sha1=...` |
|
|
82
|
+
| mix.lock | hex | `sha256=...` |
|
|
83
|
+
| rebar.lock | hex | `sha256=...` |
|
|
84
|
+
| manifest.toml (Gleam) | hex | `sha256=...` |
|
|
85
|
+
| stack.yaml.lock | hackage | `sha256=...` |
|
|
86
|
+
|
|
45
87
|
## Supported package manager file formats
|
|
46
88
|
|
|
47
89
|
- Actions
|
|
@@ -49,9 +91,15 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
|
|
|
49
91
|
- action.yaml
|
|
50
92
|
- .github/workflows/\*.yml
|
|
51
93
|
- .github/workflows/\*.yaml
|
|
94
|
+
- Alpm
|
|
95
|
+
- PKGBUILD
|
|
52
96
|
- Anaconda
|
|
53
97
|
- environment.yml
|
|
54
98
|
- environment.yaml
|
|
99
|
+
- Apk
|
|
100
|
+
- APKBUILD
|
|
101
|
+
- Bazel
|
|
102
|
+
- MODULE.bazel
|
|
55
103
|
- BentoML
|
|
56
104
|
- bentofile.yaml
|
|
57
105
|
- Bower
|
|
@@ -86,6 +134,9 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
|
|
|
86
134
|
- CRAN
|
|
87
135
|
- DESCRIPTION
|
|
88
136
|
- renv.lock
|
|
137
|
+
- Deb
|
|
138
|
+
- debian/control
|
|
139
|
+
- control
|
|
89
140
|
- Deno
|
|
90
141
|
- deno.json
|
|
91
142
|
- deno.jsonc
|
|
@@ -165,6 +216,7 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
|
|
|
165
216
|
- npm-shrinkwrap.json
|
|
166
217
|
- yarn.lock
|
|
167
218
|
- pnpm-lock.yaml
|
|
219
|
+
- pnpm-workspace.yaml
|
|
168
220
|
- bun.lock
|
|
169
221
|
- npm-ls.json
|
|
170
222
|
- Nuget
|
|
@@ -200,6 +252,8 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
|
|
|
200
252
|
- pdm.lock
|
|
201
253
|
- pip-resolved-dependencies.txt
|
|
202
254
|
- pip-dependency-graph.json
|
|
255
|
+
- Rpm
|
|
256
|
+
- \*.spec
|
|
203
257
|
- RubyGems
|
|
204
258
|
- Gemfile
|
|
205
259
|
- Gemfile.lock
|
|
@@ -222,6 +276,8 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
|
222
276
|
|
|
223
277
|
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
224
278
|
|
|
279
|
+
To regenerate the supported file formats list in this README, run `bundle exec rake readme:update`.
|
|
280
|
+
|
|
225
281
|
To release a new version:
|
|
226
282
|
* in `CHANGELOG.md`, move the changes under `"Unreleased"` into a new section with your version number
|
|
227
283
|
* bump and commit the version number in `version.rb` in the `main` branch
|
|
@@ -18,6 +18,8 @@ module Bibliothecary
|
|
|
18
18
|
# for cases where it did not match the resolved name. This can be used for features like aliasing.
|
|
19
19
|
# @source [String] source An optional string to store the location of the manifest that contained this
|
|
20
20
|
# dependency, e.g. "src/package.json".
|
|
21
|
+
# @attr_reader [String] integrity An optional integrity hash from the lockfile, stored as-is
|
|
22
|
+
# (e.g. "sha512-abc123..." for npm, "h1:xyz..." for go.sum).
|
|
21
23
|
class Dependency
|
|
22
24
|
FIELDS = %i[
|
|
23
25
|
name
|
|
@@ -31,6 +33,7 @@ module Bibliothecary
|
|
|
31
33
|
optional
|
|
32
34
|
original_name
|
|
33
35
|
source
|
|
36
|
+
integrity
|
|
34
37
|
].freeze
|
|
35
38
|
|
|
36
39
|
attr_reader(*FIELDS)
|
|
@@ -46,7 +49,8 @@ module Bibliothecary
|
|
|
46
49
|
local: nil,
|
|
47
50
|
optional: nil,
|
|
48
51
|
original_name: nil,
|
|
49
|
-
source: nil
|
|
52
|
+
source: nil,
|
|
53
|
+
integrity: nil
|
|
50
54
|
)
|
|
51
55
|
@name = name
|
|
52
56
|
@platform = platform
|
|
@@ -59,6 +63,7 @@ module Bibliothecary
|
|
|
59
63
|
@optional = optional
|
|
60
64
|
@original_name = original_name
|
|
61
65
|
@source = source
|
|
66
|
+
@integrity = integrity
|
|
62
67
|
end
|
|
63
68
|
|
|
64
69
|
def eql?(other)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bibliothecary
|
|
4
|
+
module Parsers
|
|
5
|
+
class Alpm
|
|
6
|
+
include Bibliothecary::Analyser
|
|
7
|
+
|
|
8
|
+
def self.file_patterns
|
|
9
|
+
["PKGBUILD"]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.mapping
|
|
13
|
+
{
|
|
14
|
+
match_filename("PKGBUILD") => {
|
|
15
|
+
kind: "manifest",
|
|
16
|
+
parser: :parse_pkgbuild,
|
|
17
|
+
can_have_lockfile: false,
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.parse_pkgbuild(file_contents, options: {})
|
|
23
|
+
source = options.fetch(:filename, "PKGBUILD")
|
|
24
|
+
dependencies = []
|
|
25
|
+
|
|
26
|
+
# Parse depends (runtime)
|
|
27
|
+
extract_variable(file_contents, "depends").each do |dep|
|
|
28
|
+
name, requirement = parse_dependency(dep)
|
|
29
|
+
dependencies << Dependency.new(
|
|
30
|
+
name: name,
|
|
31
|
+
requirement: requirement || "*",
|
|
32
|
+
type: "runtime",
|
|
33
|
+
source: source,
|
|
34
|
+
platform: platform_name
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Parse makedepends (build)
|
|
39
|
+
extract_variable(file_contents, "makedepends").each do |dep|
|
|
40
|
+
name, requirement = parse_dependency(dep)
|
|
41
|
+
dependencies << Dependency.new(
|
|
42
|
+
name: name,
|
|
43
|
+
requirement: requirement || "*",
|
|
44
|
+
type: "build",
|
|
45
|
+
source: source,
|
|
46
|
+
platform: platform_name
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Parse checkdepends (test)
|
|
51
|
+
extract_variable(file_contents, "checkdepends").each do |dep|
|
|
52
|
+
name, requirement = parse_dependency(dep)
|
|
53
|
+
dependencies << Dependency.new(
|
|
54
|
+
name: name,
|
|
55
|
+
requirement: requirement || "*",
|
|
56
|
+
type: "test",
|
|
57
|
+
source: source,
|
|
58
|
+
platform: platform_name
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
ParserResult.new(dependencies: dependencies)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.extract_variable(contents, var_name)
|
|
66
|
+
# PKGBUILD uses bash array syntax: depends=('pkg1' 'pkg2') or depends=(pkg1 pkg2)
|
|
67
|
+
# Can also span multiple lines
|
|
68
|
+
pattern = /^#{var_name}=\(([^)]*)\)/m
|
|
69
|
+
match = contents.match(pattern)
|
|
70
|
+
return [] unless match
|
|
71
|
+
|
|
72
|
+
# Extract items, handling both quoted and unquoted formats
|
|
73
|
+
# 'pkg1' 'pkg2' or "pkg1" "pkg2" or pkg1 pkg2
|
|
74
|
+
items = match[1].scan(/['"]([^'"]+)['"]|(\S+)/).flatten.compact
|
|
75
|
+
items.reject { |d| d.empty? || d.start_with?("$") }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.parse_dependency(dep_string)
|
|
79
|
+
# Parse version constraints like "glibc>=2.17" or "openssl>1.1"
|
|
80
|
+
# Operators: >=, <=, >, <, =
|
|
81
|
+
if dep_string =~ /^(.+?)([><=]+)(.+)$/
|
|
82
|
+
[$1, "#{$2}#{$3}"]
|
|
83
|
+
else
|
|
84
|
+
[dep_string, nil]
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bibliothecary
|
|
4
|
+
module Parsers
|
|
5
|
+
class Apk
|
|
6
|
+
include Bibliothecary::Analyser
|
|
7
|
+
|
|
8
|
+
def self.file_patterns
|
|
9
|
+
["APKBUILD"]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.mapping
|
|
13
|
+
{
|
|
14
|
+
match_filename("APKBUILD") => {
|
|
15
|
+
kind: "manifest",
|
|
16
|
+
parser: :parse_apkbuild,
|
|
17
|
+
can_have_lockfile: false,
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.parse_apkbuild(file_contents, options: {})
|
|
23
|
+
source = options.fetch(:filename, "APKBUILD")
|
|
24
|
+
dependencies = []
|
|
25
|
+
|
|
26
|
+
# Parse depends (runtime)
|
|
27
|
+
extract_variable(file_contents, "depends").each do |dep|
|
|
28
|
+
name, requirement = parse_dependency(dep)
|
|
29
|
+
dependencies << Dependency.new(
|
|
30
|
+
name: name,
|
|
31
|
+
requirement: requirement || "*",
|
|
32
|
+
type: "runtime",
|
|
33
|
+
source: source,
|
|
34
|
+
platform: platform_name
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Parse makedepends (build)
|
|
39
|
+
extract_variable(file_contents, "makedepends").each do |dep|
|
|
40
|
+
name, requirement = parse_dependency(dep)
|
|
41
|
+
dependencies << Dependency.new(
|
|
42
|
+
name: name,
|
|
43
|
+
requirement: requirement || "*",
|
|
44
|
+
type: "build",
|
|
45
|
+
source: source,
|
|
46
|
+
platform: platform_name
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Parse checkdepends (test)
|
|
51
|
+
extract_variable(file_contents, "checkdepends").each do |dep|
|
|
52
|
+
name, requirement = parse_dependency(dep)
|
|
53
|
+
dependencies << Dependency.new(
|
|
54
|
+
name: name,
|
|
55
|
+
requirement: requirement || "*",
|
|
56
|
+
type: "test",
|
|
57
|
+
source: source,
|
|
58
|
+
platform: platform_name
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
ParserResult.new(dependencies: dependencies)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.extract_variable(contents, var_name)
|
|
66
|
+
# Match variable assignment with double or single quotes, handling multi-line with backslash
|
|
67
|
+
# Examples:
|
|
68
|
+
# depends="foo bar"
|
|
69
|
+
# makedepends="foo
|
|
70
|
+
# bar"
|
|
71
|
+
# checkdepends='foo bar'
|
|
72
|
+
pattern = /^#{var_name}=["']([^"']*?)["']/m
|
|
73
|
+
match = contents.match(pattern)
|
|
74
|
+
return [] unless match
|
|
75
|
+
|
|
76
|
+
# Split on whitespace and filter out empty strings, negated packages (!), and variable references ($)
|
|
77
|
+
match[1].split(/\s+/).reject { |d| d.empty? || d.start_with?("!") || d.start_with?("$") }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def self.parse_dependency(dep_string)
|
|
81
|
+
# Parse version constraints like "openssl-dev>3" or "zlib-dev>=1.2.3"
|
|
82
|
+
# Operators: >=, <=, >, <, =, ~
|
|
83
|
+
if dep_string =~ /^(.+?)([><=~]+)(.+)$/
|
|
84
|
+
[$1, "#{$2}#{$3}"]
|
|
85
|
+
else
|
|
86
|
+
[dep_string, nil]
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bibliothecary
|
|
4
|
+
module Parsers
|
|
5
|
+
class Bazel
|
|
6
|
+
include Bibliothecary::Analyser
|
|
7
|
+
|
|
8
|
+
BAZEL_DEP_STATEMENT = %r{
|
|
9
|
+
^\s*bazel_dep\s*
|
|
10
|
+
(?<bal>
|
|
11
|
+
\(
|
|
12
|
+
(?:
|
|
13
|
+
[^()"'\\]+
|
|
14
|
+
| "(?:\\.|[^"\\])*"
|
|
15
|
+
| '(?:\\.|[^'\\])*'
|
|
16
|
+
| \\ .
|
|
17
|
+
| \g<bal>
|
|
18
|
+
)*
|
|
19
|
+
\)
|
|
20
|
+
)
|
|
21
|
+
}mx
|
|
22
|
+
|
|
23
|
+
# key/value extraction inside the call
|
|
24
|
+
DEPENDENCY_NAME = /(?:^|[,(]\s*)name\s*=\s*(?<quote>["'])(?<value>(?:\\.|(?!\k<quote>).)*)\k<quote>/m
|
|
25
|
+
DEPENDENCY_VERSION = /(?:^|[,(]\s*)version\s*=\s*(?<quote>["'])(?<value>(?:\\.|(?!\k<quote>).)*)\k<quote>/m
|
|
26
|
+
DEPENDENCY_TYPE = /(?:^|[,(]\s*)dev_dependency\s*=\s*(?<value>True|False)\b/m
|
|
27
|
+
|
|
28
|
+
def self.file_patterns
|
|
29
|
+
["MODULE.bazel"]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.mapping
|
|
33
|
+
{
|
|
34
|
+
match_filename("MODULE.bazel") => {
|
|
35
|
+
kind: "manifest",
|
|
36
|
+
parser: :parse_module_bazel,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.parse_module_bazel(file_contents, options: {})
|
|
42
|
+
source = options.fetch(:filename, nil)
|
|
43
|
+
|
|
44
|
+
dependencies = file_contents.scan(BAZEL_DEP_STATEMENT).filter_map do |(statement)|
|
|
45
|
+
name_match = statement.match(DEPENDENCY_NAME)
|
|
46
|
+
next unless name_match
|
|
47
|
+
|
|
48
|
+
parsed_version = statement.match(DEPENDENCY_VERSION)
|
|
49
|
+
parsed_type = statement.match(DEPENDENCY_TYPE)
|
|
50
|
+
version = parsed_version ? parsed_version[:value] : "*"
|
|
51
|
+
type = parsed_type && parsed_type[:value] == "True" ? "development" : "runtime"
|
|
52
|
+
|
|
53
|
+
Dependency.new(
|
|
54
|
+
platform: platform_name,
|
|
55
|
+
name: name_match[:value],
|
|
56
|
+
requirement: version,
|
|
57
|
+
type: type,
|
|
58
|
+
source: source
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
ParserResult.new(dependencies: dependencies)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -55,6 +55,7 @@ module Bibliothecary
|
|
|
55
55
|
name = block[/name\s*=\s*"([^"]+)"/, 1]
|
|
56
56
|
version = block[/version\s*=\s*"([^"]+)"/, 1]
|
|
57
57
|
source = block[/source\s*=\s*"([^"]+)"/, 1]
|
|
58
|
+
checksum = block[/checksum\s*=\s*"([^"]+)"/, 1]
|
|
58
59
|
|
|
59
60
|
# Skip packages without a registry source (local/workspace packages)
|
|
60
61
|
next unless source&.start_with?("registry+")
|
|
@@ -64,7 +65,8 @@ module Bibliothecary
|
|
|
64
65
|
requirement: version,
|
|
65
66
|
type: "runtime",
|
|
66
67
|
source: options.fetch(:filename, nil),
|
|
67
|
-
platform: platform_name
|
|
68
|
+
platform: platform_name,
|
|
69
|
+
integrity: checksum ? "sha256=#{checksum}" : nil
|
|
68
70
|
)
|
|
69
71
|
end
|
|
70
72
|
ParserResult.new(dependencies: dependencies)
|
|
@@ -49,6 +49,9 @@ module Bibliothecary
|
|
|
49
49
|
source = options.fetch(:filename, nil)
|
|
50
50
|
dependencies = []
|
|
51
51
|
|
|
52
|
+
# Parse SPEC CHECKSUMS section to build lookup table
|
|
53
|
+
checksums = parse_spec_checksums(file_contents)
|
|
54
|
+
|
|
52
55
|
# Match pod entries: " - Name (version)" or " - Name/Subspec (version)"
|
|
53
56
|
# Only process lines in PODS section (before DEPENDENCIES section)
|
|
54
57
|
pods_section = file_contents.split(/^DEPENDENCIES:/)[0]
|
|
@@ -60,13 +63,38 @@ module Bibliothecary
|
|
|
60
63
|
name: base_name,
|
|
61
64
|
requirement: version,
|
|
62
65
|
type: "runtime",
|
|
63
|
-
source: source
|
|
66
|
+
source: source,
|
|
67
|
+
integrity: checksums[base_name]
|
|
64
68
|
)
|
|
65
69
|
end
|
|
66
70
|
|
|
67
71
|
ParserResult.new(dependencies: dependencies)
|
|
68
72
|
end
|
|
69
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
|
+
|
|
70
98
|
def self.parse_podspec(file_contents, options: {})
|
|
71
99
|
source = options.fetch(:filename, nil)
|
|
72
100
|
deps = []
|
|
@@ -16,10 +16,12 @@ module Bibliothecary
|
|
|
16
16
|
match_filename("environment.yml") => {
|
|
17
17
|
parser: :parse_conda,
|
|
18
18
|
kind: "manifest",
|
|
19
|
+
can_have_lockfile: false,
|
|
19
20
|
},
|
|
20
21
|
match_filename("environment.yaml") => {
|
|
21
22
|
parser: :parse_conda,
|
|
22
23
|
kind: "manifest",
|
|
24
|
+
can_have_lockfile: false,
|
|
23
25
|
},
|
|
24
26
|
}
|
|
25
27
|
end
|
|
@@ -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
|