dependabot-cargo 0.394.0 → 0.395.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: 05b8b9151f40c346f78a07209d9880fca5ad0d524c5f0664cd343fa234fcec1e
4
- data.tar.gz: 3355e45e2e4b29238b096f1e9b171d22d267a2ebdcffe0d38e08250eca79aa1a
3
+ metadata.gz: 9b774b8f0b2b8a0a3fb28c32b85cd5efabfaa4a676f56cd01137046dd0a94363
4
+ data.tar.gz: 2a76d52fcea41bdc97f7774e764cf3c48a2cace477ebe3c4f7bb7ea88ca52873
5
5
  SHA512:
6
- metadata.gz: 7382a767e5b8b3c2d05ded80f913c1e19aa02963a0216ed00d592199ee73354762b7b757e3ea1b7992a25e00e895082b86e4e52381be4f8adf0662e5a26fb748
7
- data.tar.gz: a8f142ebf3dd3f37fb2421e8efe9e807a9141fa70ea1c1cf1effd919af5cee3da0533901d549780cc18441125824a79a088d189f3283b7c5e8aec6906e2033a4
6
+ metadata.gz: 0b9114edb24783f52711d7bd51296872c31d984794c969c8c1d392ea4d61c2883b203c33f14399f0652b51334f2087d01b6e96e5099e2cde60b7d4579f131958
7
+ data.tar.gz: 891667fa1b9ed313360204bb6b1ce23ce653222f3516d23f2e90635eec26fa58c1bec50b3c35b65ccd92f138099747ce7a664bc7e20179d95fe6335247b15e80
@@ -111,7 +111,7 @@ module Dependabot
111
111
  return unless workspace_config
112
112
 
113
113
  msg = "This project is part of a Rust workspace but is not the " \
114
- "workspace root." \
114
+ "workspace root."
115
115
 
116
116
  if cargo_toml&.directory != "/"
117
117
  msg += "Please update your settings so Dependabot points at the " \
@@ -13,7 +13,7 @@ require "dependabot/shared_helpers"
13
13
  module Dependabot
14
14
  module Cargo
15
15
  class FileUpdater
16
- # rubocop:disable Metrics/ClassLength
16
+ # rubocop:disable-next Metrics/ClassLength
17
17
  class LockfileUpdater
18
18
  extend T::Sig
19
19
 
@@ -86,14 +86,69 @@ module Dependabot
86
86
  @current_dependency = dependency_to_update
87
87
  next if previous_line_already_replaced?
88
88
 
89
+ lockfile_before = File.read("Cargo.lock")
89
90
  run_cargo_command(
90
91
  "cargo update -p #{dependency_spec}",
91
92
  fingerprint: "cargo update -p <dependency_spec>"
92
93
  )
94
+
95
+ force_precise_update if precise_update_needed?(lockfile_before)
93
96
  end
94
97
  end
95
98
  end
96
99
 
