dependabot-nuget 0.278.0 → 0.279.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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/build +1 -1
  3. data/helpers/lib/NuGetUpdater/.editorconfig +1 -0
  4. data/helpers/lib/NuGetUpdater/Directory.Build.props +1 -0
  5. data/helpers/lib/NuGetUpdater/Directory.Common.props +1 -1
  6. data/helpers/lib/NuGetUpdater/Directory.Packages.props +6 -1
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +2 -3
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +87 -83
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/Requirement.cs +2 -2
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +47 -46
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotFound.cs +6 -0
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +11 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PrivateSourceAuthenticationFailure.cs +6 -0
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UnknownError.cs +6 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/HttpApiHandler.cs +5 -0
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/IApiHandler.cs +1 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +60 -15
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +1 -1
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +1 -1
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +50 -46
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +5 -5
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +1 -1
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProcessExtensions.cs +2 -4
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/RequirementTests.cs +4 -1
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +10 -1
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +92 -0
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/TestApiHandler.cs +10 -4
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +8 -8
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +10 -1
  30. data/helpers/lib/NuGetUpdater/global.json +1 -1
  31. data/lib/dependabot/nuget/file_updater.rb +5 -1
  32. data/lib/dependabot/nuget/native_helpers.rb +4 -1
  33. data/lib/dependabot/nuget/requirement.rb +2 -0
  34. data/lib/dependabot/nuget/update_checker/repository_finder.rb +26 -2
  35. metadata +9 -5
@@ -25,57 +25,19 @@ public class UpdaterWorker
25
25
 
26
26
  public async Task RunAsync(string repoRootPath, string workspacePath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive, string? resultOutputPath = null)
27
27
  {
28
- var result = await RunAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
29
- if (resultOutputPath is { })
30
- {
31
- await WriteResultFile(result, resultOutputPath, _logger);
32
- }
33
- }
34
-
35
- public async Task<UpdateOperationResult> RunAsync(string repoRootPath, string workspacePath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive)
36
- {
37
- MSBuildHelper.RegisterMSBuild(Environment.CurrentDirectory, repoRootPath);
38
- UpdateOperationResult result;
39
-
40
- if (!Path.IsPathRooted(workspacePath) || !File.Exists(workspacePath))
41
- {
42
- workspacePath = Path.GetFullPath(Path.Join(repoRootPath, workspacePath));
43
- }
44
-
28
+ UpdateOperationResult result = new(); // assumed to be ok until proven otherwise
45
29
  try
46
30
  {
47
- if (!isTransitive)
48
- {
49
- await DotNetToolsJsonUpdater.UpdateDependencyAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, _logger);
50
- await GlobalJsonUpdater.UpdateDependencyAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, _logger);
51
- }
52
-
53
- var extension = Path.GetExtension(workspacePath).ToLowerInvariant();
54
- switch (extension)
55
- {
56
- case ".sln":
57
- await RunForSolutionAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
58
- break;
59
- case ".proj":
60
- await RunForProjFileAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
61
- break;
62
- case ".csproj":
63
- case ".fsproj":
64
- case ".vbproj":
65
- await RunForProjectAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
66
- break;
67
- default:
68
- _logger.Log($"File extension [{extension}] is not supported.");
69
- break;
70
- }
71
-
72
- result = new(); // all ok
73
- _logger.Log("Update complete.");
31
+ result = await RunAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
74
32
  }
75
33
  catch (HttpRequestException ex)
76
34
  when (ex.StatusCode == HttpStatusCode.Unauthorized || ex.StatusCode == HttpStatusCode.Forbidden)
77
35
  {
78
- // TODO: consolidate this error handling between AnalyzeWorker, DiscoveryWorker, and UpdateWorker
36
+ if (!Path.IsPathRooted(workspacePath) || !File.Exists(workspacePath))
37
+ {
38
+ workspacePath = Path.GetFullPath(Path.Join(repoRootPath, workspacePath));
39
+ }
40
+
79
41
  result = new()
80
42
  {
81
43
  ErrorType = ErrorType.AuthenticationFailure,
@@ -91,8 +53,50 @@ public class UpdaterWorker
91
53
  };
92
54
  }
93
55
 
