dependabot-nuget 0.278.0 → 0.280.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 +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
|