dependabot-nuget 0.278.0 → 0.280.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/helpers/build +1 -1
- data/helpers/lib/NuGetUpdater/.editorconfig +1 -0
- data/helpers/lib/NuGetUpdater/Directory.Build.props +1 -0
- data/helpers/lib/NuGetUpdater/Directory.Common.props +1 -1
- data/helpers/lib/NuGetUpdater/Directory.Packages.props +6 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +7 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +6 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +2 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +95 -84
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/Requirement.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +53 -46
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NativeResult.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotFound.cs +6 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +11 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PrivateSourceAuthenticationFailure.cs +6 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UnknownError.cs +6 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UpdateNotPossible.cs +6 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/HttpApiHandler.cs +5 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/IApiHandler.cs +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +67 -15
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/UpdateNotPossibleException.cs +11 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +2 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +58 -39
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +16 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProcessExtensions.cs +2 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +5 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/RequirementTests.cs +4 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +5 -8
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +10 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +92 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/TestApiHandler.cs +10 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +10 -15
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +79 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +10 -1
- data/helpers/lib/NuGetUpdater/global.json +1 -1
- data/lib/dependabot/nuget/file_updater.rb +5 -1
- data/lib/dependabot/nuget/native_helpers.rb +9 -4
- data/lib/dependabot/nuget/requirement.rb +2 -0
- data/lib/dependabot/nuget/update_checker/repository_finder.rb +26 -2
- metadata +16 -10
@@ -1,3 +1,4 @@
|
|
1
|
+
using System.Net;
|
1
2
|
using System.Text;
|
2
3
|
using System.Text.Json;
|
3
4
|
using System.Text.Json.Serialization;
|
@@ -35,25 +36,80 @@ public class RunWorker
|
|
35
36
|
await File.WriteAllTextAsync(outputFilePath.FullName, resultJson);
|
36
37
|
}
|
37
38
|
|
38
|
-
public
|
39
|
+
public Task<RunResult> RunAsync(Job job, DirectoryInfo repoContentsPath, string baseCommitSha)
|
39
40
|
{
|
40
|
-
|
41
|
+
return RunWithErrorHandlingAsync(job, repoContentsPath, baseCommitSha);
|
42
|
+
}
|
43
|
+
|
44
|
+
private async Task<RunResult> RunWithErrorHandlingAsync(Job job, DirectoryInfo repoContentsPath, string baseCommitSha)
|
45
|
+
{
|
46
|
+
JobErrorBase? error = null;
|
47
|
+
string[] lastUsedPackageSourceUrls = []; // used for error reporting below
|
48
|
+
var runResult = new RunResult()
|
49
|
+
{
|
50
|
+
Base64DependencyFiles = [],
|
51
|
+
BaseCommitSha = baseCommitSha,
|
52
|
+
};
|
41
53
|
|
42
|
-
|
43
|
-
foreach (var directory in job.GetAllDirectories())
|
54
|
+
try
|
44
55
|
{
|
45
|
-
|
46
|
-
|
56
|
+
MSBuildHelper.RegisterMSBuild(repoContentsPath.FullName, repoContentsPath.FullName);
|
57
|
+
|
58
|
+
var allDependencyFiles = new Dictionary<string, DependencyFile>();
|
59
|
+
foreach (var directory in job.GetAllDirectories())
|
47
60
|
{
|
48
|
-
|
61
|
+
var localPath = PathHelper.JoinPath(repoContentsPath.FullName, directory);
|
62
|
+
lastUsedPackageSourceUrls = NuGetContext.GetPackageSourceUrls(localPath);
|
63
|
+
var result = await RunForDirectory(job, repoContentsPath, directory, baseCommitSha);
|
64
|
+
foreach (var dependencyFile in result.Base64DependencyFiles)
|
65
|
+
{
|
66
|
+
allDependencyFiles[dependencyFile.Name] = dependencyFile;
|
67
|
+
}
|
49
68
|
}
|
69
|
+
|
70
|
+
runResult = new RunResult()
|
71
|
+
{
|
72
|
+
Base64DependencyFiles = allDependencyFiles.Values.ToArray(),
|
73
|
+
BaseCommitSha = baseCommitSha,
|
74
|
+
};
|
75
|
+
}
|
76
|
+
catch (HttpRequestException ex)
|
77
|
+
when (ex.StatusCode == HttpStatusCode.Unauthorized || ex.StatusCode == HttpStatusCode.Forbidden)
|
78
|
+
{
|
79
|
+
error = new PrivateSourceAuthenticationFailure()
|
80
|
+
{
|
81
|
+
Details = $"({string.Join("|", lastUsedPackageSourceUrls)})",
|
82
|
+
};
|
83
|
+
}
|
84
|
+
catch (MissingFileException ex)
|
85
|
+
{
|
86
|
+
error = new DependencyFileNotFound()
|
87
|
+
{
|
88
|
+
Details = ex.FilePath,
|
89
|
+
};
|
90
|
+
}
|
91
|
+
catch (UpdateNotPossibleException ex)
|
92
|
+
{
|
93
|
+
error = new UpdateNotPossible()
|
94
|
+
{
|
95
|
+
Details = ex.Dependencies,
|
96
|
+
};
|
97
|
+
}
|
98
|
+
catch (Exception ex)
|
99
|
+
{
|
100
|
+
error = new UnknownError()
|
101
|
+
{
|
102
|
+
Details = ex.ToString(),
|
103
|
+
};
|
50
104
|
}
|
51
105
|
|
52
|
-
|
106
|
+
if (error is not null)
|
53
107
|
{
|
54
|
-
|
55
|
-
|
56
|
-
|
108
|
+
await _apiHandler.RecordUpdateJobError(error);
|
109
|
+
}
|
110
|
+
|
111
|
+
await _apiHandler.MarkAsProcessed(new() { BaseCommitSha = baseCommitSha });
|
112
|
+
|
57
113
|
return runResult;
|
58
114
|
}
|
59
115
|
|
@@ -61,7 +117,6 @@ public class RunWorker
|
|
61
117
|
{
|
62
118
|
var discoveryWorker = new DiscoveryWorker(_logger);
|
63
119
|
var discoveryResult = await discoveryWorker.RunAsync(repoContentsPath.FullName, repoDirectory);
|
64
|
-
// TODO: check discoveryResult.ErrorType
|
65
120
|
|
66
121
|
_logger.Log("Discovery JSON content:");
|
67
122
|
_logger.Log(JsonSerializer.Serialize(discoveryResult, DiscoveryWorker.SerializerOptions));
|
@@ -123,7 +178,6 @@ public class RunWorker
|
|
123
178
|
};
|
124
179
|
var analysisResult = await analyzeWorker.RunAsync(repoContentsPath.FullName, discoveryResult, dependencyInfo);
|
125
180
|
// TODO: log analysisResult
|
126
|
-
// TODO: check analysisResult.ErrorType
|
127
181
|
if (analysisResult.CanUpdate)
|
128
182
|
{
|
129
183
|
// TODO: this is inefficient, but not likely causing a bottleneck
|
@@ -153,7 +207,6 @@ public class RunWorker
|
|
153
207
|
var updateWorker = new UpdaterWorker(_logger);
|
154
208
|
var dependencyFilePath = Path.Join(discoveryResult.Path, project.FilePath).NormalizePathToUnix();
|
155
209
|
var updateResult = await updateWorker.RunAsync(repoContentsPath.FullName, dependencyFilePath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, isTransitive: false);
|
156
|
-
// TODO: check specific contents of result.ErrorType
|
157
210
|
// TODO: need to report if anything was actually updated
|
158
211
|
if (updateResult.ErrorType is null || updateResult.ErrorType == ErrorType.None)
|
159
212
|
{
|
@@ -206,7 +259,6 @@ public class RunWorker
|
|
206
259
|
// TODO: throw if no updates performed
|
207
260
|
}
|
208
261
|
|
209
|
-
await _apiHandler.MarkAsProcessed(new() { BaseCommitSha = baseCommitSha });
|
210
262
|
var result = new RunResult()
|
211
263
|
{
|
212
264
|
Base64DependencyFiles = originalDependencyFileContents.Select(kvp => new DependencyFile()
|
@@ -18,7 +18,7 @@ internal static class LockFileUpdater
|
|
18
18
|
|
19
19
|
await MSBuildHelper.SidelineGlobalJsonAsync(projectDirectory, repoRootPath, async () =>
|
20
20
|
{
|
21
|
-
var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet",
|
21
|
+
var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet", ["restore", "--force-evaluate", projectPath], workingDirectory: projectDirectory);
|
22
22
|
if (exitCode != 0)
|
23
23
|
{
|
24
24
|
logger.Log($" Lock file update failed.\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}");
|
@@ -139,6 +139,7 @@ internal static class PackagesConfigUpdater
|
|
139
139
|
|
140
140
|
if (exitCodeAgain != 0)
|
141
141
|
{
|
142
|
+
MSBuildHelper.ThrowOnMissingPackages(restoreOutput);
|
142
143
|
throw new Exception($"Unable to restore.\nOutput:\n${restoreOutput}\n");
|
143
144
|
}
|
144
145
|
|
@@ -147,6 +148,7 @@ internal static class PackagesConfigUpdater
|
|
147
148
|
|
148
149
|
MSBuildHelper.ThrowOnUnauthenticatedFeed(fullOutput);
|
149
150
|
MSBuildHelper.ThrowOnMissingFile(fullOutput);
|
151
|
+
MSBuildHelper.ThrowOnMissingPackages(fullOutput);
|
150
152
|
throw new Exception(fullOutput);
|
151
153
|
}
|
152
154
|
}
|
@@ -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, stdout, stderr) = await ProcessEx.RunAsync("dotnet",
|
234
|
+
var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet", ["add", projectPath, "package", dependencyName, "--version", newDependencyVersion], workingDirectory: projectDirectory);
|
235
235
|
MSBuildHelper.ThrowOnUnauthenticatedFeed(stdout);
|
236
236
|
if (exitCode != 0)
|
237
237
|
{
|
@@ -350,7 +350,7 @@ internal static class SdkPackageUpdater
|
|
350
350
|
var specificResolvedDependency = resolvedDependencies.Where(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
|
351
351
|
if (specificResolvedDependency is null)
|
352
352
|
{
|
353
|
-
logger.Log($" Unable resolve requested dependency for {dependencyName} in {projectFile.Path}.");
|
353
|
+
logger.Log($" Unable to resolve requested dependency for {dependencyName} in {projectFile.Path}.");
|
354
354
|
continue;
|
355
355
|
}
|
356
356
|
|
@@ -25,57 +25,29 @@ 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
|
28
|
+
var result = await RunWithErrorHandlingAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
29
29
|
if (resultOutputPath is { })
|
30
30
|
{
|
31
31
|
await WriteResultFile(result, resultOutputPath, _logger);
|
32
32
|
}
|
33
33
|
}
|
34
34
|
|
35
|
-
|
35
|
+
// this is a convenient method for tests
|
36
|
+
internal async Task<UpdateOperationResult> RunWithErrorHandlingAsync(string repoRootPath, string workspacePath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive)
|
36
37
|
{
|
37
|
-
|
38
|
-
UpdateOperationResult result;
|
39
|
-
|
40
|
-
if (!Path.IsPathRooted(workspacePath) || !File.Exists(workspacePath))
|
41
|
-
{
|
42
|
-
workspacePath = Path.GetFullPath(Path.Join(repoRootPath, workspacePath));
|
43
|
-
}
|
44
|
-
|
38
|
+
UpdateOperationResult result = new(); // assumed to be ok until proven otherwise
|
45
39
|
try
|
46
40
|
{
|
47
|
-
|
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.");
|
41
|
+
result = await RunAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
74
42
|
}
|
75
43
|
catch (HttpRequestException ex)
|
76
44
|
when (ex.StatusCode == HttpStatusCode.Unauthorized || ex.StatusCode == HttpStatusCode.Forbidden)
|
77
45
|
{
|
78
|
-
|
46
|
+
if (!Path.IsPathRooted(workspacePath) || !File.Exists(workspacePath))
|
47
|
+
{
|
48
|
+
workspacePath = Path.GetFullPath(Path.Join(repoRootPath, workspacePath));
|
49
|
+
}
|
50
|
+
|
79
51
|
result = new()
|
80
52
|
{
|
81
53
|
ErrorType = ErrorType.AuthenticationFailure,
|
@@ -90,11 +62,58 @@ public class UpdaterWorker
|
|
90
62
|
ErrorDetails = ex.FilePath,
|
91
63
|
};
|
92
64
|
}
|
65
|
+
catch (UpdateNotPossibleException ex)
|
66
|
+
{
|
67
|
+
result = new()
|
68
|
+
{
|
69
|
+
ErrorType = ErrorType.UpdateNotPossible,
|
70
|
+
ErrorDetails = ex.Dependencies,
|
71
|
+
};
|
72
|
+
}
|
93
73
|
|
94
|
-
_processedProjectPaths.Clear();
|
95
74
|
return result;
|
96
75
|
}
|
97
76
|
|
77
|
+
public async Task<UpdateOperationResult> RunAsync(string repoRootPath, string workspacePath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive)
|
78
|
+
{
|
79
|
+
MSBuildHelper.RegisterMSBuild(Environment.CurrentDirectory, repoRootPath);
|
80
|
+
|
81
|
+
if (!Path.IsPathRooted(workspacePath) || !File.Exists(workspacePath))
|
82
|
+
{
|
83
|
+
workspacePath = Path.GetFullPath(Path.Join(repoRootPath, workspacePath));
|
84
|
+
}
|
85
|
+
|
86
|
+
if (!isTransitive)
|
87
|
+
{
|
88
|
+
await DotNetToolsJsonUpdater.UpdateDependencyAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, _logger);
|
89
|
+
await GlobalJsonUpdater.UpdateDependencyAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, _logger);
|
90
|
+
}
|
91
|
+
|
92
|
+
var extension = Path.GetExtension(workspacePath).ToLowerInvariant();
|
93
|
+
switch (extension)
|
94
|
+
{
|
95
|
+
case ".sln":
|
96
|
+
await RunForSolutionAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
97
|
+
break;
|
98
|
+
case ".proj":
|
99
|
+
await RunForProjFileAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
100
|
+
break;
|
101
|
+
case ".csproj":
|
102
|
+
case ".fsproj":
|
103
|
+
case ".vbproj":
|
104
|
+
await RunForProjectAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
105
|
+
break;
|
106
|
+
default:
|
107
|
+
_logger.Log($"File extension [{extension}] is not supported.");
|
108
|
+
break;
|
109
|
+
}
|
110
|
+
|
111
|
+
_logger.Log("Update complete.");
|
112
|
+
|
113
|
+
_processedProjectPaths.Clear();
|
114
|
+
return new UpdateOperationResult();
|
115
|
+
}
|
116
|
+
|
98
117
|
internal static async Task WriteResultFile(UpdateOperationResult result, string resultOutputPath, Logger logger)
|
99
118
|
{
|
100
119
|
logger.Log($" Writing update result to [{resultOutputPath}].");
|
@@ -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",
|
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",
|
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",
|
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",
|
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",
|
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)
|
@@ -833,6 +833,17 @@ internal static partial class MSBuildHelper
|
|
833
833
|
}
|
834
834
|
}
|
835
835
|
|
836
|
+
internal static void ThrowOnMissingPackages(string output)
|
837
|
+
{
|
838
|
+
var missingPackagesPattern = new Regex(@"Package '(?<PackageName>[^'].*)' is not found on source");
|
839
|
+
var matchCollection = missingPackagesPattern.Matches(output);
|
840
|
+
var missingPackages = matchCollection.Select(m => m.Groups["PackageName"].Value).Distinct().ToArray();
|
841
|
+
if (missingPackages.Length > 0)
|
842
|
+
{
|
843
|
+
throw new UpdateNotPossibleException(missingPackages);
|
844
|
+
}
|
845
|
+
}
|
846
|
+
|
836
847
|
internal static bool TryGetGlobalJsonPath(string repoRootPath, string workspacePath, [NotNullWhen(returnValue: true)] out string? globalJsonPath)
|
837
848
|
{
|
838
849
|
globalJsonPath = PathHelper.GetFileInDirectoryOrParent(workspacePath, repoRootPath, "global.json", caseSensitive: false);
|
@@ -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",
|
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 =
|
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,
|
@@ -35,10 +35,10 @@ public class AnalyzeWorkerTestBase
|
|
35
35
|
|
36
36
|
var discoveryPath = Path.GetFullPath(DiscoveryWorker.DiscoveryResultFileName, directoryPath);
|
37
37
|
var dependencyPath = Path.GetFullPath(relativeDependencyPath, directoryPath);
|
38
|
-
var analysisPath = Path.GetFullPath(AnalyzeWorker.AnalysisDirectoryName, directoryPath);
|
39
38
|
|
40
39
|
var worker = new AnalyzeWorker(new Logger(verbose: true));
|
41
|
-
await worker.
|
40
|
+
var result = await worker.RunWithErrorHandlingAsync(directoryPath, discoveryPath, dependencyPath);
|
41
|
+
return result;
|
42
42
|
});
|
43
43
|
|
44
44
|
ValidateAnalysisResult(expectedResult, actualResult);
|
@@ -78,17 +78,13 @@ public class AnalyzeWorkerTestBase
|
|
78
78
|
}
|
79
79
|
}
|
80
80
|
|
81
|
-
protected static async Task<AnalysisResult> RunAnalyzerAsync(string dependencyName, TestFile[] files, Func<string, Task
|
81
|
+
protected static async Task<AnalysisResult> RunAnalyzerAsync(string dependencyName, TestFile[] files, Func<string, Task<AnalysisResult>> action)
|
82
82
|
{
|
83
83
|
// write initial files
|
84
84
|
using var temporaryDirectory = await TemporaryDirectory.CreateWithContentsAsync(files);
|
85
85
|
|
86
86
|
// run discovery
|
87
|
-
await action(temporaryDirectory.DirectoryPath);
|
88
|
-
|
89
|
-
// gather results
|
90
|
-
var resultPath = Path.Join(temporaryDirectory.DirectoryPath, AnalyzeWorker.AnalysisDirectoryName, $"{dependencyName}.json");
|
91
|
-
var resultJson = await File.ReadAllTextAsync(resultPath);
|
92
|
-
return JsonSerializer.Deserialize<AnalysisResult>(resultJson, DiscoveryWorker.SerializerOptions)!;
|
87
|
+
var result = await action(temporaryDirectory.DirectoryPath);
|
88
|
+
return result;
|
93
89
|
}
|
94
90
|
}
|
@@ -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
|
-
|
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();
|
@@ -25,7 +25,8 @@ public class DiscoveryWorkerTestBase
|
|
25
25
|
await UpdateWorkerTestBase.MockNuGetPackagesInDirectory(packages, directoryPath);
|
26
26
|
|
27
27
|
var worker = new DiscoveryWorker(new Logger(verbose: true));
|
28
|
-
await worker.
|
28
|
+
var result = await worker.RunWithErrorHandlingAsync(directoryPath, workspacePath);
|
29
|
+
return result;
|
29
30
|
});
|
30
31
|
|
31
32
|
ValidateWorkspaceResult(expectedResult, actualResult);
|
@@ -108,18 +109,14 @@ public class DiscoveryWorkerTestBase
|
|
108
109
|
}
|
109
110
|
}
|
110
111
|
|
111
|
-
protected static async Task<WorkspaceDiscoveryResult> RunDiscoveryAsync(TestFile[] files, Func<string, Task
|
112
|
+
protected static async Task<WorkspaceDiscoveryResult> RunDiscoveryAsync(TestFile[] files, Func<string, Task<WorkspaceDiscoveryResult>> action)
|
112
113
|
{
|
113
114
|
// write initial files
|
114
115
|
using var temporaryDirectory = await TemporaryDirectory.CreateWithContentsAsync(files);
|
115
116
|
|
116
117
|
// run discovery
|
117
|
-
await action(temporaryDirectory.DirectoryPath);
|
118
|
-
|
119
|
-
// gather results
|
120
|
-
var resultPath = Path.Join(temporaryDirectory.DirectoryPath, DiscoveryWorker.DiscoveryResultFileName);
|
121
|
-
var resultJson = await File.ReadAllTextAsync(resultPath);
|
122
|
-
return JsonSerializer.Deserialize<WorkspaceDiscoveryResult>(resultJson, DiscoveryWorker.SerializerOptions)!;
|
118
|
+
var result = await action(temporaryDirectory.DirectoryPath);
|
119
|
+
return result;
|
123
120
|
}
|
124
121
|
|
125
122
|
internal class PropertyComparer : IEqualityComparer<Property>
|
@@ -315,7 +315,7 @@ namespace NuGetUpdater.Core.Test
|
|
315
315
|
</Project>
|
316
316
|
"""
|
317
317
|
);
|
318
|
-
var (exitCode, stdout, stderr) = ProcessEx.RunAsync("dotnet",
|
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
|