bibliothecary 11.0.1 → 12.1.0

Sign up to get free protection for your applications and to get access to all the features.
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