bibliothecary 12.0.0 → 12.1.1

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 (52) 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 +13 -0
  6. data/Gemfile +16 -1
  7. data/Rakefile +2 -0
  8. data/bibliothecary.gemspec +11 -13
  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 -8
  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 +20 -18
  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 +89 -75
  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 -29
  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 -1
  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 +13 -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 +19 -106
@@ -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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "ox"
2
4
 
3
5
  # Known shortcomings and unimplemented Maven features:
@@ -26,7 +28,7 @@ module Bibliothecary
26
28
 
27
29
  # Builtin methods: https://docs.gradle.org/current/userguide/java_plugin.html#tab:configurations
28
30
  # Deprecated methods: https://docs.gradle.org/current/userguide/upgrading_version_6.html#sec:configuration_removal
29
- GRADLE_DEPENDENCY_METHODS = %w(api compile compileClasspath compileOnly compileOnlyApi implementation runtime runtimeClasspath runtimeOnly testCompile testCompileOnly testImplementation testRuntime testRuntimeOnly)
31
+ GRADLE_DEPENDENCY_METHODS = %w[api compile compileClasspath compileOnly compileOnlyApi implementation runtime runtimeClasspath runtimeOnly testCompile testCompileOnly testImplementation testRuntime testRuntimeOnly].freeze
30
32
 
31
33
  # Intentionally overly-simplified regexes to scrape deps from build.gradle (Groovy) and build.gradle.kts (Kotlin) files.
32
34
  # To be truly useful bibliothecary would need full Groovy / Kotlin parsers that speaks Gradle,
@@ -34,7 +36,7 @@ module Bibliothecary
34
36
  GRADLE_VERSION_REGEXP = /[\w.-]+/ # e.g. '1.2.3'
35
37
  GRADLE_VAR_INTERPOLATION_REGEXP = /\$\w+/ # e.g. '$myVersion'
36
38
  GRADLE_CODE_INTERPOLATION_REGEXP = /\$\{.*\}/ # e.g. '${my-project-settings["version"]}'