56
+ if (resultOutputPath is { })
57
+ {
58
+ await WriteResultFile(result, resultOutputPath, _logger);
59
+ }
60
+ }
61
+
62
+ public async Task<UpdateOperationResult> RunAsync(string repoRootPath, string workspacePath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive)
63
+ {
64
+ MSBuildHelper.RegisterMSBuild(Environment.CurrentDirectory, repoRootPath);
65
+
66
+ if (!Path.IsPathRooted(workspacePath) || !File.Exists(workspacePath))
67
+ {
68
+ workspacePath = Path.GetFullPath(Path.Join(repoRootPath, workspacePath));
69
+ }
70
+
71
+ if (!isTransitive)
72
+ {
73
+ await DotNetToolsJsonUpdater.UpdateDependencyAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, _logger);
74
+ await GlobalJsonUpdater.UpdateDependencyAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, _logger);
75
+ }
76
+
77
+ var extension = Path.GetExtension(workspacePath).ToLowerInvariant();
78
+ switch (extension)
79
+ {
80
+ case ".sln":
81
+ await RunForSolutionAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
82
+ break;
83
+ case ".proj":
84
+ await RunForProjFileAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
85
+ break;
86
+ case ".csproj":
87
+ case ".fsproj":
88
+ case ".vbproj":
89
+ await RunForProjectAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
90
+ break;
91
+ default:
92
+ _logger.Log($"File extension [{extension}] is not supported.");
93
+ break;
94
+ }
95
+
96
+ _logger.Log("Update complete.");
97
+
94
98
  _processedProjectPaths.Clear();
95
- return result;
99
+ return new UpdateOperationResult();
96
100
  }
97
101
 
98
102
  internal static async Task WriteResultFile(UpdateOperationResult result, string resultOutputPath, Logger logger)
@@ -322,7 +322,7 @@ internal static partial class MSBuildHelper
322
322
  try
323
323
  {
324
324
  var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages);
325
- var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"restore \"{tempProjectPath}\"", workingDirectory: tempDirectory.FullName);
325
+ var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", ["restore", tempProjectPath], workingDirectory: tempDirectory.FullName);
326
326
 
327
327
  // NU1608: Detected package version outside of dependency constraint
328
328
 
@@ -359,7 +359,7 @@ internal static partial class MSBuildHelper
359
359
  try
360
360
  {
361
361
  string tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages);
362
- var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"restore \"{tempProjectPath}\"", workingDirectory: tempDirectory.FullName);
362
+ var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", ["restore", tempProjectPath], workingDirectory: tempDirectory.FullName);
363
363
 
364
364
  // Add Dependency[] packages to List<PackageToUpdate> existingPackages
365
365
  List<PackageToUpdate> existingPackages = packages
@@ -515,7 +515,7 @@ internal static partial class MSBuildHelper
515
515
  try
516
516
  {
517
517
  var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages);
518
- var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"restore \"{tempProjectPath}\"", workingDirectory: tempDirectory.FullName);
518
+ var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", ["restore", tempProjectPath], workingDirectory: tempDirectory.FullName);
519
519
  ThrowOnUnauthenticatedFeed(stdOut);
520
520
 
521
521
  // simple cases first
@@ -540,7 +540,7 @@ internal static partial class MSBuildHelper
540
540
  foreach ((string PackageName, NuGetVersion packageVersion) in badPackagesAndVersions)
541
541
  {
542
542
  // this command dumps a JSON object with all versions of the specified package from all package sources
543
- (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"package search {PackageName} --exact-match --format json", workingDirectory: tempDirectory.FullName);
543
+ (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", ["package", "search", PackageName, "--exact-match", "--format", "json"], workingDirectory: tempDirectory.FullName);
544
544
  if (exitCode != 0)
545
545
  {
546
546
  continue;
@@ -770,7 +770,7 @@ internal static partial class MSBuildHelper
770
770
  var topLevelPackagesNames = packages.Select(p => p.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
771
771
  var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages);
772
772
 
773
- var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet", $"build \"{tempProjectPath}\" /t:_ReportDependencies", workingDirectory: tempDirectory.FullName);
773
+ var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet", ["build", tempProjectPath, "/t:_ReportDependencies"], workingDirectory: tempDirectory.FullName);
774
774
  ThrowOnUnauthenticatedFeed(stdout);
775
775
 
776
776
  if (exitCode == 0)
@@ -26,7 +26,7 @@ internal static class NuGetHelper
26
26
  try
27
27
  {
28
28
  var tempProjectPath = await MSBuildHelper.CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, "netstandard2.0", packages, usePackageDownload: true);
29
- var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"restore \"{tempProjectPath}\"");
29
+ var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", ["restore", tempProjectPath]);
30
30
 
31
31
  return exitCode == 0;
32
32
  }
