bibliothecary 11.0.1 → 12.1.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/.circleci/config.yml +2 -1
- data/.rubocop.yml +10 -2
- data/.ruby-version +1 -1
- data/CHANGELOG.md +24 -0
- data/Gemfile +16 -1
- data/Rakefile +2 -0
- data/bibliothecary.gemspec +11 -14
- data/bin/bibliothecary +2 -1
- data/bin/console +1 -0
- data/lib/bibliothecary/analyser/analysis.rb +13 -8
- data/lib/bibliothecary/analyser/determinations.rb +2 -0
- data/lib/bibliothecary/analyser/matchers.rb +17 -17
- data/lib/bibliothecary/analyser.rb +11 -8
- data/lib/bibliothecary/cli.rb +3 -1
- data/lib/bibliothecary/configuration.rb +3 -11
- data/lib/bibliothecary/dependency.rb +17 -15
- data/lib/bibliothecary/exceptions.rb +6 -2
- data/lib/bibliothecary/file_info.rb +9 -11
- data/lib/bibliothecary/multi_parsers/bundler_like_manifest.rb +13 -10
- data/lib/bibliothecary/multi_parsers/cyclonedx.rb +10 -8
- data/lib/bibliothecary/multi_parsers/dependencies_csv.rb +11 -4
- data/lib/bibliothecary/multi_parsers/json_runtime.rb +5 -2
- data/lib/bibliothecary/multi_parsers/spdx.rb +24 -19
- data/lib/bibliothecary/parsers/bower.rb +5 -3
- data/lib/bibliothecary/parsers/cargo.rb +10 -4
- data/lib/bibliothecary/parsers/cocoapods.rb +15 -11
- data/lib/bibliothecary/parsers/conda.rb +56 -33
- data/lib/bibliothecary/parsers/cpan.rb +6 -4
- data/lib/bibliothecary/parsers/cran.rb +10 -6
- data/lib/bibliothecary/parsers/dub.rb +4 -2
- data/lib/bibliothecary/parsers/elm.rb +4 -1
- data/lib/bibliothecary/parsers/go.rb +51 -43
- data/lib/bibliothecary/parsers/haxelib.rb +2 -1
- data/lib/bibliothecary/parsers/julia.rb +5 -1
- data/lib/bibliothecary/parsers/maven.rb +93 -77
- data/lib/bibliothecary/parsers/meteor.rb +2 -0
- data/lib/bibliothecary/parsers/npm.rb +97 -34
- data/lib/bibliothecary/parsers/nuget.rb +37 -28
- data/lib/bibliothecary/parsers/packagist.rb +21 -11
- data/lib/bibliothecary/parsers/pub.rb +4 -2
- data/lib/bibliothecary/parsers/pypi.rb +48 -37
- data/lib/bibliothecary/parsers/rubygems.rb +16 -12
- data/lib/bibliothecary/parsers/shard.rb +10 -7
- data/lib/bibliothecary/purl_util.rb +2 -4
- data/lib/bibliothecary/related_files_info.rb +7 -8
- data/lib/bibliothecary/runner/multi_manifest_filter.rb +5 -4
- data/lib/bibliothecary/runner.rb +12 -10
- data/lib/bibliothecary/version.rb +3 -1
- data/lib/bibliothecary.rb +7 -4
- data/lib/sdl_parser.rb +11 -6
- metadata +18 -120
- data/lib/bibliothecary/parsers/carthage.rb +0 -52
- data/lib/bibliothecary/parsers/clojars.rb +0 -38
- data/lib/bibliothecary/parsers/hackage.rb +0 -53
- data/lib/bibliothecary/parsers/hex.rb +0 -54
- data/lib/bibliothecary/parsers/swift_pm.rb +0 -35
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Bibliothecary
|
2
4
|
module Parsers
|
3
5
|
class Pypi
|
@@ -24,15 +26,15 @@ module Bibliothecary
|
|
24
26
|
"requirements-docs.txt", "requirements/docs.txt",
|
25
27
|
"requirements-test.txt", "requirements/test.txt",
|
26
28
|
"requirements-tools.txt", "requirements/tools.txt") => {
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
kind: "manifest",
|
30
|
+
parser: :parse_requirements_txt,
|
31
|
+
},
|
32
|
+
->(p) { PIP_COMPILE_REGEXP.match(p) } => {
|
31
33
|
content_matcher: :pip_compile?,
|
32
34
|
kind: "lockfile",
|
33
35
|
parser: :parse_requirements_txt,
|
34
36
|
},
|
35
|
-
|
37
|
+
->(p) { MANIFEST_REGEXP.match(p) } => {
|
36
38
|
kind: "manifest",
|
37
39
|
parser: :parse_requirements_txt,
|
38
40
|
can_have_lockfile: false,
|
@@ -79,14 +81,6 @@ module Bibliothecary
|
|
79
81
|
parser: :parse_conda,
|
80
82
|
kind: "manifest",
|
81
83
|
},
|
82
|
-
match_filename("environment.yml.lock") => {
|
83
|
-
parser: :parse_conda,
|
84
|
-
kind: "lockfile",
|
85
|
-
},
|
86
|
-
match_filename("environment.yaml.lock") => {
|
87
|
-
parser: :parse_conda,
|
88
|
-
kind: "lockfile",
|
89
|
-
},
|
90
84
|
}
|
91
85
|
end
|
92
86
|
|
@@ -94,33 +88,34 @@ module Bibliothecary
|
|
94
88
|
add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
|
95
89
|
add_multi_parser(Bibliothecary::MultiParsers::Spdx)
|
96
90
|
|
97
|
-
def self.parse_pipfile(file_contents, options: {})
|
91
|
+
def self.parse_pipfile(file_contents, options: {})
|
98
92
|
manifest = Tomlrb.parse(file_contents)
|
99
|
-
map_dependencies(manifest["packages"], "runtime"
|
93
|
+
map_dependencies(manifest["packages"], "runtime", options.fetch(:filename, nil)) +
|
94
|
+
map_dependencies(manifest["dev-packages"], "develop", options.fetch(:filename, nil))
|
100
95
|
end
|
101
96
|
|
102
|
-
def self.parse_pyproject(file_contents, options: {})
|
97
|
+
def self.parse_pyproject(file_contents, options: {})
|
103
98
|
deps = []
|
104
99
|
|
105
100
|
file_contents = Tomlrb.parse(file_contents)
|
106
101
|
|
107
102
|
# Parse poetry [tool.poetry] deps
|
108
103
|
poetry_manifest = file_contents.fetch("tool", {}).fetch("poetry", {})
|
109
|
-
deps += map_dependencies(poetry_manifest["dependencies"], "runtime")
|
104
|
+
deps += map_dependencies(poetry_manifest["dependencies"], "runtime", options.fetch(:filename, nil))
|
110
105
|
# Poetry 1.0.0-1.2.0 way of defining dev deps
|
111
|
-
deps += map_dependencies(poetry_manifest["dev-dependencies"], "develop")
|
106
|
+
deps += map_dependencies(poetry_manifest["dev-dependencies"], "develop", options.fetch(:filename, nil))
|
112
107
|
# Poetry's 1.2.0+ of defining dev deps
|
113
108
|
poetry_manifest
|
114
109
|
.fetch("group", {})
|
115
110
|
.each_pair do |group_name, obj|
|
116
111
|
group_name = "develop" if group_name == "dev"
|
117
|
-
deps += map_dependencies(obj.fetch("dependencies", {}), group_name)
|
112
|
+
deps += map_dependencies(obj.fetch("dependencies", {}), group_name, options.fetch(:filename, nil))
|
118
113
|
end
|
119
114
|
|
120
115
|
# Parse PEP621 [project] deps
|
121
116
|
pep621_manifest = file_contents.fetch("project", {})
|
122
117
|
pep621_deps = pep621_manifest.fetch("dependencies", []).map { |d| parse_pep_508_dep_spec(d) }
|
123
|
-
deps += map_dependencies(pep621_deps, "runtime")
|
118
|
+
deps += map_dependencies(pep621_deps, "runtime", options.fetch(:filename, nil))
|
124
119
|
|
125
120
|
# We're combining both poetry+PEP621 deps instead of making them mutually exclusive, until we
|
126
121
|
# find a reason not to ingest them both.
|
@@ -133,24 +128,26 @@ module Bibliothecary
|
|
133
128
|
parse_pyproject(file_contents, options)
|
134
129
|
end
|
135
130
|
|
136
|
-
def self.parse_conda(file_contents, options: {})
|
131
|
+
def self.parse_conda(file_contents, options: {})
|
137
132
|
contents = YAML.safe_load(file_contents)
|
138
133
|
return [] unless contents
|
139
134
|
|
140
135
|
dependencies = contents["dependencies"]
|
141
|
-
pip = dependencies.find { |dep| dep.is_a?(Hash) && dep["pip"]}
|
136
|
+
pip = dependencies.find { |dep| dep.is_a?(Hash) && dep["pip"] }
|
142
137
|
return [] unless pip
|
143
138
|
|
144
|
-
Pypi.parse_requirements_txt(pip["pip"].join("\n"))
|
139
|
+
Pypi.parse_requirements_txt(pip["pip"].join("\n"), options:)
|
145
140
|
end
|
146
141
|
|
147
|
-
def self.map_dependencies(packages, type)
|
142
|
+
def self.map_dependencies(packages, type, source = nil)
|
148
143
|
return [] unless packages
|
144
|
+
|
149
145
|
packages.map do |name, info|
|
150
146
|
Dependency.new(
|
151
147
|
name: name,
|
152
148
|
requirement: map_requirements(info),
|
153
149
|
type: type,
|
150
|
+
source: source
|
154
151
|
)
|
155
152
|
end
|
156
153
|
end
|
@@ -160,7 +157,7 @@ module Bibliothecary
|
|
160
157
|
if info["version"]
|
161
158
|
info["version"]
|
162
159
|
elsif info["git"]
|
163
|
-
info[
|
160
|
+
"#{info['git']}##{info['ref']}"
|
164
161
|
else
|
165
162
|
"*"
|
166
163
|
end
|
@@ -169,24 +166,26 @@ module Bibliothecary
|
|
169
166
|
end
|
170
167
|
end
|
171
168
|
|
172
|
-
def self.parse_pipfile_lock(file_contents, options: {})
|
169
|
+
def self.parse_pipfile_lock(file_contents, options: {})
|
173
170
|
manifest = JSON.parse(file_contents)
|
174
171
|
deps = []
|
175
172
|
manifest.each do |group, dependencies|
|
176
173
|
next if group == "_meta"
|
174
|
+
|
177
175
|
group = "runtime" if group == "default"
|
178
176
|
dependencies.each do |name, info|
|
179
177
|
deps << Dependency.new(
|
180
178
|
name: name,
|
181
179
|
requirement: map_requirements(info),
|
182
180
|
type: group,
|
181
|
+
source: options.fetch(:filename, nil)
|
183
182
|
)
|
184
183
|
end
|
185
184
|
end
|
186
185
|
deps
|
187
186
|
end
|
188
187
|
|
189
|
-
def self.parse_poetry_lock(file_contents, options: {})
|
188
|
+
def self.parse_poetry_lock(file_contents, options: {})
|
190
189
|
manifest = Tomlrb.parse(file_contents)
|
191
190
|
deps = []
|
192
191
|
manifest["package"].each do |package|
|
@@ -202,23 +201,28 @@ module Bibliothecary
|
|
202
201
|
name: package["name"],
|
203
202
|
requirement: map_requirements(package),
|
204
203
|
type: group,
|
204
|
+
source: options.fetch(:filename, nil)
|
205
205
|
)
|
206
206
|
end
|
207
207
|
deps
|
208
208
|
end
|
209
209
|
|
210
|
-
def self.parse_setup_py(file_contents, options: {})
|
210
|
+
def self.parse_setup_py(file_contents, options: {})
|
211
211
|
match = file_contents.match(INSTALL_REGEXP)
|
212
212
|
return [] unless match
|
213
|
+
|
213
214
|
deps = []
|
214
215
|
match[1].gsub(/',(\s)?'/, "\n").split("\n").each do |line|
|
215
216
|
next if line.match(/^#/)
|
217
|
+
|
216
218
|
match = line.match(REQUIRE_REGEXP)
|
217
219
|
next unless match
|
220
|
+
|
218
221
|
deps << Dependency.new(
|
219
222
|
name: match[1],
|
220
223
|
requirement: match[-1],
|
221
224
|
type: "runtime",
|
225
|
+
source: options.fetch(:filename, nil)
|
222
226
|
)
|
223
227
|
end
|
224
228
|
deps
|
@@ -234,9 +238,10 @@ module Bibliothecary
|
|
234
238
|
JSON.parse(file_contents)
|
235
239
|
.map do |pkg|
|
236
240
|
Dependency.new(
|
237
|
-
|
238
|
-
|
239
|
-
|
241
|
+
name: pkg.dig("package", "package_name"),
|
242
|
+
requirement: pkg.dig("package", "installed_version"),
|
243
|
+
type: "runtime",
|
244
|
+
source: options.fetch(:filename, nil)
|
240
245
|
)
|
241
246
|
end
|
242
247
|
.uniq
|
@@ -246,7 +251,7 @@ module Bibliothecary
|
|
246
251
|
# https://pip.pypa.io/en/stable/cli/pip_install/#requirement-specifiers
|
247
252
|
# and https://pip.pypa.io/en/stable/topics/vcs-support/#git.
|
248
253
|
# Invalid lines in requirements.txt are skipped.
|
249
|
-
def self.parse_requirements_txt(file_contents, options: {})
|
254
|
+
def self.parse_requirements_txt(file_contents, options: {})
|
250
255
|
deps = []
|
251
256
|
type = case options[:filename]
|
252
257
|
when /dev/ || /docs/ || /tools/
|
@@ -260,7 +265,7 @@ module Bibliothecary
|
|
260
265
|
file_contents.split("\n").each do |line|
|
261
266
|
if line["://"]
|
262
267
|
begin
|
263
|
-
result = parse_requirements_txt_url(line, type)
|
268
|
+
result = parse_requirements_txt_url(line, type, options.fetch(:filename, nil))
|
264
269
|
rescue URI::Error, NoEggSpecified
|
265
270
|
next
|
266
271
|
end
|
@@ -271,6 +276,7 @@ module Bibliothecary
|
|
271
276
|
name: match[1],
|
272
277
|
requirement: match[-1],
|
273
278
|
type: type,
|
279
|
+
source: options.fetch(:filename, nil)
|
274
280
|
)
|
275
281
|
end
|
276
282
|
end
|
@@ -278,7 +284,7 @@ module Bibliothecary
|
|
278
284
|
deps.uniq
|
279
285
|
end
|
280
286
|
|
281
|
-
def self.parse_requirements_txt_url(url, type=nil)
|
287
|
+
def self.parse_requirements_txt_url(url, type = nil, source = nil)
|
282
288
|
uri = URI.parse(url)
|
283
289
|
raise NoEggSpecified, "No egg specified in #{url}" unless uri.fragment
|
284
290
|
|
@@ -287,11 +293,16 @@ module Bibliothecary
|
|
287
293
|
|
288
294
|
requirement = uri.path[/@(.+)$/, 1]
|
289
295
|
|
290
|
-
Dependency.new(
|
296
|
+
Dependency.new(
|
297
|
+
name: name,
|
298
|
+
requirement: requirement,
|
299
|
+
type: type,
|
300
|
+
source: source
|
301
|
+
)
|
291
302
|
end
|
292
303
|
|
293
304
|
def self.pip_compile?(file_contents)
|
294
|
-
|
305
|
+
file_contents.include?("This file is autogenerated by pip-compile")
|
295
306
|
rescue Exception # rubocop:disable Lint/RescueException
|
296
307
|
# We rescue exception here since native libs can throw a non-StandardError
|
297
308
|
# We don't want to throw errors during the matching phase, only during
|
@@ -305,7 +316,7 @@ module Bibliothecary
|
|
305
316
|
name, requirement = dep.split(PEP_508_NAME_REGEXP, 2).last(2).map(&:strip)
|
306
317
|
requirement = requirement.sub(/^[\s;]*/, "")
|
307
318
|
requirement = "*" if requirement == ""
|
308
|
-
|
319
|
+
[name, requirement]
|
309
320
|
end
|
310
321
|
end
|
311
322
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "gemnasium/parser"
|
2
4
|
|
3
5
|
module Bibliothecary
|
@@ -6,7 +8,7 @@ module Bibliothecary
|
|
6
8
|
include Bibliothecary::Analyser
|
7
9
|
extend Bibliothecary::MultiParsers::BundlerLikeManifest
|
8
10
|
|
9
|
-
NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'
|
11
|
+
NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'
|
10
12
|
NAME_VERSION_4 = /^ {4}#{NAME_VERSION}$/
|
11
13
|
BUNDLED_WITH = /BUNDLED WITH/
|
12
14
|
|
@@ -15,17 +17,17 @@ module Bibliothecary
|
|
15
17
|
match_filenames("Gemfile", "gems.rb") => {
|
16
18
|
kind: "manifest",
|
17
19
|
parser: :parse_gemfile,
|
18
|
-
related_to: [
|
20
|
+
related_to: %w[manifest lockfile],
|
19
21
|
},
|
20
22
|
match_extension(".gemspec") => {
|
21
23
|
kind: "manifest",
|
22
24
|
parser: :parse_gemspec,
|
23
|
-
related_to: [
|
25
|
+
related_to: %w[manifest lockfile],
|
24
26
|
},
|
25
27
|
match_filenames("Gemfile.lock", "gems.locked") => {
|
26
28
|
kind: "lockfile",
|
27
29
|
parser: :parse_gemfile_lock,
|
28
|
-
related_to: [
|
30
|
+
related_to: %w[manifest lockfile],
|
29
31
|
},
|
30
32
|
}
|
31
33
|
end
|
@@ -34,7 +36,7 @@ module Bibliothecary
|
|
34
36
|
add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
|
35
37
|
add_multi_parser(Bibliothecary::MultiParsers::Spdx)
|
36
38
|
|
37
|
-
def self.parse_gemfile_lock(file_contents, options: {})
|
39
|
+
def self.parse_gemfile_lock(file_contents, options: {})
|
38
40
|
file_contents.lines(chomp: true).map do |line|
|
39
41
|
match = line.match(NAME_VERSION_4)
|
40
42
|
bundler_match = line.match(BUNDLED_WITH)
|
@@ -42,29 +44,30 @@ module Bibliothecary
|
|
42
44
|
|
43
45
|
if match
|
44
46
|
name = match[1]
|
45
|
-
version = match[2].gsub(/\(|\)/,"")
|
47
|
+
version = match[2].gsub(/\(|\)/, "")
|
46
48
|
Dependency.new(
|
47
49
|
name: name,
|
48
50
|
requirement: version,
|
49
51
|
type: "runtime",
|
52
|
+
source: options.fetch(:filename, nil)
|
50
53
|
)
|
51
54
|
else
|
52
|
-
parse_bundler(file_contents)
|
55
|
+
parse_bundler(file_contents, options.fetch(:filename, nil))
|
53
56
|
end
|
54
57
|
end.compact
|
55
58
|
end
|
56
59
|
|
57
|
-
def self.parse_gemfile(file_contents, options: {})
|
60
|
+
def self.parse_gemfile(file_contents, options: {})
|
58
61
|
manifest = Gemnasium::Parser.send(:gemfile, file_contents)
|
59
|
-
parse_ruby_manifest(manifest)
|
62
|
+
parse_ruby_manifest(manifest, options.fetch(:filename, nil))
|
60
63
|
end
|
61
64
|
|
62
|
-
def self.parse_gemspec(file_contents, options: {})
|
65
|
+
def self.parse_gemspec(file_contents, options: {})
|
63
66
|
manifest = Gemnasium::Parser.send(:gemspec, file_contents)
|
64
|
-
parse_ruby_manifest(manifest)
|
67
|
+
parse_ruby_manifest(manifest, options.fetch(:filename, nil))
|
65
68
|
end
|
66
69
|
|
67
|
-
def self.parse_bundler(file_contents)
|
70
|
+
def self.parse_bundler(file_contents, source = nil)
|
68
71
|
bundled_with_index = file_contents.lines(chomp: true).find_index { |line| line.match(BUNDLED_WITH) }
|
69
72
|
version = file_contents.lines(chomp: true).fetch(bundled_with_index + 1)&.strip
|
70
73
|
|
@@ -74,6 +77,7 @@ module Bibliothecary
|
|
74
77
|
name: "bundler",
|
75
78
|
requirement: version,
|
76
79
|
type: "runtime",
|
80
|
+
source: source
|
77
81
|
)
|
78
82
|
end
|
79
83
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "yaml"
|
2
4
|
|
3
5
|
module Bibliothecary
|
@@ -20,23 +22,24 @@ module Bibliothecary
|
|
20
22
|
|
21
23
|
add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
|
22
24
|
|
23
|
-
def self.parse_yaml_lockfile(file_contents, options: {})
|
25
|
+
def self.parse_yaml_lockfile(file_contents, options: {})
|
24
26
|
manifest = YAML.load file_contents
|
25
|
-
map_dependencies(manifest, "shards", "runtime")
|
27
|
+
map_dependencies(manifest, "shards", "runtime", options.fetch(:filename, nil))
|
26
28
|
end
|
27
29
|
|
28
|
-
def self.parse_yaml_manifest(file_contents, options: {})
|
30
|
+
def self.parse_yaml_manifest(file_contents, options: {})
|
29
31
|
manifest = YAML.load file_contents
|
30
|
-
map_dependencies(manifest, "dependencies", "runtime") +
|
31
|
-
|
32
|
+
map_dependencies(manifest, "dependencies", "runtime", options.fetch(:filename, nil)) +
|
33
|
+
map_dependencies(manifest, "development_dependencies", "runtime", options.fetch(:filename, nil))
|
32
34
|
end
|
33
35
|
|
34
|
-
def self.map_dependencies(hash, key, type)
|
35
|
-
hash.fetch(key,[]).map do |name, requirement|
|
36
|
+
def self.map_dependencies(hash, key, type, source = nil)
|
37
|
+
hash.fetch(key, []).map do |name, requirement|
|
36
38
|
Dependency.new(
|
37
39
|
name: name,
|
38
40
|
requirement: requirement["version"],
|
39
41
|
type: type,
|
42
|
+
source: source
|
40
43
|
)
|
41
44
|
end
|
42
45
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Bibliothecary
|
2
4
|
class PurlUtil
|
3
5
|
# If a purl type (key) exists, it will be used in a manifest for
|
@@ -13,14 +15,10 @@ module Bibliothecary
|
|
13
15
|
"conda" => :conda,
|
14
16
|
"cran" => :cran,
|
15
17
|
"gem" => :rubygems,
|
16
|
-
"hackage" => :hackage,
|
17
|
-
"hex" => :hex,
|
18
18
|
"nuget" => :nuget,
|
19
19
|
"pypi" => :pypi,
|
20
|
-
"swift" => :swift_pm,
|
21
20
|
}.freeze
|
22
21
|
|
23
|
-
|
24
22
|
# @param purl [PackageURL]
|
25
23
|
# @return [String] The properly namespaced package name
|
26
24
|
def self.full_name(purl)
|
@@ -1,17 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Bibliothecary
|
2
4
|
class RelatedFilesInfo
|
3
|
-
attr_reader :path
|
4
|
-
attr_reader :platform
|
5
|
-
attr_reader :manifests
|
6
|
-
attr_reader :lockfiles
|
5
|
+
attr_reader :path, :platform, :manifests, :lockfiles
|
7
6
|
|
8
7
|
# Create a set of RelatedFilesInfo for the provided file_infos,
|
9
|
-
# where each RelatedFilesInfo contains all the file_infos
|
8
|
+
# where each RelatedFilesInfo contains all the file_infos
|
10
9
|
def self.create_from_file_infos(file_infos)
|
11
10
|
returns = []
|
12
11
|
|
13
12
|
file_infos_by_directory = file_infos.group_by { |info| File.dirname(info.relative_path) }
|
14
|
-
file_infos_by_directory.
|
13
|
+
file_infos_by_directory.each_value do |file_infos_for_path|
|
15
14
|
groupable, ungroupable = file_infos_for_path.partition(&:groupable?)
|
16
15
|
|
17
16
|
# add ungroupable ones as separate RFIs
|
@@ -19,9 +18,9 @@ module Bibliothecary
|
|
19
18
|
returns.append(RelatedFilesInfo.new([file_info]))
|
20
19
|
end
|
21
20
|
|
22
|
-
file_infos_by_directory_by_package_manager = groupable.group_by
|
21
|
+
file_infos_by_directory_by_package_manager = groupable.group_by(&:package_manager)
|
23
22
|
|
24
|
-
file_infos_by_directory_by_package_manager.
|
23
|
+
file_infos_by_directory_by_package_manager.each_value do |file_infos_in_directory_for_package_manager|
|
25
24
|
returns.append(RelatedFilesInfo.new(file_infos_in_directory_for_package_manager))
|
26
25
|
end
|
27
26
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Bibliothecary
|
2
4
|
class Runner
|
3
5
|
class MultiManifestFilter
|
@@ -11,8 +13,8 @@ module Bibliothecary
|
|
11
13
|
# @return [Boolean] True if we should skip processing
|
12
14
|
def skip?
|
13
15
|
!@file_analysis ||
|
14
|
-
|
15
|
-
|
16
|
+
!@file_analysis[:dependencies] ||
|
17
|
+
@file_analysis[:dependencies].empty?
|
16
18
|
end
|
17
19
|
end
|
18
20
|
|
@@ -80,7 +82,7 @@ module Bibliothecary
|
|
80
82
|
end
|
81
83
|
|
82
84
|
def partition_file_entries!
|
83
|
-
@single_file_entries, @multiple_file_entries = files_to_check.partition { |_file, count| count == 1
|
85
|
+
@single_file_entries, @multiple_file_entries = files_to_check.partition { |_file, count| count == 1 }
|
84
86
|
|
85
87
|
@single_file_entries = @single_file_entries.map(&:first)
|
86
88
|
@multiple_file_entries = @multiple_file_entries.map(&:first)
|
@@ -88,4 +90,3 @@ module Bibliothecary
|
|
88
90
|
end
|
89
91
|
end
|
90
92
|
end
|
91
|
-
|
data/lib/bibliothecary/runner.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Bibliothecary
|
2
4
|
# A class that allows bibliothecary to run with multiple configurations at once, rather than with one global.
|
3
5
|
# A runner is created every time a file is targeted to be parsed. Don't call
|
@@ -24,8 +26,8 @@ module Bibliothecary
|
|
24
26
|
analyses = analyses.flatten.compact
|
25
27
|
|
26
28
|
info_list.select { |info| info.package_manager.nil? }.each do |info|
|
27
|
-
analyses.push(Bibliothecary::Analyser
|
28
|
-
|
29
|
+
analyses.push(Bibliothecary::Analyser.create_error_analysis("unknown", info.relative_path, "unknown",
|
30
|
+
"No parser for this file type"))
|
29
31
|
end
|
30
32
|
|
31
33
|
analyses
|
@@ -34,16 +36,16 @@ module Bibliothecary
|
|
34
36
|
|
35
37
|
# deprecated; use load_file_info_list.
|
36
38
|
def load_file_list(path)
|
37
|
-
load_file_info_list(path).map
|
39
|
+
load_file_info_list(path).map(&:full_path)
|
38
40
|
end
|
39
41
|
|
40
42
|
def applicable_package_managers(info)
|
41
43
|
managers = package_managers.select { |pm| pm.match_info?(info) }
|
42
|
-
managers.
|
44
|
+
managers.empty? ? [nil] : managers
|
43
45
|
end
|
44
46
|
|
45
47
|
def package_managers
|
46
|
-
Bibliothecary::Parsers.constants.map{|c| Bibliothecary::Parsers.const_get(c) }.sort_by{|c| c.to_s.downcase }
|
48
|
+
Bibliothecary::Parsers.constants.map { |c| Bibliothecary::Parsers.const_get(c) }.sort_by { |c| c.to_s.downcase }
|
47
49
|
end
|
48
50
|
|
49
51
|
# Parses an array of format [{file_path: "", contents: ""},] to match
|
@@ -130,13 +132,13 @@ module Bibliothecary
|
|
130
132
|
# because this API is used by libraries.io and we don't want to
|
131
133
|
# download all .xml files from GitHub.
|
132
134
|
def identify_manifests(file_list)
|
133
|
-
ignored_dirs_with_slash = ignored_dirs.map { |d|
|
135
|
+
ignored_dirs_with_slash = ignored_dirs.map { |d| d.end_with?("/") ? d : "#{d}/" }
|
134
136
|
allowed_file_list = file_list.reject do |f|
|
135
137
|
ignored_dirs.include?(f) || f.start_with?(*ignored_dirs_with_slash)
|
136
138
|
end
|
137
|
-
allowed_file_list = allowed_file_list.reject{|f| ignored_files.include?(f)}
|
139
|
+
allowed_file_list = allowed_file_list.reject { |f| ignored_files.include?(f) }
|
138
140
|
package_managers.map do |pm|
|
139
|
-
allowed_file_list.select do |file_path|
|
141
|
+
allowed_file_list.select do |file_path| # rubocop:disable Style/SelectByRegexp (this is a rubocop false positive, match? is a custom method)
|
140
142
|
# this is a call to match? without file contents, which will skip
|
141
143
|
# ambiguous filenames that are only possibly a manifest
|
142
144
|
pm.match?(file_path)
|
@@ -159,7 +161,7 @@ module Bibliothecary
|
|
159
161
|
# This means we're likely analyzing these files twice in processing,
|
160
162
|
# but we need that accurate package manager information.
|
161
163
|
def filter_multi_manifest_entries(path, related_files_info_entries)
|
162
|
-
MultiManifestFilter.new(path: path, related_files_info_entries: related_files_info_entries
|
164
|
+
MultiManifestFilter.new(path: path, related_files_info_entries: related_files_info_entries, runner: self).results
|
163
165
|
end
|
164
166
|
|
165
167
|
private
|
@@ -178,4 +180,4 @@ module Bibliothecary
|
|
178
180
|
end
|
179
181
|
end
|
180
182
|
|
181
|
-
require_relative "
|
183
|
+
require_relative "runner/multi_manifest_filter"
|
data/lib/bibliothecary.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "bibliothecary/version"
|
2
4
|
require "bibliothecary/dependency"
|
3
5
|
require "bibliothecary/analyser"
|
@@ -10,16 +12,16 @@ require "bibliothecary/purl_util"
|
|
10
12
|
require "find"
|
11
13
|
require "tomlrb"
|
12
14
|
|
13
|
-
Dir[File.expand_path("
|
15
|
+
Dir[File.expand_path("bibliothecary/multi_parsers/*.rb", __dir__)].each do |file|
|
14
16
|
require file
|
15
17
|
end
|
16
|
-
Dir[File.expand_path("
|
18
|
+
Dir[File.expand_path("bibliothecary/parsers/*.rb", __dir__)].each do |file|
|
17
19
|
require file
|
18
20
|
end
|
19
21
|
|
20
22
|
module Bibliothecary
|
21
|
-
VERSION_OPERATORS = /[~^<>*"]
|
22
|
-
INVALID_UTF8_ERROR_REGEXP = /invalid byte sequence
|
23
|
+
VERSION_OPERATORS = /[~^<>*"]/
|
24
|
+
INVALID_UTF8_ERROR_REGEXP = /invalid byte sequence/
|
23
25
|
|
24
26
|
def self.analyse(path, ignore_unparseable_files: true)
|
25
27
|
runner.analyse(path, ignore_unparseable_files: ignore_unparseable_files)
|
@@ -86,6 +88,7 @@ module Bibliothecary
|
|
86
88
|
rescue ArgumentError => e
|
87
89
|
# Bibliothecary doesn't need to analyze non-UTF8 files like binary files, so just return blank.
|
88
90
|
return "" if e.message.match?(INVALID_UTF8_ERROR_REGEXP)
|
91
|
+
|
89
92
|
raise e
|
90
93
|
end
|
91
94
|
|
data/lib/sdl_parser.rb
CHANGED
@@ -1,23 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "sdl4r"
|
2
4
|
|
3
5
|
class SdlParser
|
4
6
|
attr_reader :contents, :type
|
5
|
-
|
7
|
+
|
8
|
+
def initialize(type, contents, source = nil)
|
6
9
|
@contents = contents
|
7
10
|
@type = type || "runtime"
|
11
|
+
@source = source
|
8
12
|
end
|
9
13
|
|
10
14
|
def dependencies
|
11
15
|
parse.children("dependency").inject([]) do |deps, dep|
|
12
16
|
deps.push(Bibliothecary::Dependency.new(
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
+
name: dep.value,
|
18
|
+
requirement: dep.attribute("version") || ">= 0",
|
19
|
+
type: type,
|
20
|
+
source: @source
|
21
|
+
))
|
17
22
|
end.uniq
|
18
23
|
end
|
19
24
|
|
20
25
|
def parse
|
21
|
-
SDL4R
|
26
|
+
SDL4R.read(contents)
|
22
27
|
end
|
23
28
|
end
|