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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +2 -1
  3. data/.rubocop.yml +10 -2
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +24 -0
  6. data/Gemfile +16 -1
  7. data/Rakefile +2 -0
  8. data/bibliothecary.gemspec +11 -14
  9. data/bin/bibliothecary +2 -1
  10. data/bin/console +1 -0
  11. data/lib/bibliothecary/analyser/analysis.rb +13 -8
  12. data/lib/bibliothecary/analyser/determinations.rb +2 -0
  13. data/lib/bibliothecary/analyser/matchers.rb +17 -17
  14. data/lib/bibliothecary/analyser.rb +11 -8
  15. data/lib/bibliothecary/cli.rb +3 -1
  16. data/lib/bibliothecary/configuration.rb +3 -11
  17. data/lib/bibliothecary/dependency.rb +17 -15
  18. data/lib/bibliothecary/exceptions.rb +6 -2
  19. data/lib/bibliothecary/file_info.rb +9 -11
  20. data/lib/bibliothecary/multi_parsers/bundler_like_manifest.rb +13 -10
  21. data/lib/bibliothecary/multi_parsers/cyclonedx.rb +10 -8
  22. data/lib/bibliothecary/multi_parsers/dependencies_csv.rb +11 -4
  23. data/lib/bibliothecary/multi_parsers/json_runtime.rb +5 -2
  24. data/lib/bibliothecary/multi_parsers/spdx.rb +24 -19
  25. data/lib/bibliothecary/parsers/bower.rb +5 -3
  26. data/lib/bibliothecary/parsers/cargo.rb +10 -4
  27. data/lib/bibliothecary/parsers/cocoapods.rb +15 -11
  28. data/lib/bibliothecary/parsers/conda.rb +56 -33
  29. data/lib/bibliothecary/parsers/cpan.rb +6 -4
  30. data/lib/bibliothecary/parsers/cran.rb +10 -6
  31. data/lib/bibliothecary/parsers/dub.rb +4 -2
  32. data/lib/bibliothecary/parsers/elm.rb +4 -1
  33. data/lib/bibliothecary/parsers/go.rb +51 -43
  34. data/lib/bibliothecary/parsers/haxelib.rb +2 -1
  35. data/lib/bibliothecary/parsers/julia.rb +5 -1
  36. data/lib/bibliothecary/parsers/maven.rb +93 -77
  37. data/lib/bibliothecary/parsers/meteor.rb +2 -0
  38. data/lib/bibliothecary/parsers/npm.rb +97 -34
  39. data/lib/bibliothecary/parsers/nuget.rb +37 -28
  40. data/lib/bibliothecary/parsers/packagist.rb +21 -11
  41. data/lib/bibliothecary/parsers/pub.rb +4 -2
  42. data/lib/bibliothecary/parsers/pypi.rb +48 -37
  43. data/lib/bibliothecary/parsers/rubygems.rb +16 -12
  44. data/lib/bibliothecary/parsers/shard.rb +10 -7
  45. data/lib/bibliothecary/purl_util.rb +2 -4
  46. data/lib/bibliothecary/related_files_info.rb +7 -8
  47. data/lib/bibliothecary/runner/multi_manifest_filter.rb +5 -4
  48. data/lib/bibliothecary/runner.rb +12 -10
  49. data/lib/bibliothecary/version.rb +3 -1
  50. data/lib/bibliothecary.rb +7 -4
  51. data/lib/sdl_parser.rb +11 -6
  52. metadata +18 -120
  53. data/lib/bibliothecary/parsers/carthage.rb +0 -52
  54. data/lib/bibliothecary/parsers/clojars.rb +0 -38
  55. data/lib/bibliothecary/parsers/hackage.rb +0 -53
  56. data/lib/bibliothecary/parsers/hex.rb +0 -54
  57. data/lib/bibliothecary/parsers/swift_pm.rb +0 -35
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "yaml"
2
4
  require "json"
3
5
 
