dependabot-nuget 0.315.0 → 0.316.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/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/WorkspaceDiscoveryResult.cs +6 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +3 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/ClosePullRequest.cs +15 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CreatePullRequest.cs +47 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyGroup.cs +60 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Job.cs +151 -23
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +4 -18
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PullRequestExistsForSecurityUpdate.cs +15 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/SecurityUpdateDependencyNotFound.cs +9 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/SecurityUpdateIgnored.cs +10 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/SecurityUpdateNotFound.cs +11 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/SecurityUpdateNotPossible.cs +13 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UpdatePullRequest.cs +6 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ModifiedFilesTracker.cs +151 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestTextGenerator.cs +78 -32
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +99 -111
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/CreateSecurityUpdatePullRequestHandler.cs +169 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/GroupUpdateAllVersionsHandler.cs +271 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/IUpdateHandler.cs +22 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshGroupUpdatePullRequestHandler.cs +192 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshSecurityUpdatePullRequestHandler.cs +187 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshVersionUpdatePullRequestHandler.cs +175 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateOperationBase.cs +43 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ILogger.cs +17 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +15 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MarkdownListBuilder.cs +65 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/ApiModel/JobTests.cs +405 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/EndToEndTests.cs +92 -82
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/HttpApiHandlerTests.cs +5 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MessageReportTests.cs +67 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +445 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestMessageTests.cs +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestTextTests.cs +260 -20
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +30 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +69 -10
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/CreateSecurityUpdatePullRequestHandlerTests.cs +766 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/GroupUpdateAllVersionsHandlerTests.cs +636 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshGroupUpdatePullRequestHandlerTests.cs +513 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshSecurityUpdatePullRequestHandlerTests.cs +806 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshVersionUpdatePullRequestHandlerTests.cs +589 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/UpdateHandlerSelectionTests.cs +183 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/UpdateHandlersTestsBase.cs +43 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdatedDependencyListTests.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateOperationBaseTests.cs +121 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +6 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +51 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MarkdownListBuilderTests.cs +42 -0
- metadata +26 -4
@@ -0,0 +1,187 @@
|
|
1
|
+
using System.Collections.Immutable;
|
2
|
+
|
3
|
+
using NuGetUpdater.Core.Run.ApiModel;
|
4
|
+
using NuGetUpdater.Core.Updater;
|
5
|
+
|
6
|
+
namespace NuGetUpdater.Core.Run.UpdateHandlers;
|
7
|
+
|
8
|
+
internal class RefreshSecurityUpdatePullRequestHandler : IUpdateHandler
|
9
|
+
{
|
10
|
+
public static IUpdateHandler Instance { get; } = new RefreshSecurityUpdatePullRequestHandler();
|
11
|
+
|
12
|
+
public string TagName => "update_security_pr";
|
13
|
+
|
14
|
+
public bool CanHandle(Job job)
|
15
|
+
{
|
16
|
+
if (!job.SecurityUpdatesOnly)
|
17
|
+
{
|
18
|
+
return false;
|
19
|
+
}
|
20
|
+
|
21
|
+
if (job.Dependencies.Length == 0)
|
22
|
+
{
|
23
|
+
return false;
|
24
|
+
}
|
25
|
+
|
26
|
+
return job.UpdatingAPullRequest;
|
27
|
+
}
|
28
|
+
|
29
|
+
public async Task HandleAsync(Job job, DirectoryInfo repoContentsPath, string baseCommitSha, IDiscoveryWorker discoveryWorker, IAnalyzeWorker analyzeWorker, IUpdaterWorker updaterWorker, IApiHandler apiHandler, ExperimentsManager experimentsManager, ILogger logger)
|
30
|
+
{
|
31
|
+
var jobDependencies = job.Dependencies.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
32
|
+
foreach (var directory in job.GetAllDirectories())
|
33
|
+
{
|
34
|
+
var discoveryResult = await discoveryWorker.RunAsync(repoContentsPath.FullName, directory);
|
35
|
+
logger.ReportDiscovery(discoveryResult);
|
36
|
+
if (discoveryResult.Error is not null)
|
37
|
+
{
|
38
|
+
await apiHandler.RecordUpdateJobError(discoveryResult.Error);
|
39
|
+
return;
|
40
|
+
}
|
41
|
+
|
42
|
+
var updatedDependencyList = RunWorker.GetUpdatedDependencyListFromDiscovery(discoveryResult);
|
43
|
+
await apiHandler.UpdateDependencyList(updatedDependencyList);
|
44
|
+
await this.ReportUpdaterStarted(apiHandler);
|
45
|
+
|
46
|
+
var updateOperationsPerformed = new List<UpdateOperationBase>();
|
47
|
+
var updatedDependencies = new List<ReportedDependency>();
|
48
|
+
var updateOperationsToPerform = RunWorker.GetUpdateOperations(discoveryResult).ToArray();
|
49
|
+
var groupedUpdateOperationsToPerform = updateOperationsToPerform
|
50
|
+
.GroupBy(o => o.Dependency.Name, StringComparer.OrdinalIgnoreCase)
|
51
|
+
.Where(g => jobDependencies.Contains(g.Key))
|
52
|
+
.ToDictionary(g => g.Key, g => g.ToArray(), StringComparer.OrdinalIgnoreCase);
|
53
|
+
|
54
|
+
logger.Info($"Updating dependencies: {string.Join(", ", groupedUpdateOperationsToPerform.Select(g => g.Key).Distinct().OrderBy(d => d, StringComparer.OrdinalIgnoreCase))}");
|
55
|
+
|
56
|
+
if (groupedUpdateOperationsToPerform.Count == 0)
|
57
|
+
{
|
58
|
+
await apiHandler.ClosePullRequest(ClosePullRequest.WithDependenciesRemoved(job));
|
59
|
+
continue;
|
60
|
+
}
|
61
|
+
|
62
|
+
var missingDependencies = jobDependencies
|
63
|
+
.Where(d => !groupedUpdateOperationsToPerform.ContainsKey(d))
|
64
|
+
.OrderBy(d => d, StringComparer.OrdinalIgnoreCase)
|
65
|
+
.ToImmutableArray();
|
66
|
+
if (missingDependencies.Length > 0)
|
67
|
+
{
|
68
|
+
await apiHandler.ClosePullRequest(ClosePullRequest.WithDependencyRemoved(job));
|
69
|
+
continue;
|
70
|
+
}
|
71
|
+
|
72
|
+
var tracker = new ModifiedFilesTracker(repoContentsPath);
|
73
|
+
await tracker.StartTrackingAsync(discoveryResult);
|
74
|
+
foreach (var dependencyGroupToUpdate in groupedUpdateOperationsToPerform)
|
75
|
+
{
|
76
|
+
var dependencyName = dependencyGroupToUpdate.Key;
|
77
|
+
var vulnerableDependenciesToUpdate = dependencyGroupToUpdate.Value
|
78
|
+
.Select(o => (o.ProjectPath, o.Dependency, RunWorker.GetDependencyInfo(job, o.Dependency)))
|
79
|
+
.Where(set => !job.IsDependencyIgnored(set.Dependency.Name, set.Dependency.Version!))
|
80
|
+
.Where(set => set.Item3.IsVulnerable)
|
81
|
+
.ToArray();
|
82
|
+
|
83
|
+
if (vulnerableDependenciesToUpdate.Length < dependencyGroupToUpdate.Value.Length)
|
84
|
+
{
|
85
|
+
await apiHandler.ClosePullRequest(ClosePullRequest.WithUpToDate(job));
|
86
|
+
return;
|
87
|
+
}
|
88
|
+
|
89
|
+
foreach (var (projectPath, dependency, dependencyInfo) in vulnerableDependenciesToUpdate)
|
90
|
+
{
|
91
|
+
var analysisResult = await analyzeWorker.RunAsync(repoContentsPath.FullName, discoveryResult, dependencyInfo);
|
92
|
+
if (analysisResult.Error is not null)
|
93
|
+
{
|
94
|
+
logger.Error($"Error analyzing {dependency.Name} in {projectPath}: {analysisResult.Error.GetReport()}");
|
95
|
+
await apiHandler.RecordUpdateJobError(analysisResult.Error);
|
96
|
+
return;
|
97
|
+
}
|
98
|
+
|
99
|
+
if (!analysisResult.CanUpdate)
|
100
|
+
{
|
101
|
+
logger.Info($"No updatable version found for {dependency.Name} in {projectPath}.");
|
102
|
+
await apiHandler.ClosePullRequest(ClosePullRequest.WithUpdateNoLongerPossible(job));
|
103
|
+
return;
|
104
|
+
}
|
105
|
+
|
106
|
+
logger.Info($"Attempting update of {dependency.Name} from {dependency.Version} to {analysisResult.UpdatedVersion} for {projectPath}.");
|
107
|
+
var projectDiscovery = discoveryResult.GetProjectDiscoveryFromPath(projectPath);
|
108
|
+
var updaterResult = await updaterWorker.RunAsync(repoContentsPath.FullName, projectPath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, dependency.IsTransitive);
|
109
|
+
if (updaterResult.Error is not null)
|
110
|
+
{
|
111
|
+
logger.Error($"Error updating {dependency.Name} in {projectPath}: {updaterResult.Error.GetReport()}");
|
112
|
+
await apiHandler.RecordUpdateJobError(updaterResult.Error);
|
113
|
+
continue;
|
114
|
+
}
|
115
|
+
|
116
|
+
if (updaterResult.UpdateOperations.Length == 0)
|
117
|
+
{
|
118
|
+
// nothing was done, but we may have already handled it
|
119
|
+
var alreadyHandled = updatedDependencies.Where(updated => updated.Name == dependencyName && updated.Version == analysisResult.UpdatedVersion).Any();
|
120
|
+
if (!alreadyHandled)
|
121
|
+
{
|
122
|
+
await apiHandler.ClosePullRequest(ClosePullRequest.WithUpdateNoLongerPossible(job));
|
123
|
+
return;
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
var patchedUpdateOperations = RunWorker.PatchInOldVersions(updaterResult.UpdateOperations, projectDiscovery);
|
128
|
+
var updatedDependenciesForThis = patchedUpdateOperations
|
129
|
+
.Select(o => o.ToReportedDependency(projectPath, updatedDependencyList.Dependencies, analysisResult.UpdatedDependencies))
|
130
|
+
.ToArray();
|
131
|
+
|
132
|
+
updatedDependencies.AddRange(updatedDependenciesForThis);
|
133
|
+
updateOperationsPerformed.AddRange(patchedUpdateOperations);
|
134
|
+
foreach (var o in patchedUpdateOperations)
|
135
|
+
{
|
136
|
+
logger.Info($"Update operation performed: {o.GetReport()}");
|
137
|
+
}
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
var updatedDependencyFiles = await tracker.StopTrackingAsync();
|
142
|
+
var rawDependencies = updatedDependencies.Select(d => new Dependency(d.Name, d.Version, DependencyType.Unknown)).ToArray();
|
143
|
+
if (rawDependencies.Length > 0)
|
144
|
+
{
|
145
|
+
var commitMessage = PullRequestTextGenerator.GetPullRequestCommitMessage(job, [.. updateOperationsPerformed], null);
|
146
|
+
var prTitle = PullRequestTextGenerator.GetPullRequestTitle(job, [.. updateOperationsPerformed], null);
|
147
|
+
var prBody = PullRequestTextGenerator.GetPullRequestBody(job, [.. updateOperationsPerformed], null);
|
148
|
+
|
149
|
+
var existingPullRequest = job.GetExistingPullRequestForDependencies(rawDependencies, considerVersions: true);
|
150
|
+
if (existingPullRequest is not null)
|
151
|
+
{
|
152
|
+
await apiHandler.UpdatePullRequest(new UpdatePullRequest()
|
153
|
+
{
|
154
|
+
DependencyNames = [.. jobDependencies.OrderBy(n => n, StringComparer.OrdinalIgnoreCase)],
|
155
|
+
DependencyGroup = null,
|
156
|
+
UpdatedDependencyFiles = [.. updatedDependencyFiles],
|
157
|
+
BaseCommitSha = baseCommitSha,
|
158
|
+
CommitMessage = commitMessage,
|
159
|
+
PrTitle = prTitle,
|
160
|
+
PrBody = prBody,
|
161
|
+
});
|
162
|
+
continue;
|
163
|
+
}
|
164
|
+
else
|
165
|
+
{
|
166
|
+
var existingPrButDifferent = job.GetExistingPullRequestForDependencies(rawDependencies, considerVersions: false);
|
167
|
+
if (existingPrButDifferent is not null)
|
168
|
+
{
|
169
|
+
await apiHandler.ClosePullRequest(ClosePullRequest.WithDependenciesChanged(job));
|
170
|
+
}
|
171
|
+
|
172
|
+
await apiHandler.CreatePullRequest(new CreatePullRequest()
|
173
|
+
{
|
174
|
+
Dependencies = [.. updatedDependencies],
|
175
|
+
UpdatedDependencyFiles = [.. updatedDependencyFiles],
|
176
|
+
BaseCommitSha = baseCommitSha,
|
177
|
+
CommitMessage = commitMessage,
|
178
|
+
PrTitle = prTitle,
|
179
|
+
PrBody = prBody,
|
180
|
+
DependencyGroup = null,
|
181
|
+
});
|
182
|
+
continue;
|
183
|
+
}
|
184
|
+
}
|
185
|
+
}
|
186
|
+
}
|
187
|
+
}
|
@@ -0,0 +1,175 @@
|
|
1
|
+
using System.Collections.Immutable;
|
2
|
+
|
3
|
+
using NuGetUpdater.Core.Run.ApiModel;
|
4
|
+
using NuGetUpdater.Core.Updater;
|
5
|
+
|
6
|
+
namespace NuGetUpdater.Core.Run.UpdateHandlers;
|
7
|
+
|
8
|
+
internal class RefreshVersionUpdatePullRequestHandler : IUpdateHandler
|
9
|
+
{
|
10
|
+
public static IUpdateHandler Instance { get; } = new RefreshVersionUpdatePullRequestHandler();
|
11
|
+
|
12
|
+
public string TagName => "update_version_pr";
|
13
|
+
|
14
|
+
public bool CanHandle(Job job)
|
15
|
+
{
|
16
|
+
if (job.SecurityUpdatesOnly)
|
17
|
+
{
|
18
|
+
return false;
|
19
|
+
}
|
20
|
+
|
21
|
+
if (job.Dependencies.Length == 0)
|
22
|
+
{
|
23
|
+
return false;
|
24
|
+
}
|
25
|
+
|
26
|
+
return job.UpdatingAPullRequest;
|
27
|
+
}
|
28
|
+
|
29
|
+
public async Task HandleAsync(Job job, DirectoryInfo repoContentsPath, string baseCommitSha, IDiscoveryWorker discoveryWorker, IAnalyzeWorker analyzeWorker, IUpdaterWorker updaterWorker, IApiHandler apiHandler, ExperimentsManager experimentsManager, ILogger logger)
|
30
|
+
{
|
31
|
+
var jobDependencies = job.Dependencies.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
32
|
+
foreach (var directory in job.GetAllDirectories())
|
33
|
+
{
|
34
|
+
var discoveryResult = await discoveryWorker.RunAsync(repoContentsPath.FullName, directory);
|
35
|
+
logger.ReportDiscovery(discoveryResult);
|
36
|
+
if (discoveryResult.Error is not null)
|
37
|
+
{
|
38
|
+
await apiHandler.RecordUpdateJobError(discoveryResult.Error);
|
39
|
+
return;
|
40
|
+
}
|
41
|
+
|
42
|
+
var updatedDependencyList = RunWorker.GetUpdatedDependencyListFromDiscovery(discoveryResult);
|
43
|
+
await apiHandler.UpdateDependencyList(updatedDependencyList);
|
44
|
+
await this.ReportUpdaterStarted(apiHandler);
|
45
|
+
|
46
|
+
var updateOperationsPerformed = new List<UpdateOperationBase>();
|
47
|
+
var updatedDependencies = new List<ReportedDependency>();
|
48
|
+
var updateOperationsToPerform = RunWorker.GetUpdateOperations(discoveryResult).ToArray();
|
49
|
+
var relevantUpdateOperationsToPerform = updateOperationsToPerform
|
50
|
+
.GroupBy(o => o.Dependency.Name, StringComparer.OrdinalIgnoreCase)
|
51
|
+
.Where(g => jobDependencies.Contains(g.Key))
|
52
|
+
.ToDictionary(g => g.Key, g => g.ToArray(), StringComparer.OrdinalIgnoreCase);
|
53
|
+
|
54
|
+
if (relevantUpdateOperationsToPerform.Count == 0)
|
55
|
+
{
|
56
|
+
await apiHandler.ClosePullRequest(ClosePullRequest.WithDependenciesRemoved(job));
|
57
|
+
continue;
|
58
|
+
}
|
59
|
+
|
60
|
+
var missingDependencies = jobDependencies
|
61
|
+
.Where(d => !relevantUpdateOperationsToPerform.ContainsKey(d))
|
62
|
+
.OrderBy(d => d, StringComparer.OrdinalIgnoreCase)
|
63
|
+
.ToImmutableArray();
|
64
|
+
if (missingDependencies.Length > 0)
|
65
|
+
{
|
66
|
+
await apiHandler.ClosePullRequest(ClosePullRequest.WithDependencyRemoved(job));
|
67
|
+
continue;
|
68
|
+
}
|
69
|
+
|
70
|
+
logger.Info($"Updating dependencies: {string.Join(", ", relevantUpdateOperationsToPerform.Select(g => g.Key).Distinct().OrderBy(d => d, StringComparer.OrdinalIgnoreCase))}");
|
71
|
+
|
72
|
+
var tracker = new ModifiedFilesTracker(repoContentsPath);
|
73
|
+
await tracker.StartTrackingAsync(discoveryResult);
|
74
|
+
foreach (var dependencyUpdatesToPeform in relevantUpdateOperationsToPerform)
|
75
|
+
{
|
76
|
+
var dependencyName = dependencyUpdatesToPeform.Key;
|
77
|
+
var dependencyInfosToUpdate = dependencyUpdatesToPeform.Value
|
78
|
+
.Select(o => (o.ProjectPath, o.Dependency, RunWorker.GetDependencyInfo(job, o.Dependency)))
|
79
|
+
.Where(set => !job.IsDependencyIgnored(set.Dependency.Name, set.Dependency.Version!))
|
80
|
+
.ToArray();
|
81
|
+
|
82
|
+
foreach (var (projectPath, dependency, dependencyInfo) in dependencyInfosToUpdate)
|
83
|
+
{
|
84
|
+
var analysisResult = await analyzeWorker.RunAsync(repoContentsPath.FullName, discoveryResult, dependencyInfo);
|
85
|
+
if (analysisResult.Error is not null)
|
86
|
+
{
|
87
|
+
logger.Error($"Error analyzing {dependency.Name} in {projectPath}: {analysisResult.Error.GetReport()}");
|
88
|
+
await apiHandler.RecordUpdateJobError(analysisResult.Error);
|
89
|
+
return;
|
90
|
+
}
|
91
|
+
|
92
|
+
if (!analysisResult.CanUpdate)
|
93
|
+
{
|
94
|
+
logger.Info($"No updatable version found for {dependency.Name} in {projectPath}.");
|
95
|
+
await apiHandler.ClosePullRequest(ClosePullRequest.WithUpdateNoLongerPossible(job));
|
96
|
+
return;
|
97
|
+
}
|
98
|
+
|
99
|
+
logger.Info($"Attempting update of {dependency.Name} from {dependency.Version} to {analysisResult.UpdatedVersion} for {projectPath}.");
|
100
|
+
var projectDiscovery = discoveryResult.GetProjectDiscoveryFromPath(projectPath);
|
101
|
+
var updaterResult = await updaterWorker.RunAsync(repoContentsPath.FullName, projectPath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, dependency.IsTransitive);
|
102
|
+
if (updaterResult.Error is not null)
|
103
|
+
{
|
104
|
+
logger.Error($"Error updating {dependency.Name} in {projectPath}: {updaterResult.Error.GetReport()}");
|
105
|
+
await apiHandler.RecordUpdateJobError(updaterResult.Error);
|
106
|
+
continue;
|
107
|
+
}
|
108
|
+
|
109
|
+
if (updaterResult.UpdateOperations.Length == 0)
|
110
|
+
{
|
111
|
+
await apiHandler.ClosePullRequest(ClosePullRequest.WithUpdateNoLongerPossible(job));
|
112
|
+
return;
|
113
|
+
}
|
114
|
+
|
115
|
+
var patchedUpdateOperations = RunWorker.PatchInOldVersions(updaterResult.UpdateOperations, projectDiscovery);
|
116
|
+
var updatedDependenciesForThis = patchedUpdateOperations
|
117
|
+
.Select(o => o.ToReportedDependency(projectPath, updatedDependencyList.Dependencies, analysisResult.UpdatedDependencies))
|
118
|
+
.ToArray();
|
119
|
+
|
120
|
+
updatedDependencies.AddRange(updatedDependenciesForThis);
|
121
|
+
updateOperationsPerformed.AddRange(patchedUpdateOperations);
|
122
|
+
foreach (var o in patchedUpdateOperations)
|
123
|
+
{
|
124
|
+
logger.Info($"Update operation performed: {o.GetReport()}");
|
125
|
+
}
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
var updatedDependencyFiles = await tracker.StopTrackingAsync();
|
130
|
+
var rawDependencies = updatedDependencies.Select(d => new Dependency(d.Name, d.Version, DependencyType.Unknown)).ToArray();
|
131
|
+
if (rawDependencies.Length > 0)
|
132
|
+
{
|
133
|
+
var commitMessage = PullRequestTextGenerator.GetPullRequestCommitMessage(job, [.. updateOperationsPerformed], null);
|
134
|
+
var prTitle = PullRequestTextGenerator.GetPullRequestTitle(job, [.. updateOperationsPerformed], null);
|
135
|
+
var prBody = PullRequestTextGenerator.GetPullRequestBody(job, [.. updateOperationsPerformed], null);
|
136
|
+
|
137
|
+
var existingPullRequest = job.GetExistingPullRequestForDependencies(rawDependencies, considerVersions: true);
|
138
|
+
if (existingPullRequest is not null)
|
139
|
+
{
|
140
|
+
await apiHandler.UpdatePullRequest(new UpdatePullRequest()
|
141
|
+
{
|
142
|
+
DependencyNames = [.. jobDependencies.OrderBy(n => n, StringComparer.OrdinalIgnoreCase)],
|
143
|
+
DependencyGroup = null,
|
144
|
+
UpdatedDependencyFiles = [.. updatedDependencyFiles],
|
145
|
+
BaseCommitSha = baseCommitSha,
|
146
|
+
CommitMessage = commitMessage,
|
147
|
+
PrTitle = prTitle,
|
148
|
+
PrBody = prBody,
|
149
|
+
});
|
150
|
+
continue;
|
151
|
+
}
|
152
|
+
else
|
153
|
+
{
|
154
|
+
var existingPrButDifferent = job.GetExistingPullRequestForDependencies(rawDependencies, considerVersions: false);
|
155
|
+
if (existingPrButDifferent is not null)
|
156
|
+
{
|
157
|
+
await apiHandler.ClosePullRequest(ClosePullRequest.WithDependenciesChanged(job));
|
158
|
+
}
|
159
|
+
|
160
|
+
await apiHandler.CreatePullRequest(new CreatePullRequest()
|
161
|
+
{
|
162
|
+
Dependencies = [.. updatedDependencies],
|
163
|
+
UpdatedDependencyFiles = [.. updatedDependencyFiles],
|
164
|
+
BaseCommitSha = baseCommitSha,
|
165
|
+
CommitMessage = commitMessage,
|
166
|
+
PrTitle = prTitle,
|
167
|
+
PrBody = prBody,
|
168
|
+
DependencyGroup = null,
|
169
|
+
});
|
170
|
+
continue;
|
171
|
+
}
|
172
|
+
}
|
173
|
+
}
|
174
|
+
}
|
175
|
+
}
|
@@ -4,6 +4,7 @@ using System.Text.Json.Serialization;
|
|
4
4
|
|
5
5
|
using NuGet.Versioning;
|
6
6
|
|
7
|
+
using NuGetUpdater.Core.Run.ApiModel;
|
7
8
|
using NuGetUpdater.Core.Utilities;
|
8
9
|
|
9
10
|
|
@@ -16,11 +17,38 @@ public abstract record UpdateOperationBase
|
|
16
17
|
{
|
17
18
|
public abstract string Type { get; }
|
18
19
|
public required string DependencyName { get; init; }
|
20
|
+
public NuGetVersion? OldVersion { get; init; } = null;
|
19
21
|
public required NuGetVersion NewVersion { get; init; }
|
20
22
|
public required ImmutableArray<string> UpdatedFiles { get; init; }
|
21
23
|
|
22
24
|
public abstract string GetReport();
|
23
25
|
|
26
|
+
public ReportedDependency ToReportedDependency(string projectPath, IEnumerable<ReportedDependency> previouslyReportedDependencies, IEnumerable<Dependency> updatedDependencies)
|
27
|
+
{
|
28
|
+
var updatedFilesSet = UpdatedFiles.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
29
|
+
var previousDependency = previouslyReportedDependencies
|
30
|
+
.SingleOrDefault(d => d.Name.Equals(DependencyName, StringComparison.OrdinalIgnoreCase) && PathComparer.Instance.Equals(d.Requirements.Single().File, projectPath));
|
31
|
+
return new ReportedDependency()
|
32
|
+
{
|
33
|
+
Name = DependencyName,
|
34
|
+
Version = NewVersion.ToString(),
|
35
|
+
Requirements = [
|
36
|
+
new()
|
37
|
+
{
|
38
|
+
File = projectPath,
|
39
|
+
Requirement = NewVersion.ToString(),
|
40
|
+
Groups = previousDependency?.Requirements.FirstOrDefault()?.Groups ?? [],
|
41
|
+
Source = new()
|
42
|
+
{
|
43
|
+
SourceUrl = updatedDependencies.FirstOrDefault(d => d.Name.Equals(DependencyName, StringComparison.OrdinalIgnoreCase))?.InfoUrl,
|
44
|
+
}
|
45
|
+
}
|
46
|
+
],
|
47
|
+
PreviousVersion = previousDependency?.Version,
|
48
|
+
PreviousRequirements = previousDependency?.Requirements,
|
49
|
+
};
|
50
|
+
}
|
51
|
+
|
24
52
|
internal static string GenerateUpdateOperationReport(IEnumerable<UpdateOperationBase> updateOperations)
|
25
53
|
{
|
26
54
|
var updateMessages = updateOperations.Select(u => u.GetReport()).ToImmutableArray();
|
@@ -36,7 +64,7 @@ public abstract record UpdateOperationBase
|
|
36
64
|
internal static ImmutableArray<UpdateOperationBase> NormalizeUpdateOperationCollection(string repoRootPath, IEnumerable<UpdateOperationBase> updateOperations)
|
37
65
|
{
|
38
66
|
var groupedByKindWithCombinedFiles = updateOperations
|
39
|
-
.GroupBy(u => (u.GetType(), u.DependencyName, u.NewVersion))
|
67
|
+
.GroupBy(u => (u.GetType(), u.DependencyName, u.OldVersion, u.NewVersion))
|
40
68
|
.Select(g =>
|
41
69
|
{
|
42
70
|
if (g.Key.Item1 == typeof(DirectUpdate))
|
@@ -44,6 +72,7 @@ public abstract record UpdateOperationBase
|
|
44
72
|
return new DirectUpdate()
|
45
73
|
{
|
46
74
|
DependencyName = g.Key.DependencyName,
|
75
|
+
OldVersion = g.Key.OldVersion,
|
47
76
|
NewVersion = g.Key.NewVersion,
|
48
77
|
UpdatedFiles = [.. g.SelectMany(u => u.UpdatedFiles)],
|
49
78
|
} as UpdateOperationBase;
|
@@ -53,6 +82,7 @@ public abstract record UpdateOperationBase
|
|
53
82
|
return new PinnedUpdate()
|
54
83
|
{
|
55
84
|
DependencyName = g.Key.DependencyName,
|
85
|
+
OldVersion = g.Key.OldVersion,
|
56
86
|
NewVersion = g.Key.NewVersion,
|
57
87
|
UpdatedFiles = [.. g.SelectMany(u => u.UpdatedFiles)],
|
58
88
|
};
|
@@ -63,6 +93,7 @@ public abstract record UpdateOperationBase
|
|
63
93
|
return new ParentUpdate()
|
64
94
|
{
|
65
95
|
DependencyName = g.Key.DependencyName,
|
96
|
+
OldVersion = g.Key.OldVersion,
|
66
97
|
NewVersion = g.Key.NewVersion,
|
67
98
|
UpdatedFiles = [.. g.SelectMany(u => u.UpdatedFiles)],
|
68
99
|
ParentDependencyName = parentUpdate.ParentDependencyName,
|
@@ -82,6 +113,7 @@ public abstract record UpdateOperationBase
|
|
82
113
|
var ordered = uniqueUpdateOperations
|
83
114
|
.OrderBy(u => u.GetType().Name)
|
84
115
|
.ThenBy(u => u.DependencyName)
|
116
|
+
.ThenBy(u => u.OldVersion)
|
85
117
|
.ThenBy(u => u.NewVersion)
|
86
118
|
.ThenBy(u => u.UpdatedFiles.Length)
|
87
119
|
.ThenBy(u => string.Join(",", u.UpdatedFiles))
|
@@ -95,6 +127,7 @@ public abstract record UpdateOperationBase
|
|
95
127
|
{
|
96
128
|
var hash = new HashCode();
|
97
129
|
hash.Add(DependencyName);
|
130
|
+
hash.Add(OldVersion);
|
98
131
|
hash.Add(NewVersion);
|
99
132
|
hash.Add(UpdatedFiles.Length);
|
100
133
|
for (int i = 0; i < UpdatedFiles.Length; i++)
|
@@ -111,7 +144,14 @@ public abstract record UpdateOperationBase
|
|
111
144
|
public record DirectUpdate : UpdateOperationBase
|
112
145
|
{
|
113
146
|
public override string Type => nameof(DirectUpdate);
|
114
|
-
public override string GetReport()
|
147
|
+
public override string GetReport()
|
148
|
+
{
|
149
|
+
var fromText = OldVersion is null
|
150
|
+
? string.Empty
|
151
|
+
: $"from {OldVersion} ";
|
152
|
+
return $"Updated {DependencyName} {fromText}to {NewVersion} in {string.Join(", ", UpdatedFiles)}";
|
153
|
+
}
|
154
|
+
|
115
155
|
public sealed override string ToString() => GetString();
|
116
156
|
}
|
117
157
|
|
@@ -185,6 +225,7 @@ public class UpdateOperationBaseComparer : IEqualityComparer<UpdateOperationBase
|
|
185
225
|
}
|
186
226
|
|
187
227
|
if (x.DependencyName != y.DependencyName ||
|
228
|
+
x.OldVersion != y.OldVersion ||
|
188
229
|
x.NewVersion != y.NewVersion ||
|
189
230
|
!x.UpdatedFiles.SequenceEqual(y.UpdatedFiles))
|
190
231
|
{
|
@@ -1,3 +1,8 @@
|
|
1
|
+
using System.Text.Json;
|
2
|
+
|
3
|
+
using NuGetUpdater.Core.Analyze;
|
4
|
+
using NuGetUpdater.Core.Discover;
|
5
|
+
|
1
6
|
namespace NuGetUpdater.Core;
|
2
7
|
|
3
8
|
public interface ILogger
|
@@ -11,6 +16,18 @@ public static class LoggerExtensions
|
|
11
16
|
public static void Warn(this ILogger logger, string message) => logger.LogWithLevel("WARN", message);
|
12
17
|
public static void Error(this ILogger logger, string message) => logger.LogWithLevel("ERROR", message);
|
13
18
|
|
19
|
+
public static void ReportAnalysis(this ILogger logger, AnalysisResult analysisResult)
|
20
|
+
{
|
21
|
+
logger.Info("Analysis JSON content:");
|
22
|
+
logger.Info(JsonSerializer.Serialize(analysisResult, AnalyzeWorker.SerializerOptions));
|
23
|
+
}
|
24
|
+
|
25
|
+
public static void ReportDiscovery(this ILogger logger, WorkspaceDiscoveryResult discoveryResult)
|
26
|
+
{
|
27
|
+
logger.Info("Discovery JSON content:");
|
28
|
+
logger.Info(JsonSerializer.Serialize(discoveryResult, DiscoveryWorker.SerializerOptions));
|
29
|
+
}
|
30
|
+
|
14
31
|
private static void LogWithLevel(this ILogger logger, string level, string message) => logger.LogRaw($"{GetCurrentTimestamp()} {level} {message}");
|
15
32
|
private static string GetCurrentTimestamp() => DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm:ss");
|
16
33
|
}
|
@@ -968,7 +968,7 @@ internal static partial class MSBuildHelper
|
|
968
968
|
ThrowOnMissingPackages(output);
|
969
969
|
ThrowOnUpdateNotPossible(output);
|
970
970
|
ThrowOnRateLimitExceeded(output);
|
971
|
-
|
971
|
+
ThrowOnBadResponse(output);
|
972
972
|
ThrowOnUnparseableFile(output);
|
973
973
|
}
|
974
974
|
|
@@ -1001,16 +1001,20 @@ internal static partial class MSBuildHelper
|
|
1001
1001
|
}
|
1002
1002
|
}
|
1003
1003
|
|
1004
|
-
private static void
|
1004
|
+
private static void ThrowOnBadResponse(string stdout)
|
1005
1005
|
{
|
1006
|
-
var
|
1006
|
+
var patterns = new[]
|
1007
1007
|
{
|
1008
|
-
"
|
1009
|
-
"
|
1008
|
+
new Regex(@"500 \(Internal Server Error\)"),
|
1009
|
+
new Regex(@"503 \(Service Unavailable\)"),
|
1010
|
+
new Regex(@"Response status code does not indicate success: 50\d"),
|
1011
|
+
new Regex(@"The file is not a valid nupkg"),
|
1012
|
+
new Regex(@"The response ended prematurely\. \(ResponseEnded\)"),
|
1013
|
+
new Regex(@"The content at '.*' is not valid XML\."),
|
1010
1014
|
};
|
1011
|
-
if (
|
1015
|
+
if (patterns.Any(p => p.IsMatch(stdout)))
|
1012
1016
|
{
|
1013
|
-
throw new HttpRequestException(message: stdout, inner: null, statusCode: System.Net.HttpStatusCode.
|
1017
|
+
throw new HttpRequestException(message: stdout, inner: null, statusCode: System.Net.HttpStatusCode.InternalServerError);
|
1014
1018
|
}
|
1015
1019
|
}
|
1016
1020
|
|
@@ -1030,11 +1034,12 @@ internal static partial class MSBuildHelper
|
|
1030
1034
|
new Regex(@"Package '(?<PackageName>[^']*)' is not found on source '(?<PackageSource>[^$\r\n]*)'\."),
|
1031
1035
|
new Regex(@"Unable to find package (?<PackageName>[^ ]+)\. No packages exist with this id in source\(s\): (?<PackageSource>.*)$", RegexOptions.Multiline),
|
1032
1036
|
new Regex(@"Unable to find package (?<PackageName>[^ ]+) with version \((?<PackageVersion>[^)]+)\)"),
|
1037
|
+
new Regex(@"Unable to find package '(?<PackageName>[^ ]+)'\."),
|
1033
1038
|
new Regex(@"Could not resolve SDK ""(?<PackageName>[^ ]+)""\."),
|
1034
1039
|
new Regex(@"Failed to fetch results from V2 feed at '.*FindPackagesById\(\)\?id='(?<PackageName>[^']+)'&semVerLevel=2\.0\.0' with following message : Response status code does not indicate success: 404\."),
|
1035
1040
|
};
|
1036
|
-
var matches = patterns.Select(p => p.Match(output)).Where(m => m.Success);
|
1037
|
-
if (matches.
|
1041
|
+
var matches = patterns.Select(p => p.Match(output)).Where(m => m.Success).ToArray();
|
1042
|
+
if (matches.Length > 0)
|
1038
1043
|
{
|
1039
1044
|
var packages = matches.Select(m =>
|
1040
1045
|
{
|
@@ -1075,6 +1080,7 @@ internal static partial class MSBuildHelper
|
|
1075
1080
|
var patterns = new[]
|
1076
1081
|
{
|
1077
1082
|
new Regex(@"\nAn error occurred while reading file '(?<FilePath>[^']+)': (?<Message>[^\n]*)\n"),
|
1083
|
+
new Regex(@"NuGet\.Config is not valid XML\. Path: '(?<FilePath>[^']+)'\.\n\s*(?<Message>[^\n]*)(\n|$)"),
|
1078
1084
|
};
|
1079
1085
|
var match = patterns.Select(p => p.Match(output)).Where(m => m.Success).FirstOrDefault();
|
1080
1086
|
if (match is not null)
|
@@ -0,0 +1,65 @@
|
|
1
|
+
namespace NuGetUpdater.Core.Utilities;
|
2
|
+
|
3
|
+
public class MarkdownListBuilder
|
4
|
+
{
|
5
|
+
public static string FromObject(object obj)
|
6
|
+
{
|
7
|
+
return string.Join(Environment.NewLine, LinesFromObject(obj));
|
8
|
+
}
|
9
|
+
|
10
|
+
private static string[] LinesFromObject(object obj)
|
11
|
+
{
|
12
|
+
var lines = new List<string>();
|
13
|
+
switch (obj)
|
14
|
+
{
|
15
|
+
case IDictionary<string, object> dict:
|
16
|
+
// key1: value1
|
17
|
+
// key2: value2
|
18
|
+
foreach (var (key, value) in dict)
|
19
|
+
{
|
20
|
+
if (key == "error-backtrace")
|
21
|
+
{
|
22
|
+
continue;
|
23
|
+
}
|
24
|
+
|
25
|
+
var childLines = LinesFromObject(value);
|
26
|
+
if (childLines.Length == 1)
|
27
|
+
{
|
28
|
+
// display inline
|
29
|
+
lines.Add($"- {key}: {childLines[0]}");
|
30
|
+
}
|
31
|
+
else
|
32
|
+
{
|
33
|
+
// display in sub-list
|
34
|
+
lines.Add($"- {key}:");
|
35
|
+
foreach (var childLine in childLines)
|
36
|
+
{
|
37
|
+
lines.Add($" {childLine}");
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
break;
|
42
|
+
case IEnumerable<object> values:
|
43
|
+
// - value1
|
44
|
+
// - value2
|
45
|
+
foreach (var value in values)
|
46
|
+
{
|
47
|
+
var valueLines = LinesFromObject(value);
|
48
|
+
lines.Add($"- {valueLines[0]}");
|
49
|
+
foreach (var valueLine in valueLines.Skip(1))
|
50
|
+
{
|
51
|
+
lines.Add($" {valueLine}");
|
52
|
+
}
|
53
|
+
}
|
54
|
+
break;
|
55
|
+
case bool b:
|
56
|
+
lines.Add(b.ToString().ToLowerInvariant());
|
57
|
+
break;
|
58
|
+
default:
|
59
|
+
lines.Add(obj.ToString()!);
|
60
|
+
break;
|
61
|
+
}
|
62
|
+
|
63
|
+
return [.. lines];
|
64
|
+
}
|
65
|
+
}
|