37
- GRADLE_GAV_REGEXP = /([\w.-]+)\:([\w.-]+)(?:\:(#{GRADLE_VERSION_REGEXP}|#{GRADLE_VAR_INTERPOLATION_REGEXP}|#{GRADLE_CODE_INTERPOLATION_REGEXP}))?/ # e.g. "group:artifactId:1.2.3"
39
+ GRADLE_GAV_REGEXP = /([\w.-]+):([\w.-]+)(?::(#{GRADLE_VERSION_REGEXP}|#{GRADLE_VAR_INTERPOLATION_REGEXP}|#{GRADLE_CODE_INTERPOLATION_REGEXP}))?/ # e.g. "group:artifactId:1.2.3"
38
40
  GRADLE_GROOVY_SIMPLE_REGEXP = /(#{GRADLE_DEPENDENCY_METHODS.join('|')})\s*\(?\s*['"]#{GRADLE_GAV_REGEXP}['"]/m
39
41
  GRADLE_KOTLIN_SIMPLE_REGEXP = /(#{GRADLE_DEPENDENCY_METHODS.join('|')})\s*\(\s*"#{GRADLE_GAV_REGEXP}"/m
40
42
 
@@ -56,7 +58,6 @@ module Bibliothecary
56
58
  # e.g. "[info] "
57
59
  SBT_IGNORE_REGEXP = /^\[info\]\s*$/
58
60
 
59
-
60
61
  # Copied from the "strings-ansi" gem, because it seems abandoned: https://github.com/piotrmurach/strings-ansi/pull/2
61
62
  # From: https://github.com/piotrmurach/strings-ansi/blob/35d0c9430cf0a8022dc12bdab005bce296cb9f00/lib/strings/ansi.rb#L14-L29
62
63
  # License: MIT
@@ -75,8 +76,8 @@ module Bibliothecary
75
76
  |
76
77
  \]8;[^;]*;.*?(\033\\|\07) # hyperlink
77
78
  ))
78
- }x.freeze
79
-
79
+ }x
80
+
80
81
  def self.mapping
81
82
  {
82
83
  match_filename("ivy.xml", case_insensitive: true) => {
@@ -123,7 +124,7 @@ module Bibliothecary
123
124
  add_multi_parser(Bibliothecary::MultiParsers::Spdx)
124
125
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
125
126
 
126
- def self.parse_ivy_manifest(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
127
+ def self.parse_ivy_manifest(file_contents, options: {})
127
128
  manifest = Ox.parse file_contents
128
129
  manifest.dependencies.locate("dependency").map do |dependency|
129
130
  attrs = dependency.attributes
@@ -131,6 +132,7 @@ module Bibliothecary
131
132
  name: "#{attrs[:org]}:#{attrs[:name]}",
132
133
  requirement: attrs[:rev],
133
134
  type: "runtime",
135
+ source: options.fetch(:filename, nil)
134
136
  )
135
137
  end
136
138
  end
@@ -138,7 +140,7 @@ module Bibliothecary
138
140
  def self.ivy_report?(file_contents)
139
141
  doc = Ox.parse file_contents
140
142
  root = doc&.locate("ivy-report")&.first
141
- return !root.nil?
143
+ !root.nil?
142
144
  rescue Exception # rubocop:disable Lint/RescueException
143
145
  # We rescue exception here since native libs can throw a non-StandardError
144
146
  # We don't want to throw errors during the matching phase, only during
@@ -146,12 +148,14 @@ module Bibliothecary
146
148
  false
147
149
  end
148
150
 
149
- def self.parse_ivy_report(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
151
+ def self.parse_ivy_report(file_contents, options: {})
150
152
  doc = Ox.parse file_contents
151
153
  root = doc.locate("ivy-report").first
152
154
  raise "ivy-report document does not have ivy-report at the root" if root.nil?
155
+
153
156
  info = doc.locate("ivy-report/info").first
154
157
  raise "ivy-report document lacks <info> element" if info.nil?
158
+
155
159
  type = info.attributes[:conf]
156
160
  type = "unknown" if type.nil?
157
161
  modules = doc.locate("ivy-report/dependencies/module")
@@ -159,19 +163,20 @@ module Bibliothecary
159
163
  attrs = mod.attributes
160
164
  org = attrs[:organisation]
161
165
  name = attrs[:name]
162
- version = mod.locate("revision").first&.attributes[:name]
166
+ version = mod.locate("revision").first&.attributes&.[](:name)
163
167
 
164
- next nil if org.nil? or name.nil? or version.nil?
168
+ next nil if org.nil? || name.nil? || version.nil?
165
169
 
166
170
  Dependency.new(
167
171
  name: "#{org}:#{name}",
168
172
  requirement: version,
169
173
  type: type,
174
+ source: options.fetch(:filename, nil)
170
175
  )
171
176
  end.compact
172
177
  end
173
178
 
174
- def self.parse_gradle_resolved(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
179
+ def self.parse_gradle_resolved(file_contents, options: {})
175
180
  current_type = nil
176
181
 
177
182
  file_contents.split("\n").map do |line|
@@ -190,7 +195,7 @@ module Bibliothecary
190
195
  next if project_match[1].nil?
191
196
 
192
197
  # project names can have colons (e.g. for gradle projects in subfolders), which breaks maven artifact naming assumptions, so just replace them with hyphens.
193
- project_name = project_match[1].gsub(/:/, "-")
198
+ project_name = project_match[1].gsub(":", "-")
194
199
  line = line.sub(GRADLE_PROJECT_REGEXP, "internal:#{project_name}:1.0.0")
195
200
  end
196
201
 
@@ -209,20 +214,22 @@ module Bibliothecary
209
214
  if dep.count == 6
210
215
  # get name from renamed package resolution "org:name:version -> renamed_org:name:version"
211
216
  Dependency.new(
212
- original_name: dep[0,2].join(":"),
217
+ original_name: dep[0, 2].join(":"),
213
218
  original_requirement: dep[2],
214
219
  name: dep[-3..-2].join(":"),
215
220
  requirement: dep[-1],
216
221
  type: current_type,
222
+ source: options.fetch(:filename, nil)
217
223
  )
218
224
  elsif dep.count == 5
219
225
  # get name from renamed package resolution "org:name -> renamed_org:name:version"
220
226
  Dependency.new(
221
- original_name: dep[0,2].join(":"),
227
+ original_name: dep[0, 2].join(":"),
222
228
  original_requirement: "*",
223
229
  name: dep[-3..-2].join(":"),
224
230
  requirement: dep[-1],
225
231
  type: current_type,
232
+ source: options.fetch(:filename, nil)
226
233
  )
227
234
  else
228
235
  # get name from version conflict resolution ("org:name:version -> version") and no-resolution ("org:name:version")
@@ -230,6 +237,7 @@ module Bibliothecary
230
237
  name: dep[0..1].join(":"),
231
238
  requirement: dep[-1],
232
239
  type: current_type,
240
+ source: options.fetch(:filename, nil)
233
241
  )
234
242
  end
235
243
  end
@@ -237,16 +245,16 @@ module Bibliothecary
237
245
  .uniq { |item| [item.name, item.requirement, item.type, item.original_name, item.original_requirement] }
238
246
  end
239
247
 
240
- def self.parse_maven_resolved(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
248
+ def self.parse_maven_resolved(file_contents, options: {})
241
249
  file_contents
242
250
  .gsub(ANSI_MATCHER, "")
243
251
  .split("\n")
244
- .map(&method(:parse_resolved_dep_line))
252
+ .map { |line| parse_resolved_dep_line(line, options: options) }
245
253
  .compact
246
254
  .uniq
247
255
  end
248
256
 
249
- def self.parse_maven_tree(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
257
+ def self.parse_maven_tree(file_contents, options: {})
250
258
  captures = file_contents
251
259
  .gsub(ANSI_MATCHER, "")
252
260
  .gsub(/\r\n?/, "\n")
@@ -267,68 +275,71 @@ module Bibliothecary
267
275
  name: parts[0..1].join(":"),
268
276
  requirement: version,
269
277
  type: type,
278
+ source: options.fetch(:filename, nil)
270
279
  )
271
280
  end
272
281
 
273
282
  # First dep line will be the package itself (unless we're only analyzing a single line)
274
283
  package = deps[0]
275
- deps.size < 2 ? deps : deps[1..-1].reject { |d| d.name == package.name && d.requirement == package.requirement }
284
+ deps.size < 2 ? deps : deps[1..].reject { |d| d.name == package.name && d.requirement == package.requirement }
276
285
  end
277
286
 
278
- def self.parse_resolved_dep_line(line)
287
+ def self.parse_resolved_dep_line(line, options: {})
279
288
  # filter out anything that doesn't look like a
280
289
  # resolved dep line
281
290
  return unless line[/ .*:[^-]+-- /]
282
291
 
283
292
  dep_parts = line.strip.split(":")
284
293
  return unless dep_parts.length == 5
294
+
285
295
  # org.springframework.boot:spring-boot-starter-web:jar:2.0.3.RELEASE:compile -- module spring.boot.starter.web [auto]
286
296
  Dependency.new(
287
297
  name: dep_parts[0, 2].join(":"),
288
298
  requirement: dep_parts[3],
289
299
  type: dep_parts[4].split("--").first.strip,
300
+ source: options.fetch(:filename, nil)
290
301
  )
291
302
  end
292
303
 
293
304
  def self.parse_standalone_pom_manifest(file_contents, options: {})
294
- parse_pom_manifest(file_contents, {}, options: options)
305
+ parse_pom_manifest(file_contents, {}, options:)
295
306
  end
296
307
 
297
- def self.parse_pom_manifest(file_contents, parent_properties = {}, options: {}) # rubocop:disable Lint/UnusedMethodArgument
298
- parse_pom_manifests([file_contents], parent_properties)
308
+ def self.parse_pom_manifest(file_contents, parent_properties = {}, options: {})
309
+ parse_pom_manifests([file_contents], parent_properties, options.fetch(:filename, nil))
299
310
  end
300
311
 
301
312
  # @param files [Array<String>] Ordered array of strings containing the
302
313
  # pom.xml bodies. The first element should be the child file.
303
314
  # @param merged_properties [Hash]
304
- def self.parse_pom_manifests(files, merged_properties)
315
+ def self.parse_pom_manifests(files, merged_properties, source = nil)
305
316
  documents = files.map do |file|
306
317
  doc = Ox.parse(file)
307
318
  doc.respond_to?("project") ? doc.project : doc
308
319
  end
309
320
 
310
- mergedDependencyManagements = {}
321
+ merged_dependency_managements = {}
311
322
  documents.each do |document|
312
- document.locate("dependencyManagement/dependencies/dependency").each do |dep|
313
- groupId = extract_pom_dep_info(document, dep, "groupId", merged_properties)
314
- artifactId = extract_pom_dep_info(document, dep, "artifactId", merged_properties)
315
- key = "#{groupId}:#{artifactId}"
316
- mergedDependencyManagements[key] ||=
317
- {
318
- groupId: groupId,
319
- artifactId: artifactId,
320
- version: extract_pom_dep_info(document, dep, "version", merged_properties),
321
- scope: extract_pom_dep_info(document, dep, "scope", merged_properties),
322
- }
323
- end
323
+ document.locate("dependencyManagement/dependencies/dependency").each do |dep|
324
+ group_id = extract_pom_dep_info(document, dep, "groupId", merged_properties)
325
+ artifact_id = extract_pom_dep_info(document, dep, "artifactId", merged_properties)
326
+ key = "#{group_id}:#{artifact_id}"
327
+ merged_dependency_managements[key] ||=
328
+ {
329
+ groupId: group_id,
330
+ artifactId: artifact_id,
331
+ version: extract_pom_dep_info(document, dep, "version", merged_properties),
332
+ scope: extract_pom_dep_info(document, dep, "scope", merged_properties),
333
+ }
334
+ end
324
335
  end
325
336
 
326
337
  dep_hashes = {}
327
338
  documents.each do |document|
328
339
  document.locate("dependencies/dependency").each do |dep|
329
- groupId = extract_pom_dep_info(document, dep, "groupId", merged_properties)
330
- artifactId = extract_pom_dep_info(document, dep, "artifactId", merged_properties)
331
- key = "#{groupId}:#{artifactId}"
340
+ group_id = extract_pom_dep_info(document, dep, "groupId", merged_properties)
341
+ artifact_id = extract_pom_dep_info(document, dep, "artifactId", merged_properties)
342
+ key = "#{group_id}:#{artifact_id}"
332
343
  unless dep_hashes.key?(key)
333
344
  dep_hashes[key] = {
334
345
  name: key,
@@ -353,41 +364,44 @@ module Bibliothecary
353
364
  # Anything that wasn't covered by a dependency version, get from the
354
365
  # dependencyManagements
355
366
  dep_hashes.each do |key, dep_hash|
356
- if (dependencyManagement = mergedDependencyManagements[key])
357
- dep_hash[:requirement] ||= dependencyManagement[:version]
358
- dep_hash[:type] ||= dependencyManagement[:scope]
367
+ if (dependency_management = merged_dependency_managements[key])
368
+ dep_hash[:requirement] ||= dependency_management[:version]
369
+ dep_hash[:type] ||= dependency_management[:scope]
359
370
  end
360
371
 
361
372
  dep_hash[:type] ||= "runtime"
373
+ dep_hash[:source] = source
362
374
  end
363
375
 
364
- dep_hashes.map{|key, dep_hash| Dependency.new(**dep_hash)}
376
+ dep_hashes.map { |_key, dep_hash| Dependency.new(**dep_hash) }
365
377
  end
366
378
 
367
- def self.parse_gradle(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
379
+ def self.parse_gradle(file_contents, options: {})
368
380
  file_contents
369
- .scan(GRADLE_GROOVY_SIMPLE_REGEXP) # match 'implementation "group:artifactId:version"'
370
- .reject { |(_type, group, artifactId, _version)| group.nil? || artifactId.nil? } # remove any matches with missing group/artifactId
371
- .map { |(type, group, artifactId, version)|
381
+ .scan(GRADLE_GROOVY_SIMPLE_REGEXP) # match 'implementation "group:artifactId:version"'
382
+ .reject { |(_type, group, artifact_id, _version)| group.nil? || artifact_id.nil? } # remove any matches with missing group/artifactId
383
+ .map do |(type, group, artifact_id, version)|
372
384
  Dependency.new(
373
- name: [group, artifactId].join(":"),
385
+ name: [group, artifact_id].join(":"),
374
386
  requirement: version,
375
387
  type: type,
388
+ source: options.fetch(:filename, nil)
376
389
  )
377
- }
390
+ end
378
391
  end
379
392
 
380
- def self.parse_gradle_kts(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
393
+ def self.parse_gradle_kts(file_contents, options: {})
381
394
  file_contents
382
- .scan(GRADLE_KOTLIN_SIMPLE_REGEXP) # match 'implementation("group:artifactId:version")'
383
- .reject { |(_type, group, artifactId, _version)| group.nil? || artifactId.nil? } # remove any matches with missing group/artifactId
384
- .map { |(type, group, artifactId, version)|
395
+ .scan(GRADLE_KOTLIN_SIMPLE_REGEXP) # match 'implementation("group:artifactId:version")'
396
+ .reject { |(_type, group, artifact_id, _version)| group.nil? || artifact_id.nil? } # remove any matches with missing group/artifactId
397
+ .map do |(type, group, artifact_id, version)|
385
398
  Dependency.new(
386
- name: [group, artifactId].join(":"),
399
+ name: [group, artifact_id].join(":"),
387
400
  requirement: version,
388
401
  type: type,
402
+ source: options.fetch(:filename, nil)
389
403
  )
390
- }
404
+ end
391
405
  end
392
406
 
393
407
  def self.gradle_dependency_name(group, name)
@@ -420,11 +434,9 @@ module Bibliothecary
420
434
  # whitespace in dependency tags should be ignored
421
435
  value = value&.strip
422
436
  match = value&.match(MAVEN_PROPERTY_REGEXP)
423
- if match
424
- return extract_property(xml, match[1], value, parent_properties)
425
- else
426
- return value
427
- end
437
+ return extract_property(xml, match[1], value, parent_properties) if match
438
+
439
+ value
428
440
  end
429
441
 
430
442
  def self.replace_value_with_prop(original_value, property_value, property_name)
@@ -434,17 +446,16 @@ module Bibliothecary
434
446
  def self.extract_property(xml, property_name, value, parent_properties = {}, depth = 0)
435
447
  prop_value = property_value(xml, property_name, parent_properties)
436
448
  return value unless prop_value
449
+
437
450
  # don't resolve more than 5 levels deep to avoid potential circular references
438
451
 
439
452
  resolved_value = replace_value_with_prop(value, prop_value, property_name)
440
453
  # check to see if we just resolved to another property name
441
454
  match = resolved_value.match(MAVEN_PROPERTY_REGEXP)
442
- if match && depth < MAX_DEPTH
443
- depth += 1
444
- return extract_property(xml, match[1], resolved_value, parent_properties, depth)
445
- else
446
- return resolved_value
447
- end
455
+ return resolved_value unless match && depth < MAX_DEPTH
456
+
457
+ depth += 1
458
+ extract_property(xml, match[1], resolved_value, parent_properties, depth)
448
459
  end
449
460
 
450
461
  def self.property_value(xml, property_name, parent_properties)
@@ -453,9 +464,9 @@ module Bibliothecary
453
464
  non_prop_name = property_name.gsub(".", "/").gsub("project/", "")
454
465
 
455
466
  prop_field = xml.properties.locate(property_name).first if xml.respond_to?("properties")
456
- parent_prop = parent_properties[property_name] || # e.g. "${foo}"
457
- parent_properties[property_name.sub(/^project\./, "")] || # e.g. "${project.foo}"
458
- parent_properties[property_name.sub(/^project\.parent\./, "")] # e.g. "${project.parent.foo}"
467
+ parent_prop = parent_properties[property_name] || # e.g. "${foo}"
468
+ parent_properties[property_name.sub(/^project\./, "")] || # e.g. "${project.foo}"
469
+ parent_properties[property_name.sub(/^project\.parent\./, "")] # e.g. "${project.parent.foo}"
459
470
 
460
471
  if prop_field
461
472
  prop_field.nodes.first
@@ -472,7 +483,7 @@ module Bibliothecary
472
483
  end
473
484
  end
474
485
 
475
- def self.parse_sbt_update_full(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
486
+ def self.parse_sbt_update_full(file_contents, options: {})
476
487
  all_deps = []
477
488
  lines = file_contents.split("\n")
478
489
  while lines.any?
@@ -480,6 +491,7 @@ module Bibliothecary
480
491
 
481
492
  type_match = SBT_TYPE_REGEXP.match(line)
482
493
  next unless type_match
494
+
483
495
  type = type_match.captures[0]
484
496
 
485
497
  deps = parse_sbt_deps(type, lines)
@@ -487,8 +499,8 @@ module Bibliothecary
487
499
  end
488
500
 
489
501
  # strip out evicted dependencies
490
- all_deps.select! do |dep|
491
- dep[:fields]["evicted"] != "true"
502
+ all_deps.reject! do |dep|
503
+ dep[:fields]["evicted"] == "true"
492
504
  end
493
505
 
494
506
  # in the future, we could use "callers" in the fields to
@@ -498,19 +510,23 @@ module Bibliothecary
498
510
 
499
511
  # clean out any duplicates (I'm pretty sure sbt will have done this for
500
512
  # us so this is paranoia, basically)
501
- squished = all_deps.compact.uniq {|item| [item[:name], item[:requirement], item[:type]]}
513
+ squished = all_deps.compact.uniq { |item| [item[:name], item[:requirement], item[:type]] }
502
514
 
503
515
  # get rid of the fields
504
516
  squished.each do |dep|
505
517
  dep.delete(:fields)
506
518
  end
507
519
 
508
- return squished.map { |dep_kvs| Dependency.new(**dep_kvs) }
520
+ squished.map do |dep_kvs|
521
+ Dependency.new(
522
+ **dep_kvs, source: options.fetch(:filename, nil)
523
+ )
524
+ end
509
525
  end
510
526
 
511
527
  def self.parse_sbt_deps(type, lines)
512
528
  deps = []
513
- while lines.any? and not SBT_TYPE_REGEXP.match(lines[0])
529
+ while lines.any? && !SBT_TYPE_REGEXP.match(lines[0])
514
530
  line = lines.shift
515
531
 
516
532
  next if SBT_IGNORE_REGEXP.match(line)
@@ -530,7 +546,7 @@ module Bibliothecary
530
546
 
531
547
  def self.parse_sbt_versions(type, name, lines)
532
548
  versions = []
533
- while lines.any? and not SBT_TYPE_REGEXP.match(lines[0])
549
+ while lines.any? && !SBT_TYPE_REGEXP.match(lines[0])
534
550
  line = lines.shift
535
551
 
536
552
  version_match = SBT_VERSION_REGEXP.match(line)
@@ -547,7 +563,7 @@ module Bibliothecary
547
563
 
548
564
  def self.parse_sbt_version(type, name, version, lines)
549
565
  fields = {}
550
- while lines.any? and not SBT_TYPE_REGEXP.match(lines[0])
566
+ while lines.any? && !SBT_TYPE_REGEXP.match(lines[0])
551
567
  line = lines.shift
552
568
 
553
569
  field_match = SBT_FIELD_REGEXP.match(line)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "json"
2
4
 
3
5
  module Bibliothecary