dependabot-uv 0.313.0 → 0.314.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: fbc50316d34c2fb262d3d28ce963efbf368c7d1653a2e0f7ba6819b0c655aab5
4
- data.tar.gz: 1bb339dbce03547b428a62ee5c16421c132dcbacc02adf733c1ed51ba4737a2d
3
+ metadata.gz: f2f78bea59c8500e0193c416cb5e6f2502e37450c34429b642a9d563ee67fdac
4
+ data.tar.gz: 6a802c357783217e10881e46df0e5d55f42fefbfc9bd7a087cb17e97572d5785
5
5
  SHA512:
6
- metadata.gz: e594d5cb529546ebe5c5bbde6c686b4ca50e744f887d1f4f81b8f110bc36cc857cff42afe6497b74b69fb9897162964af6b403999d7c29e3a57c131ca3947e7d
7
- data.tar.gz: bba343a3e2f36e78f4c7eda9c73e6ffee1232beea6d9c80c9157a17a34c3375ea425342c9ca820d2d3c0ff924afcc50877a96681368e5f6b9a9f3552e6e6138f
6
+ metadata.gz: 5695bf2e04719f3aecdf56622074439632ecb715f29fa0ab91059b42e79b17ca603f041c3fabe1475283d12d19fa32b40299bcd3db92ca14e0d5c03cd699a259
7
+ data.tar.gz: 5bb0d5859b3948363da8708e1765695c841a688ea35cbd0bcf1c7afbc937aba47c348b8bd2bcb1093ccc582c674c71b86eae8fc6f4f899b13a4bf5b484fa53b8
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "toml-rb"
@@ -17,33 +17,56 @@ module Dependabot
17
17
  module Uv
18
18
  class FileUpdater
19
19
  class LockFileUpdater
20
+ extend T::Sig
20
21
  require_relative "pyproject_preparer"
21
22
 
22
23
  REQUIRED_FILES = %w(pyproject.toml uv.lock).freeze # At least one of these files should be present
23
24
 
25
+ sig { returns(T::Array[Dependency]) }
24
26
  attr_reader :dependencies
27
+
28
+ sig { returns(T::Array[DependencyFile]) }
25
29
  attr_reader :dependency_files
30
+
31
+ sig { returns(T::Array[Dependabot::Credential]) }
26
32
  attr_reader :credentials
33
+
34
+ sig { returns(T.nilable(T::Array[String])) }
27
35
  attr_reader :index_urls
28
36
 
37
+ sig do
38
+ params(
39
+ dependencies: T::Array[Dependency],
40
+ dependency_files: T::Array[DependencyFile],
41
+ credentials: T::Array[Dependabot::Credential],
42
+ index_urls: T.nilable(T::Array[String])
43
+ ).void
44
+ end
29
45
  def initialize(dependencies:, dependency_files:, credentials:, index_urls: nil)
30
46
  @dependencies = dependencies
31
47
  @dependency_files = dependency_files
32
48
  @credentials = credentials
33
49
  @index_urls = index_urls
50
+ @prepared_pyproject = T.let(nil, T.nilable(String))
51
+ @updated_lockfile_content = T.let(nil, T.nilable(String))
52
+ @pyproject = T.let(nil, T.nilable(Dependabot::DependencyFile))
34
53
  end
35
54
 
55
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
36
56
  def updated_dependency_files
37
- @updated_dependency_files ||= fetch_updated_dependency_files
57
+ @updated_dependency_files ||= T.let(fetch_updated_dependency_files,
58
+ T.nilable(T::Array[Dependabot::DependencyFile]))
38
59
  end
39
60
 
40
61
  private
41
62
 
63
+ sig { returns(T.nilable(Dependabot::Dependency)) }
42
64
  def dependency
43
65
  # For now, we'll only ever be updating a single dependency
44
- dependencies.first
66
+ T.must(dependencies.first)
45
67
  end
46
68
 
69
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
47
70
  def fetch_updated_dependency_files
48
71
  return [] unless create_or_update_lock_file?
49
72
 
@@ -52,32 +75,33 @@ module Dependabot
52
75
  if file_changed?(pyproject)
53
76
  updated_files <<
54
77
  updated_file(
55
- file: pyproject,
56
- content: updated_pyproject_content
78
+ file: T.must(pyproject),
79
+ content: T.must(updated_pyproject_content)
57
80
  )
