dependabot-cargo 0.320.0 → 0.321.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 456d0a29e38e5d82efcac6d4dc9c61ac255200a7878b153ce29fbe4f8ef5950f
4
- data.tar.gz: 8bc0a3d97c153197754a9061e3d18ac6905f0e0d11168cdb2050327452d592e7
3
+ metadata.gz: c526ec59aec95145c21fd881e22410135a2c342a8b631060ade37b89271427c7
4
+ data.tar.gz: cefe48ec5bed87ebc31c4dde569f739448060ad975fd0161810b705cbfafcf49
5
5
  SHA512:
6
- metadata.gz: 1e7deb3de9f1c7cf0a0a3f061417981beac4af6a5fefa1ba3ce84c839b7075297e084c98bfce34b1032a9240bf2982ce39d64befd80faa8221fdac7b6d56a171
7
- data.tar.gz: c90e14e9c87a1cea521f82d8644a3ca474a2a45bb90bd95dc227e03b28efdafa723f8f5c0b9bd0132538fff9663cfdfcb53f38c4e5db78509a0301007aea8308
6
+ metadata.gz: e5877226f7656615f62cf1e5d1376d159f9bbe71b2d7d1dc575d9f5edbcbcfc14d7ba93fb70f0b802fc5fb1f7faca182171787261a58df8e05a50183767b35ff
7
+ data.tar.gz: 489b23fc468b4997093d4d7e4adabb1af961f82de82c05f029e5cc103c7b7d6d0e779aef2a45cb9e0ed122c6a1a49941eac1e9fae1f034229d22a24abc11234b
@@ -1,6 +1,7 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
4
5
  require "toml-rb"
5
6
  require "open3"
6
7
  require "dependabot/git_commit_checker"
@@ -12,22 +13,39 @@ require "dependabot/shared_helpers"
12
13
  module Dependabot
13
14
  module Cargo
14
15
  class FileUpdater
16
+ # rubocop:disable Metrics/ClassLength
15
17
  class LockfileUpdater
18
+ extend T::Sig
16
19
  LOCKFILE_ENTRY_REGEX = /
17
20
  \[\[package\]\]\n