@@ -74,60 +76,63 @@ module Bibliothecary
74
76
  add_multi_parser(Bibliothecary::MultiParsers::Spdx)
75
77
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
76
78
 
77
- def self.parse_godep_json(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
79
+ def self.parse_godep_json(file_contents, options: {})
78
80
  manifest = JSON.parse file_contents
79
- map_dependencies(manifest, "Deps", "ImportPath", "Rev", "runtime")
81
+ map_dependencies(manifest, "Deps", "ImportPath", "Rev", "runtime", options.fetch(:filename, nil))
80
82
  end
81
83
 
82
- def self.parse_gpm(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
84
+ def self.parse_gpm(file_contents, options: {})
83
85
  deps = []
84
86
  file_contents.split("\n").each do |line|
85
87
  match = line.gsub(/(\#(.*))/, "").match(GPM_REGEXP)
86
88
  next unless match
89
+
87
90
  deps << Dependency.new(
88
91
  name: match[1].strip,
89
92
  requirement: match[2].strip,
90
93
  type: "runtime",
94
+ source: options.fetch(:filename, nil)
91
95
  )
92
96
  end
93
97
  deps
94
98
  end
95
99
 
96
- def self.parse_govendor(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
97
- manifest = JSON.load file_contents
98
- map_dependencies(manifest, "package", "path", "revision", "runtime")
100
+ def self.parse_govendor(file_contents, options: {})
101
+ manifest = JSON.parse file_contents
102
+ map_dependencies(manifest, "package", "path", "revision", "runtime", options.fetch(:filename, nil))
99
103
  end
100
104
 
101
- def self.parse_glide_yaml(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
105
+ def self.parse_glide_yaml(file_contents, options: {})
102
106
  manifest = YAML.load file_contents
103
- map_dependencies(manifest, "import", "package", "version", "runtime") +
104
- map_dependencies(manifest, "devImports", "package", "version", "development")
107
+ map_dependencies(manifest, "import", "package", "version", "runtime", options.fetch(:filename, nil)) +
108
+ map_dependencies(manifest, "devImports", "package", "version", "development", options.fetch(:filename, nil))
105
109
  end
106
110
 
107
- def self.parse_glide_lockfile(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
108
- manifest = YAML.load file_contents
109
- map_dependencies(manifest, "imports", "name", "version", "runtime")
111
+ def self.parse_glide_lockfile(file_contents, options: {})
112
+ # glide.lock files contain an "updated" Time field, but Ruby 3.2+ requires us to safelist that class
113
+ manifest = YAML.load file_contents, permitted_classes: [Time]
114
+ map_dependencies(manifest, "imports", "name", "version", "runtime", options.fetch(:filename, nil))
110
115
  end
111
116
 
112
- def self.parse_gb_manifest(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
117
+ def self.parse_gb_manifest(file_contents, options: {})
113
118
  manifest = JSON.parse file_contents
114
- map_dependencies(manifest, "dependencies", "importpath", "revision", "runtime")
119
+ map_dependencies(manifest, "dependencies", "importpath", "revision", "runtime", options.fetch(:filename, nil))
115
120
  end
116
121
 
117
- def self.parse_dep_toml(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
122
+ def self.parse_dep_toml(file_contents, options: {})
118
123
  manifest = Tomlrb.parse file_contents
119
- map_dependencies(manifest, "constraint", "name", "version", "runtime")
124
+ map_dependencies(manifest, "constraint", "name", "version", "runtime", options.fetch(:filename, nil))
120
125
  end
121
126
 
122
- def self.parse_dep_lockfile(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
127
+ def self.parse_dep_lockfile(file_contents, options: {})
123
128
  manifest = Tomlrb.parse file_contents
124
- map_dependencies(manifest, "projects", "name", "revision", "runtime")
129
+ map_dependencies(manifest, "projects", "name", "revision", "runtime", options.fetch(:filename, nil))
125
130
  end
126
131
 
127
- def self.parse_go_mod(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
128
- categorized_deps = parse_go_mod_categorized_deps(file_contents)
132
+ def self.parse_go_mod(file_contents, options: {})
133
+ categorized_deps = parse_go_mod_categorized_deps(file_contents, options.fetch(:filename, nil))
129
134
 
130
- deps = categorized_deps["require"]
135
+ categorized_deps["require"]
131
136
  .map do |dep|
132
137
  # NOTE: A "replace" directive doesn't add the dep to the module graph unless the original dep is also in a "require" directive,
133
138
  # so we need to track down replacements here and use those instead of the originals, if present.
@@ -143,11 +148,9 @@ module Bibliothecary
143
148
 
144
149
  replaced_dep || dep
145
150
  end
146
-
147
- return deps
148
151
  end
149
152
 
150
- def self.parse_go_mod_categorized_deps(file_contents)
153
+ def self.parse_go_mod_categorized_deps(file_contents, source)
151
154
  current_multiline_category = nil
152
155
  # docs: https://go.dev/ref/mod#go-mod-file-require
153
156
  categorized_deps = {
@@ -159,38 +162,39 @@ module Bibliothecary
159
162
  file_contents
160
163
  .lines
161
164
  .map(&:strip)
162
- .reject { |line| line =~ /^#{GOMOD_COMMENT_REGEXP}/ } # ignore comment lines
165
+ .grep_v(/^#{GOMOD_COMMENT_REGEXP}/) # ignore comment lines
163
166
  .each do |line|
164
167
  if line.match(GOMOD_MULTILINE_END_REGEXP) # detect the end of a multiline
165
168
  current_multiline_category = nil
166
169
  elsif (match = line.match(GOMOD_MULTILINE_START_REGEXP)) # or, detect the start of a multiline
167
170
  current_multiline_category = match[1]
168
171
  elsif (match = line.match(GOMOD_SINGLELINE_DEP_REGEXP)) # or, detect a singleline dep
169
- categorized_deps[match[:category]] << go_mod_category_relative_dep(category: match[:category], line: line, match: match)
170
- elsif (current_multiline_category && match = line.match(GOMOD_MULTILINE_DEP_REGEXP)) # otherwise, parse the multiline dep
171
- categorized_deps[current_multiline_category] << go_mod_category_relative_dep(category: current_multiline_category, line: line, match: match)
172
+ categorized_deps[match[:category]] << go_mod_category_relative_dep(category: match[:category], line: line, match: match, source: source)
173
+ elsif current_multiline_category && (match = line.match(GOMOD_MULTILINE_DEP_REGEXP)) # otherwise, parse the multiline dep
174
+ categorized_deps[current_multiline_category] << go_mod_category_relative_dep(category: current_multiline_category, line: line, match: match, source: source)
172
175
  end
173
176
  end
174
177
  categorized_deps
175
178
  end
176
179
 
177
- def self.parse_go_sum(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
180
+ def self.parse_go_sum(file_contents, options: {})
178
181
  deps = []
179
182
  file_contents.lines.map(&:strip).each do |line|
180
- if (match = line.match(GOSUM_REGEXP))
181
- deps << Dependency.new(
182
- name: match[1].strip,
183
- requirement: match[2].strip.split("/").first,
184
- type: "runtime",
185
- )
186
- end
183
+ next unless (match = line.match(GOSUM_REGEXP))
184
+
185
+ deps << Dependency.new(
186
+ name: match[1].strip,
187
+ requirement: match[2].strip.split("/").first,
188
+ type: "runtime",
189
+ source: options.fetch(:filename, nil)
190
+ )
187
191
  end
188
192
  deps.uniq
189
193
  end
190
194
 
191
- def self.parse_go_resolved(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
195
+ def self.parse_go_resolved(file_contents, options: {})
192
196
  JSON.parse(file_contents)
193
- .select { |dep| dep["Main"] != "true" }
197
+ .reject { |dep| dep["Main"] == "true" }
194
198
  .map do |dep|
195
199
  if dep["Replace"].is_a?(String) && dep["Replace"] != "<nil>" && dep["Replace"] != ""
196
200
  # NOTE: The "replace" directive doesn't actually change the version reported from Go (e.g. "go mod graph"), it only changes
@@ -199,28 +203,29 @@ module Bibliothecary
199
203
  name, requirement = dep["Replace"].split(" ", 2)
200
204
  requirement = "*" if requirement.to_s.strip == ""
201
205
  Dependency.new(
202
- name: name, requirement: requirement, original_name: dep["Path"], original_requirement: dep["Version"], type: dep.fetch("Scope") { "runtime" }
206
+ name: name, requirement: requirement, original_name: dep["Path"], original_requirement: dep["Version"], type: dep.fetch("Scope", "runtime"), source: options.fetch(:filename, nil)
203
207
  )
204
208
  else
205
209
  Dependency.new(
206
- name: dep["Path"], requirement: dep["Version"], type: dep.fetch("Scope") { "runtime" }
210
+ name: dep["Path"], requirement: dep["Version"], type: dep.fetch("Scope", "runtime"), source: options.fetch(:filename, nil)
207
211
  )
208
212
  end
209
213
  end
210
214
  end
211
215
 
212
- def self.map_dependencies(manifest, attr_name, dep_attr_name, version_attr_name, type)
213
- manifest.fetch(attr_name,[]).map do |dependency|
216
+ def self.map_dependencies(manifest, attr_name, dep_attr_name, version_attr_name, type, source = nil)
217
+ manifest.fetch(attr_name, []).map do |dependency|
214
218
  Dependency.new(
215
219
  name: dependency[dep_attr_name],
216
220
  requirement: dependency[version_attr_name],
217
221
  type: type,
222
+ source: source
218
223
  )
219
224
  end
220
225
  end
221
226
 
222
227
  # Returns our standard-ish dep Hash based on the category of dep matched ("require", "replace", etc.)
223
- def self.go_mod_category_relative_dep(category:, line:, match:)
228
+ def self.go_mod_category_relative_dep(category:, line:, match:, source: nil)
224
229
  case category
225
230
  when "replace"
226
231
  replacement_dep = line.split(GOMOD_REPLACEMENT_SEPARATOR_REGEXP, 2).last
@@ -232,6 +237,7 @@ module Bibliothecary
232
237
  requirement: replacement_match[:requirement],
233
238
  type: "runtime",
234
239
  direct: !match[:indirect],
240
+ source: source
235
241
  )
236
242
  when "retract"
237
243
  Dependency.new(
@@ -240,6 +246,7 @@ module Bibliothecary
240
246
  type: "runtime",
241
247
  deprecated: true,
242
248
  direct: !match[:indirect],
249
+ source: source
243
250
  )
244
251
  else
245
252
  Dependency.new(
@@ -247,6 +254,7 @@ module Bibliothecary
247
254
  requirement: match[:requirement],
248
255
  type: "runtime",
249
256
  direct: !match[:indirect],
257
+ source: source
250
258
  )
251
259
  end
252
260
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "json"
2
4
 
3
5
  module Bibliothecary
@@ -19,4 +21,3 @@ module Bibliothecary
19
21
  end
20
22
  end
21
23
  end
22
-
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bibliothecary
2
4
  module Parsers
3
5
  class Julia
@@ -14,10 +16,11 @@ module Bibliothecary
14
16
 
15
17
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
16
18
 
17
- def self.parse_require(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
19
+ def self.parse_require(file_contents, options: {})
18
20
  deps = []
19
21
  file_contents.split("\n").each do |line|
20
22
  next if line.match(/^#/) || line.empty?
23
+
21
24
  split = line.split(/\s/)
22
25
  if line.match(/^@/)
23
26
  name = split[1]
@@ -33,6 +36,7 @@ module Bibliothecary
33
36
  name: name,
34
37
  requirement: reqs,
35
38
  type: "runtime",
39
+ source: options.fetch(:filename, nil)
36
40
  )
37
41
  end
38
42
  deps