58
81
  end
59
82
 
60
83
  if lockfile
61
84
  # Use updated_lockfile_content which might raise if the lockfile doesn't change
62
85
  new_content = updated_lockfile_content
63
- raise "Expected lockfile to change!" if lockfile.content == new_content
86
+ raise "Expected lockfile to change!" if T.must(lockfile).content == new_content
64
87
 
65
- updated_files << updated_file(file: lockfile, content: new_content)
88
+ updated_files << updated_file(file: T.must(lockfile), content: new_content)
66
89
  end
67
90
 
68
91
  updated_files
69
92
  end
70
93
 
94
+ sig { returns(T.nilable(String)) }
71
95
  def updated_pyproject_content
72
- content = pyproject.content
73
- return content unless file_changed?(pyproject)
96
+ content = T.must(pyproject).content
97
+ return content unless file_changed?(T.must(pyproject))
74
98
 
75
99
  updated_content = content.dup
76
100
 
77
- dependency.requirements.zip(dependency.previous_requirements).each do |new_r, old_r|
78
- next unless new_r[:file] == pyproject.name && old_r[:file] == pyproject.name
101
+ T.must(dependency).requirements.zip(T.must(T.must(dependency).previous_requirements)).each do |new_r, old_r|
102
+ next unless new_r[:file] == T.must(pyproject).name && T.must(old_r)[:file] == T.must(pyproject).name
79
103
 
80
- updated_content = replace_dep(dependency, updated_content, new_r, old_r)
104
+ updated_content = replace_dep(T.must(dependency), T.must(updated_content), new_r, T.must(old_r))
81
105
  end
82
106
 
83
107
  raise DependencyFileContentNotChanged, "Content did not change!" if content == updated_content
@@ -85,6 +109,14 @@ module Dependabot
85
109
  updated_content
86
110
  end
87
111
 
112
+ sig do
113
+ params(
114
+ dep: Dependabot::Dependency,
115
+ content: String,
116
+ new_r: T::Hash[Symbol, T.untyped],
117
+ old_r: T::Hash[Symbol, T.untyped]
118
+ ).returns(String)
119
+ end
88
120
  def replace_dep(dep, content, new_r, old_r)
89
121
  new_req = new_r[:requirement]
90
122
  old_req = old_r[:requirement]
@@ -93,28 +125,29 @@ module Dependabot
93
125
  declaration_match = content.match(declaration_regex)
94
126
  if declaration_match
95
127
  declaration = declaration_match[:declaration]
96
- new_declaration = declaration.sub(old_req, new_req)
97
- content.sub(declaration, new_declaration)
128
+ new_declaration = T.must(declaration).sub(old_req, new_req)
129
+ content.sub(T.must(declaration), new_declaration)
98
130
  else
99
131
  content
100
132
  end
101
133
  end
102
134
 
135
+ sig { returns(String) }
103
136
  def updated_lockfile_content
104
137
  @updated_lockfile_content ||=
105
138
  begin
106
- original_content = lockfile.content
139
+ original_content = T.must(lockfile).content
107
140
  # Extract the original requires-python value to preserve it
108
- original_requires_python = original_content
109
- .match(/requires-python\s*=\s*["']([^"']+)["']/)&.captures&.first
141
+ original_requires_python = T.must(original_content)
142
+ .match(/requires-python\s*=\s*["']([^"']+)["']/)&.captures&.first
110
143
 
111
144
  # Store the original Python version requirement for later use
112
- @original_python_version = original_requires_python
145
+ @original_python_version = T.let(original_requires_python, T.nilable(String))
113
146
 
114
147
  new_lockfile = updated_lockfile_content_for(prepared_pyproject)
115
148
 
116
149
  # Normalize line endings to ensure proper comparison
117
- new_lockfile = normalize_line_endings(new_lockfile, original_content)
150
+ new_lockfile = normalize_line_endings(new_lockfile, T.must(original_content))
118
151
 
119
152
  result = new_lockfile
120
153
 
@@ -129,6 +162,7 @@ module Dependabot
129
162
  end
130
163
 
131
164
  # Helper method to normalize line endings between two strings
165
+ sig { params(content: String, reference: String).returns(String) }
132
166
  def normalize_line_endings(content, reference)
133
167
  # Check if reference has escaped newlines like "\n" +
134
168
  if reference.include?("\\n")