@@ -5,17 +5,15 @@ namespace NuGetUpdater.Core;
5
5
 
6
6
  public static class ProcessEx
7
7
  {
8
- public static Task<(int ExitCode, string Output, string Error)> RunAsync(string fileName, string arguments = "", string? workingDirectory = null)
8
+ public static Task<(int ExitCode, string Output, string Error)> RunAsync(string fileName, IEnumerable<string>? arguments = null, string? workingDirectory = null)
9
9
  {
10
10
  var tcs = new TaskCompletionSource<(int, string, string)>();
11
11
 
12
12
  var redirectInitiated = new ManualResetEventSlim();
13
13
  var process = new Process
14
14
  {
15
- StartInfo =
15
+ StartInfo = new ProcessStartInfo(fileName, arguments ?? [])
16
16
  {
17
- FileName = fileName,
18
- Arguments = arguments,
19
17
  UseShellExecute = false, // required to redirect output
20
18
  RedirectStandardOutput = true,
21
19
  RedirectStandardError = true,
@@ -46,11 +46,14 @@ public class RequirementTests
46
46
  }
47
47
 
48
48
  [Theory]
49
+ [InlineData("> *", "> 0")] // standard wildcard, no digits
49
50
  [InlineData("> 1.*", "> 1.0")] // standard wildcard, single digit
50
51
  [InlineData("> 1.2.*", "> 1.2.0")] // standard wildcard, multiple digit
52
+ [InlineData("> a", "> 0")] // alternate wildcard, no digits
51
53
  [InlineData("> 1.a", "> 1.0")] // alternate wildcard, single digit
52
54
  [InlineData("> 1.2.a", "> 1.2.0")] // alternate wildcard, multiple digit
53
- public void Parse_ConvertsWildcardInVersion(string givenRequirementString, string expectedRequirementString)
55
+ [InlineData(">= 1.40.0, ", ">= 1.40.0")] // empty string following comma
56
+ public void Parse_Requirement(string givenRequirementString, string expectedRequirementString)
54
57
  {
55
58
  var parsedRequirement = Requirement.Parse(givenRequirementString);
56
59
  var actualRequirementString = parsedRequirement.ToString();
@@ -315,7 +315,7 @@ namespace NuGetUpdater.Core.Test
315
315
  </Project>
316
316
  """
317
317
  );
318
- var (exitCode, stdout, stderr) = ProcessEx.RunAsync("dotnet", $"msbuild {projectPath} /t:_ReportCurrentSdkVersion").Result;
318
+ var (exitCode, stdout, stderr) = ProcessEx.RunAsync("dotnet", ["msbuild", projectPath, "/t:_ReportCurrentSdkVersion"]).Result;
319
319
  if (exitCode != 0)
320
320
  {
321
321
  throw new Exception($"Failed to report the current SDK version:\n{stdout}\n{stderr}");
@@ -391,6 +391,7 @@ namespace NuGetUpdater.Core.Test
391
391
  WellKnownReferencePackage("Microsoft.AspNetCore.App", "net6.0"),
392
392
  WellKnownReferencePackage("Microsoft.AspNetCore.App", "net7.0"),
393
393
  WellKnownReferencePackage("Microsoft.AspNetCore.App", "net8.0"),
394
+ WellKnownReferencePackage("Microsoft.AspNetCore.App", "net9.0"),
394
395
  WellKnownReferencePackage("Microsoft.NETCore.App", "net6.0",
395
396
  [
396
397
  ("data/FrameworkList.xml", Encoding.UTF8.GetBytes("""
@@ -412,9 +413,17 @@ namespace NuGetUpdater.Core.Test
412
413
  </FileList>
413
414
  """))
414
415
  ]),
416
+ WellKnownReferencePackage("Microsoft.NETCore.App", "net9.0",
417
+ [
418
+ ("data/FrameworkList.xml", Encoding.UTF8.GetBytes("""
419
+ <FileList TargetFrameworkIdentifier=".NETCoreApp" TargetFrameworkVersion="9.0" FrameworkName="Microsoft.NETCore.App" Name=".NET Runtime">
420
+ </FileList>
421
+ """))
422
+ ]),
415
423
  WellKnownReferencePackage("Microsoft.WindowsDesktop.App", "net6.0"),
416
424
  WellKnownReferencePackage("Microsoft.WindowsDesktop.App", "net7.0"),
417
425
  WellKnownReferencePackage("Microsoft.WindowsDesktop.App", "net8.0"),
426
+ WellKnownReferencePackage("Microsoft.WindowsDesktop.App", "net9.0"),
418
427
  ];
419
428
  }
420
429
  }
@@ -169,6 +169,98 @@ public class RunWorkerTests
169
169
  );
170
170
  }
171
171
 
172
+ [Fact]
173
+ public async Task PrivateSourceAuthenticationFailureIsForwaredToApiHandler()
174
+ {
175
+ static (int, string) TestHttpHandler(string uriString)
176
+ {
177
+ var uri = new Uri(uriString, UriKind.Absolute);
178
+ var baseUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}";
179
+ return uri.PathAndQuery switch
180
+ {
181
+ // initial request is good
182
+ "/index.json" => (200, $$"""
183
+ {
184
+ "version": "3.0.0",
185
+ "resources": [
186
+ {
187
+ "@id": "{{baseUrl}}/download",
188
+ "@type": "PackageBaseAddress/3.0.0"
189
+ },
190
+ {
191
+ "@id": "{{baseUrl}}/query",
192
+ "@type": "SearchQueryService"
193
+ },
194
+ {
195
+ "@id": "{{baseUrl}}/registrations",
196
+ "@type": "RegistrationsBaseUrl"
197
+ }
198
+ ]
199
+ }
200
+ """),
201
+ // all other requests are unauthorized
202
+ _ => (401, "{}"),
203
+ };
204
+ }
205
+ using var http = TestHttpServer.CreateTestStringServer(TestHttpHandler);
206
+ await RunAsync(
207
+ packages:
208
+ [
209
+ ],
210
+ job: new Job()
211
+ {
212
+ PackageManager = "nuget",
213
+ Source = new()
214
+ {
215
+ Provider = "github",
216
+ Repo = "test/repo",
217
+ Directory = "/",
218
+ },
219
+ AllowedUpdates =
220
+ [
221
+ new() { UpdateType = "all" }
222
+ ]
223
+ },
224
+ files:
225
+ [
226
+ ("NuGet.Config", $"""
227
+ <configuration>
228
+ <packageSources>
229
+ <clear />
230
+ <add key="private_feed" value="{http.BaseUrl.TrimEnd('/')}/index.json" allowInsecureConnections="true" />
231
+ </packageSources>
232
+ </configuration>
233
+ """),
234
+ ("project.csproj", """
235
+ <Project Sdk="Microsoft.NET.Sdk">
236
+ <PropertyGroup>
237
+ <TargetFramework>net8.0</TargetFramework>
238
+ </PropertyGroup>
239
+ <ItemGroup>
240
+ <PackageReference Include="Some.Package" Version="1.0.0" />
241
+ </ItemGroup>
242
+ </Project>
243
+ """)
244
+ ],
245
+ expectedResult: new RunResult()
246
+ {
247
+ Base64DependencyFiles = [],
248
+ BaseCommitSha = "TEST-COMMIT-SHA",
249
+ },
250
+ expectedApiMessages:
251
+ [
252
+ new PrivateSourceAuthenticationFailure()
253
+ {
254
+ Details = $"({http.BaseUrl.TrimEnd('/')}/index.json)"
255
+ },
256
+ new MarkAsProcessed()
257
+ {
258
+ BaseCommitSha = "TEST-COMMIT-SHA",
259
+ }
260
+ ]
261
+ );
262
+ }
263
+
172
264
  private static async Task RunAsync(Job job, TestFile[] files, RunResult expectedResult, object[] expectedApiMessages, MockNuGetPackage[]? packages = null)
173
265
  {
174
266
  // arrange
@@ -9,27 +9,33 @@ internal class TestApiHandler : IApiHandler
9
9
 
10
10
  public IEnumerable<(Type Type, object Object)> ReceivedMessages => _receivedMessages;
11
11
 
12
+ public Task RecordUpdateJobError(JobErrorBase error)
13
+ {
14
+ _receivedMessages.Add((error.GetType(), error));
15
+ return Task.CompletedTask;
16
+ }
17
+
12
18
  public Task UpdateDependencyList(UpdatedDependencyList updatedDependencyList)
13
19
  {
14
- _receivedMessages.Add((typeof(UpdatedDependencyList), updatedDependencyList));
20
+ _receivedMessages.Add((updatedDependencyList.GetType(), updatedDependencyList));
15
21
  return Task.CompletedTask;
16
22
  }
17
23
 
18
24
  public Task IncrementMetric(IncrementMetric incrementMetric)
19
25
  {
20
- _receivedMessages.Add((typeof(IncrementMetric), incrementMetric));
26
+ _receivedMessages.Add((incrementMetric.GetType(), incrementMetric));
21
27
  return Task.CompletedTask;
22
28
  }
23
29
 
24
30
  public Task CreatePullRequest(CreatePullRequest createPullRequest)
25
31
  {
26
- _receivedMessages.Add((typeof(CreatePullRequest), createPullRequest));
32
+ _receivedMessages.Add((createPullRequest.GetType(), createPullRequest));
27
33
  return Task.CompletedTask;
28
34
  }
29
35
 
30
36
  public Task MarkAsProcessed(MarkAsProcessed markAsProcessed)
31
37
  {
32
- _receivedMessages.Add((typeof(MarkAsProcessed), markAsProcessed));
38
+ _receivedMessages.Add((markAsProcessed.GetType(), markAsProcessed));
33
39
  return Task.CompletedTask;
34
40
  }
35
41
  }
@@ -257,14 +257,6 @@ public abstract class UpdateWorkerTestBase : TestBase
257
257
  package.WriteToDirectory(localFeedPath);
258
258
  }
259
259
 
260
- // override various nuget locations
261
- foreach (var envName in new[] { "NUGET_PACKAGES", "NUGET_HTTP_CACHE_PATH", "NUGET_SCRATCH", "NUGET_PLUGINS_CACHE_PATH" })
262
- {
263
- string dir = Path.Join(temporaryDirectory, envName);
264
- Directory.CreateDirectory(dir);
265
- Environment.SetEnvironmentVariable(envName, dir);
266
- }
267
-
268
260
  // ensure only the test feed is used
269
261
  string relativeLocalFeedPath = Path.GetRelativePath(temporaryDirectory, localFeedPath);
270
262
  await File.WriteAllTextAsync(Path.Join(temporaryDirectory, "NuGet.Config"), $"""
@@ -278,6 +270,14 @@ public abstract class UpdateWorkerTestBase : TestBase
278
270
  """
279
271
  );
280
272
  }
273
+
274
+ // override various nuget locations
275
+ foreach (var envName in new[] { "NUGET_PACKAGES", "NUGET_HTTP_CACHE_PATH", "NUGET_SCRATCH", "NUGET_PLUGINS_CACHE_PATH" })
276
+ {
277
+ string dir = Path.Join(temporaryDirectory, envName);
278
+ Directory.CreateDirectory(dir);
279
+ Environment.SetEnvironmentVariable(envName, dir);
280
+ }
281
281
  }
282
282
 
283
283
  protected static async Task<TestFile[]> RunUpdate(TestFile[] files, Func<string, Task> action)
@@ -16,6 +16,7 @@ public partial class UpdateWorkerTests
16
16
  [InlineData("net472")]
17
17
  [InlineData("net7.0")]
18
18
  [InlineData("net8.0")]
19
+ [InlineData("net9.0")]
19
20
  public async Task UpdateVersionAttribute_InProjectFile_ForPackageReferenceInclude(string tfm)
20
21
  {
21
22
  // update Some.Package from 9.0.1 to 13.0.1
@@ -186,6 +187,7 @@ public partial class UpdateWorkerTests
186
187
  projectContents: $"""
187
188
  <Project Sdk="Microsoft.NET.Sdk">
188
189
  <PropertyGroup>
190
+ <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
189
191
  <TargetFramework>net8.0</TargetFramework>
190
192
  </PropertyGroup>
191
193
  <ItemGroup>
@@ -199,6 +201,7 @@ public partial class UpdateWorkerTests
199
201
  (Path: "src/Project/Project.csproj", Content: """
200
202
  <Project Sdk="Microsoft.NET.Sdk">
201
203
  <PropertyGroup>
204
+ <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
202
205
  <TargetFramework>net8.0</TargetFramework>
203
206
  </PropertyGroup>
204
207
  <ItemGroup>
@@ -211,6 +214,7 @@ public partial class UpdateWorkerTests
211
214
  expectedProjectContents: $"""
212
215
  <Project Sdk="Microsoft.NET.Sdk">
213
216
  <PropertyGroup>
217
+ <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
214
218
  <TargetFramework>net8.0</TargetFramework>
215
219
  </PropertyGroup>
216
220
  <ItemGroup>
@@ -224,6 +228,7 @@ public partial class UpdateWorkerTests
224
228
  (Path: "src/Project/Project.csproj", Content: """
225
229
  <Project Sdk="Microsoft.NET.Sdk">
226
230
  <PropertyGroup>
231
+ <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
227
232
  <TargetFramework>net8.0</TargetFramework>
228
233
  </PropertyGroup>
229
234
  <ItemGroup>
@@ -244,7 +249,7 @@ public partial class UpdateWorkerTests
244
249
  MockNuGetPackage.CreateSimplePackage("Some.Package", "9.0.1", "net8.0"),
245
250
  MockNuGetPackage.CreateSimplePackage("Some.Package", "13.0.1", "net8.0"),
246
251
  // necessary for the `net8.0-windows10.0.19041.0` TFM
247
- new("Microsoft.Windows.SDK.NET.Ref", "10.0.19041.31", Files:
252
+ new("Microsoft.Windows.SDK.NET.Ref", "10.0.19041.34", Files:
248
253
  [
249
254
  ("data/FrameworkList.xml", Encoding.UTF8.GetBytes("""
250
255
  <FileList Name="Windows SDK .NET 6.0">
@@ -548,6 +553,7 @@ public partial class UpdateWorkerTests
548
553
  <Project Sdk="Microsoft.NET.Sdk">
549
554
 
550
555
  <PropertyGroup>
556
+ <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
551
557
  <TargetFramework>net8.0</TargetFramework>
552
558
  </PropertyGroup>
553
559
 
@@ -562,6 +568,7 @@ public partial class UpdateWorkerTests
562
568
  <Project Sdk="Microsoft.NET.Sdk">
563
569
 
564
570
  <PropertyGroup>
571
+ <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
565
572
  <TargetFramework>net8.0</TargetFramework>
566
573
  </PropertyGroup>
567
574
 
@@ -588,6 +595,7 @@ public partial class UpdateWorkerTests
588
595
  projectContents: """
589
596
  <Project Sdk="Microsoft.NET.Sdk">
590
597
  <PropertyGroup>
598
+ <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
591
599
  <TargetFramework>net8.0</TargetFramework>
592
600
  </PropertyGroup>
593
601
  <ItemGroup>
@@ -608,6 +616,7 @@ public partial class UpdateWorkerTests
608
616
  expectedProjectContents: """
609
617
  <Project Sdk="Microsoft.NET.Sdk">
610
618
  <PropertyGroup>
619
+ <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
611
620
  <TargetFramework>net8.0</TargetFramework>
612
621
  </PropertyGroup>
613
622
  <ItemGroup>
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "sdk": {
3
- "version": "8.0.300",
3
+ "version": "9.0.100-rc.1.24452.12",
4
4
  "rollForward": "latestMinor"
5
5
  }
6
6
  }
@@ -51,7 +51,7 @@ module Dependabot
51
51
  sig { override.returns(T::Array[Dependabot::DependencyFile]) }
52
52
  def updated_dependency_files
53
53
  base_dir = "/"
54
- SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
54
+ all_updated_files = SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
55
55
  dependencies.each do |dependency|
56
56
  try_update_projects(dependency) || try_update_json(dependency)
57
57
  end
@@ -70,6 +70,10 @@ module Dependabot
70
70
  end
71
71
  updated_files
72
72
  end
73
+
74
+ raise UpdateNotPossible, dependencies.map(&:name) if all_updated_files.empty?
75
+
76
+ all_updated_files
73
77
  end
74
78
 
75
79
  private
@@ -242,8 +242,11 @@ module Dependabot
242
242
  puts "running NuGet updater:\n" + command
243
243
 
244
244
  NuGetConfigCredentialHelpers.patch_nuget_config_for_action(credentials) do
245
+ # default to UseNewNugetPackageResolved _unless_ nuget_legacy_dependency_solver is enabled
245
246
  env = {}
246
- env["UseNewNugetPackageResolver"] = "true" if Dependabot::Experiments.enabled?(:nuget_dependency_solver)
247
+ unless Dependabot::Experiments.enabled?(:nuget_legacy_dependency_solver)
248
+ env["UseNewNugetPackageResolver"] = "true"
249
+ end
247
250
  output = SharedHelpers.run_shell_command(command,
248
251
  allow_unsafe_shell_command: true,
249
252
  fingerprint: fingerprint,
@@ -42,6 +42,8 @@ module Dependabot
42
42
  convert_dotnet_constraint_to_ruby_constraint(req_string)
43
43
  end
44
44
 
45
+ requirements = requirements.compact.reject(&:empty?)
46
+
45
47
  super(requirements)
46
48
  end
47
49
 
@@ -13,6 +13,7 @@ require "dependabot/nuget/http_response_helpers"
13
13
 
14
14
  module Dependabot
15
15
  module Nuget
16
+ # rubocop:disable Metrics/ClassLength
16
17
  class RepositoryFinder
17
18
  extend T::Sig
18
19
 
@@ -48,7 +49,15 @@ module Dependabot
48
49
  @known_repositories << { url: DEFAULT_REPOSITORY_URL, token: nil } if @known_repositories.empty?
49
50
 
50
51
  @known_repositories = @known_repositories.map do |repo|
51
- { url: URI::DEFAULT_PARSER.escape(repo[:url]), token: repo[:token] }
52
+ url = repo[:url]
53
+ begin
54
+ url = URI::DEFAULT_PARSER.parse(url).to_s
55
+ rescue URI::InvalidURIError
56
+ # e.g., the url has spaces or unacceptable symbols
57
+ url = URI::DEFAULT_PARSER.escape(url)
58
+ end
59
+
60
+ { url: url, token: repo[:token] }
52
61
  end
53
62
  @known_repositories.uniq
54
63
  end
@@ -68,6 +77,20 @@ module Dependabot
68
77
  }
69
78
  end
70
79
 
80
+ sig { params(source_name: String).returns(String) }
81
+ def self.escape_source_name_to_element_name(source_name)
82
+ source_name.chars.map do |c|
83
+ case c
84
+ when /[A-Za-z0-9\-_.]/
85
+ # letters, digits, hyphens, underscores, and periods are all directly allowed
86
+ c
87
+ else
88
+ # otherwise it needs to be escaped as a 4 digit hex value
89
+ "_x#{c.ord.to_s(16).rjust(4, '0')}_"
90
+ end
91
+ end.join
92
+ end
93
+
71
94
  private
72
95
 
73
96
  sig { returns(Dependabot::Dependency) }
@@ -376,7 +399,7 @@ module Dependabot
376
399
  next source_details[:token] = nil unless key
377
400
  next source_details[:token] = nil if key.match?(/^\d/)
378
401
 
379
- tag = key.gsub(" ", "_x0020_")
402
+ tag = RepositoryFinder.escape_source_name_to_element_name(key)
380
403
  creds_nodes = doc.css("configuration > packageSourceCredentials " \
381
404
  "> #{tag} > add")
382
405
 
@@ -438,5 +461,6 @@ module Dependabot
438
461
  end
439
462
  end
440
463
  end
464
+ # rubocop:enable Metrics/ClassLength
441
465
  end
442
466
  end