dependabot-npm_and_yarn 0.309.0 → 0.310.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.
@@ -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 "uri"
5
6
 
6
7
  require "dependabot/npm_and_yarn"
@@ -21,20 +22,37 @@ module Dependabot
21
22
  require_relative "package_json_updater"
22
23
  require_relative "package_json_preparer"
23
24
 
25
+ extend T::Sig
26
+
27
+ sig do
28
+ params(
29
+ dependencies: T::Array[Dependabot::Dependency],
30
+ dependency_files: T::Array[Dependabot::DependencyFile],
31
+ repo_contents_path: T.nilable(String),
32
+ credentials: T::Array[Dependabot::Credential]
33
+ )
34
+ .void
35
+ end
24
36
  def initialize(dependencies:, dependency_files:, repo_contents_path:, credentials:)
25
37
  @dependencies = dependencies
26
38
  @dependency_files = dependency_files
27
39
  @repo_contents_path = repo_contents_path
28
40
  @credentials = credentials
29
- @error_handler = YarnErrorHandler.new(
30
- dependencies: dependencies,
31
- dependency_files: dependency_files
41
+ @error_handler = T.let(
42
+ YarnErrorHandler.new(
43
+ dependencies: dependencies,
44
+ dependency_files: dependency_files
45
+ ),
46
+ YarnErrorHandler
32
47
  )
33
48
  end
34
49
 
50
+ sig do
51
+ params(yarn_lock: Dependabot::DependencyFile).returns(String)
52
+ end
35
53
  def updated_yarn_lock_content(yarn_lock)
36
- @updated_yarn_lock_content ||= {}
37
- return @updated_yarn_lock_content[yarn_lock.name] if @updated_yarn_lock_content[yarn_lock.name]
54
+ @updated_yarn_lock_content ||= T.let({}, T.nilable(T::Hash[String, String]))
55
+ return T.must(@updated_yarn_lock_content[yarn_lock.name]) if @updated_yarn_lock_content[yarn_lock.name]
38
56
 
39
57
  new_content = updated_yarn_lock(yarn_lock)
40
58
 
@@ -44,22 +62,34 @@ module Dependabot
44
62
 
45
63
  private
46
64
 
65
+ sig { returns(T::Array[Dependabot::Dependency]) }
47
66
  attr_reader :dependencies
67
+
68
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
48
69
  attr_reader :dependency_files
70
+
71
+ sig { returns(T.nilable(String)) }
49
72
  attr_reader :repo_contents_path
73
+
74
+ sig { returns(T::Array[Dependabot::Credential]) }
50
75
  attr_reader :credentials
76
+
77
+ sig { returns(YarnErrorHandler) }
51
78
  attr_reader :error_handler
52
79
 
80
+ sig { returns(T::Array[Dependabot::Dependency]) }
53
81
  def top_level_dependencies
54
82
  dependencies.select(&:top_level?)
55
83
  end
56
84
 
85
+ sig { returns(T::Array[Dependabot::Dependency]) }
57
86
  def sub_dependencies
58
87
  dependencies.reject(&:top_level?)
59
88
  end
60
89
 
90
+ sig { params(yarn_lock: Dependabot::DependencyFile).returns(String) }
61
91
  def updated_yarn_lock(yarn_lock)
62
- base_dir = dependency_files.first.directory
92
+ base_dir = T.must(dependency_files.first).directory
63
93
  SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
64
94
  write_temporary_dependency_files(yarn_lock)
65
95
  lockfile_name = Pathname.new(yarn_lock.name).basename.to_s
@@ -74,6 +104,12 @@ module Dependabot
74
104
  handle_yarn_lock_updater_error(e, yarn_lock)
75
105
  end
76
106
 
107
+ sig do
108
+ params(
109
+ path: String,
110
+ yarn_lock: Dependabot::DependencyFile
111
+ ).returns(T::Hash[String, String])
112
+ end
77
113
  def run_current_yarn_update(path:, yarn_lock:)
78
114
  top_level_dependency_updates = top_level_dependencies.map do |d|
79
115
  {
@@ -90,13 +126,19 @@ module Dependabot
90
126
  )
91
127
  end
92
128
 
129
+ sig do
130
+ params(
131
+ path: String,
132
+ yarn_lock: Dependabot::DependencyFile
133
+ ).returns(T::Hash[String, String])
134
+ end
93
135
  def run_previous_yarn_update(path:, yarn_lock:)
94
136
  previous_top_level_dependencies = top_level_dependencies.map do |d|
95
137
  {
96
138
  name: d.name,
97
139
  version: d.previous_version,
98
140
  requirements: requirements_for_path(
99
- d.previous_requirements, path
141
+ T.must(d.previous_requirements), path
100
142
  )
101
143
  }
102
144
  end
@@ -109,6 +151,13 @@ module Dependabot
109
151
  end
110
152
 
111
153
  # rubocop:disable Metrics/PerceivedComplexity
154
+ sig do
155
+ params(
156
+ path: String,
157
+ yarn_lock: Dependabot::DependencyFile,
158
+ top_level_dependency_updates: T::Array[T::Hash[Symbol, T.untyped]]
159
+ ).returns(T::Hash[String, String])
160
+ end
112
161
  def run_yarn_updater(path:, yarn_lock:, top_level_dependency_updates:)
113
162
  SharedHelpers.with_git_configured(credentials: credentials) do
114
163
  Dir.chdir(path) do
@@ -117,7 +166,6 @@ module Dependabot
117
166
  run_yarn_berry_top_level_updater(top_level_dependency_updates: top_level_dependency_updates,
118
167
  yarn_lock: yarn_lock)
119
168
  else
120
-
121
169
  run_yarn_top_level_updater(
122
170
  top_level_dependency_updates: top_level_dependency_updates
123
171
  )
@@ -147,9 +195,15 @@ module Dependabot
147
195
  sleep(rand(3.0..10.0))
148
196
  retry
149
197
  end
150
-
151
198
  # rubocop:enable Metrics/PerceivedComplexity
152
199
 
200
+ sig do
201
+ params(
202
+ top_level_dependency_updates: T::Array[T::Hash[Symbol, T.untyped]],
203
+ yarn_lock: Dependabot::DependencyFile
204
+ )
205
+ .returns(T::Hash[String, String])
206
+ end
153
207
  def run_yarn_berry_top_level_updater(top_level_dependency_updates:, yarn_lock:)
154
208
  write_temporary_dependency_files(yarn_lock)
155
209
  # If the requirements have changed, it means we've updated the
@@ -172,13 +226,17 @@ module Dependabot
172
226
  { yarn_lock.name => File.read(yarn_lock.name) }
173
227
  end
174
228
 
229
+ sig { params(dependency_name: String).returns(T::Boolean) }
175
230
  def requirements_changed?(dependency_name)
176
- dep = top_level_dependencies.first { |d| d.name == dependency_name }
231
+ dep = top_level_dependencies.find { |d| d.name == dependency_name }
232
+ return false unless dep
233
+
177
234
  dep.requirements != dep.previous_requirements
178
235
  end
179
236
 
237
+ sig { params(yarn_lock: Dependabot::DependencyFile).returns(T::Hash[String, String]) }
180
238
  def run_yarn_berry_subdependency_updater(yarn_lock:)
181
- dep = sub_dependencies.first
239
+ dep = T.must(sub_dependencies.first)
182
240
  update = "#{dep.name}@#{dep.version}"
183
241
 
184
242
  commands = [
@@ -191,30 +249,59 @@ module Dependabot
191
249
  { yarn_lock.name => File.read(yarn_lock.name) }
192
250
  end
193
251
 
252
+ sig { returns(String) }
194
253
  def yarn_berry_args
195
- @yarn_berry_args ||= Helpers.yarn_berry_args
254
+ @yarn_berry_args ||= T.let(
255
+ Helpers.yarn_berry_args,
256
+ T.nilable(String)
257
+ )
196
258
  end
197
259
 
260
+ sig do
261
+ params(
262
+ top_level_dependency_updates: T::Array[T::Hash[Symbol, T.untyped]]
263
+ )
264
+ .returns(T::Hash[String, String])
265
+ end
198
266
  def run_yarn_top_level_updater(top_level_dependency_updates:)
199
- SharedHelpers.run_helper_subprocess(
200
- command: NativeHelpers.helper_path,
201
- function: "yarn:update",
202
- args: [
203
- Dir.pwd,
204
- top_level_dependency_updates
205
- ]
267
+ T.cast(
268
+ SharedHelpers.run_helper_subprocess(
269
+ command: NativeHelpers.helper_path,
270
+ function: "yarn:update",
271
+ args: T.unsafe([
272
+ Dir.pwd,
273
+ top_level_dependency_updates
274
+ ])
275
+ ),
276
+ T::Hash[String, String]
206
277
  )
207
278
  end
208
279
 
280
+ sig do
281
+ params(
282
+ yarn_lock: Dependabot::DependencyFile
283
+ )
284
+ .returns(T::Hash[String, String])
285
+ end
209
286
  def run_yarn_subdependency_updater(yarn_lock:)
210
287
  lockfile_name = Pathname.new(yarn_lock.name).basename.to_s
211
- SharedHelpers.run_helper_subprocess(
212
- command: NativeHelpers.helper_path,
213
- function: "yarn:updateSubdependency",
214
- args: [Dir.pwd, lockfile_name, sub_dependencies.map(&:to_h)]
288
+ T.cast(
289
+ SharedHelpers.run_helper_subprocess(
290
+ command: NativeHelpers.helper_path,
291
+ function: "yarn:updateSubdependency",
292
+ args: [Dir.pwd, lockfile_name, sub_dependencies.map(&:to_h)]
293
+ ),
294
+ T::Hash[String, String]
215
295
  )
216
296
  end
217
297
 
298
+ sig do
299
+ params(
300
+ requirements: T::Array[T::Hash[Symbol, T.untyped]],
301
+ path: String
302
+ )
303
+ .returns(T::Array[T::Hash[Symbol, T.untyped]])
304
+ end
218
305
  def requirements_for_path(requirements, path)
219
306
  return requirements if path.to_s == "."
220
307
 
@@ -225,6 +312,13 @@ module Dependabot
225
312
  end
226
313
  end
227
314
 
315
+ sig do
316
+ params(
317
+ error: SharedHelpers::HelperSubprocessFailed,
318
+ yarn_lock: Dependabot::DependencyFile
319
+ )
320
+ .returns(T.noreturn)
321
+ end
228
322
  def handle_yarn_lock_updater_error(error, yarn_lock)
229
323
  error_message = error.message
230
324
 
@@ -290,13 +384,14 @@ module Dependabot
290
384
  raise error
291
385
  end
292
386
 
387
+ sig { params(yarn_lock: Dependabot::DependencyFile).returns(T::Boolean) }
293
388
  def resolvable_before_update?(yarn_lock)
294
- @resolvable_before_update ||= {}
295
- return @resolvable_before_update[yarn_lock.name] if @resolvable_before_update.key?(yarn_lock.name)
389
+ @resolvable_before_update ||= T.let({}, T.nilable(T::Hash[String, T::Boolean]))
390
+ return T.must(@resolvable_before_update[yarn_lock.name]) if @resolvable_before_update.key?(yarn_lock.name)
296
391
 
297
392
  @resolvable_before_update[yarn_lock.name] =
298
393
  begin
299
- base_dir = dependency_files.first.directory
394
+ base_dir = T.must(dependency_files.first).directory
300
395
  SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
301
396
  write_temporary_dependency_files(yarn_lock, update_package_json: false)
302
397
  path = Pathname.new(yarn_lock.name).dirname.to_s
@@ -309,15 +404,23 @@ module Dependabot
309
404
  end
310
405
  end
311
406
 
407
+ sig { params(message: String).returns(T::Boolean) }
312
408
  def dependencies_in_error_message?(message)
313
409
  names = dependencies.map { |dep| dep.name.split("/").first }
314
410
  # Example format: Couldn't find any versions for
315
411
  # "@dependabot/dummy-pkg-b" that matches "^1.3.0"
316
412
  names.any? do |name|
317
- message.match?(%r{"#{Regexp.quote(name)}["\/]})
413
+ message.match?(%r{"#{Regexp.quote(T.must(name))}["\/]})
318
414
  end
319
415
  end
320
416
 
417
+ sig do
418
+ params(
419
+ yarn_lock: Dependabot::DependencyFile,
420
+ update_package_json: T::Boolean
421
+ )
422
+ .void
423
+ end
321
424
  def write_temporary_dependency_files(yarn_lock, update_package_json: true)
322
425
  write_lockfiles
323
426
 
@@ -340,19 +443,21 @@ module Dependabot
340
443
  file.content
341
444
  end
342
445
 
343
- updated_content = package_json_preparer(updated_content).prepared_content
446
+ updated_content = package_json_preparer(T.must(updated_content)).prepared_content
344
447
  File.write(file.name, updated_content)
345
448
  end
346
449
 
347
450
  clean_npmrc_in_path(yarn_lock)
348
451
  end
349
452
 
453
+ sig { params(content: String).returns(String) }
350
454
  def sanitize_yarnrc_content(content)
351
455
  # Replace all "${...}" and ${...} occurrences with dummy strings. We use
352
456
  # dummy strings instead of empty strings to prevent issues with npmAlwaysAuth
353
457
  content.gsub(/"\$\{.*?}"/, '"DUMMYCREDS"').gsub(/\$\{.*?}/, '"DUMMYCREDS"')
354
458
  end
355
459
 
460
+ sig { params(yarn_lock: Dependabot::DependencyFile).void }
356
461
  def clean_npmrc_in_path(yarn_lock)
357
462
  # Berry does not read npmrc files.
358
463
  return if Helpers.yarn_berry?(yarn_lock)
@@ -371,6 +476,7 @@ module Dependabot
371
476
  end
372
477
  end
373
478
 
479
+ sig { void }
374
480
  def write_lockfiles
375
481
  yarn_locks.each do |f|
376
482
  FileUtils.mkdir_p(Pathname.new(f.name).dirname)
@@ -378,14 +484,17 @@ module Dependabot
378
484
  end
379
485
  end
380
486
 
487
+ sig { returns(T::Array[String]) }
381
488
  def git_ssh_requirements_to_swap
382
- return @git_ssh_requirements_to_swap if @git_ssh_requirements_to_swap
383
-
384
- @git_ssh_requirements_to_swap = package_files.flat_map do |file|
385
- package_json_preparer(file.content).swapped_ssh_requirements
386
- end
489
+ @git_ssh_requirements_to_swap ||= T.let(
490
+ package_files.flat_map do |file|
491
+ package_json_preparer(T.must(file.content)).swapped_ssh_requirements
492
+ end,
493
+ T.nilable(T::Array[String])
494
+ )
387
495
  end
388
496
 
497
+ sig { params(lockfile_content: String).returns(String) }
389
498
  def post_process_yarn_lockfile(lockfile_content)
390
499
  updated_content = lockfile_content
391
500
 
@@ -405,49 +514,73 @@ module Dependabot
405
514
  updated_content
406
515
  end
407
516
 
517
+ sig { returns(T::Boolean) }
408
518
  def remove_integrity_lines?
409
- yarn_locks.none? { |f| f.content.include?(" integrity sha") }
519
+ yarn_locks.none? { |f| f.content&.include?(" integrity sha") }
410
520
  end
411
521
 
522
+ sig { params(content: String).returns(String) }
412
523
  def remove_integrity_lines(content)
413
524
  content.lines.reject { |l| l.match?(/\s*integrity sha/) }.join
414
525
  end
415
526
 
527
+ sig { params(lockfile: Dependabot::DependencyFile).returns(T::Array[Dependabot::Dependency]) }
416
528
  def lockfile_dependencies(lockfile)
417
- @lockfile_dependencies ||= {}
418
- @lockfile_dependencies[lockfile.name] ||=
529
+ @lockfile_dependencies ||= T.let({}, T.nilable(T::Hash[String, T::Array[Dependabot::Dependency]]))
530
+ @lockfile_dependencies[lockfile.name] =
419
531
  NpmAndYarn::FileParser.new(
420
532
  dependency_files: [lockfile, *package_files],
421
533
  source: nil,
422
- credentials: credentials
534
+ credentials: T.unsafe(credentials)
423
535
  ).parse
424
536
  end
425
537
 
538
+ sig do
539
+ params(
540
+ package_name: T.nilable(String),
541
+ error_message: T.nilable(String),
542
+ yarn_lock: Dependabot::DependencyFile
543
+ )
544
+ .void
545
+ end
426
546
  def handle_missing_package(package_name, error_message, yarn_lock)
427
547
  missing_dep = lockfile_dependencies(yarn_lock)
428
548
  .find { |dep| dep.name == package_name }
429
549
 
430
- error_handler.raise_resolvability_error(error_message, yarn_lock) unless missing_dep
550
+ error_handler.raise_resolvability_error(T.must(error_message), yarn_lock) unless missing_dep
431
551
 
432
552
  reg = Package::RegistryFinder.new(
433
- dependency: missing_dep,
553
+ dependency: T.must(missing_dep),
434
554
  credentials: credentials,
435
555
  npmrc_file: npmrc_file,
436
556
  yarnrc_file: yarnrc_file,
437
557
  yarnrc_yml_file: yarnrc_yml_file
438
558
  ).registry
439
559
 
440
- return if Package::RegistryFinder.central_registry?(reg) && !package_name.start_with?("@")
560
+ return if Package::RegistryFinder.central_registry?(reg) && !package_name&.start_with?("@")
441
561
 
442
562
  raise PrivateSourceAuthenticationFailure, reg
443
563
  end
444
564
 
565
+ sig do
566
+ params(
567
+ error_message: String,
568
+ yarn_lock: Dependabot::DependencyFile
569
+ ).void
570
+ end
445
571
  def handle_timeout(error_message, yarn_lock)
446
- url = error_message.match(TIMEOUT_FETCHING_PACKAGE_REGEX)
447
- .named_ # rubocop:enable Metrics/ClassLength#RI(url).host == NPM_REGISTERY
572
+ match_data = error_message.match(TIMEOUT_FETCHING_PACKAGE_REGEX)
573
+ return unless match_data
574
+
575
+ url = match_data.named_captures["url"]
576
+ return unless url
577
+
578
+ uri = URI(url)
579
+ return unless uri.host == NPM_REGISTRY
580
+
581
+ package_name = match_data.named_captures["package"]
582
+ return unless package_name
448
583
 
449
- package_name = error_message.match(TIMEOUT_FETCHING_PACKAGE_REGEX)
450
- .named_captures["package"]
451
584
  sanitized_name = sanitize_package_name(package_name)
452
585
 
453
586
  dep = lockfile_dependencies(yarn_lock)
@@ -460,6 +593,7 @@ module Dependabot
460
593
  )
461
594
  end
462
595
 
596
+ sig { returns(String) }
463
597
  def npmrc_content
464
598
  NpmrcBuilder.new(
465
599
  credentials: credentials,
@@ -467,35 +601,41 @@ module Dependabot
467
601
  ).npmrc_content
468
602
  end
469
603
 
604
+ sig { params(file: Dependabot::DependencyFile).returns(String) }
470
605
  def updated_package_json_content(file)
471
- PackageJsonUpdater.new(
472
- package_json: file,
473
- dependencies: top_level_dependencies
474
- ).updated_package_json.content
606
+ T.must(
607
+ PackageJsonUpdater.new(
608
+ package_json: file,
609
+ dependencies: top_level_dependencies
610
+ ).updated_package_json.content
611
+ )
475
612
  end
476
613
 
614
+ sig { params(content: String).returns(PackageJsonPreparer) }
477
615
  def package_json_preparer(content)
478
- @package_json_preparer ||= {}
616
+ @package_json_preparer ||= T.let({}, T.nilable(T::Hash[String, PackageJsonPreparer]))
479
617
  @package_json_preparer[content] ||=
480
618
  PackageJsonPreparer.new(
481
619
  package_json_content: content
482
620
  )
483
621
  end
484
622
 
623
+ sig { returns(T::Boolean) }
485
624
  def npmrc_disables_lockfile?
486
625
  npmrc_content.match?(/^package-lock\s*=\s*false/)
487
626
  end
488
627
 
628
+ sig { returns(T::Boolean) }
489
629
  def yarnrc_specifies_private_reg?
490
630
  return false unless yarnrc_file
491
631
 
492
632
  regex = Package::RegistryFinder::YARN_GLOBAL_REGISTRY_REGEX
493
633
  yarnrc_global_registry =
494
- yarnrc_file.content
495
- .lines.find { |line| line.match?(regex) }
496
- &.match(regex)
497
- &.named_captures
498
- &.fetch("registry")
634
+ T.must(T.must(yarnrc_file).content)
635
+ .lines.find { |line| line.match?(regex) }
636
+ &.match(regex)
637
+ &.named_captures
638
+ &.fetch("registry")
499
639
 
500
640
  return false unless yarnrc_global_registry
501
641
 
@@ -504,41 +644,51 @@ module Dependabot
504
644
  end
505
645
  end
506
646
 
647
+ sig { returns(String) }
507
648
  def yarnrc_content
508
649
  NpmrcBuilder.new(
509
- credentials: credentials,
650
+ credentials: T.unsafe(credentials),
510
651
  dependency_files: dependency_files
511
652
  ).yarnrc_content
512
653
  end
513
654
 
655
+ sig { params(package_name: String).returns(String) }
514
656
  def sanitize_package_name(package_name)
515
657
  package_name.gsub("%2f", "/").gsub("%2F", "/")
516
658
  end
517
659
 
660
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
518
661
  def yarn_locks
519
- @yarn_locks ||=
662
+ @yarn_locks ||= T.let(
520
663
  dependency_files
521
- .select { |f| f.name.end_with?("yarn.lock") }
664
+ .select { |f| f.name.end_with?("yarn.lock") },
665
+ T.nilable(T::Array[Dependabot::DependencyFile])
666
+ )
522
667
  end
523
668
 
669
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
524
670
  def package_files
525
671
  dependency_files.select { |f| f.name.end_with?("package.json") }
526
672
  end
527
673
 
674
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
528
675
  def yarnrc_file
529
676
  dependency_files.find { |f| f.name == ".yarnrc" }
530
677
  end
531
678
 
679
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
532
680
  def npmrc_file
533
681
  dependency_files.find { |f| f.name == ".npmrc" }
534
682
  end
535
683
 
684
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
536
685
  def yarnrc_yml_file
537
686
  dependency_files.find { |f| f.name.end_with?(".yarnrc.yml") }
538
687
  end
539
688
 
689
+ sig { returns(String) }
540
690
  def yarnrc_yml_content
541
- yarnrc_yml_file.content
691
+ T.must(T.must(yarnrc_yml_file).content)
542
692
  end
543
693
  end
544
694
  end
@@ -551,7 +701,8 @@ module Dependabot
551
701
  params(
552
702
  dependencies: T::Array[Dependabot::Dependency],
553
703
  dependency_files: T::Array[Dependabot::DependencyFile]
554
- ).void
704
+ )
705
+ .void
555
706
  end
556
707
  def initialize(dependencies:, dependency_files:)
557
708
  @dependencies = dependencies
@@ -460,7 +460,7 @@ module Dependabot
460
460
  BunLockfileUpdater.new(
461
461
  dependencies: dependencies,
462
462
  dependency_files: dependency_files,
463
- repo_contents_path: repo_contents_path,
463
+ repo_contents_path: T.must(repo_contents_path),
464
464
  credentials: credentials
465
465
  ),
466
466
  T.nilable(Dependabot::NpmAndYarn::FileUpdater::BunLockfileUpdater)