dependabot-nuget 0.248.0 → 0.250.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: 60679d8e50f3e96aa05400b03ccc9e0dd491c1a7cf340c4c7a39bcb48675c3f6
4
- data.tar.gz: 235eb5b57423534578945568766179c2805c6c1e19b5bdb806cc4fd5a5e78dc7
3
+ metadata.gz: 144460a4e43c2169e3526841828a5a3baa717f83e0becb327f128cb971a3deb8
4
+ data.tar.gz: 1fdb2e2284043c4c27eb536dbbc0eaef6d180a59c61e6c32c2a74f4aadfb9a85
5
5
  SHA512:
6
- metadata.gz: 1976d20f2b23920eb44176098e89109ee5e969f7e12eaa959e06948e543b9383dcf08c7521eeb9f69602fab37f17ffaafc937e203cce68439327d9735590e1b9
7
- data.tar.gz: d240a0489a0ab0dc72283ea736a55965cb13a2fe375cb7eb5a98beb473f9e227bf6ec52a07fba05f2177ee228c716197f60d5fe35d3170b1687aaa07648915eb
6
+ metadata.gz: 1d1c53febbc055e0eeecbff13cb9101c17f0b989b039caa719baf2cf6a69e4fc43f8babbe7e533cf75baeb97dd3d58e7344a1ede890410a4c5046ab7951fd2f3
7
+ data.tar.gz: 285ed503fcefca8cd3ed0d731f59543c76eca79b0da7c738ae1bef523521697c5ee05fcc0c2d8425d86e8c657d1b579464822f359699ed67ca01077d27eb5972
@@ -3,6 +3,8 @@ using System.IO;
3
3
  using System.Text;
4
4
  using System.Threading.Tasks;
5
5
 
6
+ using NuGetUpdater.Core;
7
+ using NuGetUpdater.Core.Test;
6
8
  using NuGetUpdater.Core.Test.Update;
7
9
 
8
10
  using Xunit;
@@ -291,6 +293,61 @@ public partial class EntryPointTests
291
293
  );
292
294
  }
293
295
 
296
+ [Fact]
297
+ public async Task UpdaterDoesNotUseRepoGlobalJsonForMSBuildTasks()
298
+ {
299
+ // This is a _very_ specific scenario where the `NuGetUpdater.Cli` tool might pick up a `global.json` from
300
+ // the root of the repo under test and use it's `sdk` property when trying to locate MSBuild. To properly
301
+ // test this, it must be tested in a new process where MSBuild has not been loaded yet and the runner tool
302
+ // must be started with its working directory at the test repo's root.
303
+ using var tempDir = new TemporaryDirectory();
304
+ await File.WriteAllTextAsync(Path.Join(tempDir.DirectoryPath, "global.json"), """
305
+ {
306
+ "sdk": {
307
+ "version": "99.99.99"
308
+ }
309
+ }
310
+ """);
311
+ await File.WriteAllTextAsync(Path.Join(tempDir.DirectoryPath, "project.csproj"), """
312
+ <Project Sdk="Microsoft.NET.Sdk">
313
+ <PropertyGroup>
314
+ <TargetFramework>net8.0</TargetFramework>
315
+ </PropertyGroup>
316
+ <ItemGroup>
317
+ <PackageReference Include="Newtonsoft.Json" Version="7.0.1" />
318
+ </ItemGroup>
319
+ </Project>
320
+ """);
321
+ var executableName = $"NuGetUpdater.Cli{(Environment.OSVersion.Platform == PlatformID.Win32NT ? ".exe" : "")}";
322
+ var executableArgs = string.Join(" ",
323
+ [
324
+ "update",
325
+ "--repo-root",
326
+ tempDir.DirectoryPath,
327
+ "--solution-or-project",
328
+ Path.Join(tempDir.DirectoryPath, "project.csproj"),
329
+ "--dependency",
330
+ "Newtonsoft.Json",
331
+ "--new-version",
332
+ "13.0.1",
333
+ "--previous-version",
334
+ "7.0.1",
335
+ "--verbose"
336
+ ]);
337
+
338
+ // verify base run
339
+ var (exitCode, output, error) = await ProcessEx.RunAsync(executableName, executableArgs, workingDirectory: tempDir.DirectoryPath);
340
+ Assert.True(exitCode == 0, $"Error running update on unsupported SDK.\nSTDOUT:\n{output}\nSTDERR:\n{error}");
341
+
342
+ // verify project update
343
+ var updatedProjectContents = await File.ReadAllTextAsync(Path.Join(tempDir.DirectoryPath, "project.csproj"));
344
+ Assert.Contains("13.0.1", updatedProjectContents);
345
+
346
+ // verify `global.json` untouched
347
+ var updatedGlobalJsonContents = await File.ReadAllTextAsync(Path.Join(tempDir.DirectoryPath, "global.json"));
348
+ Assert.Contains("99.99.99", updatedGlobalJsonContents);
349
+ }
350
+
294
351
  private static async Task Run(Func<string, string[]> getArgs, (string Path, string Content)[] initialFiles, (string, string)[] expectedFiles)