18
- (?:(?!^\[(\[package|metadata)).)+
21
+ (?:(?!^\[(?:\[package|metadata)).)+
19
22
  /mx
20
23
 
21
24
  LOCKFILE_CHECKSUM_REGEX = /^"checksum .*$/
22
25
 
23
- def initialize(dependencies:, dependency_files:, credentials:)
24
- @dependencies = dependencies
25
- @dependency_files = dependency_files
26
- @credentials = credentials
26
+ sig do
27
+ params(
28
+ dependencies: T::Array[Dependabot::Dependency],
29
+ dependency_files: T::Array[Dependabot::DependencyFile],
30
+ credentials: T::Array[Dependabot::Credential]
31
+ ).void
27
32
  end
28
-
33
+ def initialize(dependencies:, dependency_files:, credentials:)
34
+ @dependencies = T.let(dependencies, T::Array[Dependabot::Dependency])
35
+ @dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
36
+ @credentials = T.let(credentials, T::Array[Dependabot::Credential])
37
+ @custom_specification = T.let(nil, T.nilable(String))
38
+ @git_ssh_requirements_to_swap = T.let(nil, T.nilable(T::Hash[String, String]))
39
+ @manifest_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
40
+ @path_dependency_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
41
+ @lockfile = T.let(nil, T.nilable(Dependabot::DependencyFile))
42
+ @toolchain = T.let(nil, T.nilable(Dependabot::DependencyFile))
43
+ @config = T.let(nil, T.nilable(Dependabot::DependencyFile))
44
+ end
45
+
46
+ sig { returns(T.any(String, T.noreturn)) }
29
47
  def updated_lockfile_content
30
- base_directory = dependency_files.first.directory
48
+ base_directory = T.must(dependency_files.first).directory
31
49
  SharedHelpers.in_a_temporary_directory(base_directory) do
32
50
  write_temporary_dependency_files
33
51
 
@@ -42,6 +60,18 @@ module Dependabot
42
60
 
43
61
  next updated_lockfile if updated_lockfile.include?(desired_lockfile_content)
44
62
 
63
+ # If exact version match fails, accept any update
64
+ if dependency_updated?(updated_lockfile, dependency)
65
+ actual_version = extract_actual_version(updated_lockfile, dependency.name)
66
+ if actual_version && actual_version != dependency.version
67
+ Dependabot.logger.info(
68
+ "Cargo selected version #{actual_version} instead of #{dependency.version} for #{dependency.name} " \
69
+ "due to dependency constraints"
70
+ )
71
+ end
72
+ next updated_lockfile
73
+ end
74
+
45
75
  raise "Failed to update #{dependency.name}!"
46
76
  end
47
77
  rescue Dependabot::SharedHelpers::HelperSubprocessFailed => e
@@ -51,15 +81,22 @@ module Dependabot
51
81
 
52
82
  private
53
83
 
84
+ sig { returns(T::Array[Dependabot::Dependency]) }
54
85
  attr_reader :dependencies
86
+
87
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
55
88
  attr_reader :dependency_files
89
+
90
+ sig { returns(T::Array[T.untyped]) }
56
91
  attr_reader :credentials
57
92
 
58
93
  # Currently, there will only be a single updated dependency
94
+ sig { returns(Dependabot::Dependency) }
59
95
  def dependency
60
- dependencies.first
96
+ T.must(dependencies.first)
61
97
  end
62
98
 
99
+ sig { params(error: StandardError).returns(T.noreturn) }
63
100
  def handle_cargo_error(error)
64
101
  raise unless error.message.include?("failed to select a version") ||
65
102
  error.message.include?("no matching version") ||
@@ -72,6 +109,7 @@ module Dependabot
72
109
  # rubocop:disable Metrics/PerceivedComplexity
73
110
  # rubocop:disable Metrics/CyclomaticComplexity
74
111
  # rubocop:disable Metrics/AbcSize
112
+ sig { params(error: StandardError).returns(T::Boolean) }
75
113
  def better_specification_needed?(error)
76
114
  return false if @custom_specification
77
115
  return false unless error.message.match?(/specification .* is ambigu/)
@@ -85,16 +123,16 @@ module Dependabot
85
123
  dependency.version
86
124
  end
87
125
 
88
- if spec_options.count { |s| s.end_with?(ver) } == 1
126
+ if ver && spec_options.count { |s| s.end_with?(ver) } == 1
89
127
  @custom_specification = spec_options.find { |s| s.end_with?(ver) }
90
128
  return true
91
- elsif spec_options.count { |s| s.end_with?(ver) } > 1
129
+ elsif ver && spec_options.count { |s| s.end_with?(ver) } > 1
92
130
  spec_options.select! { |s| s.end_with?(ver) }
93
131
  end
94
132
 
95
133
  if git_dependency? && git_source_url &&
96
- spec_options.count { |s| s.include?(git_source_url) } >= 1
97
- spec_options.select! { |s| s.include?(git_source_url) }
134
+ spec_options.count { |s| s.include?(T.must(git_source_url)) } >= 1
135
+ spec_options.select! { |s| s.include?(T.must(git_source_url)) }
98
136
  end
99
137
 
100
138
  @custom_specification = spec_options.first
@@ -104,6 +142,7 @@ module Dependabot
104
142
  # rubocop:enable Metrics/CyclomaticComplexity
105
143
  # rubocop:enable Metrics/PerceivedComplexity
106
144
 
145
+ sig { returns(String) }
107
146
  def dependency_spec
108
147
  return @custom_specification if @custom_specification
109
148
 
@@ -118,26 +157,30 @@ module Dependabot
118
157
  spec
119
158
  end
120
159
 
160
+ sig { returns(T.nilable(String)) }
121
161
  def git_previous_version
122
162
  TomlRB.parse(lockfile.content)
123
163
  .fetch("package", [])
124
164
  .select { |p| p["name"] == dependency.name }
125
165
  .find { |p| p["source"].end_with?(dependency.previous_version) }
126
- .fetch("version")
166
+ &.fetch("version")
127
167
  end
128
168
 
169
+ sig { returns(T.nilable(String)) }
129
170
  def git_source_url
130
171
  dependency.previous_requirements
131
- .find { |r| r.dig(:source, :type) == "git" }
172
+ &.find { |r| r.dig(:source, :type) == "git" }
132
173
  &.dig(:source, :url)
133
174
  end
134
175
 
176
+ sig { returns(String) }
135
177
  def desired_lockfile_content
136
- return dependency.version if git_dependency?
178
+ return T.must(dependency.version) if git_dependency?
137
179
 
138
180
  %(name = "#{dependency.name}"\nversion = "#{dependency.version}")
139
181
  end
140
182
 
183
+ sig { params(command: String, fingerprint: String).void }
141
184
  def run_cargo_command(command, fingerprint:)
142
185
  start = Time.now
143
186
  command = SharedHelpers.escape_command(command)
@@ -176,6 +219,7 @@ module Dependabot
176
219
  )
177
220
  end
178
221
 
222
+ sig { params(message: String).returns(T::Boolean) }
179
223
  def using_old_toolchain?(message)
180
224
  return true if message.include?("usage of sparse registries requires `-Z sparse-registry`")
181
225
 
@@ -185,18 +229,20 @@ module Dependabot
185
229
  version_class.new(version_log[:version]) < version_class.new("1.68")
186
230
  end
187
231
 
232
+ sig { void }
188
233
  def write_temporary_dependency_files
189
234
  write_temporary_manifest_files
190
235
  write_temporary_path_dependency_files
191
236
 
192
237
  File.write(lockfile.name, lockfile.content)
193
- File.write(toolchain.name, toolchain.content) if toolchain
238
+ File.write(T.must(toolchain).name, T.must(toolchain).content) if toolchain
194
239
  return unless config
195
240
 
196
- FileUtils.mkdir_p(File.dirname(config.name))
197
- File.write(config.name, config.content)
241
+ FileUtils.mkdir_p(File.dirname(T.must(config).name))
242
+ File.write(T.must(config).name, T.must(config).content)
198
243
  end
199
244
 
245
+ sig { void }
200
246
  def write_temporary_manifest_files
201
247
  manifest_files.each do |file|
202
248
  path = file.name
@@ -214,6 +260,7 @@ module Dependabot
214
260
  end
215
261
  end
216
262
 
263
+ sig { void }
217
264
  def write_temporary_path_dependency_files
218
265
  path_dependency_files.each do |file|
219
266
  path = file.name
@@ -227,6 +274,7 @@ module Dependabot
227
274
  end
228
275
  end
229
276
 
277
+ sig { params(file: Dependabot::DependencyFile).returns(String) }
230
278
  def prepared_manifest_content(file)
231
279
  content = updated_manifest_content(file)
232
280
  content = pin_version(content) unless git_dependency?
@@ -236,12 +284,14 @@ module Dependabot
236
284
  content
237
285
  end
238
286
 
287
+ sig { params(file: Dependabot::DependencyFile).returns(String) }
239
288
  def prepared_path_dependency_content(file)
240
- content = file.content.dup
289
+ content = T.must(file.content).dup
241
290
  content = replace_ssh_urls(content)
242
291
  content
243
292
  end
244
293
 
294
+ sig { params(file: Dependabot::DependencyFile).returns(String) }
245
295
  def updated_manifest_content(file)
246
296
  ManifestUpdater.new(
247
297
  dependencies: dependencies,
@@ -249,6 +299,7 @@ module Dependabot
249
299
  ).updated_manifest_content
250
300
  end
251
301
 
302
+ sig { params(content: String).returns(String) }
252
303
  def pin_version(content)
253
304
  parsed_manifest = TomlRB.parse(content)
254
305
 
@@ -269,6 +320,7 @@ module Dependabot
269
320
  TomlRB.dump(parsed_manifest)
270
321
  end
271
322
 
323
+ sig { params(parsed_manifest: T::Hash[String, T.untyped]).void }
272
324
  def pin_target_specific_dependencies!(parsed_manifest)
273
325
  parsed_manifest.fetch("target", {}).each do |target, t_details|
274
326
  Cargo::FileParser::DEPENDENCY_TYPES.each do |type|
@@ -288,6 +340,7 @@ module Dependabot
288
340
  end
289
341
  end
290
342
 
343
+ sig { params(content: String).returns(String) }
291
344
  def replace_ssh_urls(content)
292
345
  git_ssh_requirements_to_swap.each do |ssh_url, https_url|
293
346
  content = content.gsub(ssh_url, https_url)
@@ -295,18 +348,21 @@ module Dependabot
295
348
  content
296
349
  end
297
350
 
351
+ sig { params(content: String).returns(String) }
298
352
  def remove_binary_specifications(content)
299
353
  parsed_manifest = TomlRB.parse(content)
300
354
  parsed_manifest.delete("bin")
301
355
  TomlRB.dump(parsed_manifest)
302
356
  end
303
357
 
358
+ sig { params(content: String).returns(String) }
304
359
  def remove_default_run_specification(content)
305
360
  parsed_manifest = TomlRB.parse(content)
306
361
  parsed_manifest["package"].delete("default-run") if parsed_manifest.dig("package", "default-run")
307
362
  TomlRB.dump(parsed_manifest)
308
363
  end
309
364
 
365
+ sig { params(content: String).returns(String) }
310
366
  def post_process_lockfile(content)
311
367
  git_ssh_requirements_to_swap.each do |ssh_url, https_url|
312
368
  content = content.gsub(https_url, ssh_url)
@@ -316,6 +372,7 @@ module Dependabot
316
372
  content
317
373
  end
318
374
 
375
+ sig { returns(T::Hash[String, String]) }
319
376
  def git_ssh_requirements_to_swap
320
377
  return @git_ssh_requirements_to_swap if @git_ssh_requirements_to_swap
321
378
 
@@ -338,6 +395,7 @@ module Dependabot
338
395
  @git_ssh_requirements_to_swap
339
396
  end
340
397
 
398
+ sig { params(lockfile_content: String).returns(String) }
341
399
  def remove_duplicate_lockfile_entries(lockfile_content)
342
400
  # Loop through the lockfile entries looking for duplicates. Replace
343
401
  # any that are found
@@ -368,10 +426,12 @@ module Dependabot
368
426
  lockfile_content
369
427
  end
370
428
 
429
+ sig { returns(String) }
371
430
  def dummy_app_content
372
431
  %{fn main() {\nprintln!("Hello, world!");\n}}
373
432
  end
374
433
 
434
+ sig { returns(T::Boolean) }
375
435
  def git_dependency?
376
436
  GitCommitChecker.new(
377
437
  dependency: dependency,
@@ -379,6 +439,7 @@ module Dependabot
379
439
  ).git_dependency?
380
440
  end
381
441
 
442
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
382
443
  def manifest_files
383
444
  @manifest_files ||=
384
445
  dependency_files
@@ -386,6 +447,7 @@ module Dependabot
386
447
  .reject(&:support_file?)
387
448
  end
388
449
 
450
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
389
451
  def path_dependency_files
390
452
  @path_dependency_files ||=
391
453
  dependency_files
@@ -393,27 +455,77 @@ module Dependabot
393
455
  .select(&:support_file?)
394
456
  end
395
457
 
458
+ sig { returns(Dependabot::DependencyFile) }
396
459
  def lockfile
397
460
  @lockfile ||= dependency_files.find { |f| f.name == "Cargo.lock" }
461
+ T.must(@lockfile)
398
462
  end
399
463
 
464
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
400
465
  def toolchain
401
466
  @toolchain ||=
402
467
  dependency_files.find { |f| f.name == "rust-toolchain" }
403
468
  end
404
469
 
470
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
405
471
  def config
406
472
  @config ||= dependency_files.find { |f| f.name == ".cargo/config.toml" }
407
473
  end
408
474
 
475
+ sig { params(file: Dependabot::DependencyFile).returns(T::Boolean) }
409
476
  def virtual_manifest?(file)
410
- !file.content.include?("[package]")
477
+ !T.must(file.content).include?("[package]")
411
478
  end
412
479
 
480
+ sig { returns(T.class_of(Gem::Version)) }
413
481
  def version_class
414
482
  dependency.version_class
415
483
  end
484
+
485
+ sig { params(lockfile_content: String, dependency: Dependabot::Dependency).returns(T::Boolean) }
486
+ def dependency_updated?(lockfile_content, dependency)
487
+ return false unless dependency.previous_version
488
+
489
+ # For multiple versions case, we need to check the specific entry
490
+ # that corresponds to our dependency (the one used by our package)
491
+ entries = T.let([], T::Array[String])
492
+ lockfile_content.scan(LOCKFILE_ENTRY_REGEX) do
493
+ entries << Regexp.last_match.to_s
494
+ end
495
+ entries.select! { |entry| entry.include?("name = \"#{dependency.name}\"") }
496
+
497
+ # Check if any entry has a version newer than the previous version
498
+ entries.any? do |entry|
499
+ version_match = entry.match(/version = "([^"]+)"/)
500
+ next false unless version_match
501
+
502
+ new_version = version_match[1]
503
+ # Only consider it updated if it's newer than the previous version
504
+ # and either matches our expected version or is at least newer than previous
505
+ dependency.version_class.new(new_version) > dependency.version_class.new(dependency.previous_version)
506
+ end
507
+ end
508
+
509
+ sig { params(lockfile_content: String, dependency_name: String).returns(T.nilable(String)) }
510
+ def extract_actual_version(lockfile_content, dependency_name)
511
+ entries = T.let([], T::Array[String])
512
+ lockfile_content.scan(LOCKFILE_ENTRY_REGEX) do
513
+ entries << Regexp.last_match.to_s
514
+ end
515
+ entries.select! { |entry| entry.include?("name = \"#{dependency_name}\"") }
516
+
517
+ # Get the highest version from matching entries
518
+ versions = entries.filter_map do |entry|
519
+ version_match = entry.match(/version = "([^"]+)"/)
520
+ version_match&.captures&.first
521
+ end
522
+
523
+ return nil if versions.empty?
524
+
525
+ versions.max_by { |v| version_class.new(v) }
526
+ end
416
527
  end
528
+ # rubocop:enable Metrics/ClassLength
417
529
  end
418
530
  end
419
531
  end
@@ -56,7 +56,7 @@ module Dependabot
56
56
 
57
57
  sig { override.returns(T::Boolean) }
58
58
  def cooldown_enabled?
59
- Dependabot::Experiments.enabled?(:enable_cooldown_for_cargo)
59
+ true
60
60
  end
61
61
 
62
62
  private
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-cargo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.320.0
4
+ version: 0.321.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.320.0
18
+ version: 0.321.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.320.0
25
+ version: 0.321.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: debug
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -265,7 +265,7 @@ licenses:
265
265
  - MIT
266
266
  metadata:
267
267
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
268
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.320.0
268
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.321.0
269
269
  rdoc_options: []
270
270
  require_paths:
271
271
  - lib