dependabot-nuget 0.244.0 → 0.246.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/Updater/PackagesConfigUpdater.cs +42 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +164 -90
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +38 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +68 -18
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +115 -14
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/{UpdateWorker.DirsProj.cs → UpdateWorkerTests.DirsProj.cs} +22 -24
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +66 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +385 -81
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +7 -4
- data/lib/dependabot/nuget/file_parser/project_file_parser.rb +0 -4
- data/lib/dependabot/nuget/file_parser.rb +15 -1
- data/lib/dependabot/nuget/http_response_helpers.rb +14 -0
- data/lib/dependabot/nuget/metadata_finder.rb +6 -2
- data/lib/dependabot/nuget/native_helpers.rb +21 -12
- data/lib/dependabot/nuget/nuget_client.rb +8 -13
- data/lib/dependabot/nuget/update_checker/dependency_finder.rb +23 -13
- data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +75 -11
- data/lib/dependabot/nuget/update_checker/repository_finder.rb +4 -10
- data/lib/dependabot/nuget/update_checker.rb +2 -3
- metadata +7 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterTests.cs +0 -317
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e0fef609dc45e4b8d8f1bc8bf9c4d62ee6fd83a4fea8e28ae9022a469151875
|
4
|
+
data.tar.gz: caab818c3d702d5f34c1986e7ca9dca6b7457eade3a0c5f4d4df004682193d98
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce37d23d0440b68f8fce93364bcda68d61fb093b2435e853f956549c6c44e1bb68940ad06365e54c70ec18d667faa251455dc28f201b1b93b9410bfc1861681c
|
7
|
+
data.tar.gz: '0396865c4813a9c0b357826545eb6dcbee4c350d62fceed1d97469a9de4c0f2ec751974d07862d7667b0824a24bff12453f8f5309789e6a10a1ca7c25cb0a4f6'
|
@@ -48,7 +48,7 @@ internal static class PackagesConfigUpdater
|
|
48
48
|
var packagesDirectory = PathHelper.JoinPath(projectDirectory, packagesSubDirectory);
|
49
49
|
Directory.CreateDirectory(packagesDirectory);
|
50
50
|
|
51
|
-
var
|
51
|
+
var updateArgs = new List<string>
|
52
52
|
{
|
53
53
|
"update",
|
54
54
|
packagesConfigPath,
|
@@ -61,17 +61,29 @@ internal static class PackagesConfigUpdater
|
|
61
61
|
"-NonInteractive",
|
62
62
|
};
|
63
63
|
|
64
|
+
var restoreArgs = new List<string>
|
65
|
+
{
|
66
|
+
"restore",
|
67
|
+
projectPath,
|
68
|
+
"-PackagesDirectory",
|
69
|
+
packagesDirectory,
|
70
|
+
"-NonInteractive",
|
71
|
+
};
|
72
|
+
|
64
73
|
logger.Log(" Finding MSBuild...");
|
65
74
|
var msbuildDirectory = MSBuildHelper.MSBuildPath;
|
66
75
|
if (msbuildDirectory is not null)
|
67
76
|
{
|
68
|
-
args
|
69
|
-
|
77
|
+
foreach (var args in new[] { updateArgs, restoreArgs })
|
78
|
+
{
|
79
|
+
args.Add("-MSBuildPath");
|
80
|
+
args.Add(msbuildDirectory); // e.g., /usr/share/dotnet/sdk/7.0.203
|
81
|
+
}
|
70
82
|
}
|
71
83
|
|
72
84
|
using (new WebApplicationTargetsConditionPatcher(projectPath))
|
73
85
|
{
|
74
|
-
RunNuget(
|
86
|
+
RunNuget(updateArgs, restoreArgs, packagesDirectory, logger);
|
75
87
|
}
|
76
88
|
|
77
89
|
projectBuildFile = ProjectBuildFile.Open(repoRootPath, projectPath);
|
@@ -84,7 +96,7 @@ internal static class PackagesConfigUpdater
|
|
84
96
|
await projectBuildFile.SaveAsync();
|
85
97
|
}
|
86
98
|
|
87
|
-
private static void RunNuget(List<string>
|
99
|
+
private static void RunNuget(List<string> updateArgs, List<string> restoreArgs, string packagesDirectory, Logger logger)
|
88
100
|
{
|
89
101
|
var outputBuilder = new StringBuilder();
|
90
102
|
var writer = new StringWriter(outputBuilder);
|
@@ -97,15 +109,38 @@ internal static class PackagesConfigUpdater
|
|
97
109
|
var currentDir = Environment.CurrentDirectory;
|
98
110
|
try
|
99
111
|
{
|
100
|
-
logger.Log($" Running NuGet.exe with args: {string.Join(" ", args)}");
|
101
112
|
|
102
113
|
Environment.CurrentDirectory = packagesDirectory;
|
103
|
-
var
|
114
|
+
var retryingAfterRestore = false;
|
115
|
+
|
116
|
+
doRestore:
|
117
|
+
logger.Log($" Running NuGet.exe with args: {string.Join(" ", updateArgs)}");
|
118
|
+
outputBuilder.Clear();
|
119
|
+
var result = Program.Main(updateArgs.ToArray());
|
104
120
|
var fullOutput = outputBuilder.ToString();
|
105
121
|
logger.Log($" Result: {result}");
|
106
122
|
logger.Log($" Output:\n{fullOutput}");
|
107
123
|
if (result != 0)
|
108
124
|
{
|
125
|
+
// If the `packages.config` file contains a delisted package, the initial `update` operation will fail
|
126
|
+
// with the message listed below. The solution is to run `nuget.exe restore ...` and retry.
|
127
|
+
if (!retryingAfterRestore &&
|
128
|
+
fullOutput.Contains("Existing packages must be restored before performing an install or update."))
|
129
|
+
{
|
130
|
+
logger.Log($" Running NuGet.exe with args: {string.Join(" ", restoreArgs)}");
|
131
|
+
retryingAfterRestore = true;
|
132
|
+
outputBuilder.Clear();
|
133
|
+
var exitCodeAgain = Program.Main(restoreArgs.ToArray());
|
134
|
+
var restoreOutput = outputBuilder.ToString();
|
135
|
+
|
136
|
+
if (exitCodeAgain != 0)
|
137
|
+
{
|
138
|
+
throw new Exception($"Unable to restore.\nOutput:\n${restoreOutput}\n");
|
139
|
+
}
|
140
|
+
|
141
|
+
goto doRestore;
|
142
|
+
}
|
143
|
+
|
109
144
|
throw new Exception(fullOutput);
|
110
145
|
}
|
111
146
|
}
|
@@ -20,132 +20,124 @@ internal static class SdkPackageUpdater
|
|
20
20
|
string previousDependencyVersion,
|
21
21
|
string newDependencyVersion,
|
22
22
|
bool isTransitive,
|
23
|
-
Logger logger
|
24
|
-
)
|
23
|
+
Logger logger)
|
25
24
|
{
|
26
25
|
// SDK-style project, modify the XML directly
|
27
26
|
logger.Log(" Running for SDK-style project");
|
28
|
-
var buildFiles = await MSBuildHelper.LoadBuildFiles(repoRootPath, projectPath);
|
29
|
-
|
30
|
-
var newDependencyNuGetVersion = NuGetVersion.Parse(newDependencyVersion);
|
31
27
|
|
32
|
-
|
28
|
+
var buildFiles = await MSBuildHelper.LoadBuildFiles(repoRootPath, projectPath);
|
33
29
|
var tfms = MSBuildHelper.GetTargetFrameworkMonikers(buildFiles);
|
34
30
|
|
35
31
|
// Get the set of all top-level dependencies in the current project
|
36
32
|
var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray();
|
33
|
+
if (!await DoesDependencyRequireUpdateAsync(repoRootPath, projectPath, tfms, topLevelDependencies, dependencyName, newDependencyVersion, logger))
|
34
|
+
{
|
35
|
+
return;
|
36
|
+
}
|
37
37
|
|
38
|
-
|
39
|
-
var packageNeedsUpdating = false;
|
40
|
-
|
41
|
-
foreach (var tfm in tfms)
|
38
|
+
if (isTransitive)
|
42
39
|
{
|
43
|
-
|
44
|
-
|
40
|
+
await UpdateTransitiveDependencyAsnyc(projectPath, dependencyName, newDependencyVersion, buildFiles, logger);
|
41
|
+
}
|
42
|
+
else
|
43
|
+
{
|
44
|
+
var peerDependencies = await GetUpdatedPeerDependenciesAsync(repoRootPath, projectPath, tfms, dependencyName, newDependencyVersion, logger);
|
45
|
+
if (peerDependencies is null)
|
45
46
|
{
|
46
|
-
|
47
|
-
{
|
48
|
-
packageFoundInDependencies = true;
|
49
|
-
|
50
|
-
var nugetVersion = NuGetVersion.Parse(packageVersion);
|
51
|
-
if (nugetVersion < newDependencyNuGetVersion)
|
52
|
-
{
|
53
|
-
packageNeedsUpdating = true;
|
54
|
-
}
|
55
|
-
}
|
47
|
+
return;
|
56
48
|
}
|
57
|
-
}
|
58
49
|
|
59
|
-
|
60
|
-
if (!packageFoundInDependencies)
|
61
|
-
{
|
62
|
-
logger.Log($" Package [{dependencyName}] Does not exist as a dependency in [{projectPath}].");
|
63
|
-
return;
|
50
|
+
UpdateTopLevelDepdendency(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, peerDependencies, logger);
|
64
51
|
}
|
65
52
|
|
66
|
-
|
67
|
-
if (!packageNeedsUpdating)
|
53
|
+
if (!await AreDependenciesCoherentAsync(repoRootPath, projectPath, dependencyName, logger, buildFiles, tfms))
|
68
54
|
{
|
69
|
-
logger.Log($" Package [{dependencyName}] already meets the requested dependency version in [{projectPath}].");
|
70
55
|
return;
|
71
56
|
}
|
72
57
|
|
73
|
-
|
74
|
-
|
75
|
-
foreach (var tfm in tfms)
|
76
|
-
{
|
77
|
-
var dependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, newDependency, logger);
|
78
|
-
tfmsAndDependencies[tfm] = dependencies;
|
79
|
-
}
|
58
|
+
await SaveBuildFilesAsync(buildFiles, logger);
|
59
|
+
}
|
80
60
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
61
|
+
/// <summary>
|
62
|
+
/// Verifies that the package does not already satisfy the requested dependency version.
|
63
|
+
/// </summary>
|
64
|
+
/// <returns>Returns false if the package is not found or does not need to be updated.</returns>
|
65
|
+
private static async Task<bool> DoesDependencyRequireUpdateAsync(
|
66
|
+
string repoRootPath,
|
67
|
+
string projectPath,
|
68
|
+
string[] tfms,
|
69
|
+
Dependency[] topLevelDependencies,
|
70
|
+
string dependencyName,
|
71
|
+
string newDependencyVersion,
|
72
|
+
Logger logger)
|
73
|
+
{
|
74
|
+
var newDependencyNuGetVersion = NuGetVersion.Parse(newDependencyVersion);
|
75
|
+
|
76
|
+
bool packageFound = false;
|
77
|
+
bool needsUpdate = false;
|
78
|
+
|
79
|
+
foreach (var tfm in tfms)
|
85
80
|
{
|
81
|
+
var dependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(
|
82
|
+
repoRootPath,
|
83
|
+
projectPath,
|
84
|
+
tfm,
|
85
|
+
topLevelDependencies,
|
86
|
+
logger);
|
86
87
|
foreach (var (packageName, packageVersion, _, _, _, _) in dependencies)
|
87
88
|
{
|
88
|
-
if (
|
89
|
-
existingVersion != packageVersion)
|
89
|
+
if (packageVersion is null)
|
90
90
|
{
|
91
|
-
|
92
|
-
conflictingPackageVersionsFound = true;
|
91
|
+
continue;
|
93
92
|
}
|
94
|
-
|
93
|
+
|
94
|
+
if (packageName.Equals(dependencyName, StringComparison.OrdinalIgnoreCase))
|
95
95
|
{
|
96
|
-
|
96
|
+
packageFound = true;
|
97
|
+
|
98
|
+
var nugetVersion = NuGetVersion.Parse(packageVersion);
|
99
|
+
if (nugetVersion < newDependencyNuGetVersion)
|
100
|
+
{
|
101
|
+
needsUpdate = true;
|
102
|
+
break;
|
103
|
+
}
|
97
104
|
}
|
98
105
|
}
|
99
|
-
}
|
100
106
|
|
101
|
-
|
102
|
-
|
103
|
-
|
107
|
+
if (packageFound && needsUpdate)
|
108
|
+
{
|
109
|
+
break;
|
110
|
+
}
|
104
111
|
}
|
105
112
|
|
106
|
-
|
107
|
-
if (
|
113
|
+
// Skip updating the project if the dependency does not exist in the graph
|
114
|
+
if (!packageFound)
|
108
115
|
{
|
109
|
-
logger.Log($"
|
110
|
-
return;
|
116
|
+
logger.Log($" Package [{dependencyName}] Does not exist as a dependency in [{projectPath}].");
|
117
|
+
return false;
|
111
118
|
}
|
112
119
|
|
113
|
-
if
|
114
|
-
|
115
|
-
var directoryPackagesWithPinning = buildFiles.OfType<ProjectBuildFile>()
|
116
|
-
.FirstOrDefault(bf => IsCpmTransitivePinningEnabled(bf));
|
117
|
-
if (directoryPackagesWithPinning is not null)
|
118
|
-
{
|
119
|
-
PinTransitiveDependency(directoryPackagesWithPinning, dependencyName, newDependencyVersion, logger);
|
120
|
-
}
|
121
|
-
else
|
122
|
-
{
|
123
|
-
await AddTransitiveDependencyAsync(projectPath, dependencyName, newDependencyVersion, logger);
|
124
|
-
}
|
125
|
-
}
|
126
|
-
else
|
120
|
+
// Skip updating the project if the dependency version meets or exceeds the newDependencyVersion
|
121
|
+
if (!needsUpdate)
|
127
122
|
{
|
128
|
-
|
123
|
+
logger.Log($" Package [{dependencyName}] already meets the requested dependency version in [{projectPath}].");
|
124
|
+
return false;
|
129
125
|
}
|
130
126
|
|
131
|
-
|
132
|
-
|
127
|
+
return true;
|
128
|
+
}
|
129
|
+
|
130
|
+
private static async Task UpdateTransitiveDependencyAsnyc(string projectPath, string dependencyName, string newDependencyVersion, ImmutableArray<ProjectBuildFile> buildFiles, Logger logger)
|
131
|
+
{
|
132
|
+
var directoryPackagesWithPinning = buildFiles.OfType<ProjectBuildFile>()
|
133
|
+
.FirstOrDefault(bf => IsCpmTransitivePinningEnabled(bf));
|
134
|
+
if (directoryPackagesWithPinning is not null)
|
133
135
|
{
|
134
|
-
|
135
|
-
var dependenciesAreCoherent = await MSBuildHelper.DependenciesAreCoherentAsync(repoRootPath, projectPath, tfm, updatedPackages, logger);
|
136
|
-
if (!dependenciesAreCoherent)
|
137
|
-
{
|
138
|
-
logger.Log($" Package [{dependencyName}] could not be updated in [{projectPath}] because it would cause a dependency conflict.");
|
139
|
-
return;
|
140
|
-
}
|
136
|
+
PinTransitiveDependency(directoryPackagesWithPinning, dependencyName, newDependencyVersion, logger);
|
141
137
|
}
|
142
|
-
|
143
|
-
foreach (var buildFile in buildFiles)
|
138
|
+
else
|
144
139
|
{
|
145
|
-
|
146
|
-
{
|
147
|
-
logger.Log($" Saved [{buildFile.RepoRelativePath}].");
|
148
|
-
}
|
140
|
+
await AddTransitiveDependencyAsync(projectPath, dependencyName, newDependencyVersion, logger);
|
149
141
|
}
|
150
142
|
}
|
151
143
|
|
@@ -246,14 +238,68 @@ internal static class SdkPackageUpdater
|
|
246
238
|
}
|
247
239
|
}
|
248
240
|
|
241
|
+
/// <summary>
|
242
|
+
/// Gets the set of peer dependencies that need to be updated.
|
243
|
+
/// </summary>
|
244
|
+
/// <returns>Returns null if there are conflicting versions.</returns>
|
245
|
+
private static async Task<Dictionary<string, string>?> GetUpdatedPeerDependenciesAsync(
|
246
|
+
string repoRootPath,
|
247
|
+
string projectPath,
|
248
|
+
string[] tfms,
|
249
|
+
string dependencyName,
|
250
|
+
string newDependencyVersion,
|
251
|
+
Logger logger)
|
252
|
+
{
|
253
|
+
var newDependency = new[] { new Dependency(dependencyName, newDependencyVersion, DependencyType.Unknown) };
|
254
|
+
var tfmsAndDependencies = new Dictionary<string, Dependency[]>();
|
255
|
+
foreach (var tfm in tfms)
|
256
|
+
{
|
257
|
+
var dependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, newDependency, logger);
|
258
|
+
tfmsAndDependencies[tfm] = dependencies;
|
259
|
+
}
|
260
|
+
|
261
|
+
var unupgradableTfms = tfmsAndDependencies.Where(kvp => !kvp.Value.Any()).Select(kvp => kvp.Key);
|
262
|
+
if (unupgradableTfms.Any())
|
263
|
+
{
|
264
|
+
logger.Log($" The following target frameworks could not find packages to upgrade: {string.Join(", ", unupgradableTfms)}");
|
265
|
+
return null;
|
266
|
+
}
|
267
|
+
|
268
|
+
var conflictingPackageVersionsFound = false;
|
269
|
+
var packagesAndVersions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
270
|
+
foreach (var (_, dependencies) in tfmsAndDependencies)
|
271
|
+
{
|
272
|
+
foreach (var (packageName, packageVersion, _, _, _, _) in dependencies)
|
273
|
+
{
|
274
|
+
if (packagesAndVersions.TryGetValue(packageName, out var existingVersion) &&
|
275
|
+
existingVersion != packageVersion)
|
276
|
+
{
|
277
|
+
logger.Log($" Package [{packageName}] tried to update to version [{packageVersion}], but found conflicting package version of [{existingVersion}].");
|
278
|
+
conflictingPackageVersionsFound = true;
|
279
|
+
}
|
280
|
+
else
|
281
|
+
{
|
282
|
+
packagesAndVersions[packageName] = packageVersion!;
|
283
|
+
}
|
284
|
+
}
|
285
|
+
}
|
286
|
+
|
287
|
+
// stop update process if we find conflicting package versions
|
288
|
+
if (conflictingPackageVersionsFound)
|
289
|
+
{
|
290
|
+
return null;
|
291
|
+
}
|
292
|
+
|
293
|
+
return packagesAndVersions;
|
294
|
+
}
|
295
|
+
|
249
296
|
private static void UpdateTopLevelDepdendency(
|
250
297
|
ImmutableArray<ProjectBuildFile> buildFiles,
|
251
298
|
string dependencyName,
|
252
299
|
string previousDependencyVersion,
|
253
300
|
string newDependencyVersion,
|
254
|
-
IDictionary<string, string>
|
255
|
-
Logger logger
|
256
|
-
)
|
301
|
+
IDictionary<string, string> peerDependencies,
|
302
|
+
Logger logger)
|
257
303
|
{
|
258
304
|
var result = TryUpdateDependencyVersion(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, logger);
|
259
305
|
if (result == UpdateResult.NotFound)
|
@@ -262,7 +308,7 @@ internal static class SdkPackageUpdater
|
|
262
308
|
return;
|
263
309
|
}
|
264
310
|
|
265
|
-
foreach (var (packageName, packageVersion) in
|
311
|
+
foreach (var (packageName, packageVersion) in peerDependencies.Where(kvp => string.Compare(kvp.Key, dependencyName, StringComparison.OrdinalIgnoreCase) != 0))
|
266
312
|
{
|
267
313
|
TryUpdateDependencyVersion(buildFiles, packageName, previousDependencyVersion: null, newDependencyVersion: packageVersion, logger);
|
268
314
|
}
|
@@ -515,4 +561,32 @@ internal static class SdkPackageUpdater
|
|
515
561
|
packageName,
|
516
562
|
StringComparison.OrdinalIgnoreCase) &&
|
517
563
|
(e.GetAttributeOrSubElementValue("Version", StringComparison.OrdinalIgnoreCase) ?? e.GetAttributeOrSubElementValue("VersionOverride", StringComparison.OrdinalIgnoreCase)) is not null);
|
564
|
+
|
565
|
+
private static async Task<bool> AreDependenciesCoherentAsync(string repoRootPath, string projectPath, string dependencyName, Logger logger, ImmutableArray<ProjectBuildFile> buildFiles, string[] tfms)
|
566
|
+
{
|
567
|
+
var updatedTopLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray();
|
568
|
+
foreach (var tfm in tfms)
|
569
|
+
{
|
570
|
+
var updatedPackages = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, updatedTopLevelDependencies, logger);
|
571
|
+
var dependenciesAreCoherent = await MSBuildHelper.DependenciesAreCoherentAsync(repoRootPath, projectPath, tfm, updatedPackages, logger);
|
572
|
+
if (!dependenciesAreCoherent)
|
573
|
+
{
|
574
|
+
logger.Log($" Package [{dependencyName}] could not be updated in [{projectPath}] because it would cause a dependency conflict.");
|
575
|
+
return false;
|
576
|
+
}
|
577
|
+
}
|
578
|
+
|
579
|
+
return true;
|
580
|
+
}
|
581
|
+
|
582
|
+
private static async Task SaveBuildFilesAsync(ImmutableArray<ProjectBuildFile> buildFiles, Logger logger)
|
583
|
+
{
|
584
|
+
foreach (var buildFile in buildFiles)
|
585
|
+
{
|
586
|
+
if (await buildFile.SaveAsync())
|
587
|
+
{
|
588
|
+
logger.Log($" Saved [{buildFile.RepoRelativePath}].");
|
589
|
+
}
|
590
|
+
}
|
591
|
+
}
|
518
592
|
}
|
@@ -1,6 +1,7 @@
|
|
1
1
|
using System;
|
2
2
|
using System.Collections.Generic;
|
3
3
|
using System.IO;
|
4
|
+
using System.Linq;
|
4
5
|
using System.Threading.Tasks;
|
5
6
|
|
6
7
|
namespace NuGetUpdater.Core;
|
@@ -9,6 +10,7 @@ public class UpdaterWorker
|
|
9
10
|
{
|
10
11
|
private readonly Logger _logger;
|
11
12
|
private readonly HashSet<string> _processedGlobalJsonPaths = new(StringComparer.OrdinalIgnoreCase);
|
13
|
+
private readonly HashSet<string> _processedProjectPaths = new(StringComparer.OrdinalIgnoreCase);
|
12
14
|
|
13
15
|
public UpdaterWorker(Logger logger)
|
14
16
|
{
|
@@ -49,6 +51,7 @@ public class UpdaterWorker
|
|
49
51
|
}
|
50
52
|
|
51
53
|
_processedGlobalJsonPaths.Clear();
|
54
|
+
_processedProjectPaths.Clear();
|
52
55
|
}
|
53
56
|
|
54
57
|
private async Task RunForSolutionAsync(
|
@@ -101,7 +104,40 @@ public class UpdaterWorker
|
|
101
104
|
string newDependencyVersion,
|
102
105
|
bool isTransitive)
|
103
106
|
{
|
104
|
-
_logger.Log($"Running for project [{projectPath}]");
|
107
|
+
_logger.Log($"Running for project file [{Path.GetRelativePath(repoRootPath, projectPath)}]");
|
108
|
+
if (!File.Exists(projectPath))
|
109
|
+
{
|
110
|
+
_logger.Log($"File [{projectPath}] does not exist.");
|
111
|
+
return;
|
112
|
+
}
|
113
|
+
|
114
|
+
var projectFilePaths = MSBuildHelper.GetProjectPathsFromProject(projectPath);
|
115
|
+
foreach (var projectFullPath in projectFilePaths.Concat([projectPath]))
|
116
|
+
{
|
117
|
+
// If there is some MSBuild logic that needs to run to fully resolve the path skip the project
|
118
|
+
if (File.Exists(projectFullPath))
|
119
|
+
{
|
120
|
+
await RunUpdaterAsync(repoRootPath, projectFullPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
|
121
|
+
}
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
private async Task RunUpdaterAsync(
|
126
|
+
string repoRootPath,
|
127
|
+
string projectPath,
|
128
|
+
string dependencyName,
|
129
|
+
string previousDependencyVersion,
|
130
|
+
string newDependencyVersion,
|
131
|
+
bool isTransitive)
|
132
|
+
{
|
133
|
+
if (_processedProjectPaths.Contains(projectPath))
|
134
|
+
{
|
135
|
+
return;
|
136
|
+
}
|
137
|
+
|
138
|
+
_processedProjectPaths.Add(projectPath);
|
139
|
+
|
140
|
+
_logger.Log($"Updating project [{projectPath}]");
|
105
141
|
|
106
142
|
if (!isTransitive
|
107
143
|
&& MSBuildHelper.GetGlobalJsonPath(repoRootPath, projectPath) is { } globalJsonPath
|
@@ -111,7 +147,7 @@ public class UpdaterWorker
|
|
111
147
|
await GlobalJsonUpdater.UpdateDependencyAsync(repoRootPath, globalJsonPath, dependencyName, previousDependencyVersion, newDependencyVersion, _logger);
|
112
148
|
}
|
113
149
|
|
114
|
-
if (NuGetHelper.
|
150
|
+
if (NuGetHelper.HasPackagesConfigFile(projectPath))
|
115
151
|
{
|
116
152
|
await PackagesConfigUpdater.UpdateDependencyAsync(repoRootPath, projectPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive, _logger);
|
117
153
|
}
|
@@ -21,6 +21,8 @@ using NuGetUpdater.Core.Utilities;
|
|
21
21
|
|
22
22
|
namespace NuGetUpdater.Core;
|
23
23
|
|
24
|
+
using EvaluationResult = (MSBuildHelper.EvaluationResultType ResultType, string EvaluatedValue, string? ErrorMessage);
|
25
|
+
|
24
26
|
internal static partial class MSBuildHelper
|
25
27
|
{
|
26
28
|
public static string MSBuildPath { get; private set; } = string.Empty;
|
@@ -57,7 +59,10 @@ internal static partial class MSBuildHelper
|
|
57
59
|
if (property.Name.Equals("TargetFramework", StringComparison.OrdinalIgnoreCase) ||
|
58
60
|
property.Name.Equals("TargetFrameworks", StringComparison.OrdinalIgnoreCase))
|
59
61
|
{
|
60
|
-
|
62
|
+
foreach (var tfm in property.Value.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
63
|
+
{
|
64
|
+
targetFrameworkValues.Add(tfm);
|
65
|
+
}
|
61
66
|
}
|
62
67
|
else if (property.Name.Equals("TargetFrameworkVersion", StringComparison.OrdinalIgnoreCase))
|
63
68
|
{
|
@@ -75,8 +80,16 @@ internal static partial class MSBuildHelper
|
|
75
80
|
|
76
81
|
foreach (var targetFrameworkValue in targetFrameworkValues)
|
77
82
|
{
|
78
|
-
var tfms =
|
79
|
-
|
83
|
+
var (resultType, tfms, errorMessage) =
|
84
|
+
GetEvaluatedValue(targetFrameworkValue, propertyInfo, propertiesToIgnore: ["TargetFramework", "TargetFrameworks"]);
|
85
|
+
if (resultType == EvaluationResultType.PropertyIgnored)
|
86
|
+
{
|
87
|
+
continue;
|
88
|
+
}
|
89
|
+
else if (resultType != EvaluationResultType.Success)
|
90
|
+
{
|
91
|
+
throw new InvalidDataException(errorMessage);
|
92
|
+
}
|
80
93
|
|
81
94
|
if (string.IsNullOrEmpty(tfms))
|
82
95
|
{
|
@@ -101,10 +114,18 @@ internal static partial class MSBuildHelper
|
|
101
114
|
public static IEnumerable<string> GetProjectPathsFromProject(string projFilePath)
|
102
115
|
{
|
103
116
|
var projectStack = new Stack<(string folderPath, ProjectRootElement)>();
|
104
|
-
var projectRootElement = ProjectRootElement.Open(projFilePath);
|
105
117
|
var processedProjectFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
118
|
+
using var projectCollection = new ProjectCollection();
|
106
119
|
|
107
|
-
|
120
|
+
try
|
121
|
+
{
|
122
|
+
var projectRootElement = ProjectRootElement.Open(projFilePath, projectCollection);
|
123
|
+
projectStack.Push((Path.GetFullPath(Path.GetDirectoryName(projFilePath)!), projectRootElement));
|
124
|
+
}
|
125
|
+
catch (InvalidProjectFileException)
|
126
|
+
{
|
127
|
+
yield break; // Skip invalid project files
|
128
|
+
}
|
108
129
|
|
109
130
|
while (projectStack.Count > 0)
|
110
131
|
{
|
@@ -137,7 +158,7 @@ internal static partial class MSBuildHelper
|
|
137
158
|
// If there is some MSBuild logic that needs to run to fully resolve the path skip the project
|
138
159
|
if (File.Exists(file))
|
139
160
|
{
|
140
|
-
var additionalProjectRootElement = ProjectRootElement.Open(file);
|
161
|
+
var additionalProjectRootElement = ProjectRootElement.Open(file, projectCollection);
|
141
162
|
projectStack.Push((Path.GetFullPath(Path.GetDirectoryName(file)!), additionalProjectRootElement));
|
142
163
|
processedProjectFiles.Add(file);
|
143
164
|
}
|
@@ -222,9 +243,13 @@ internal static partial class MSBuildHelper
|
|
222
243
|
}
|
223
244
|
|
224
245
|
// Walk the property replacements until we don't find another one.
|
225
|
-
|
246
|
+
var evaluationResult = GetEvaluatedValue(packageVersion, propertyInfo);
|
247
|
+
if (evaluationResult.ResultType != EvaluationResultType.Success)
|
248
|
+
{
|
249
|
+
throw new InvalidDataException(evaluationResult.ErrorMessage);
|
250
|
+
}
|
226
251
|
|
227
|
-
packageVersion =
|
252
|
+
packageVersion = evaluationResult.EvaluatedValue.TrimStart('[', '(').TrimEnd(']', ')');
|
228
253
|
|
229
254
|
// We don't know the version for range requirements or wildcard
|
230
255
|
// requirements, so return "" for these.
|
@@ -237,25 +262,32 @@ internal static partial class MSBuildHelper
|
|
237
262
|
/// <summary>
|
238
263
|
/// Given an MSBuild string and a set of properties, returns our best guess at the final value MSBuild will evaluate to.
|
239
264
|
/// </summary>
|
240
|
-
|
241
|
-
/// <param name="propertyInfo"></param>
|
242
|
-
/// <returns></returns>
|
243
|
-
public static string GetRootedValue(string msbuildString, Dictionary<string, string> propertyInfo)
|
265
|
+
public static EvaluationResult GetEvaluatedValue(string msbuildString, Dictionary<string, string> propertyInfo, params string[] propertiesToIgnore)
|
244
266
|
{
|
267
|
+
var ignoredProperties = new HashSet<string>(propertiesToIgnore, StringComparer.OrdinalIgnoreCase);
|
245
268
|
var seenProperties = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
269
|
+
|
246
270
|
while (TryGetPropertyName(msbuildString, out var propertyName))
|
247
271
|
{
|
272
|
+
if (ignoredProperties.Contains(propertyName))
|
273
|
+
{
|
274
|
+
return (EvaluationResultType.PropertyIgnored, msbuildString, $"Property '{propertyName}' is ignored.");
|
275
|
+
}
|
276
|
+
|
248
277
|
if (!seenProperties.Add(propertyName))
|
249
278
|
{
|
250
|
-
|
279
|
+
return (EvaluationResultType.CircularReference, msbuildString, $"Property '{propertyName}' has a circular reference.");
|
280
|
+
}
|
281
|
+
|
282
|
+
if (!propertyInfo.TryGetValue(propertyName, out var propertyValue))
|
283
|
+
{
|
284
|
+
return (EvaluationResultType.PropertyNotFound, msbuildString, $"Property '{propertyName}' was not found.");
|
251
285
|
}
|
252
286
|
|
253
|
-
msbuildString =
|
254
|
-
? msbuildString.Replace($"$({propertyName})", propertyValue)
|
255
|
-
: throw new InvalidDataException($"Property '{propertyName}' was not found.");
|
287
|
+
msbuildString = msbuildString.Replace($"$({propertyName})", propertyValue);
|
256
288
|
}
|
257
289
|
|
258
|
-
return msbuildString;
|
290
|
+
return (EvaluationResultType.Success, msbuildString, null);
|
259
291
|
}
|
260
292
|
|
261
293
|
public static bool TryGetPropertyName(string versionContent, [NotNullWhen(true)] out string? propertyName)
|
@@ -360,7 +392,17 @@ internal static partial class MSBuildHelper
|
|
360
392
|
await File.WriteAllTextAsync(tempProjectPath, projectContents);
|
361
393
|
|
362
394
|
// prevent directory crawling
|
363
|
-
await File.WriteAllTextAsync(
|
395
|
+
await File.WriteAllTextAsync(
|
396
|
+
Path.Combine(tempDir.FullName, "Directory.Build.props"),
|
397
|
+
"""
|
398
|
+
<Project>
|
399
|
+
<PropertyGroup>
|
400
|
+
<!-- For Windows-specific apps -->
|
401
|
+
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
402
|
+
</PropertyGroup>
|
403
|
+
</Project>
|
404
|
+
""");
|
405
|
+
|
364
406
|
await File.WriteAllTextAsync(Path.Combine(tempDir.FullName, "Directory.Build.targets"), "<Project />");
|
365
407
|
await File.WriteAllTextAsync(Path.Combine(tempDir.FullName, "Directory.Packages.props"), "<Project />");
|
366
408
|
|
@@ -482,4 +524,12 @@ internal static partial class MSBuildHelper
|
|
482
524
|
|
483
525
|
[GeneratedRegex("^\\s*NuGetData::Package=(?<PackageName>[^,]+), Version=(?<PackageVersion>.+)$")]
|
484
526
|
private static partial Regex PackagePattern();
|
527
|
+
|
528
|
+
internal enum EvaluationResultType
|
529
|
+
{
|
530
|
+
Success,
|
531
|
+
PropertyIgnored,
|
532
|
+
CircularReference,
|
533
|
+
PropertyNotFound,
|
534
|
+
}
|
485
535
|
}
|
@@ -6,7 +6,7 @@ internal static class NuGetHelper
|
|
6
6
|
{
|
7
7
|
internal const string PackagesConfigFileName = "packages.config";
|
8
8
|
|
9
|
-
public static bool
|
9
|
+
public static bool HasPackagesConfigFile(string projectPath)
|
10
10
|
{
|
11
11
|
var projectDirectory = Path.GetDirectoryName(projectPath);
|
12
12
|
var packagesConfigPath = PathHelper.JoinPath(projectDirectory, PackagesConfigFileName);
|