dependabot-nuget 0.248.0 → 0.249.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60679d8e50f3e96aa05400b03ccc9e0dd491c1a7cf340c4c7a39bcb48675c3f6
4
- data.tar.gz: 235eb5b57423534578945568766179c2805c6c1e19b5bdb806cc4fd5a5e78dc7
3
+ metadata.gz: c23304dabfd9e4506478efc6188332716cc7ff097f8042b45c67bfda9cf1b6ae
4
+ data.tar.gz: 74f8d095b039666ca96e098913667e8c40552df5963187cf4ae66a86ac429ae1
5
5
  SHA512:
6
- metadata.gz: 1976d20f2b23920eb44176098e89109ee5e969f7e12eaa959e06948e543b9383dcf08c7521eeb9f69602fab37f17ffaafc937e203cce68439327d9735590e1b9
7
- data.tar.gz: d240a0489a0ab0dc72283ea736a55965cb13a2fe375cb7eb5a98beb473f9e227bf6ec52a07fba05f2177ee228c716197f60d5fe35d3170b1687aaa07648915eb
6
+ metadata.gz: 904dae0561b8288c0cc8b319269a6352cdfa1d4d443e01b052e99bf3294277523f1a227944a7787024a216ad1661a21647330e116f6e5a9c66d95cbd630d7f47
7
+ data.tar.gz: 75cff9a26706bc60d8f5423d8e096fe4b1a1ccf20c3bb3fff5a5c6428ac6cfdd3b58e0a9ce490a9fb6577e852bd59417917bde19ad3697ed8d76aa789bdb4591
@@ -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
  {
@@ -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