295
352
  {
296
353
  var actualFiles = await RunUpdate(initialFiles, async path =>
@@ -231,7 +231,7 @@ internal static class SdkPackageUpdater
231
231
  logger.Log($" Adding [{dependencyName}/{newDependencyVersion}] as a top-level package reference.");
232
232
 
233
233
  // see https://learn.microsoft.com/nuget/consume-packages/install-use-packages-dotnet-cli
234
- var (exitCode, _, _) = await ProcessEx.RunAsync("dotnet", $"add {projectPath} package {dependencyName} --version {newDependencyVersion}");
234
+ var (exitCode, _, _) = await ProcessEx.RunAsync("dotnet", $"add {projectPath} package {dependencyName} --version {newDependencyVersion}", workingDirectory: Path.GetDirectoryName(projectPath));
235
235
  if (exitCode != 0)
236
236
  {
237
237
  logger.Log($" Transitive dependency [{dependencyName}/{newDependencyVersion}] was not added.");
@@ -41,9 +41,29 @@ internal static partial class MSBuildHelper
41
41
  // Ensure MSBuild types are registered before calling a method that loads the types
42
42
  if (!IsMSBuildRegistered)
43
43
  {
44
- var defaultInstance = MSBuildLocator.QueryVisualStudioInstances().First();
45
- MSBuildPath = defaultInstance.MSBuildPath;
46
- MSBuildLocator.RegisterInstance(defaultInstance);
44
+ var globalJsonPath = "global.json";
45
+ var tempGlobalJsonPath = globalJsonPath + Guid.NewGuid().ToString();
46
+ var globalJsonExists = File.Exists(globalJsonPath);
47
+ try
48
+ {
49
+ if (globalJsonExists)
50
+ {
51
+ Console.WriteLine("Temporarily removing `global.json` for MSBuild detection.");
52
+ File.Move(globalJsonPath, tempGlobalJsonPath);
53
+ }
54
+
55
+ var defaultInstance = MSBuildLocator.QueryVisualStudioInstances().First();
56
+ MSBuildPath = defaultInstance.MSBuildPath;
57
+ MSBuildLocator.RegisterInstance(defaultInstance);
58
+ }
59
+ finally
60
+ {
61
+ if (globalJsonExists)
62
+ {
63
+ Console.WriteLine("Restoring `global.json` after MSBuild detection.");
64
+ File.Move(tempGlobalJsonPath, globalJsonPath);
65
+ }
66
+ }
47
67
  }
48
68
  }
49
69
 
@@ -311,7 +331,7 @@ internal static partial class MSBuildHelper
311
331
  try
312
332
  {
313
333
  var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages);
314
- var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"restore \"{tempProjectPath}\"");
334
+ var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"restore \"{tempProjectPath}\"", workingDirectory: tempDirectory.FullName);
315
335
 
316
336
  // NU1608: Detected package version outside of dependency constraint
317
337
 