100
+ # The `--precise` retry is a best-effort recovery, not a hard gate. If
101
+ # the movement detector misfires (e.g. an earlier command already
102
+ # repointed this dependency while another consumer legitimately keeps
103
+ # the old version), forcing `--precise` can fail on the surviving
104
+ # transitive requirement. Swallow that failure, restore the plain
105
+ # result, and let `validate_updates` decide — it tolerates "Cargo
106
+ # selected a different valid version" and fails only on a real no-op.
107
+ sig { void }
108
+ def force_precise_update
109
+ lockfile_before_precise = File.read("Cargo.lock")
110
+ run_cargo_command(
111
+ "cargo update -p #{dependency_spec} --precise #{dependency.version}",
112
+ fingerprint: "cargo update -p <dependency_spec> --precise <version>"
113
+ )
114
+ rescue Dependabot::SharedHelpers::HelperSubprocessFailed => e
115
+ Dependabot.logger.info(
116
+ "Precise fallback for #{dependency.name} failed (#{e.message.lines.first&.strip}); " \
117
+ "keeping the plain update result for validation"
118
+ )
119
+ File.write("Cargo.lock", lockfile_before_precise)
120
+ end
121
+
122
+ # `cargo update -p name:version` won't move a package whose bump
123
+ # requires lockstep updates to sibling crates shared with other members
124
+ # (e.g. the `futures` family: 0.3.34 needs `futures-*` at `^0.3.34`).
125
+ # Cargo leaves the line unchanged and a later validation fails, so we
126
+ # retry with `--precise` to force the exact target and cascade the
127
+ # siblings.
128
+ #
129
+ # Only retry on a genuine no-op. Comparing the dependency's entries
130
+ # *and* its incoming edges before/after the plain command distinguishes
131
+ # a stuck line from one Cargo did resolve — including an edge repointed
132
+ # onto an already-present target entry, where forcing `--precise` would
133
+ # wrongly fail.
134
+ sig { params(lockfile_before: String).returns(T::Boolean) }
135
+ def precise_update_needed?(lockfile_before)
136
+ return false if git_dependency?
137
+
138
+ version = dependency.version
139
+ return false unless version && version_class.correct?(version)
140
+
141
+ previous_version = dependency.previous_version
142
+ return false unless previous_version && version_class.correct?(previous_version)
143
+ return false if previous_version == version
144
+
145
+ lockfile_after = File.read("Cargo.lock")
146
+ return false if dependency_move_signature(lockfile_before, dependency) !=
147
+ dependency_move_signature(lockfile_after, dependency)
148
+
149
+ package_version_count(lockfile_after, dependency, previous_version).positive?
150
+ end
151
+
97
152
  # An earlier command in this run may already have resolved this
98
153
  # dependency's line, in which case `cargo update -p name:version`
99
154
  # would match no package and fail hard. Skip the command when the
@@ -710,6 +765,78 @@ module Dependabot
710
765
  entries
711
766
  end
712
767
 
768
+ # A "did this dependency move?" signature: the dependency's own package
769
+ # identity plus its incoming edges. An edge repointed onto an
770
+ # already-present target entry moves the dependency without changing any
771
+ # `[[package]]` block, so identity alone is not a reliable signal.
772
+ sig do
773
+ params(lockfile_content: String, dependency: Dependabot::Dependency)
774
+ .returns(T::Array[String])
775
+ end
776
+ def dependency_move_signature(lockfile_content, dependency)
777
+ dependency_identity_signature(lockfile_content, dependency) +
778
+ dependency_reference_edges(lockfile_content, dependency)
779
+ end
780
+
781
+ # Identity of each of the dependency's own `[[package]]` blocks, reduced
782
+ # to the fields that define which crate instance is present: `name`,
783
+ # `version` and `source`.
784
+ #
785
+ # This deliberately excludes the block's outgoing `dependencies` array
786
+ # (and checksum): during a grouped update Cargo may add or drop version
787
+ # qualifiers on this crate's own outgoing edges when a sibling starts or
788
+ # stops coexisting, even though this crate itself did not move. Folding
789
+ # the whole block in would misread that as movement and wrongly suppress
790
+ # the `--precise` fallback, leaving the requested version stuck.
791
+ sig do
792
+ params(lockfile_content: String, dependency: Dependabot::Dependency)
793
+ .returns(T::Array[String])
794
+ end
795
+ def dependency_identity_signature(lockfile_content, dependency)
796
+ dependency_lockfile_entries(lockfile_content, dependency).map do |entry|
797
+ entry.lines.filter_map do |line|
798
+ stripped = line.strip
799
+ stripped if stripped.match?(/\A(?:name|version|source) = /)
800
+ end.join("\n")
801
+ end.sort
802
+ end
803
+
804
+ # Incoming edges to the dependency (`"futures"` or `"futures 0.3.33"`
805
+ # inside other packages' `dependencies` arrays), each qualified by the
806
+ # parent package that owns it. Cargo only appends the version when
807
+ # several versions of the crate coexist, so a repointed edge is visible
808
+ # here even when both entries stay in place. Qualifying by parent means a
809
+ # pair of edges swapping targets between two parents (`foo 1` -> `foo 2`
810
+ # in one, `foo 2` -> `foo 1` in another) is still detected as movement
811
+ # rather than cancelling out in a globally-sorted list.
812
+ #
813
+ # The parent is identified by Cargo's full package identity
814
+ # (name/version/source), not just name/version: a lockfile can carry the
815
+ # same name *and* version from different sources (see the
816
+ # `duplicate_source_versions` fixture), and dropping the source would let
817
+ # such a pair swap edges without the signature changing.
818
+ sig do
819
+ params(lockfile_content: String, dependency: Dependabot::Dependency)
820
+ .returns(T::Array[String])
821
+ end
822
+ def dependency_reference_edges(lockfile_content, dependency)
823
+ edge_regex = /\A"#{Regexp.escape(dependency.name)}( [^"]+)?",?\z/
824
+ edges = T.let([], T::Array[String])
825
+ lockfile_content.scan(LOCKFILE_ENTRY_REGEX) do
826
+ block = Regexp.last_match.to_s
827
+ parent_id = [
828
+ block[/^name = "[^"]+"$/],
829
+ block[/^version = "[^"]+"$/],
830
+ block[/^source = "[^"]+"$/]
831
+ ].compact.join(" ")
832
+ block.lines.each do |line|
833
+ stripped = line.strip
834
+ edges << "#{parent_id} => #{stripped}" if stripped.match?(edge_regex)
835
+ end
836
+ end
837
+ edges.sort
838
+ end
839
+
713
840
  # A git dependency can legitimately resolve to a different commit than