@@ -138,33 +172,24 @@ module Dependabot
138
172
  end
139
173
  end
140
174
 
141
- def with_original_python_version(original_requires_python)
142
- if original_requires_python
143
- original_python_version = @original_python_version
144
- @original_python_version = original_requires_python
145
- result = yield
146
- @original_python_version = original_python_version
147
- result
148
- else
149
- yield
150
- end
151
- end
152
-
175
+ sig { returns(String) }
153
176
  def prepared_pyproject
154
177
  @prepared_pyproject ||=
155
178
  begin
156
179
  content = updated_pyproject_content
157
- content = sanitize(content)
180
+ content = sanitize(T.must(content))
158
181
  content
159
182
  end
160
183
  end
161
184
 
185
+ sig { params(pyproject_content: String).returns(String) }
162
186
  def sanitize(pyproject_content)
163
187
  PyprojectPreparer
164
188
  .new(pyproject_content: pyproject_content)
165
189
  .sanitize
166
190
  end
167
191
 
192
+ sig { params(pyproject_content: String).returns(String) }
168
193
  def updated_lockfile_content_for(pyproject_content)
169
194
  SharedHelpers.in_a_temporary_directory do
170
195
  SharedHelpers.with_git_configured(credentials: credentials) do
@@ -180,19 +205,25 @@ module Dependabot
180
205
  end
181
206
  end
182
207
 
208
+ sig { returns(T.nilable(String)) }
183
209
  def run_update_command
210
+ options = lock_options
211
+ options_fingerprint = lock_options_fingerprint(options)
212
+
184
213
  # Use pyenv exec to ensure we're using the correct Python environment
185
- command = "pyenv exec uv lock --upgrade-package #{dependency.name}"
186
- fingerprint = "pyenv exec uv lock --upgrade-package <dependency_name>"
214
+ command = "pyenv exec uv lock --upgrade-package #{T.must(dependency).name} #{options}"
215
+ fingerprint = "pyenv exec uv lock --upgrade-package <dependency_name> #{options_fingerprint}"
187
216
 
188
217
  run_command(command, fingerprint:)
189
218
  end
190
219
 
220
+ sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
191
221
  def run_command(command, fingerprint: nil)
192
222
  Dependabot.logger.info("Running command: #{command}")
193
223
  SharedHelpers.run_shell_command(command, fingerprint: fingerprint)
194
224
  end
195
225
 
226
+ sig { params(pyproject_content: String).returns(Integer) }
196
227
  def write_temporary_dependency_files(pyproject_content)
197
228
  dependency_files.each do |file|
198
229
  path = file.name
@@ -204,6 +235,7 @@ module Dependabot
204
235
  File.write("pyproject.toml", pyproject_content)
205
236
  end
206
237
 
238
+ sig { void }
207
239
  def setup_python_environment
208
240
  # Use LanguageVersionManager to determine and install the appropriate Python version
209
241
  Dependabot.logger.info("Setting up Python environment using LanguageVersionManager")
@@ -225,10 +257,12 @@ module Dependabot
225
257
  end
226
258
  end
227
259
 
260
+ sig { params(url: String).returns(String) }
228
261
  def sanitize_env_name(url)