@@ -451,7 +471,7 @@ internal static partial class MSBuildHelper
451
471
  {
452
472
  var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages);
453
473
 
454
- var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet", $"build \"{tempProjectPath}\" /t:_ReportDependencies");
474
+ var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet", $"build \"{tempProjectPath}\" /t:_ReportDependencies", workingDirectory: tempDirectory.FullName);
455
475
 
456
476
  if (exitCode == 0)
457
477
  {
@@ -548,6 +568,7 @@ internal static partial class MSBuildHelper
548
568
  var repoRootPathPrefix = repoRootPath.NormalizePathToUnix() + "/";
549
569
  var buildFilesInRepo = buildFileList
550
570
  .Where(f => f.StartsWith(repoRootPathPrefix, StringComparison.OrdinalIgnoreCase))
571
+ .Where(File.Exists)
551
572
  .Distinct()
552
573
  .ToArray();
553
574
  var result = buildFilesInRepo
@@ -2497,5 +2497,36 @@ public partial class UpdateWorkerTests
2497
2497
  """
2498
2498
  );
2499
2499
  }
2500
+
2501
+ [Fact]
2502
+ public async Task ProcessingProjectWithAspireDoesNotFailEvenThoughWorkloadIsNotInstalled()
2503
+ {
2504
+ // enumerating the build files will fail if the Aspire workload is not installed; this test ensures we can
2505
+ // still process the update
2506
+ await TestUpdateForProject("Newtonsoft.Json", "7.0.1", "13.0.1",
2507
+ projectContents: """
2508
+ <Project Sdk="Microsoft.NET.Sdk">
2509
+ <PropertyGroup>
2510
+ <TargetFramework>net8.0</TargetFramework>
2511
+ <IsAspireHost>true</IsAspireHost>
2512
+ </PropertyGroup>
2513
+ <ItemGroup>
2514
+ <PackageReference Include="Newtonsoft.Json" Version="7.0.1" />
2515
+ </ItemGroup>
2516
+ </Project>
2517
+ """,
2518
+ expectedProjectContents: """
2519
+ <Project Sdk="Microsoft.NET.Sdk">
2520
+ <PropertyGroup>
2521
+ <TargetFramework>net8.0</TargetFramework>
2522
+ <IsAspireHost>true</IsAspireHost>
2523
+ </PropertyGroup>
2524
+ <ItemGroup>
2525
+ <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
2526
+ </ItemGroup>
2527
+ </Project>
2528
+ """
2529
+ );
2530
+ }
2500
2531
  }
2501
2532
  }
@@ -3,6 +3,7 @@
3
3
 
4
4
  require "nokogiri"
5
5
  require "pathname"
6
+ require "sorbet-runtime"
6
7
 
7
8
  require "dependabot/nuget/file_fetcher"
8
9
 
@@ -2,6 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "pathname"
5
+ require "sorbet-runtime"
6
+
5
7
  require "dependabot/nuget/file_fetcher"
6
8
 
7
9
  module Dependabot
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "json"
5
+ require "sorbet-runtime"
5
6
 
6
7
  require "dependabot/dependency"
7
8
  require "dependabot/nuget/file_parser"
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "json"
5
+ require "sorbet-runtime"
5
6
 
6
7
  require "dependabot/dependency"
7
8
  require "dependabot/nuget/file_parser"
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "nokogiri"
5
+ require "sorbet-runtime"
5
6
 
6
7
  require "dependabot/dependency"
7
8
  require "dependabot/nuget/file_parser"
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "nokogiri"
5
+ require "sorbet-runtime"
5
6
 
6
7
  require "dependabot/dependency"
7
8
  require "dependabot/nuget/file_parser"
@@ -1,6 +1,8 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
5
+
4
6
  require "dependabot/nuget/file_fetcher/import_paths_finder"
5
7
  require "dependabot/nuget/file_parser"
6
8
 
@@ -63,9 +63,18 @@ module Dependabot
63
63
  def project_file_dependencies
64
64
  dependency_set = DependencySet.new
65
65
 
66
- (project_files + project_import_files).each do |file|
67
- parser = project_file_parser
68
- dependency_set += parser.dependency_set(project_file: file)
66
+ project_files.each do |project_file|
67
+ tfms = project_file_parser.target_frameworks(project_file: project_file)
68
+ unless tfms.any?
69
+ Dependabot.logger.warn "Excluding project file '#{project_file.name}' due to unresolvable target framework"
70
+ next
71
+ end
72
+
73
+ dependency_set += project_file_parser.dependency_set(project_file: project_file)
74
+ end
75
+
76
+ proj_files.each do |proj_file|
77
+ dependency_set += project_file_parser.dependency_set(project_file: proj_file)
69
78
  end
70
79
 
71
80
  dependency_set
@@ -109,14 +118,21 @@ module Dependabot
109
118
  )
110
119
  end
111
120
 
121
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
122
+ def proj_files
123
+ projfile = /\.proj$/
124
+
125
+ dependency_files.select do |df|
126
+ df.name.match?(projfile)
127
+ end
128
+ end
129
+
112
130
  sig { returns(T::Array[Dependabot::DependencyFile]) }
113
131
  def project_files
114
- projfile = /\.([a-z]{2})?proj$/
115
- packageprops = /[Dd]irectory.[Pp]ackages.props/
132
+ projectfile = /\.(cs|vb|fs)proj$/
116
133
 
117
134
  dependency_files.select do |df|
118
- df.name.match?(projfile) ||
119
- df.name.match?(packageprops)
135
+ df.name.match?(projectfile)
120
136
  end
121
137
  end
122
138
 
@@ -144,19 +160,24 @@ module Dependabot
144
160
 
145
161
  sig { returns(T.nilable(Dependabot::DependencyFile)) }
146
162
  def global_json
147
- dependency_files.find { |f| f.name.casecmp("global.json")&.zero? }
163
+ dependency_files.find { |f| f.name.casecmp?("global.json") }
148
164
  end
149
165
 
150
166
  sig { returns(T.nilable(Dependabot::DependencyFile)) }
151
167
  def dotnet_tools_json
152
- dependency_files.find { |f| f.name.casecmp(".config/dotnet-tools.json")&.zero? }
168
+ dependency_files.find { |f| f.name.casecmp?(".config/dotnet-tools.json") }
153
169
  end
154
170
 
155
171
  sig { override.void }
156
172
  def check_required_files
157
- return if project_files.any? || packages_config_files.any?
173
+ if project_files.any? || proj_files.any? || packages_config_files.any? || global_json || dotnet_tools_json
174
+ return
175
+ end
158
176
 
159
- raise "No project file or packages.config!"
177
+ raise Dependabot::DependencyFileNotFound.new(
178
+ "*.(cs|vb|fs)proj, *.proj, packages.config, global.json, dotnet-tools.json",
179
+ "No project file, *.proj, packages.config, global.json, or dotnet-tools.json!"
180
+ )
160
181
  end
161
182
  end
162
183
  end
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "nokogiri"
5
+ require "sorbet-runtime"
5
6
 
6
7
  require "dependabot/dependency_file"
7
8
  require "dependabot/nuget/file_updater"
@@ -1,22 +1,34 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
5
+
4
6
  require "dependabot/update_checkers/base"
5
7
 
6
8
  module Dependabot
7
9
  module Nuget
8
10
  class CompatibilityChecker
11
+ extend T::Sig
12
+
9
13
  require_relative "nuspec_fetcher"
10
14
  require_relative "nupkg_fetcher"
11
15
  require_relative "tfm_finder"
12
16
  require_relative "tfm_comparer"
13
17
 
18
+ sig do
19
+ params(
20
+ dependency_urls: T::Array[T::Hash[Symbol, String]],
21
+ dependency: Dependabot::Dependency,
22
+ tfm_finder: Dependabot::Nuget::TfmFinder
23
+ ).void
24
+ end
14
25
  def initialize(dependency_urls:, dependency:, tfm_finder:)
15
26
  @dependency_urls = dependency_urls
16
27
  @dependency = dependency
17
28
  @tfm_finder = tfm_finder
18
29
  end
19
30
 
31
+ sig { params(version: String).returns(T::Boolean) }
20
32
  def compatible?(version)
21
33
  nuspec_xml = NuspecFetcher.fetch_nuspec(dependency_urls, dependency.name, version)
22
34
  return false unless nuspec_xml
@@ -32,15 +44,23 @@ module Dependabot
32
44
  return true if package_tfms.nil?
33
45
  return false if package_tfms.empty?
34
46
 
35
- return false if project_tfms.nil? || project_tfms.empty?
47
+ return false if project_tfms.nil? || project_tfms&.empty?
36
48
 
37
- TfmComparer.are_frameworks_compatible?(project_tfms, package_tfms)
49
+ TfmComparer.are_frameworks_compatible?(T.must(project_tfms), package_tfms)
38
50
  end
39
51
 
40
52
  private
41
53
 
42
- attr_reader :dependency_urls, :dependency, :tfm_finder
54
+ sig { returns(T::Array[T::Hash[Symbol, String]]) }
55
+ attr_reader :dependency_urls
43
56
 
57
+ sig { returns(Dependabot::Dependency) }
58
+ attr_reader :dependency
59
+
60
+ sig { returns(Dependabot::Nuget::TfmFinder) }
61
+ attr_reader :tfm_finder
62
+
63
+ sig { params(nuspec_xml: Nokogiri::XML::Document).returns(T::Boolean) }
44
64
  def pure_development_dependency?(nuspec_xml)
45
65
  contents = nuspec_xml.at_xpath("package/metadata/developmentDependency")&.content&.strip
46
66
  return false unless contents # no `developmentDependency` element
@@ -55,16 +75,17 @@ module Dependabot
55
75
  dependency_groups_with_target_framework.to_a.empty?
56
76
  end
57
77
 
78
+ sig { params(nuspec_xml: Nokogiri::XML::Document).returns(T::Array[String]) }
58
79
  def parse_package_tfms(nuspec_xml)
59
80
  nuspec_xml.xpath("//dependencies/group").filter_map { |group| group.attribute("targetFramework") }
60
81
  end
61
82
 
83
+ sig { returns(T.nilable(T::Array[String])) }
62
84
  def project_tfms
63
- return @project_tfms if defined?(@project_tfms)
64
-
65
- @project_tfms = tfm_finder.frameworks(dependency)
85
+ @project_tfms ||= T.let(tfm_finder.frameworks(dependency), T.nilable(T::Array[String]))
66
86
  end
67
87
 
88
+ sig { params(dependency_version: String).returns(T.nilable(T::Array[String])) }
68
89
  def fetch_package_tfms(dependency_version)
69
90
  cache = CacheManager.cache("compatibility_checker_tfms_cache")
70
91
  key = "#{dependency.name}::#{dependency_version}"
@@ -1,9 +1,11 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "nokogiri"
5
- require "zip"
5
+ require "sorbet-runtime"
6
6
  require "stringio"
7
+ require "zip"
8
+
7
9
  require "dependabot/update_checkers/base"
8
10
  require "dependabot/nuget/version"
9
11
 
@@ -11,21 +13,34 @@ module Dependabot
11
13
  module Nuget
12
14
  class UpdateChecker < Dependabot::UpdateCheckers::Base
13
15
  class DependencyFinder
16
+ extend T::Sig
17
+
14
18
  require_relative "requirements_updater"
15
19
  require_relative "nuspec_fetcher"
16
20
 
21
+ sig { returns(T::Hash[String, T.untyped]) }
17
22
  def self.transitive_dependencies_cache
18
23
  CacheManager.cache("dependency_finder_transitive_dependencies")
19
24
  end
20
25
 
26
+ sig { returns(T::Hash[String, T.untyped]) }
21
27
  def self.updated_peer_dependencies_cache
22
28
  CacheManager.cache("dependency_finder_updated_peer_dependencies")
23
29
  end
24
30
 
31
+ sig { returns(T::Hash[String, T.untyped]) }
25
32
  def self.fetch_dependencies_cache
26
33
  CacheManager.cache("dependency_finder_fetch_dependencies")
27
34
  end
28
35
 
36
+ sig do
37
+ params(
38
+ dependency: Dependabot::Dependency,
39
+ dependency_files: T::Array[Dependabot::DependencyFile],
40
+ credentials: T::Array[Dependabot::Credential],
41
+ repo_contents_path: T.nilable(String)
42
+ ).void
43
+ end
29
44
  def initialize(dependency:, dependency_files:, credentials:, repo_contents_path:)
30
45
  @dependency = dependency
31
46
  @dependency_files = dependency_files
@@ -33,6 +48,7 @@ module Dependabot
33
48
  @repo_contents_path = repo_contents_path
34
49
  end
35
50
 
51
+ sig { returns(T::Array[Dependabot::Dependency]) }
36
52
  def transitive_dependencies
37
53
  key = "#{dependency.name.downcase}::#{dependency.version}"
38
54
  cache = DependencyFinder.transitive_dependencies_cache
@@ -44,7 +60,7 @@ module Dependabot
44
60
 
45
61
  cache[key] = fetch_transitive_dependencies(
46
62
  @dependency.name,
47
- @dependency.version
63
+ T.must(@dependency.version)
48
64
  ).map do |dependency_info|
49
65
  package_name = dependency_info["packageName"]
50
66
  target_version = dependency_info["version"]
@@ -65,13 +81,14 @@ module Dependabot
65
81
  cache[key]
66
82
  end
67
83
 
84
+ sig { returns(T::Array[Dependabot::Dependency]) }
68
85
  def updated_peer_dependencies
69
86
  key = "#{dependency.name.downcase}::#{dependency.version}"
70
87
  cache = DependencyFinder.updated_peer_dependencies_cache
71
88
 
72
89
  cache[key] ||= fetch_transitive_dependencies(
73
90
  @dependency.name,
74
- @dependency.version
91
+ T.must(@dependency.version)
75
92
  ).filter_map do |dependency_info|
76
93
  package_name = dependency_info["packageName"]
77
94
  target_version = dependency_info["version"]
@@ -104,48 +121,79 @@ module Dependabot
104
121
 
105
122
  private
106
123
 
107
- attr_reader :dependency, :dependency_files, :credentials, :repo_contents_path
124
+ sig { returns(Dependabot::Dependency) }
125
+ attr_reader :dependency
126
+
127
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
128
+ attr_reader :dependency_files
129
+
130
+ sig { returns(T::Array[Dependabot::Credential]) }
131
+ attr_reader :credentials
108
132
 
133
+ sig { returns(T.nilable(String)) }
134
+ attr_reader :repo_contents_path
135
+
136
+ sig do
137
+ params(
138
+ dep: Dependabot::Dependency,
139
+ target_version_details: T::Hash[Symbol, T.untyped]
140
+ )
141
+ .returns(T::Array[T::Hash[String, T.untyped]])
142
+ end
109
143
  def updated_requirements(dep, target_version_details)
110
- @updated_requirements ||= {}
144
+ @updated_requirements ||= T.let({}, T.nilable(T::Hash[String, T.untyped]))
111
145
  @updated_requirements[dep.name] ||=
112
146
  RequirementsUpdater.new(
113
147
  requirements: dep.requirements,
114
148
  latest_version: target_version_details.fetch(:version).to_s,
115
- source_details: target_version_details
116
- &.slice(:nuspec_url, :repo_url, :source_url)
149
+ source_details: target_version_details.slice(:nuspec_url, :repo_url, :source_url)
117
150
  ).updated_requirements
118
151
  end
119
152
 
153
+ sig { returns(T::Array[Dependabot::Dependency]) }
120
154
  def top_level_dependencies
121
155
  @top_level_dependencies ||=
122
- Nuget::FileParser.new(
123
- dependency_files: dependency_files,
124
- source: nil
125
- ).parse.select(&:top_level?)
156
+ T.let(
157
+ Nuget::FileParser.new(
158
+ dependency_files: dependency_files,
159
+ source: nil
160
+ ).parse.select(&:top_level?),
161
+ T.nilable(T::Array[Dependabot::Dependency])
162
+ )
126
163
  end
127
164
 
165
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
128
166
  def nuget_configs
129
167
  @nuget_configs ||=
130
- @dependency_files.select { |f| f.name.match?(/nuget\.config$/i) }
168
+ T.let(
169
+ @dependency_files.select { |f| f.name.match?(/nuget\.config$/i) },
170
+ T.nilable(T::Array[Dependabot::DependencyFile])
171
+ )
131
172
  end
132
173
 
174
+ sig { returns(T::Array[T::Hash[Symbol, String]]) }
133
175
  def dependency_urls
134
176
  @dependency_urls ||=
135
- RepositoryFinder.new(
136
- dependency: @dependency,
137
- credentials: @credentials,
138
- config_files: nuget_configs
139
- ).dependency_urls
140
- .select { |url| url.fetch(:repository_type) == "v3" }
177
+ T.let(
178
+ RepositoryFinder.new(
179
+ dependency: @dependency,
180
+ credentials: @credentials,
181
+ config_files: nuget_configs
182
+ )
183
+ .dependency_urls
184
+ .select { |url| url.fetch(:repository_type) == "v3" },
185
+ T.nilable(T::Array[T::Hash[Symbol, String]])
186
+ )
141
187
  end
142
188
 
189
+ sig { params(package_id: String, package_version: String).returns(T::Array[T::Hash[String, T.untyped]]) }
143
190
  def fetch_transitive_dependencies(package_id, package_version)
144
191
  all_dependencies = {}
145
192
  fetch_transitive_dependencies_impl(package_id, package_version, all_dependencies)
146
193
  all_dependencies.map { |_, dependency_info| dependency_info }
147
194
  end
148
195
 
196
+ sig { params(package_id: String, package_version: String, all_dependencies: T::Hash[String, T.untyped]).void }
149
197
  def fetch_transitive_dependencies_impl(package_id, package_version, all_dependencies)
150
198
  dependencies = fetch_dependencies(package_id, package_version)
151
199
  return unless dependencies.any?
@@ -175,6 +223,7 @@ module Dependabot
175
223
  end
176
224
  end
177
225
 
226
+ sig { params(package_id: String, package_version: String).returns(T::Array[T::Hash[String, T.untyped]]) }
178
227
  def fetch_dependencies(package_id, package_version)
179
228
  key = "#{package_id.downcase}::#{package_version}"
180
229
  cache = DependencyFinder.fetch_dependencies_cache
@@ -191,6 +240,7 @@ module Dependabot
191
240
  cache[key]
192
241
  end
193
242
 
243
+ sig { params(nuspec_xml: Nokogiri::XML::Document).returns(T::Array[T::Hash[String, String]]) }
194
244
  def read_dependencies_from_nuspec(nuspec_xml) # rubocop:disable Metrics/PerceivedComplexity
195
245
  # we want to exclude development dependencies from the lookup
196
246
  allowed_attributes = %w(all compile native runtime)
@@ -223,6 +273,7 @@ module Dependabot
223
273
  dependency_list
224
274
  end
225
275
 
276
+ sig { params(dep: Dependabot::Dependency).returns(Dependabot::Nuget::UpdateChecker::VersionFinder) }
226
277
  def version_finder(dep)
227
278
  VersionFinder.new(
228
279
  dependency: dep,
@@ -18,7 +18,7 @@ module Dependabot
18
18
  params(
19
19
  dependency_urls: T::Array[T::Hash[Symbol, String]],
20
20
  package_id: String,
21
- package_version: String
21
+ package_version: T.nilable(String)
22
22
  )
23
23
  .returns(T.nilable(Nokogiri::XML::Document))
24
24
  end