714
841
  # the checker expected (e.g. the tracked branch advanced between the
715
842
  # check and this update). Accept the update when the dependency's git
@@ -794,7 +921,6 @@ module Dependabot
794
921
  end
795
922
  end
796
923
  end
797
- # rubocop:enable Metrics/ClassLength
798
924
  end
799
925
  end
800
926
  end
@@ -348,7 +348,7 @@ module Dependabot
348
348
  return unless TomlRB.parse(T.must(cargo_toml).content)["workspace"]
349
349
 
350
350
  msg = "This project is part of a Rust workspace but is not the " \
351
- "workspace root." \
351
+ "workspace root."
352
352
 
353
353
  if T.must(cargo_toml).directory != "/"
354
354
  msg += "Please update your settings so Dependabot points at the " \
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.394.0
4
+ version: 0.395.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.394.0
18
+ version: 0.395.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.394.0
25
+ version: 0.395.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: debug
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -178,19 +178,19 @@ dependencies:
178
178
  - !ruby/object:Gem::Version
179
179
  version: '1.1'
180
180
  - !ruby/object:Gem::Dependency
181
- name: turbo_tests
181
+ name: turbo_tests2
182
182
  requirement: !ruby/object:Gem::Requirement
183
183
  requirements:
184
184
  - - "~>"
185
185
  - !ruby/object:Gem::Version
186
- version: 2.2.5
186
+ version: 3.2.7
187
187
  type: :development
188
188
  prerelease: false
189
189
  version_requirements: !ruby/object:Gem::Requirement
190
190
  requirements:
191
191
  - - "~>"
192
192
  - !ruby/object:Gem::Version
193
- version: 2.2.5
193
+ version: 3.2.7
194
194
  - !ruby/object:Gem::Dependency
195
195
  name: vcr
196
196
  requirement: !ruby/object:Gem::Requirement
@@ -266,7 +266,7 @@ licenses:
266
266
  - MIT
267
267
  metadata:
268
268
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
269
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.394.0
269
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.395.0
270
270
  rdoc_options: []
271
271
  require_paths:
272
272
  - lib