dependabot-cargo 0.340.0 → 0.342.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.
- checksums.yaml +4 -4
- data/lib/dependabot/cargo/file_fetcher.rb +63 -30
- data/lib/dependabot/cargo/file_updater/lockfile_updater.rb +82 -17
- data/lib/dependabot/cargo/file_updater.rb +0 -8
- data/lib/dependabot/cargo/update_checker/file_preparer.rb +19 -0
- data/lib/dependabot/cargo/update_checker/version_resolver.rb +86 -42
- data/lib/dependabot/cargo/update_checker.rb +14 -1
- data/lib/dependabot/cargo/version.rb +116 -0
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 24474e4539bb3a8547a639f5a16737166f4d0e329c8b8d857245a2133719282e
|
|
4
|
+
data.tar.gz: fe51accf4820675950bbf0837704608f998a66cf67ccc5d0c4c405f400cea3b8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3b8448fe1beb3afd164187c8240c7dc67c7ae7ff07d67fbc7b112dd066b6ec6838af4d5c79024333261dc1240941b72ce05765b44581b189ac600c6c2034e3fa
|
|
7
|
+
data.tar.gz: d6a9626c6e705b73597dbe0cd94e5b95e3bbdaa532c98d7f2826f978095267e4ffadb7ae369b9ef01807f05ddf679638abde40ccbb337304e776af01ea7bc62d
|
|
@@ -57,6 +57,13 @@ module Dependabot
|
|
|
57
57
|
fetched_files << T.must(rust_toolchain) if rust_toolchain
|
|
58
58
|
fetched_files += fetch_path_dependency_and_workspace_files
|
|
59
59
|
|
|
60
|
+
# If the main Cargo.toml uses workspace dependencies, ensure we have the workspace root
|
|
61
|
+
parsed_manifest = parsed_file(cargo_toml)
|
|
62
|
+
if uses_workspace_dependencies?(parsed_manifest) || workspace_member?(parsed_manifest)
|
|
63
|
+
workspace_root = find_workspace_root(cargo_toml)
|
|
64
|
+
fetched_files << workspace_root if workspace_root && !fetched_files.include?(workspace_root)
|
|
65
|
+
end
|
|
66
|
+
|
|
60
67
|
# Filter excluded files from final collection
|
|
61
68
|
filtered_files = fetched_files.reject do |file|
|
|
62
69
|
Dependabot::FileFiltering.should_exclude_path?(file.name, "file from final collection", @exclude_paths)
|
|
@@ -131,17 +138,17 @@ module Dependabot
|
|
|
131
138
|
|
|
132
139
|
next if previously_fetched_files.map(&:name).include?(path)
|
|
133
140
|
next if file.name == path
|
|
134
|
-
|
|
135
141
|
next if Dependabot::FileFiltering.should_exclude_path?(path, "file from final collection", @exclude_paths)
|
|
136
142
|
|
|
137
143
|
fetched_file = fetch_file_from_host(path, fetch_submodules: true)
|
|
138
144
|
previously_fetched_files << fetched_file
|
|
139
|
-
grandchild_requirement_files =
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
+
grandchild_requirement_files = fetch_workspace_files(
|
|
146
|
+
file: fetched_file,
|
|
147
|
+
previously_fetched_files: previously_fetched_files
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
workspace_root = workspace_root_for_file(fetched_file)
|
|
151
|
+
[fetched_file, *grandchild_requirement_files, workspace_root]
|
|
145
152
|
end.compact
|
|
146
153
|
|
|
147
154
|
files.each { |f| f.support_file = file != cargo_toml }
|
|
@@ -168,24 +175,18 @@ module Dependabot
|
|
|
168
175
|
|
|
169
176
|
next if previously_fetched_files.map(&:name).include?(path)
|
|
170
177
|
next if file.name == path
|
|
171
|
-
|
|
172
178
|
next if Dependabot::FileFiltering.should_exclude_path?(path, "file from final collection", @exclude_paths)
|
|
173
179
|
|
|
174
180
|
fetched_file = fetch_file_from_host(path, fetch_submodules: true)
|
|
175
181
|
.tap { |f| f.support_file = true }
|
|
176
182
|
previously_fetched_files << fetched_file
|
|
177
|
-
grandchild_requirement_files =
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
# its root workspace, we search for the root to include it so Cargo can
|
|
185
|
-
# resolve the path dependency file manifest properly.
|
|
186
|
-
root = find_workspace_root(fetched_file) if workspace_member?(parsed_file(fetched_file))
|
|
187
|
-
|
|
188
|
-
[fetched_file, *grandchild_requirement_files, root]
|
|
183
|
+
grandchild_requirement_files = fetch_path_dependency_files(
|
|
184
|
+
file: fetched_file,
|
|
185
|
+
previously_fetched_files: previously_fetched_files
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
workspace_root = workspace_root_for_file(fetched_file)
|
|
189
|
+
[fetched_file, *grandchild_requirement_files, workspace_root]
|
|
189
190
|
rescue Dependabot::DependencyFileNotFound
|
|
190
191
|
next unless required_path?(file, path)
|
|
191
192
|
|
|
@@ -198,15 +199,21 @@ module Dependabot
|
|
|
198
199
|
unfetchable_required_path_deps
|
|
199
200
|
end
|
|
200
201
|
|
|
202
|
+
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(Dependabot::DependencyFile)) }
|
|
203
|
+
def workspace_root_for_file(file)
|
|
204
|
+
parsed_manifest = parsed_file(file)
|
|
205
|
+
return unless workspace_member?(parsed_manifest) || uses_workspace_dependencies?(parsed_manifest)
|
|
206
|
+
|
|
207
|
+
find_workspace_root(file)
|
|
208
|
+
end
|
|
209
|
+
|
|
201
210
|
sig { params(dependencies: T::Hash[T.untyped, T.untyped]).returns(T::Array[String]) }
|
|
202
211
|
def collect_path_dependencies_paths(dependencies)
|
|
203
|
-
|
|
204
|
-
dependencies.each do |_, details|
|
|
212
|
+
dependencies.filter_map do |_, details|
|
|
205
213
|
next unless details.is_a?(Hash) && details["path"]
|
|
206
214
|
|
|
207
|
-
|
|
215
|
+
File.join(details["path"], "Cargo.toml").delete_prefix("/")
|
|
208
216
|
end
|
|
209
|
-
paths
|
|
210
217
|
end
|
|
211
218
|
|
|
212
219
|
# rubocop:enable Metrics/PerceivedComplexity
|
|
@@ -229,8 +236,7 @@ module Dependabot
|
|
|
229
236
|
end
|
|
230
237
|
end
|
|
231
238
|
|
|
232
|
-
paths
|
|
233
|
-
paths
|
|
239
|
+
paths + replacement_path_dependency_paths_from_file(file)
|
|
234
240
|
end
|
|
235
241
|
|
|
236
242
|
sig { params(file: Dependabot::DependencyFile).returns(T::Array[String]) }
|
|
@@ -239,8 +245,7 @@ module Dependabot
|
|
|
239
245
|
|
|
240
246
|
# Paths specified as replacements
|
|
241
247
|
parsed_file(file).fetch("replace", {}).each do |_, details|
|
|
242
|
-
next unless details.is_a?(Hash)
|
|
243
|
-
next unless details["path"]
|
|
248
|
+
next unless details.is_a?(Hash) && details["path"]
|
|
244
249
|
|
|
245
250
|
paths << File.join(details["path"], "Cargo.toml")
|
|
246
251
|
end
|
|
@@ -250,8 +255,7 @@ module Dependabot
|
|
|
250
255
|
next unless details.is_a?(Hash)
|
|
251
256
|
|
|
252
257
|
details.each do |_, dep_details|
|
|
253
|
-
next unless dep_details.is_a?(Hash)
|
|
254
|
-
next unless dep_details["path"]
|
|
258
|
+
next unless dep_details.is_a?(Hash) && dep_details["path"]
|
|
255
259
|
|
|
256
260
|
paths << File.join(dep_details["path"], "Cargo.toml")
|
|
257
261
|
end
|
|
@@ -260,6 +264,35 @@ module Dependabot
|
|
|
260
264
|
paths
|
|
261
265
|
end
|
|
262
266
|
|
|
267
|
+
# Check if this Cargo manifest uses workspace dependencies
|
|
268
|
+
# (e.g. dependency = { workspace = true }).
|
|
269
|
+
sig { params(parsed_manifest: T::Hash[T.untyped, T.untyped]).returns(T::Boolean) }
|
|
270
|
+
def uses_workspace_dependencies?(parsed_manifest)
|
|
271
|
+
# Check regular dependencies
|
|
272
|
+
workspace_deps = Cargo::FileParser::DEPENDENCY_TYPES.any? do |type|
|
|
273
|
+
deps = parsed_manifest.fetch(type, {})
|
|
274
|
+
deps.any? do |_, details|
|
|
275
|
+
next false unless details.is_a?(Hash)
|
|
276
|
+
|
|
277
|
+
details["workspace"] == true
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
return true if workspace_deps
|
|
282
|
+
|
|
283
|
+
# Check target-specific dependencies
|
|
284
|
+
parsed_manifest.fetch("target", {}).any? do |_, target_details|
|
|
285
|
+
Cargo::FileParser::DEPENDENCY_TYPES.any? do |type|
|
|
286
|
+
deps = target_details.fetch(type, {})
|
|
287
|
+
deps.any? do |_, details|
|
|
288
|
+
next false unless details.is_a?(Hash)
|
|
289
|
+
|
|
290
|
+
details["workspace"] == true
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
263
296
|
# See if this Cargo manifest inherits any property from a workspace
|
|
264
297
|
# (e.g. edition = { workspace = true }).
|
|
265
298
|
sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Boolean) }
|
|
@@ -99,12 +99,44 @@ module Dependabot
|
|
|
99
99
|
|
|
100
100
|
sig { params(error: StandardError).returns(T.noreturn) }
|
|
101
101
|
def handle_cargo_error(error)
|
|
102
|
-
raise unless error.message
|
|
103
|
-
error.message.include?("no matching version") ||
|
|
104
|
-
error.message.include?("unexpected end of input while parsing major version number")
|
|
102
|
+
raise unless resolvable_cargo_error?(error.message)
|
|
105
103
|
raise if error.message.include?("`#{dependency.name} ")
|
|
106
104
|
|
|
107
|
-
|
|
105
|
+
extract_binary_path_error(error.message)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
sig { params(message: String).returns(T::Boolean) }
|
|
109
|
+
def resolvable_cargo_error?(message)
|
|
110
|
+
message.include?("failed to select a version") ||
|
|
111
|
+
message.include?("no matching version") ||
|
|
112
|
+
message.include?("unexpected end of input while parsing major version number") ||
|
|
113
|
+
message.match?(/couldn't find `[^`]+\.rs`/) ||
|
|
114
|
+
message.match?(/failed to find `[^`]+\.rs`/) ||
|
|
115
|
+
message.match?(/could not find `[^`]+\.rs`/) ||
|
|
116
|
+
message.match?(/cannot find binary `[^`]+`/) ||
|
|
117
|
+
message.include?("Please specify bin.path if you want to use a non-default path") ||
|
|
118
|
+
message.include?("binary target")
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
sig { params(message: String).returns(T.noreturn) }
|
|
122
|
+
def extract_binary_path_error(message)
|
|
123
|
+
if (match = message.match(/can't find `([^`]+)` bin at `([^`]+)`/))
|
|
124
|
+
binary_name = match[1]
|
|
125
|
+
expected_path = match[2]
|
|
126
|
+
raise Dependabot::DependencyFileNotResolvable,
|
|
127
|
+
"Binary '#{binary_name}' not found at expected path '#{expected_path}'. " \
|
|
128
|
+
"Please check the bin.path configuration in Cargo.toml."
|
|
129
|
+
elsif (match = message.match(/(couldn't find|failed to find|could not find) `([^`]+\.rs)`/))
|
|
130
|
+
file_path = match[2]
|
|
131
|
+
raise Dependabot::DependencyFileNotResolvable,
|
|
132
|
+
"Source file '#{file_path}' not found. Please check the bin.path configuration in Cargo.toml."
|
|
133
|
+
elsif (match = message.match(/cannot find binary `([^`]+)`/))
|
|
134
|
+
binary_name = match[1]
|
|
135
|
+
raise Dependabot::DependencyFileNotResolvable,
|
|
136
|
+
"Binary target '#{binary_name}' not found. Please check the [[bin]] configuration in Cargo.toml."
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
raise Dependabot::DependencyFileNotResolvable, message
|
|
108
140
|
end
|
|
109
141
|
|
|
110
142
|
# rubocop:disable Metrics/PerceivedComplexity
|
|
@@ -186,28 +218,24 @@ module Dependabot
|
|
|
186
218
|
start = Time.now
|
|
187
219
|
command = SharedHelpers.escape_command(command)
|
|
188
220
|
Helpers.setup_credentials_in_environment(credentials)
|
|
189
|
-
# Pass through any registry tokens supplied via CARGO_REGISTRIES_...
|
|
190
|
-
# environment variables.
|
|
191
221
|
env = ENV.select { |key, _value| key.match(/^CARGO_REGISTRIES_/) }
|
|
192
222
|
stdout, process = Open3.capture2e(env, command)
|
|
193
223
|
time_taken = Time.now - start
|
|
194
224
|
|
|
195
|
-
# Raise an error with the output from the shell session if Cargo
|
|
196
|
-
# returns a non-zero status
|
|
197
225
|
return if process.success?
|
|
198
226
|
|
|
227
|
+
handle_cargo_command_error(stdout, command, fingerprint, time_taken)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
sig { params(stdout: String, command: String, fingerprint: String, time_taken: Float).returns(T.noreturn) }
|
|
231
|
+
def handle_cargo_command_error(stdout, command, fingerprint, time_taken)
|
|
199
232
|
if using_old_toolchain?(stdout)
|
|
200
233
|
raise Dependabot::DependencyFileNotEvaluatable, "Dependabot only supports toolchain 1.68 and up."
|
|
201
234
|
end
|
|
202
235
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
if (match = /error: no matching package found\nsearched package name: `([^`]+)`/m.match(stdout))
|
|
209
|
-
raise Dependabot::DependencyFileNotResolvable, match[1]
|
|
210
|
-
end
|
|
236
|
+
check_ambiguous_package_error(stdout)
|
|
237
|
+
check_missing_package_error(stdout)
|
|
238
|
+
check_binary_path_error(stdout)
|
|
211
239
|
|
|
212
240
|
raise SharedHelpers::HelperSubprocessFailed.new(
|
|
213
241
|
message: stdout,
|
|
@@ -215,11 +243,48 @@ module Dependabot
|
|
|
215
243
|
command: command,
|
|
216
244
|
fingerprint: fingerprint,
|
|
217
245
|
time_taken: time_taken,
|
|
218
|
-
process_exit_value:
|
|
246
|
+
process_exit_value: "non-zero"
|
|
219
247
|
}
|
|
220
248
|
)
|
|
221
249
|
end
|
|
222
250
|
|
|
251
|
+
sig { params(stdout: String).void }
|
|
252
|
+
def check_ambiguous_package_error(stdout)
|
|
253
|
+
ambiguous_match = stdout.match(/There are multiple `([^`]+)` packages.*specification `([^`]+)` is ambiguous/)
|
|
254
|
+
return unless ambiguous_match
|
|
255
|
+
|
|
256
|
+
raise Dependabot::DependencyFileNotEvaluatable, "Ambiguous package specification: #{ambiguous_match[2]}"
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
sig { params(stdout: String).void }
|
|
260
|
+
def check_missing_package_error(stdout)
|
|
261
|
+
if (match = stdout.match(/no matching package named `([^`]+)` found/))
|
|
262
|
+
raise Dependabot::DependencyFileNotResolvable, match[1]
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
if (match = /error: no matching package found\nsearched package name: `([^`]+)`/m.match(stdout))
|
|
266
|
+
raise Dependabot::DependencyFileNotResolvable, match[1]
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
sig { params(stdout: String).void }
|
|
271
|
+
def check_binary_path_error(stdout)
|
|
272
|
+
return unless binary_path_error?(stdout)
|
|
273
|
+
|
|
274
|
+
extract_binary_path_error(stdout)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
sig { params(stdout: String).returns(T::Boolean) }
|
|
278
|
+
def binary_path_error?(stdout)
|
|
279
|
+
stdout.match?(/couldn't find `[^`]+\.rs`/) ||
|
|
280
|
+
stdout.match?(/failed to find `[^`]+\.rs`/) ||
|
|
281
|
+
stdout.match?(/could not find `[^`]+\.rs`/) ||
|
|
282
|
+
stdout.match?(/cannot find binary `[^`]+`/) ||
|
|
283
|
+
stdout.match?(/binary target `[^`]+` not found/) ||
|
|
284
|
+
stdout.include?("Please specify bin.path if you want to use a non-default path") ||
|
|
285
|
+
(stdout.include?("binary target") && stdout.include?("not found"))
|
|
286
|
+
end
|
|
287
|
+
|
|
223
288
|
sig { params(message: String).returns(T::Boolean) }
|
|
224
289
|
def using_old_toolchain?(message)
|
|
225
290
|
return true if message.include?("usage of sparse registries requires `-Z sparse-registry`")
|
|
@@ -18,14 +18,6 @@ module Dependabot
|
|
|
18
18
|
require_relative "file_updater/lockfile_updater"
|
|
19
19
|
require_relative "file_updater/workspace_manifest_updater"
|
|
20
20
|
|
|
21
|
-
sig { override.returns(T::Array[Regexp]) }
|
|
22
|
-
def self.updated_files_regex
|
|
23
|
-
[
|
|
24
|
-
/Cargo\.toml$/, # Matches Cargo.toml in the root directory or any subdirectory
|
|
25
|
-
/Cargo\.lock$/ # Matches Cargo.lock in the root directory or any subdirectory
|
|
26
|
-
]
|
|
27
|
-
end
|
|
28
|
-
|
|
29
21
|
sig { override.returns(T::Array[Dependabot::DependencyFile]) }
|
|
30
22
|
def updated_dependency_files
|
|
31
23
|
# Returns an array of updated files. Only files that have been updated
|
|
@@ -118,6 +118,7 @@ module Dependabot
|
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
replace_req_on_target_specific_deps!(parsed_manifest, filename)
|
|
121
|
+
replace_req_on_workspace_deps!(parsed_manifest, filename)
|
|
121
122
|
|
|
122
123
|
TomlRB.dump(parsed_manifest)
|
|
123
124
|
end
|
|
@@ -148,6 +149,24 @@ module Dependabot
|
|
|
148
149
|
end
|
|
149
150
|
end
|
|
150
151
|
|
|
152
|
+
sig { params(parsed_manifest: T::Hash[String, T.untyped], filename: String).void }
|
|
153
|
+
def replace_req_on_workspace_deps!(parsed_manifest, filename)
|
|
154
|
+
workspace = parsed_manifest.fetch("workspace", {})
|
|
155
|
+
workspace_deps = workspace.fetch("dependencies", {})
|
|
156
|
+
|
|
157
|
+
workspace_deps.each do |name, req|
|
|
158
|
+
next unless dependency.name == name_from_declaration(name, req)
|
|
159
|
+
|
|
160
|
+
updated_req = temporary_requirement_for_resolution(filename)
|
|
161
|
+
|
|
162
|
+
if req.is_a?(Hash)
|
|
163
|
+
parsed_manifest["workspace"]["dependencies"][name]["version"] = updated_req
|
|
164
|
+
else
|
|
165
|
+
parsed_manifest["workspace"]["dependencies"][name] = updated_req
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
151
170
|
sig { params(content: String).returns(String) }
|
|
152
171
|
def replace_git_pin(content)
|
|
153
172
|
parsed_manifest = TomlRB.parse(content)
|
|
@@ -238,75 +238,100 @@ module Dependabot
|
|
|
238
238
|
raise Dependabot::DependencyFileNotResolvable, msg
|
|
239
239
|
end
|
|
240
240
|
|
|
241
|
-
# rubocop:disable Metrics/AbcSize
|
|
242
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
|
243
241
|
sig { params(error: StandardError).void }
|
|
244
242
|
def handle_cargo_errors(error)
|
|
245
|
-
if
|
|
243
|
+
return if missing_feature_error?(error)
|
|
244
|
+
return if recoverable_workspace_error?(error)
|
|
245
|
+
|
|
246
|
+
handle_git_authentication_errors(error)
|
|
247
|
+
handle_git_reference_errors(error)
|
|
248
|
+
handle_toolchain_errors(error)
|
|
249
|
+
handle_resolvability_errors(error)
|
|
250
|
+
|
|
251
|
+
raise
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
sig { params(error: StandardError).returns(T::Boolean) }
|
|
255
|
+
def missing_feature_error?(error)
|
|
256
|
+
if error.message.include?("does not have these features") ||
|
|
257
|
+
error.message.include?("does not have that feature")
|
|
246
258
|
# TODO: Ideally we should update the declaration not to ask
|
|
247
259
|
# for the specified features
|
|
248
|
-
return
|
|
260
|
+
return true
|
|
249
261
|
end
|
|
250
262
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
263
|
+
false
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
sig { params(error: StandardError).void }
|
|
267
|
+
def handle_git_authentication_errors(error)
|
|
268
|
+
return unless error.message.include?("authenticate when downloading repo") ||
|
|
269
|
+
error.message.include?("fatal: Authentication failed for")
|
|
270
|
+
|
|
271
|
+
urls = unreachable_git_urls
|
|
272
|
+
|
|
273
|
+
if T.must(urls).none?
|
|
274
|
+
url = T.must(
|
|
275
|
+
T.must(error.message.match(UNABLE_TO_UPDATE))
|
|
276
|
+
.named_captures.fetch("url")
|
|
277
|
+
).split(/[#?]/).first
|
|
278
|
+
raise if T.must(reachable_git_urls).include?(url)
|
|
267
279
|
|
|
268
|
-
|
|
280
|
+
T.must(urls) << T.must(url)
|
|
269
281
|
end
|
|
270
282
|
|
|
283
|
+
raise Dependabot::GitDependenciesNotReachable, T.must(urls)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
sig { params(error: StandardError).void }
|
|
287
|
+
def handle_git_reference_errors(error)
|
|
271
288
|
[BRANCH_NOT_FOUND_REGEX, REF_NOT_FOUND_REGEX, GIT_REF_NOT_FOUND_REGEX, NOT_OUR_REF_REGEX].each do |regex|
|
|
272
289
|
next unless error.message.match?(regex)
|
|
273
290
|
|
|
274
291
|
dependency_url = T.must(T.must(error.message.match(regex)).named_captures.fetch("url")).split(/[#?]/).first
|
|
275
|
-
# Fix: Wrap dependency_url in T.must since split().first can return nil
|
|
276
292
|
raise Dependabot::GitDependencyReferenceNotFound, T.must(dependency_url)
|
|
277
293
|
end
|
|
294
|
+
end
|
|
278
295
|
|
|
296
|
+
sig { params(error: StandardError).returns(T::Boolean) }
|
|
297
|
+
def recoverable_workspace_error?(error)
|
|
279
298
|
if workspace_native_library_update_error?(error.message)
|
|
280
299
|
# This happens when we're updating one part of a workspace which
|
|
281
300
|
# triggers an update of a subdependency that uses a native library,
|
|
282
301
|
# whilst leaving another part of the workspace using an older
|
|
283
302
|
# version. Ideally we would prevent the subdependency update.
|
|
284
|
-
return
|
|
303
|
+
return true
|
|
285
304
|
end
|
|
286
305
|
|
|
287
306
|
if git_dependency? && error.message.include?("no matching package")
|
|
288
307
|
# This happens when updating a git dependency whose version has
|
|
289
308
|
# changed from a release to a pre-release version
|
|
290
|
-
return
|
|
309
|
+
return true
|
|
291
310
|
end
|
|
292
311
|
|
|
293
312
|
if error.message.include?("all possible versions conflict")
|
|
294
313
|
# This happens when a top-level requirement locks us to an old
|
|
295
314
|
# patch release of a dependency that is a sub-dep of what we're
|
|
296
315
|
# updating. It's (probably) a Cargo bug.
|
|
297
|
-
return
|
|
316
|
+
return true
|
|
298
317
|
end
|
|
299
318
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
end
|
|
319
|
+
false
|
|
320
|
+
end
|
|
303
321
|
|
|
304
|
-
|
|
322
|
+
sig { params(error: StandardError).void }
|
|
323
|
+
def handle_toolchain_errors(error)
|
|
324
|
+
return unless using_old_toolchain?(error.message)
|
|
305
325
|
|
|
306
|
-
raise
|
|
326
|
+
raise Dependabot::DependencyFileNotEvaluatable, "Dependabot only supports toolchain 1.68 and up."
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
sig { params(error: StandardError).void }
|
|
330
|
+
def handle_resolvability_errors(error)
|
|
331
|
+
return unless resolvability_error?(error.message)
|
|
332
|
+
|
|
333
|
+
raise Dependabot::DependencyFileNotResolvable, error.message
|
|
307
334
|
end
|
|
308
|
-
# rubocop:enable Metrics/AbcSize
|
|
309
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
|
310
335
|
|
|
311
336
|
sig { params(message: T.nilable(String)).returns(T.any(Dependabot::Version, T::Boolean)) }
|
|
312
337
|
def using_old_toolchain?(message)
|
|
@@ -360,20 +385,37 @@ module Dependabot
|
|
|
360
385
|
|
|
361
386
|
sig { params(message: String).returns(T::Boolean) }
|
|
362
387
|
def resolvability_error?(message)
|
|
363
|
-
return true if
|
|
364
|
-
return true if
|
|
365
|
-
return true if message.include?("wasn't a root")
|
|
366
|
-
return true if message.include?("requires a nightly version")
|
|
367
|
-
return true if message.match?(/feature `[^\`]+` is required/)
|
|
368
|
-
return true if message.include?("unexpected end of input while parsing major version number")
|
|
388
|
+
return true if common_resolvability_error?(message)
|
|
389
|
+
return true if binary_path_error?(message)
|
|
369
390
|
|
|
370
391
|
original_requirements_resolvable = original_requirements_resolvable?
|
|
371
|
-
|
|
372
392
|
return false if original_requirements_resolvable == :unknown
|
|
373
393
|
|
|
374
394
|
!original_requirements_resolvable
|
|
375
395
|
end
|
|
376
396
|
|
|
397
|
+
sig { params(message: String).returns(T::Boolean) }
|
|
398
|
+
def common_resolvability_error?(message)
|
|
399
|
+
message.include?("failed to parse lock") ||
|
|
400
|
+
message.include?("believes it's in a workspace") ||
|
|
401
|
+
message.include?("wasn't a root") ||
|
|
402
|
+
message.include?("requires a nightly version") ||
|
|
403
|
+
message.match?(/feature `[^\`]+` is required/) ||
|
|
404
|
+
message.include?("unexpected end of input while parsing major version number")
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
sig { params(message: String).returns(T::Boolean) }
|
|
408
|
+
def binary_path_error?(message)
|
|
409
|
+
message.match?(/couldn't find `[^`]+\.rs`/) ||
|
|
410
|
+
message.match?(/failed to find `[^`]+\.rs`/) ||
|
|
411
|
+
message.match?(/could not find `[^`]+\.rs`/) ||
|
|
412
|
+
message.match?(/cannot find binary `[^`]+`/) ||
|
|
413
|
+
message.match?(/binary target `[^`]+` not found/) ||
|
|
414
|
+
message.include?("Please specify bin.path if you want to use a non-default path") ||
|
|
415
|
+
message.include?("binary target") ||
|
|
416
|
+
message.include?("target not found")
|
|
417
|
+
end
|
|
418
|
+
|
|
377
419
|
sig { returns(T.any(TrueClass, FalseClass, Symbol)) }
|
|
378
420
|
def original_requirements_resolvable?
|
|
379
421
|
base_directory = T.must(original_dependency_files.first).directory
|
|
@@ -421,9 +463,11 @@ module Dependabot
|
|
|
421
463
|
|
|
422
464
|
T.must(manifest_files).each do |file|
|
|
423
465
|
path = file.name
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
466
|
+
# Convert absolute paths to relative paths to avoid permission errors
|
|
467
|
+
relative_path = path.start_with?("/") ? path[1..-1] || "." : path
|
|
468
|
+
dir = Pathname.new(relative_path).dirname
|
|
469
|
+
FileUtils.mkdir_p(dir) unless dir.to_s == "."
|
|
470
|
+
File.write(relative_path, sanitized_manifest_content(T.must(file.content)))
|
|
427
471
|
|
|
428
472
|
next if virtual_manifest?(file)
|
|
429
473
|
|
|
@@ -261,12 +261,25 @@ module Dependabot
|
|
|
261
261
|
latest_allowable_version: latest_version
|
|
262
262
|
).prepared_dependency_files
|
|
263
263
|
|
|
264
|
-
VersionResolver.new(
|
|
264
|
+
result = VersionResolver.new(
|
|
265
265
|
dependency: dependency,
|
|
266
266
|
prepared_dependency_files: prepared_files,
|
|
267
267
|
original_dependency_files: dependency_files,
|
|
268
268
|
credentials: credentials
|
|
269
269
|
).latest_resolvable_version
|
|
270
|
+
|
|
271
|
+
# If the resolver returns a version higher than latest_version, cap it at latest_version
|
|
272
|
+
# This handles cases where the resolver might pick prereleases or incorrect versions
|
|
273
|
+
# Only apply this logic for semantic versions, not git SHAs
|
|
274
|
+
if result && latest_version &&
|
|
275
|
+
!git_dependency? &&
|
|
276
|
+
version_class.correct?(result.to_s) &&
|
|
277
|
+
version_class.correct?(latest_version.to_s) &&
|
|
278
|
+
version_class.new(result.to_s) > version_class.new(latest_version.to_s)
|
|
279
|
+
latest_version
|
|
280
|
+
else
|
|
281
|
+
result
|
|
282
|
+
end
|
|
270
283
|
end
|
|
271
284
|
|
|
272
285
|
sig { returns(T.nilable(T.any(String, Gem::Version))) }
|
|
@@ -43,6 +43,122 @@ module Dependabot
|
|
|
43
43
|
|
|
44
44
|
version.to_s.match?(ANCHORED_VERSION_PATTERN)
|
|
45
45
|
end
|
|
46
|
+
|
|
47
|
+
# Cargo uses a different semantic versioning approach for pre-1.0 versions:
|
|
48
|
+
# - For 0.y.z versions: changes in y are considered major/breaking
|
|
49
|
+
# - For 0.0.z versions: changes in z are considered major/breaking
|
|
50
|
+
# - Only the leftmost non-zero component is considered for compatibility
|
|
51
|
+
|
|
52
|
+
sig { override.returns(T::Array[String]) }
|
|
53
|
+
def ignored_patch_versions
|
|
54
|
+
parts = to_s.split(".")
|
|
55
|
+
major = parts[0].to_i
|
|
56
|
+
minor = parts[1].to_i
|
|
57
|
+
|
|
58
|
+
# For 0.0.z versions, patch changes are breaking, so treat as major
|
|
59
|
+
return ignored_major_versions if major.zero? && minor.zero?
|
|
60
|
+
|
|
61
|
+
# For 0.y.z versions, patch changes are compatible, use standard logic
|
|
62
|
+
return super if major.zero?
|
|
63
|
+
|
|
64
|
+
# For 1.y.z+ versions, use standard semantic versioning
|
|
65
|
+
super
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
sig { override.returns(T::Array[String]) }
|
|
69
|
+
def ignored_minor_versions
|
|
70
|
+
parts = to_s.split(".")
|
|
71
|
+
major = parts[0].to_i
|
|
72
|
+
|
|
73
|
+
# For 0.y.z versions, minor changes are breaking, so treat as major
|
|
74
|
+
return ignored_major_versions if major.zero?
|
|
75
|
+
|
|
76
|
+
# For 1.y.z+ versions, use standard semantic versioning
|
|
77
|
+
super
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Determines the correct update type for a version change according to Cargo's semantic versioning rules
|
|
81
|
+
# For pre-1.0 versions, Cargo treats changes in the leftmost non-zero component as breaking
|
|
82
|
+
sig { params(from_version: T.any(String, Dependabot::Cargo::Version), to_version: T.any(String, Dependabot::Cargo::Version)).returns(String) }
|
|
83
|
+
def self.update_type(from_version, to_version)
|
|
84
|
+
from_v, to_v = normalize_versions(from_version, to_version)
|
|
85
|
+
from_major, from_minor, from_patch, to_major, to_minor, to_patch = extract_version_parts(from_v, to_v)
|
|
86
|
+
|
|
87
|
+
# Standard semver for 1.0.0+ versions
|
|
88
|
+
return standard_semver_type(from_major, from_minor, from_patch, to_major, to_minor, to_patch) if from_major >= 1
|
|
89
|
+
|
|
90
|
+
# Cargo pre-1.0 semver rules
|
|
91
|
+
cargo_pre_1_0_type(from_major, from_minor, from_patch, to_major, to_minor, to_patch)
|
|
92
|
+
rescue StandardError => e
|
|
93
|
+
# Log the error but return a safe default
|
|
94
|
+
Dependabot.logger.warn("Error in Cargo::Version.update_type: #{e.message}")
|
|
95
|
+
"major" # Default to major for safety
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
sig do
|
|
99
|
+
params(
|
|
100
|
+
from_version: T.any(String, Dependabot::Cargo::Version),
|
|
101
|
+
to_version: T.any(String, Dependabot::Cargo::Version)
|
|
102
|
+
).returns([Dependabot::Cargo::Version, Dependabot::Cargo::Version])
|
|
103
|
+
end
|
|
104
|
+
def self.normalize_versions(from_version, to_version)
|
|
105
|
+
from_v = from_version.is_a?(String) ? T.cast(new(from_version), Dependabot::Cargo::Version) : from_version
|
|
106
|
+
to_v = to_version.is_a?(String) ? T.cast(new(to_version), Dependabot::Cargo::Version) : to_version
|
|
107
|
+
[from_v, to_v]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
sig { params(from_v: Dependabot::Cargo::Version, to_v: Dependabot::Cargo::Version).returns([Integer, Integer, Integer, Integer, Integer, Integer]) }
|
|
111
|
+
def self.extract_version_parts(from_v, to_v)
|
|
112
|
+
from_parts = from_v.to_s.split(".").map(&:to_i)
|
|
113
|
+
to_parts = to_v.to_s.split(".").map(&:to_i)
|
|
114
|
+
|
|
115
|
+
[from_parts[0] || 0, from_parts[1] || 0, from_parts[2] || 0,
|
|
116
|
+
to_parts[0] || 0, to_parts[1] || 0, to_parts[2] || 0]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# rubocop:disable Metrics/ParameterLists
|
|
120
|
+
sig do
|
|
121
|
+
params(
|
|
122
|
+
from_major: Integer,
|
|
123
|
+
from_minor: Integer,
|
|
124
|
+
from_patch: Integer,
|
|
125
|
+
to_major: Integer,
|
|
126
|
+
to_minor: Integer,
|
|
127
|
+
to_patch: Integer
|
|
128
|
+
).returns(String)
|
|
129
|
+
end
|
|
130
|
+
def self.standard_semver_type(from_major, from_minor, from_patch, to_major, to_minor, to_patch)
|
|
131
|
+
return "major" if to_major > from_major
|
|
132
|
+
return "minor" if to_minor > from_minor
|
|
133
|
+
return "patch" if to_patch > from_patch
|
|
134
|
+
|
|
135
|
+
"patch"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
sig do
|
|
139
|
+
params(
|
|
140
|
+
from_major: Integer,
|
|
141
|
+
from_minor: Integer,
|
|
142
|
+
from_patch: Integer,
|
|
143
|
+
to_major: Integer,
|
|
144
|
+
to_minor: Integer,
|
|
145
|
+
to_patch: Integer
|
|
146
|
+
).returns(String)
|
|
147
|
+
end
|
|
148
|
+
def self.cargo_pre_1_0_type(from_major, from_minor, from_patch, to_major, to_minor, to_patch)
|
|
149
|
+
# Any major version increase is always major
|
|
150
|
+
return "major" if to_major > from_major
|
|
151
|
+
|
|
152
|
+
# For 0.0.z versions, any change is breaking
|
|
153
|
+
return "major" if from_minor.zero? && (to_minor > from_minor || to_patch > from_patch)
|
|
154
|
+
|
|
155
|
+
# For 0.y.z versions, minor changes are breaking
|
|
156
|
+
return "major" if to_minor > from_minor
|
|
157
|
+
|
|
158
|
+
# Only patch changes remain
|
|
159
|
+
"patch"
|
|
160
|
+
end
|
|
161
|
+
# rubocop:enable Metrics/ParameterLists
|
|
46
162
|
end
|
|
47
163
|
end
|
|
48
164
|
end
|
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.
|
|
4
|
+
version: 0.342.1
|
|
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.
|
|
18
|
+
version: 0.342.1
|
|
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.
|
|
25
|
+
version: 0.342.1
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: debug
|
|
28
28
|
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.
|
|
269
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.342.1
|
|
270
270
|
rdoc_options: []
|
|
271
271
|
require_paths:
|
|
272
272
|
- lib
|