dependabot-nuget 0.352.0 → 0.353.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.Core/Discover/DiscoveryWorker.cs +15 -41
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +102 -46
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +0 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyGroup.cs +19 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Job.cs +23 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +4 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/OutOfDisk.cs +9 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/IApiHandler.cs +11 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +25 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/CreateSecurityUpdatePullRequestHandler.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/GroupUpdateAllVersionsHandler.cs +4 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshGroupUpdatePullRequestHandler.cs +3 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshSecurityUpdatePullRequestHandler.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshVersionUpdatePullRequestHandler.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +20 -23
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +41 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +93 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +2 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +21 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +23 -104
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +1 -66
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/ApiModel/JobTests.cs +39 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/EndToEndTests.cs +142 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/HttpApiHandlerTests.cs +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/JobErrorBaseTests.cs +7 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MessageReportTests.cs +11 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +76 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +8 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackageReferenceUpdaterTests.cs +30 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +25 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/PathHelperTests.cs +250 -0
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7abf57af01e4a8a8aec9fb9d1979e6104a5059e057b4001bd53fa25e32c9a891
|
|
4
|
+
data.tar.gz: 6b983c64fbd42200d023004d03f039a68ca8a4b8dace9a63efaea0328f9dd572
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bcc2466108e839493cb8146b2dcae4079c257956f8eaf0f2181f6d24ae9cdb2fe8b20bea620d5530a33f4a62b2ac3a31bc17d80def3049e51b4ae8969d8d626d
|
|
7
|
+
data.tar.gz: d4bbc768c4c02cc017a4e066d885fb66b5c7dd00fa5c60b22605a5bf078cfceac52bfa98cc798018aa5a5fe0e3a290b7c38f6fc72d9718527da8b9409967d6d4
|
|
@@ -252,52 +252,26 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
|
252
252
|
filesToExpand.Push(projectPath);
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
|
-
|
|
256
|
-
if (experimentsManager.UseSingleRestore)
|
|
255
|
+
else if (extension == ".proj")
|
|
257
256
|
{
|
|
258
|
-
|
|
259
|
-
|
|
257
|
+
var foundProjects = ExpandItemGroupFilesFromProject(candidateEntryPoint, "ProjectFile", "ProjectReference");
|
|
258
|
+
foreach (var foundProject in foundProjects)
|
|
260
259
|
{
|
|
261
|
-
|
|
262
|
-
case ".csproj":
|
|
263
|
-
case ".fbproj":
|
|
264
|
-
case ".fsproj":
|
|
265
|
-
expandedProjects.Add(candidateEntryPoint);
|
|
266
|
-
break;
|
|
267
|
-
default:
|
|
268
|
-
// unsupported project
|
|
269
|
-
break;
|
|
260
|
+
filesToExpand.Push(foundProject);
|
|
270
261
|
}
|
|
271
262
|
}
|
|
272
|
-
|
|
263
|
+
|
|
264
|
+
// projects get shunted directly to the result because regular discovery handles it from there
|
|
265
|
+
switch (extension)
|
|
273
266
|
{
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
else
|
|
283
|
-
{
|
|
284
|
-
switch (extension)
|
|
285
|
-
{
|
|
286
|
-
case ".csproj":
|
|
287
|
-
case ".fsproj":
|
|
288
|
-
case ".vbproj":
|
|
289
|
-
// keep this project and check for references
|
|
290
|
-
expandedProjects.Add(candidateEntryPoint);
|
|
291
|
-
IEnumerable<string> referencedProjects = ExpandItemGroupFilesFromProject(candidateEntryPoint, "ProjectReference");
|
|
292
|
-
foreach (string referencedProject in referencedProjects)
|
|
293
|
-
{
|
|
294
|
-
filesToExpand.Push(referencedProject);
|
|
295
|
-
}
|
|
296
|
-
break;
|
|
297
|
-
default:
|
|
298
|
-
continue;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
267
|
+
case ".csproj":
|
|
268
|
+
case ".vbproj":
|
|
269
|
+
case ".fsproj":
|
|
270
|
+
expandedProjects.Add(candidateEntryPoint);
|
|
271
|
+
break;
|
|
272
|
+
default:
|
|
273
|
+
// unsupported project
|
|
274
|
+
break;
|
|
301
275
|
}
|
|
302
276
|
}
|
|
303
277
|
}
|
|
@@ -5,7 +5,9 @@ using System.Xml.XPath;
|
|
|
5
5
|
|
|
6
6
|
using Microsoft.Build.Logging.StructuredLogger;
|
|
7
7
|
|
|
8
|
+
using NuGet;
|
|
8
9
|
using NuGet.Frameworks;
|
|
10
|
+
using NuGet.Versioning;
|
|
9
11
|
|
|
10
12
|
using NuGetUpdater.Core.Utilities;
|
|
11
13
|
|
|
@@ -58,7 +60,7 @@ internal static class SdkProjectDiscovery
|
|
|
58
60
|
[
|
|
59
61
|
"Restore",
|
|
60
62
|
"ResolveProjectReferences",
|
|
61
|
-
"
|
|
63
|
+
"GenerateBuildDependencyFile"
|
|
62
64
|
];
|
|
63
65
|
|
|
64
66
|
public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverAsync(string repoRootPath, string workspacePath, string startingProjectPath, ExperimentsManager experimentsManager, ILogger logger)
|
|
@@ -88,6 +90,9 @@ internal static class SdkProjectDiscovery
|
|
|
88
90
|
Dictionary<string, Dictionary<string, Dictionary<string, string>>> explicitPackageVersionsPerProject = new(PathComparer.Instance);
|
|
89
91
|
// projectPath, tfm, packageName, packageVersion
|
|
90
92
|
|
|
93
|
+
Dictionary<string, int> packageReferenceElementCounts = new(PathComparer.Instance);
|
|
94
|
+
// projectPath, count of `<PackageReference>` elements
|
|
95
|
+
|
|
91
96
|
Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesReplacedBySdkPerProject = new(PathComparer.Instance);
|
|
92
97
|
// projectPath tfm packageName packageVersion
|
|
93
98
|
|
|
@@ -106,47 +111,49 @@ internal static class SdkProjectDiscovery
|
|
|
106
111
|
Dictionary<string, HashSet<string>> additionalFiles = new(PathComparer.Instance);
|
|
107
112
|
// projectPath additionalFiles
|
|
108
113
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
// due to how MSBuild handles multi-TFM projects with target platforms we may need to process each TFM separately
|
|
115
|
+
// we detect that by determining if there are multiple target frameworks specified and if any of them have a platform suffix (e.g., `-windows`, `-android`, etc)
|
|
116
|
+
var projectTfms = await MSBuildHelper.GetProjectTargetFrameworksAsync(startingProjectPath, logger);
|
|
117
|
+
var requiresIndividualRestores = projectTfms.Any(tfm => tfm.Contains('-'));
|
|
118
|
+
if (!requiresIndividualRestores)
|
|
119
|
+
{
|
|
120
|
+
projectTfms = [string.Empty]; // a single restore can handle everything, but we need to loop at least once and an empty TFM is our signal to not specify anything
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
foreach (var tfm in projectTfms)
|
|
114
124
|
{
|
|
125
|
+
var isSingleTfmRestore = !string.IsNullOrEmpty(tfm);
|
|
126
|
+
|
|
115
127
|
// create a binlog
|
|
116
128
|
var binLogPath = Path.Combine(Path.GetTempPath(), $"msbuild_{Guid.NewGuid():d}.binlog");
|
|
117
129
|
try
|
|
118
130
|
{
|
|
119
|
-
//
|
|
131
|
+
// when using single restore, we can directly invoke the relevant targets
|
|
132
|
+
var args = new List<string>() { "msbuild", startingProjectPath };
|
|
133
|
+
var targets = await MSBuildHelper.GetProjectTargetsAsync(startingProjectPath, logger);
|
|
134
|
+
var useDirectRestore = SingleRestoreTargetNames.All(targets.Contains);
|
|
135
|
+
if (useDirectRestore || isSingleTfmRestore)
|
|
136
|
+
{
|
|
137
|
+
// directly call the required targets
|
|
138
|
+
args.Add($"/t:{string.Join(",", SingleRestoreTargetNames)}");
|
|
139
|
+
}
|
|
140
|
+
else
|
|
141
|
+
{
|
|
142
|
+
// delegate to the inner build and call those targets
|
|
143
|
+
args.Add("/t:Build");
|
|
144
|
+
args.Add($"/p:InnerTargets=\"{string.Join(";", SingleRestoreTargetNames)}\"");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// inject various props and targets to help with discovery
|
|
120
148
|
var dependencyDiscoveryTargetingPacksPropsPath = MSBuildHelper.GetFileFromRuntimeDirectory("DependencyDiscoveryTargetingPacks.props");
|
|
121
149
|
var dependencyDiscoveryTargetsPath = MSBuildHelper.GetFileFromRuntimeDirectory("DependencyDiscovery.targets");
|
|
122
|
-
|
|
123
|
-
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
$"/p:TargetFramework={discoveredTfm}",
|
|
128
|
-
$"/p:CustomBeforeMicrosoftCommonProps={dependencyDiscoveryTargetingPacksPropsPath}",
|
|
129
|
-
$"/p:CustomAfterMicrosoftCommonCrossTargetingTargets={dependencyDiscoveryTargetsPath}",
|
|
130
|
-
$"/p:CustomAfterMicrosoftCommonTargets={dependencyDiscoveryTargetsPath}",
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
if (experimentsManager.UseSingleRestore)
|
|
150
|
+
args.Add($"/p:CustomBeforeMicrosoftCommonProps={dependencyDiscoveryTargetingPacksPropsPath}");
|
|
151
|
+
args.Add($"/p:CustomAfterMicrosoftCommonCrossTargetingTargets={dependencyDiscoveryTargetsPath}");
|
|
152
|
+
args.Add($"/p:CustomAfterMicrosoftCommonTargets={dependencyDiscoveryTargetsPath}");
|
|
153
|
+
|
|
154
|
+
if (isSingleTfmRestore)
|
|
134
155
|
{
|
|
135
|
-
|
|
136
|
-
args = ["msbuild", startingProjectPath];
|
|
137
|
-
var targets = await MSBuildHelper.GetProjectTargetsAsync(startingProjectPath, logger);
|
|
138
|
-
var useDirectRestore = SingleRestoreTargetNames.All(targets.Contains);
|
|
139
|
-
if (useDirectRestore)
|
|
140
|
-
{
|
|
141
|
-
// directly call the required targets
|
|
142
|
-
args.Add($"/t:{string.Join(",", SingleRestoreTargetNames)}");
|
|
143
|
-
}
|
|
144
|
-
else
|
|
145
|
-
{
|
|
146
|
-
// delegate to the inner build and call those targets
|
|
147
|
-
args.Add("/t:Build");
|
|
148
|
-
args.Add($"/p:InnerTargets=\"{string.Join(";", SingleRestoreTargetNames)}\"");
|
|
149
|
-
}
|
|
156
|
+
args.Add($"/p:TargetFramework={tfm}");
|
|
150
157
|
}
|
|
151
158
|
|
|
152
159
|
// if using CPM and a project also sets TreatWarningsAsErrors to true, this can cause discovery to fail; explicitly don't allow that
|
|
@@ -166,11 +173,6 @@ internal static class SdkProjectDiscovery
|
|
|
166
173
|
}
|
|
167
174
|
|
|
168
175
|
MSBuildHelper.ThrowOnError(stdOut);
|
|
169
|
-
if (stdOut.Contains("_DependencyDiscovery_LegacyProjects::UseTemporaryProject"))
|
|
170
|
-
{
|
|
171
|
-
// special case - legacy project with <PackageReference> elements; this requires extra handling below
|
|
172
|
-
requiresManualPackageResolution = true;
|
|
173
|
-
}
|
|
174
176
|
if (exitCode != 0)
|
|
175
177
|
{
|
|
176
178
|
// log error, but still try to resolve what we can
|
|
@@ -200,8 +202,8 @@ internal static class SdkProjectDiscovery
|
|
|
200
202
|
// props and targets files might have been imported from these, but they're not to be considered as dependency files
|
|
201
203
|
var forbiddenDirectories = new[]
|
|
202
204
|
{
|
|
203
|
-
|
|
204
|
-
|
|
205
|
+
GetPropertyValueFromProjectEvaluation(projectEvaluation, "BaseIntermediateOutputPath"), // e.g., "obj/"
|
|
206
|
+
GetPropertyValueFromProjectEvaluation(projectEvaluation, "BaseOutputPath"), // e.g., "bin/"
|
|
205
207
|
}
|
|
206
208
|
.Where(p => !string.IsNullOrEmpty(p))
|
|
207
209
|
.Select(p => Path.Combine(Path.GetDirectoryName(projectEvaluation.ProjectFile)!, p!))
|
|
@@ -220,7 +222,7 @@ internal static class SdkProjectDiscovery
|
|
|
220
222
|
}
|
|
221
223
|
break;
|
|
222
224
|
case NamedNode namedNode when namedNode is AddItem or RemoveItem:
|
|
223
|
-
ProcessResolvedPackageReference(namedNode, packagesPerProject, topLevelPackagesPerProject, explicitPackageVersionsPerProject);
|
|
225
|
+
ProcessResolvedPackageReference(namedNode, packagesPerProject, topLevelPackagesPerProject, explicitPackageVersionsPerProject, packageReferenceElementCounts);
|
|
224
226
|
|
|
225
227
|
if (namedNode is AddItem addItem)
|
|
226
228
|
{
|
|
@@ -369,6 +371,23 @@ internal static class SdkProjectDiscovery
|
|
|
369
371
|
}
|
|
370
372
|
}
|
|
371
373
|
|
|
374
|
+
var requiresManualPackageResolution = false;
|
|
375
|
+
foreach (var projectPath in resolvedProperties.Keys)
|
|
376
|
+
{
|
|
377
|
+
var projectProperties = resolvedProperties[projectPath];
|
|
378
|
+
var isProjectLegacy = !projectProperties.ContainsKey("NETCoreSdkVersion"); // legacy projects don't contain this property
|
|
379
|
+
if (isProjectLegacy)
|
|
380
|
+
{
|
|
381
|
+
// if any TFM had any explicit packages defined, we need to do manual package resolution
|
|
382
|
+
if (explicitPackageVersionsPerProject.TryGetValue(projectPath, out var projectTfmRefs) &&
|
|
383
|
+
projectTfmRefs.Values.Any(v => v.Count > 0))
|
|
384
|
+
{
|
|
385
|
+
requiresManualPackageResolution = true;
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
372
391
|
if (requiresManualPackageResolution)
|
|
373
392
|
{
|
|
374
393
|
// we were able to collect all <PackageReference> elements, but no transitive dependencies were resolved
|
|
@@ -376,7 +395,6 @@ internal static class SdkProjectDiscovery
|
|
|
376
395
|
packagesPerProject = await RebuildPackagesPerProject(
|
|
377
396
|
repoRootPath,
|
|
378
397
|
startingProjectPath,
|
|
379
|
-
discoveredTfms,
|
|
380
398
|
packagesPerProject,
|
|
381
399
|
explicitPackageVersionsPerProject,
|
|
382
400
|
experimentsManager,
|
|
@@ -601,13 +619,18 @@ internal static class SdkProjectDiscovery
|
|
|
601
619
|
private static async Task<Dictionary<string, Dictionary<string, Dictionary<string, string>>>> RebuildPackagesPerProject(
|
|
602
620
|
string repoRootPath,
|
|
603
621
|
string projectPath,
|
|
604
|
-
ImmutableArray<string> targetFrameworks,
|
|
605
622
|
Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesPerProject,
|
|
606
623
|
Dictionary<string, Dictionary<string, Dictionary<string, string>>> explicitPackageVersionsPerProject,
|
|
607
624
|
ExperimentsManager experimentsManager,
|
|
608
625
|
ILogger logger
|
|
609
626
|
)
|
|
610
627
|
{
|
|
628
|
+
// the secondary keys of these are TFMs
|
|
629
|
+
var targetFrameworks = packagesPerProject.Values.SelectMany(p => p.Keys)
|
|
630
|
+
.Concat(explicitPackageVersionsPerProject.Values.SelectMany(p => p.Keys))
|
|
631
|
+
.Distinct(StringComparer.OrdinalIgnoreCase)
|
|
632
|
+
.OrderBy(tfm => tfm)
|
|
633
|
+
.ToImmutableArray();
|
|
611
634
|
var tempDirectory = Directory.CreateTempSubdirectory("legacy-package-reference-resolution_");
|
|
612
635
|
try
|
|
613
636
|
{
|
|
@@ -651,7 +674,8 @@ internal static class SdkProjectDiscovery
|
|
|
651
674
|
NamedNode node,
|
|
652
675
|
Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesPerProject, // projectPath -> tfm -> (packageName, packageVersion)
|
|
653
676
|
Dictionary<string, Dictionary<string, HashSet<string>>> topLevelPackagesPerProject, // projectPath -> tfm -> packageName
|
|
654
|
-
Dictionary<string, Dictionary<string, Dictionary<string, string>>> packageVersionsPerProject // projectPath -> tfm -> (packageName, packageVersion)
|
|
677
|
+
Dictionary<string, Dictionary<string, Dictionary<string, string>>> packageVersionsPerProject, // projectPath -> tfm -> (packageName, packageVersion)
|
|
678
|
+
Dictionary<string, int> packageReferenceElementCounts // projectPath -> count of `<PackageReference>` elements
|
|
655
679
|
)
|
|
656
680
|
{
|
|
657
681
|
var doRemoveOperation = node is RemoveItem;
|
|
@@ -670,7 +694,11 @@ internal static class SdkProjectDiscovery
|
|
|
670
694
|
continue;
|
|
671
695
|
}
|
|
672
696
|
|
|
673
|
-
|
|
697
|
+
// count instances of `<PackageReference>`
|
|
698
|
+
//var packageReferenceElements = packageReferenceElementCounts.GetOrAdd(projectEvaluation.ProjectFile, () => 0);
|
|
699
|
+
//packageReferenceElementCounts[projectEvaluation.ProjectFile] = packageReferenceElements + 1;
|
|
700
|
+
|
|
701
|
+
var tfm = GetTargetFrameworkFromProjectEvaluation(projectEvaluation);
|
|
674
702
|
if (tfm is not null)
|
|
675
703
|
{
|
|
676
704
|
var topLevelPackages = topLevelPackagesPerProject.GetOrAdd(projectEvaluation.ProjectFile, () => new(StringComparer.OrdinalIgnoreCase));
|
|
@@ -838,4 +866,32 @@ internal static class SdkProjectDiscovery
|
|
|
838
866
|
|
|
839
867
|
return property.Value;
|
|
840
868
|
}
|
|
869
|
+
|
|
870
|
+
private static string? GetTargetFrameworkFromProjectEvaluation(ProjectEvaluation projectEvaluation)
|
|
871
|
+
{
|
|
872
|
+
// try direct access of SDK-style property
|
|
873
|
+
var tfm = GetPropertyValueFromProjectEvaluation(projectEvaluation, "TargetFramework");
|
|
874
|
+
if (tfm is null)
|
|
875
|
+
{
|
|
876
|
+
// fall back to legacy properties
|
|
877
|
+
var frameworkMoniker = GetPropertyValueFromProjectEvaluation(projectEvaluation, "TargetFrameworkMoniker");
|
|
878
|
+
if (frameworkMoniker is not null)
|
|
879
|
+
{
|
|
880
|
+
var platformMoniker = GetPropertyValueFromProjectEvaluation(projectEvaluation, "TargetPlatformMoniker");
|
|
881
|
+
try
|
|
882
|
+
{
|
|
883
|
+
var framework = string.IsNullOrEmpty(platformMoniker)
|
|
884
|
+
? NuGetFramework.Parse(frameworkMoniker)
|
|
885
|
+
: NuGetFramework.ParseComponents(frameworkMoniker, platformMoniker);
|
|
886
|
+
tfm = framework.GetShortFolderName();
|
|
887
|
+
}
|
|
888
|
+
catch
|
|
889
|
+
{
|
|
890
|
+
// if unable to parse, retain null
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
return tfm;
|
|
896
|
+
}
|
|
841
897
|
}
|
|
@@ -8,14 +8,12 @@ namespace NuGetUpdater.Core;
|
|
|
8
8
|
public record ExperimentsManager
|
|
9
9
|
{
|
|
10
10
|
public bool GenerateSimplePrBody { get; init; } = false;
|
|
11
|
-
public bool UseSingleRestore { get; init; } = false;
|
|
12
11
|
|
|
13
12
|
public Dictionary<string, object> ToDictionary()
|
|
14
13
|
{
|
|
15
14
|
return new()
|
|
16
15
|
{
|
|
17
16
|
["nuget_generate_simple_pr_body"] = GenerateSimplePrBody,
|
|
18
|
-
["nuget_use_single_restore"] = UseSingleRestore,
|
|
19
17
|
};
|
|
20
18
|
}
|
|
21
19
|
|
|
@@ -24,7 +22,6 @@ public record ExperimentsManager
|
|
|
24
22
|
return new ExperimentsManager()
|
|
25
23
|
{
|
|
26
24
|
GenerateSimplePrBody = IsEnabled(experiments, "nuget_generate_simple_pr_body"),
|
|
27
|
-
UseSingleRestore = IsEnabled(experiments, "nuget_use_single_restore"),
|
|
28
25
|
};
|
|
29
26
|
}
|
|
30
27
|
|
|
@@ -13,15 +13,24 @@ public record DependencyGroup
|
|
|
13
13
|
// "patterns" => string[] where each element is a wildcard name pattern
|
|
14
14
|
// "exclude-patterns"=> string[] where each element is a wildcard name pattern
|
|
15
15
|
// "dependency-type" => production|development // not used for nuget?
|
|
16
|
+
// "update-types" => string[] where each element is one of major|minor|patch
|
|
16
17
|
public Dictionary<string, object> Rules { get; init; } = new();
|
|
17
18
|
|
|
18
19
|
public GroupMatcher GetGroupMatcher() => GroupMatcher.FromRules(Rules);
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
public enum GroupUpdateType
|
|
23
|
+
{
|
|
24
|
+
Major,
|
|
25
|
+
Minor,
|
|
26
|
+
Patch,
|
|
27
|
+
}
|
|
28
|
+
|
|
21
29
|
public class GroupMatcher
|
|
22
30
|
{
|
|
23
31
|
public ImmutableArray<string> Patterns { get; init; } = ImmutableArray<string>.Empty;
|
|
24
32
|
public ImmutableArray<string> ExcludePatterns { get; init; } = ImmutableArray<string>.Empty;
|
|
33
|
+
public ImmutableArray<GroupUpdateType> UpdateTypes { get; init; } = ImmutableArray<GroupUpdateType>.Empty;
|
|
25
34
|
|
|
26
35
|
public bool IsMatch(string dependencyName)
|
|
27
36
|
{
|
|
@@ -35,11 +44,21 @@ public class GroupMatcher
|
|
|
35
44
|
{
|
|
36
45
|
var patterns = GetStringArray(rules, "patterns", ["*"]); // default to matching everything unless explicitly excluded
|
|
37
46
|
var excludePatterns = GetStringArray(rules, "exclude-patterns", []);
|
|
47
|
+
var updateTypes = GetStringArray(rules, "update-types", ["major", "minor", "patch"]) // default to everything unless explicitly specified
|
|
48
|
+
.Select(s => s.ToLowerInvariant() switch
|
|
49
|
+
{
|
|
50
|
+
"major" => GroupUpdateType.Major,
|
|
51
|
+
"minor" => GroupUpdateType.Minor,
|
|
52
|
+
"patch" => GroupUpdateType.Patch,
|
|
53
|
+
_ => throw new InvalidOperationException($"Unknown update type: {s}"),
|
|
54
|
+
})
|
|
55
|
+
.ToImmutableArray();
|
|
38
56
|
|
|
39
57
|
return new GroupMatcher()
|
|
40
58
|
{
|
|
41
59
|
Patterns = patterns,
|
|
42
60
|
ExcludePatterns = excludePatterns,
|
|
61
|
+
UpdateTypes = updateTypes,
|
|
43
62
|
};
|
|
44
63
|
}
|
|
45
64
|
|
|
@@ -42,7 +42,7 @@ public sealed record Job
|
|
|
42
42
|
public int MaxUpdaterRunTime { get; init; } = 0;
|
|
43
43
|
public Cooldown? Cooldown { get; init; } = null;
|
|
44
44
|
|
|
45
|
-
public ImmutableArray<string>
|
|
45
|
+
public ImmutableArray<string> GetRawDirectories()
|
|
46
46
|
{
|
|
47
47
|
var builder = ImmutableArray.CreateBuilder<string>();
|
|
48
48
|
if (Source.Directory is not null)
|
|
@@ -59,6 +59,27 @@ public sealed record Job
|
|
|
59
59
|
return builder.ToImmutable();
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
public ImmutableArray<string> GetAllDirectories(string repoRoot)
|
|
63
|
+
{
|
|
64
|
+
// where possible we want to maintain the order of the specified directories, so we have to manually handle each one
|
|
65
|
+
var seenDirectories = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
66
|
+
var rawDirectories = GetRawDirectories();
|
|
67
|
+
var result = new List<string>();
|
|
68
|
+
foreach (var directory in rawDirectories)
|
|
69
|
+
{
|
|
70
|
+
var expandedDirectories = PathHelper.GetMatchingDirectoriesUnder(repoRoot, directory, caseSensitive: false);
|
|
71
|
+
foreach (var expanded in expandedDirectories)
|
|
72
|
+
{
|
|
73
|
+
if (seenDirectories.Add(expanded))
|
|
74
|
+
{
|
|
75
|
+
result.Add(expanded);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return [.. result];
|
|
81
|
+
}
|
|
82
|
+
|
|
62
83
|
public ImmutableArray<DependencyGroup> GetRelevantDependencyGroups()
|
|
63
84
|
{
|
|
64
85
|
var appliesToKey = SecurityUpdatesOnly ? "security-updates" : "version-updates";
|
|
@@ -121,7 +142,7 @@ public sealed record Job
|
|
|
121
142
|
}
|
|
122
143
|
|
|
123
144
|
var version = NuGetVersion.Parse(dependency.Version);
|
|
124
|
-
var dependencyInfo = RunWorker.GetDependencyInfo(this, dependency, allowCooldown: false);
|
|
145
|
+
var dependencyInfo = RunWorker.GetDependencyInfo(this, dependency, groupMatchers: [], allowCooldown: false);
|
|
125
146
|
var isVulnerable = dependencyInfo.Vulnerabilities.Any(v => v.IsVulnerable(version));
|
|
126
147
|
|
|
127
148
|
bool IsAllowed(AllowedUpdate allowedUpdate)
|
|
@@ -73,13 +73,15 @@ public abstract record JobErrorBase : MessageBase
|
|
|
73
73
|
case InvalidDataException invalidData when invalidData.Message == "Central Directory corrupt.":
|
|
74
74
|
return new PrivateSourceBadResponse(NuGetContext.GetPackageSourceUrls(currentDirectory), invalidData.Message);
|
|
75
75
|
case InvalidProjectFileException invalidProjectFile:
|
|
76
|
-
return new DependencyFileNotParseable(invalidProjectFile.ProjectFile);
|
|
76
|
+
return new DependencyFileNotParseable(Path.GetRelativePath(currentDirectory, invalidProjectFile.ProjectFile).NormalizePathToUnix());
|
|
77
|
+
case IOException ioException when ioException.Message.Contains("No space left on device", StringComparison.OrdinalIgnoreCase):
|
|
78
|
+
return new OutOfDisk();
|
|
77
79
|
case MissingFileException missingFile:
|
|
78
80
|
return new DependencyFileNotFound(missingFile.FilePath, missingFile.Message);
|
|
79
81
|
case PrivateSourceTimedOutException timeout:
|
|
80
82
|
return new PrivateSourceTimedOut(timeout.Url);
|
|
81
83
|
case UnparseableFileException unparseableFile:
|
|
82
|
-
return new DependencyFileNotParseable(unparseableFile.FilePath, unparseableFile.Message);
|
|
84
|
+
return new DependencyFileNotParseable(Path.GetRelativePath(currentDirectory, unparseableFile.FilePath).NormalizePathToUnix(), unparseableFile.Message);
|
|
83
85
|
case UpdateNotPossibleException updateNotPossible:
|
|
84
86
|
return new UpdateNotPossible(updateNotPossible.Dependencies);
|
|
85
87
|
default:
|
|
@@ -30,7 +30,17 @@ public static class IApiHandlerExtensions
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
public static Task UpdateDependencyList(this IApiHandler handler, UpdatedDependencyList updatedDependencyList)
|
|
33
|
+
public static async Task UpdateDependencyList(this IApiHandler handler, UpdatedDependencyList updatedDependencyList)
|
|
34
|
+
{
|
|
35
|
+
if (updatedDependencyList.Dependencies.Length == 0 && updatedDependencyList.DependencyFiles.Length == 0)
|
|
36
|
+
{
|
|
37
|
+
// directory wildcard expansion can lead to empty directories; don't report those
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
await handler.PostAsJson("update_dependency_list", updatedDependencyList);
|
|
42
|
+
}
|
|
43
|
+
|
|
34
44
|
public static Task IncrementMetric(this IApiHandler handler, IncrementMetric incrementMetric) => handler.PostAsJson("increment_metric", incrementMetric);
|
|
35
45
|
public static Task CreatePullRequest(this IApiHandler handler, CreatePullRequest createPullRequest) => handler.PostAsJson("create_pull_request", createPullRequest);
|
|
36
46
|
public static Task ClosePullRequest(this IApiHandler handler, ClosePullRequest closePullRequest) => handler.PostAsJson("close_pull_request", closePullRequest);
|
|
@@ -157,7 +157,7 @@ public class RunWorker
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
internal static DependencyInfo GetDependencyInfo(Job job, Dependency dependency, bool allowCooldown)
|
|
160
|
+
internal static DependencyInfo GetDependencyInfo(Job job, Dependency dependency, IEnumerable<GroupMatcher> groupMatchers, bool allowCooldown)
|
|
161
161
|
{
|
|
162
162
|
var dependencyVersion = NuGetVersion.Parse(dependency.Version!);
|
|
163
163
|
var securityAdvisories = job.SecurityAdvisories.Where(s => s.DependencyName.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase)).ToArray();
|
|
@@ -178,8 +178,29 @@ public class RunWorker
|
|
|
178
178
|
var ignoredUpdateTypes = job.IgnoreConditions
|
|
179
179
|
.Where(c => FileSystemName.MatchesSimpleExpression(c.DependencyName, dependency.Name))
|
|
180
180
|
.SelectMany(c => c.UpdateTypes ?? [])
|
|
181
|
-
.
|
|
182
|
-
|
|
181
|
+
.ToHashSet();
|
|
182
|
+
|
|
183
|
+
// if an update type isn't explicitly allowed by a group matcher, add it to the ignored set
|
|
184
|
+
foreach (var groupMatcher in groupMatchers)
|
|
185
|
+
{
|
|
186
|
+
if (groupMatcher.IsMatch(dependency.Name))
|
|
187
|
+
{
|
|
188
|
+
// group update types defaults to everything, so if it's not allowed then it's to be ignored
|
|
189
|
+
var allowedUpdateTypes = new HashSet<GroupUpdateType>(groupMatcher.UpdateTypes);
|
|
190
|
+
if (!allowedUpdateTypes.Contains(GroupUpdateType.Major))
|
|
191
|
+
{
|
|
192
|
+
ignoredUpdateTypes.Add(ConditionUpdateType.SemVerMajor);
|
|
193
|
+
}
|
|
194
|
+
if (!allowedUpdateTypes.Contains(GroupUpdateType.Minor))
|
|
195
|
+
{
|
|
196
|
+
ignoredUpdateTypes.Add(ConditionUpdateType.SemVerMinor);
|
|
197
|
+
}
|
|
198
|
+
if (!allowedUpdateTypes.Contains(GroupUpdateType.Patch))
|
|
199
|
+
{
|
|
200
|
+
ignoredUpdateTypes.Add(ConditionUpdateType.SemVerPatch);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
183
204
|
|
|
184
205
|
// while it would be nice to lift the cooldown options into the IgnoredUpdateTypes field, we don't know the
|
|
185
206
|
// publish date of the packages, so we have to pass along the whole object for the version finder to sort out
|
|
@@ -193,7 +214,7 @@ public class RunWorker
|
|
|
193
214
|
IsVulnerable = isVulnerable,
|
|
194
215
|
IgnoredVersions = ignoredVersions,
|
|
195
216
|
Vulnerabilities = vulnerabilities,
|
|
196
|
-
IgnoredUpdateTypes = ignoredUpdateTypes,
|
|
217
|
+
IgnoredUpdateTypes = [.. ignoredUpdateTypes.OrderBy(t => t)],
|
|
197
218
|
Cooldown = includeCooldown ? job.Cooldown : null,
|
|
198
219
|
};
|
|
199
220
|
return dependencyInfo;
|
|
@@ -31,7 +31,7 @@ internal class CreateSecurityUpdatePullRequestHandler : IUpdateHandler
|
|
|
31
31
|
{
|
|
32
32
|
var repoContentsPath = caseInsensitiveRepoContentsPath ?? originalRepoContentsPath;
|
|
33
33
|
var jobDependencies = job.Dependencies.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
34
|
-
foreach (var directory in job.GetAllDirectories())
|
|
34
|
+
foreach (var directory in job.GetAllDirectories(repoContentsPath.FullName))
|
|
35
35
|
{
|
|
36
36
|
var discoveryResult = await discoveryWorker.RunAsync(repoContentsPath.FullName, directory);
|
|
37
37
|
logger.ReportDiscovery(discoveryResult);
|
|
@@ -67,7 +67,7 @@ internal class CreateSecurityUpdatePullRequestHandler : IUpdateHandler
|
|
|
67
67
|
{
|
|
68
68
|
var dependencyName = dependencyGroupToUpdate.Key;
|
|
69
69
|
var vulnerableCandidateDependenciesToUpdate = dependencyGroupToUpdate.Value
|
|
70
|
-
.Select(o => (o.ProjectPath, o.Dependency, RunWorker.GetDependencyInfo(job, o.Dependency, allowCooldown: false)))
|
|
70
|
+
.Select(o => (o.ProjectPath, o.Dependency, RunWorker.GetDependencyInfo(job, o.Dependency, groupMatchers: [], allowCooldown: false)))
|
|
71
71
|
.Where(set => set.Item3.IsVulnerable)
|
|
72
72
|
.ToArray();
|
|
73
73
|
var vulnerableDependenciesToUpdate = vulnerableCandidateDependenciesToUpdate
|
data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/GroupUpdateAllVersionsHandler.cs
CHANGED
|
@@ -56,7 +56,7 @@ internal class GroupUpdateAllVersionsHandler : IUpdateHandler
|
|
|
56
56
|
var updateOperationsPerformed = new List<UpdateOperationBase>();
|
|
57
57
|
var updatedDependencies = new List<ReportedDependency>();
|
|
58
58
|
var allUpdatedDependencyFiles = ImmutableArray.Create<DependencyFile>();
|
|
59
|
-
foreach (var directory in job.GetAllDirectories())
|
|
59
|
+
foreach (var directory in job.GetAllDirectories(repoContentsPath.FullName))
|
|
60
60
|
{
|
|
61
61
|
var discoveryResult = await discoveryWorker.RunAsync(repoContentsPath.FullName, directory);
|
|
62
62
|
logger.ReportDiscovery(discoveryResult);
|
|
@@ -91,7 +91,7 @@ internal class GroupUpdateAllVersionsHandler : IUpdateHandler
|
|
|
91
91
|
continue;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
var dependencyInfo = RunWorker.GetDependencyInfo(job, dependency, allowCooldown: true);
|
|
94
|
+
var dependencyInfo = RunWorker.GetDependencyInfo(job, dependency, groupMatchers: [groupMatcher], allowCooldown: true);
|
|
95
95
|
var analysisResult = await analyzeWorker.RunAsync(repoContentsPath.FullName, discoveryResult, dependencyInfo);
|
|
96
96
|
if (analysisResult.Error is not null)
|
|
97
97
|
{
|
|
@@ -169,7 +169,7 @@ internal class GroupUpdateAllVersionsHandler : IUpdateHandler
|
|
|
169
169
|
private async Task RunUngroupedDependencyUpdates(Job job, DirectoryInfo originalRepoContentsPath, DirectoryInfo? caseInsensitiveRepoContentsPath, string baseCommitSha, IDiscoveryWorker discoveryWorker, IAnalyzeWorker analyzeWorker, IUpdaterWorker updaterWorker, IApiHandler apiHandler, ExperimentsManager experimentsManager, ILogger logger)
|
|
170
170
|
{
|
|
171
171
|
var repoContentsPath = caseInsensitiveRepoContentsPath ?? originalRepoContentsPath;
|
|
172
|
-
foreach (var directory in job.GetAllDirectories())
|
|
172
|
+
foreach (var directory in job.GetAllDirectories(repoContentsPath.FullName))
|
|
173
173
|
{
|
|
174
174
|
var discoveryResult = await discoveryWorker.RunAsync(repoContentsPath.FullName, directory);
|
|
175
175
|
logger.ReportDiscovery(discoveryResult);
|
|
@@ -213,7 +213,7 @@ internal class GroupUpdateAllVersionsHandler : IUpdateHandler
|
|
|
213
213
|
continue;
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
var dependencyInfo = RunWorker.GetDependencyInfo(job, dependency, allowCooldown: true);
|
|
216
|
+
var dependencyInfo = RunWorker.GetDependencyInfo(job, dependency, groupMatchers: [], allowCooldown: true);
|
|
217
217
|
var analysisResult = await analyzeWorker.RunAsync(repoContentsPath.FullName, discoveryResult, dependencyInfo);
|
|
218
218
|
if (analysisResult.Error is not null)
|
|
219
219
|
{
|
|
@@ -21,7 +21,7 @@ internal class RefreshGroupUpdatePullRequestHandler : IUpdateHandler
|
|
|
21
21
|
return false;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
if (job.
|
|
24
|
+
if (job.GetRawDirectories().Length > 1)
|
|
25
25
|
{
|
|
26
26
|
return true;
|
|
27
27
|
}
|
|
@@ -62,7 +62,7 @@ internal class RefreshGroupUpdatePullRequestHandler : IUpdateHandler
|
|
|
62
62
|
|
|
63
63
|
var groupMatcher = group.GetGroupMatcher();
|
|
64
64
|
var jobDependencies = job.Dependencies.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
65
|
-
foreach (var directory in job.GetAllDirectories())
|
|
65
|
+
foreach (var directory in job.GetAllDirectories(repoContentsPath.FullName))
|
|
66
66
|
{
|
|
67
67
|
var discoveryResult = await discoveryWorker.RunAsync(repoContentsPath.FullName, directory);
|
|
68
68
|
logger.ReportDiscovery(discoveryResult);
|
|
@@ -93,7 +93,7 @@ internal class RefreshGroupUpdatePullRequestHandler : IUpdateHandler
|
|
|
93
93
|
var dependencyName = dependencyGroupToUpdate.Key;
|
|
94
94
|
var relevantDependenciesToUpdate = dependencyGroupToUpdate.Value
|
|
95
95
|
.Where(o => !job.IsDependencyIgnoredByNameOnly(o.Dependency.Name))
|
|
96
|
-
.Select(o => (o.ProjectPath, o.Dependency, RunWorker.GetDependencyInfo(job, o.Dependency, allowCooldown: true)))
|
|
96
|
+
.Select(o => (o.ProjectPath, o.Dependency, RunWorker.GetDependencyInfo(job, o.Dependency, groupMatchers: [groupMatcher], allowCooldown: true)))
|
|
97
97
|
.ToArray();
|
|
98
98
|
|
|
99
99
|
foreach (var (projectPath, dependency, dependencyInfo) in relevantDependenciesToUpdate)
|
|
@@ -30,7 +30,7 @@ internal class RefreshSecurityUpdatePullRequestHandler : IUpdateHandler
|
|
|
30
30
|
{
|
|
31
31
|
var repoContentsPath = caseInsensitiveRepoContentsPath ?? originalRepoContentsPath;
|
|
32
32
|
var jobDependencies = job.Dependencies.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
33
|
-
foreach (var directory in job.GetAllDirectories())
|
|
33
|
+
foreach (var directory in job.GetAllDirectories(repoContentsPath.FullName))
|
|
34
34
|
{
|
|
35
35
|
var discoveryResult = await discoveryWorker.RunAsync(repoContentsPath.FullName, directory);
|
|
36
36
|
logger.ReportDiscovery(discoveryResult);
|
|
@@ -82,7 +82,7 @@ internal class RefreshSecurityUpdatePullRequestHandler : IUpdateHandler
|
|
|
82
82
|
var dependencyName = dependencyGroupToUpdate.Key;
|
|
83
83
|
var vulnerableDependenciesToUpdate = dependencyGroupToUpdate.Value
|
|
84
84
|
.Where(o => !job.IsDependencyIgnoredByNameOnly(o.Dependency.Name))
|
|
85
|
-
.Select(o => (o.ProjectPath, o.Dependency, RunWorker.GetDependencyInfo(job, o.Dependency, allowCooldown: false)))
|
|
85
|
+
.Select(o => (o.ProjectPath, o.Dependency, RunWorker.GetDependencyInfo(job, o.Dependency, groupMatchers: [], allowCooldown: false)))
|
|
86
86
|
.Where(set => set.Item3.IsVulnerable)
|
|
87
87
|
.ToArray();
|
|
88
88
|
|