229
262
  url.gsub(%r{^https?://}, "").gsub(/[^a-zA-Z0-9]/, "_").upcase
230
263
  end
231
264
 
265
+ sig { params(dep: T.untyped, old_req: T.untyped).returns(Regexp) }
232
266
  def declaration_regex(dep, old_req)
233
267
  escaped_name = Regexp.escape(dep.name)
234
268
  # Extract the requirement operator and version
@@ -246,10 +280,43 @@ module Dependabot
246
280
  /x
247
281
  end
248
282
 
283
+ sig { returns(String) }
284
+ def lock_options
285
+ options = lock_index_options
286
+
287
+ options.join(" ")
288
+ end
289
+
290
+ sig { returns(T::Array[String]) }
291
+ def lock_index_options
292
+ credentials
293
+ .select { |cred| cred["type"] == "python_index" }
294
+ .map do |cred|
295
+ authed_url = AuthedUrlBuilder.authed_url(credential: cred)
296
+
297
+ if cred.replaces_base?
298
+ "--default-index #{authed_url}"
299
+ else
300
+ "--index #{authed_url}"
301
+ end
302
+ end
303
+ end
304
+
305
+ sig { params(options: String).returns(String) }
306
+ def lock_options_fingerprint(options)
307
+ options.sub(
308
+ /--default-index\s+\S+/, "--default-index <default_index>"
309
+ ).sub(
310
+ /--index\s+\S+/, "--index <index>"
311
+ )
312
+ end
313
+
314
+ sig { params(name: T.any(String, Symbol)).returns(String) }
249
315
  def escape(name)
250
316
  Regexp.escape(name).gsub("\\-", "[-_.]")
251
317
  end
252
318
 
319
+ sig { params(file: T.nilable(DependencyFile)).returns(T::Boolean) }
253
320
  def file_changed?(file)
254
321
  return false unless file
255
322
 
@@ -259,56 +326,71 @@ module Dependabot
259
326
  end
260
327
  end
261
328
 
329
+ sig do
330
+ params(file: T.nilable(DependencyFile), dependency: Dependency)
331
+ .returns(T::Boolean)
332
+ end
262
333
  def requirement_changed?(file, dependency)
263
334
  changed_requirements =
264
- dependency.requirements - dependency.previous_requirements
335
+ dependency.requirements - T.must(dependency.previous_requirements)
265
336
 
266
- changed_requirements.any? { |f| f[:file] == file.name }
337
+ changed_requirements.any? { |f| f[:file] == T.must(file).name }
267
338
  end
268
339
 
340
+ sig { params(file: Dependabot::DependencyFile, content: String).returns(Dependabot::DependencyFile) }
269
341
  def updated_file(file:, content:)
270
342
  updated_file = file.dup
271
343
  updated_file.content = content
272
344
  updated_file
273
345
  end
274
346
 
347
+ sig { params(name: String).returns(String) }
275
348
  def normalise(name)
276
349
  NameNormaliser.normalise(name)
277
350
  end
278
351
 
352
+ sig { returns(Dependabot::Uv::FileParser::PythonRequirementParser) }
279
353
  def python_requirement_parser
280
- @python_requirement_parser ||=
354
+ @python_requirement_parser ||= T.let(
281
355
  FileParser::PythonRequirementParser.new(
282
356
  dependency_files: dependency_files
283
- )
357
+ ), T.nilable(FileParser::PythonRequirementParser)
358
+ )
284
359
  end
285
360
 
361
+ sig { returns(Dependabot::Uv::LanguageVersionManager) }
286
362
  def language_version_manager
287
- @language_version_manager ||=
363
+ @language_version_manager ||= T.let(
288
364
  LanguageVersionManager.new(
289
365
  python_requirement_parser: python_requirement_parser
290
- )
366
+ ), T.nilable(LanguageVersionManager)
367
+ )
291
368
  end
292
369
 
370
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
293
371
  def pyproject
294
- @pyproject ||=
295
- dependency_files.find { |f| f.name == "pyproject.toml" }
372
+ @pyproject ||= T.let(dependency_files.find { |f| f.name == "pyproject.toml" },
373
+ T.nilable(Dependabot::DependencyFile))
296
374
  end
297
375
 
376
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
298
377
  def lockfile
299
- @lockfile ||= uv_lock
378
+ @lockfile ||= T.let(uv_lock, T.nilable(Dependabot::DependencyFile))
300
379
  end
301
380
 
381
+ sig { returns(String) }
302
382
  def python_helper_path
303
383
  NativeHelpers.python_helper_path
304
384
  end
305
385
 
386
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
306
387
  def uv_lock
307
388
  dependency_files.find { |f| f.name == "uv.lock" }
308
389
  end
309
390
 
391
+ sig { returns(T::Boolean) }
310
392
  def create_or_update_lock_file?
311
- dependency.requirements.select { _1[:file].end_with?(*REQUIRED_FILES) }.any?
393
+ T.must(dependency).requirements.select { _1[:file].end_with?(*REQUIRED_FILES) }.any?
312
394
  end
313
395
  end
314
396
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-uv
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.313.0
4
+ version: 0.314.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-05-15 00:00:00.000000000 Z
10
+ date: 2025-05-22 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: dependabot-common
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.313.0
18
+ version: 0.314.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.313.0
25
+ version: 0.314.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: debug
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -284,7 +284,7 @@ licenses:
284
284
  - MIT
285
285
  metadata:
286
286
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
287
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.313.0
287
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.314.0
288
288
  rdoc_options: []
289
289
  require_paths:
290
290
  - lib