dependabot-nuget 0.252.0 → 0.253.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +27 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +66 -65
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +44 -6
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +181 -93
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +18 -13
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +24 -6
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +18 -10
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +25 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestBase.cs +10 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +1 -8
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DirsProj.cs +0 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs +0 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +0 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +0 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +0 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +30 -23
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +44 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterHelperTests.cs +1 -1
- metadata +6 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 272069533f6bea5533fb423db28c745b469884c904e393d9708091398a93f9a3
|
4
|
+
data.tar.gz: dd0c3fad1ab56f2bc32e509f89552068cc59b02cd21b1878ef1edcca84af45ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a66d28929da003117a1a5ef125be1acd2dd91a522f715fa1c263610a63e333e5b60ec3343836a1d19b6516527595b88425aed9f7df7a5c3f254579b1a12ec77c
|
7
|
+
data.tar.gz: 4fa773a0ff8f2c5e423fd505d111eaf7f84439f99111b68a87e77700290ae0ae97d52318ec9ddabc4d5c80e953bdaef761da96730c82baa8f5c0e36034798e3b
|
@@ -293,22 +293,30 @@ public partial class EntryPointTests
|
|
293
293
|
);
|
294
294
|
}
|
295
295
|
|
296
|
-
[
|
297
|
-
|
296
|
+
[Theory]
|
297
|
+
[InlineData(null)]
|
298
|
+
[InlineData("src")]
|
299
|
+
public async Task UpdaterDoesNotUseRepoGlobalJsonForMSBuildTasks(string? workingDirectoryPath)
|
298
300
|
{
|
299
301
|
// This is a _very_ specific scenario where the `NuGetUpdater.Cli` tool might pick up a `global.json` from
|
300
302
|
// the root of the repo under test and use it's `sdk` property when trying to locate MSBuild. To properly
|
301
303
|
// test this, it must be tested in a new process where MSBuild has not been loaded yet and the runner tool
|
302
304
|
// must be started with its working directory at the test repo's root.
|
303
305
|
using var tempDir = new TemporaryDirectory();
|
304
|
-
|
306
|
+
var globalJsonPath = Path.Join(tempDir.DirectoryPath, "global.json");
|
307
|
+
var srcGlobalJsonPath = Path.Join(tempDir.DirectoryPath, "src", "global.json");
|
308
|
+
string globalJsonContent = """
|
305
309
|
{
|
306
310
|
"sdk": {
|
307
311
|
"version": "99.99.99"
|
308
312
|
}
|
309
313
|
}
|
310
|
-
"""
|
311
|
-
await File.WriteAllTextAsync(
|
314
|
+
""";
|
315
|
+
await File.WriteAllTextAsync(globalJsonPath, globalJsonContent);
|
316
|
+
Directory.CreateDirectory(Path.Join(tempDir.DirectoryPath, "src"));
|
317
|
+
await File.WriteAllTextAsync(srcGlobalJsonPath, globalJsonContent);
|
318
|
+
var projectPath = Path.Join(tempDir.DirectoryPath, "src", "project.csproj");
|
319
|
+
await File.WriteAllTextAsync(projectPath, """
|
312
320
|
<Project Sdk="Microsoft.NET.Sdk">
|
313
321
|
<PropertyGroup>
|
314
322
|
<TargetFramework>net8.0</TargetFramework>
|
@@ -325,7 +333,7 @@ public partial class EntryPointTests
|
|
325
333
|
"--repo-root",
|
326
334
|
tempDir.DirectoryPath,
|
327
335
|
"--solution-or-project",
|
328
|
-
|
336
|
+
projectPath,
|
329
337
|
"--dependency",
|
330
338
|
"Newtonsoft.Json",
|
331
339
|
"--new-version",
|
@@ -336,15 +344,25 @@ public partial class EntryPointTests
|
|
336
344
|
]);
|
337
345
|
|
338
346
|
// verify base run
|
339
|
-
var
|
347
|
+
var workingDirectory = tempDir.DirectoryPath;
|
348
|
+
if (workingDirectoryPath is not null)
|
349
|
+
{
|
350
|
+
workingDirectory = Path.Join(workingDirectory, workingDirectoryPath);
|
351
|
+
}
|
352
|
+
|
353
|
+
var (exitCode, output, error) = await ProcessEx.RunAsync(executableName, executableArgs, workingDirectory: workingDirectory);
|
340
354
|
Assert.True(exitCode == 0, $"Error running update on unsupported SDK.\nSTDOUT:\n{output}\nSTDERR:\n{error}");
|
341
355
|
|
342
356
|
// verify project update
|
343
|
-
var updatedProjectContents = await File.ReadAllTextAsync(
|
357
|
+
var updatedProjectContents = await File.ReadAllTextAsync(projectPath);
|
344
358
|
Assert.Contains("13.0.1", updatedProjectContents);
|
345
359
|
|
346
360
|
// verify `global.json` untouched
|
347
|
-
var updatedGlobalJsonContents = await File.ReadAllTextAsync(
|
361
|
+
var updatedGlobalJsonContents = await File.ReadAllTextAsync(globalJsonPath);
|
362
|
+
Assert.Contains("99.99.99", updatedGlobalJsonContents);
|
363
|
+
|
364
|
+
// verify `src/global.json` untouched
|
365
|
+
var updatedSrcGlobalJsonContents = await File.ReadAllTextAsync(srcGlobalJsonPath);
|
348
366
|
Assert.Contains("99.99.99", updatedGlobalJsonContents);
|
349
367
|
}
|
350
368
|
|
@@ -26,7 +26,7 @@ public partial class DiscoveryWorker
|
|
26
26
|
|
27
27
|
public async Task RunAsync(string repoRootPath, string workspacePath, string outputPath)
|
28
28
|
{
|
29
|
-
MSBuildHelper.RegisterMSBuild();
|
29
|
+
MSBuildHelper.RegisterMSBuild(Environment.CurrentDirectory, repoRootPath);
|
30
30
|
|
31
31
|
// When running under unit tests, the workspace path may not be rooted.
|
32
32
|
if (!Path.IsPathRooted(workspacePath) || !Directory.Exists(workspacePath))
|
@@ -9,86 +9,87 @@ internal static class SdkProjectDiscovery
|
|
9
9
|
public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverAsync(string repoRootPath, string workspacePath, string projectPath, Logger logger)
|
10
10
|
{
|
11
11
|
// Determine which targets and props files contribute to the build.
|
12
|
-
var buildFiles = await MSBuildHelper.
|
12
|
+
var (buildFiles, projectTargetFrameworks) = await MSBuildHelper.LoadBuildFilesAndTargetFrameworksAsync(repoRootPath, projectPath);
|
13
|
+
var tfms = projectTargetFrameworks.Order().ToImmutableArray();
|
13
14
|
|
14
15
|
// Get all the dependencies which are directly referenced from the project file or indirectly referenced from
|
15
16
|
// targets and props files.
|
16
17
|
var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles);
|
17
18
|
|
18
19
|
var results = ImmutableArray.CreateBuilder<ProjectDiscoveryResult>();
|
19
|
-
|
20
|
+
if (tfms.Length > 0)
|
20
21
|
{
|
21
|
-
|
22
|
-
if (buildFile.IsOutsideBasePath)
|
22
|
+
foreach (var buildFile in buildFiles)
|
23
23
|
{
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
// The build file dependencies have the correct DependencyType and the TopLevelDependencies have the evaluated version.
|
28
|
-
// Combine them to have the set of dependencies that are directly referenced from the build file.
|
29
|
-
var fileDependencies = BuildFile.GetDependencies(buildFile)
|
30
|
-
.ToDictionary(d => d.Name, StringComparer.OrdinalIgnoreCase);
|
31
|
-
var sdkDependencies = fileDependencies.Values
|
32
|
-
.Where(d => d.Type == DependencyType.MSBuildSdk)
|
33
|
-
.ToImmutableArray();
|
34
|
-
var indirectDependencies = topLevelDependencies
|
35
|
-
.Where(d => !fileDependencies.ContainsKey(d.Name))
|
36
|
-
.ToImmutableArray();
|
37
|
-
var directDependencies = topLevelDependencies
|
38
|
-
.Where(d => fileDependencies.ContainsKey(d.Name))
|
39
|
-
.Select(d =>
|
24
|
+
// Only include build files that exist beneath the RepoRootPath.
|
25
|
+
if (buildFile.IsOutsideBasePath)
|
40
26
|
{
|
41
|
-
|
42
|
-
|
43
|
-
{
|
44
|
-
Type = dependency.Type,
|
45
|
-
IsDirect = true
|
46
|
-
};
|
47
|
-
}).ToImmutableArray();
|
48
|
-
|
49
|
-
if (buildFile.GetFileType() == ProjectBuildFileType.Project)
|
50
|
-
{
|
51
|
-
// Collect information that is specific to the project file.
|
52
|
-
var tfms = MSBuildHelper.GetTargetFrameworkMonikers(buildFiles)
|
53
|
-
.OrderBy(tfm => tfm)
|
54
|
-
.ToImmutableArray();
|
55
|
-
var properties = MSBuildHelper.GetProperties(buildFiles).Values
|
56
|
-
.Where(p => !p.SourceFilePath.StartsWith(".."))
|
57
|
-
.OrderBy(p => p.Name)
|
58
|
-
.ToImmutableArray();
|
59
|
-
var referencedProjectPaths = MSBuildHelper.GetProjectPathsFromProject(projectPath)
|
60
|
-
.Select(path => Path.GetRelativePath(workspacePath, path))
|
61
|
-
.OrderBy(p => p)
|
62
|
-
.ToImmutableArray();
|
27
|
+
continue;
|
28
|
+
}
|
63
29
|
|
64
|
-
//
|
65
|
-
|
66
|
-
|
67
|
-
.
|
30
|
+
// The build file dependencies have the correct DependencyType and the TopLevelDependencies have the evaluated version.
|
31
|
+
// Combine them to have the set of dependencies that are directly referenced from the build file.
|
32
|
+
var fileDependencies = BuildFile.GetDependencies(buildFile)
|
33
|
+
.ToDictionary(d => d.Name, StringComparer.OrdinalIgnoreCase);
|
34
|
+
var sdkDependencies = fileDependencies.Values
|
35
|
+
.Where(d => d.Type == DependencyType.MSBuildSdk)
|
68
36
|
.ToImmutableArray();
|
69
|
-
var
|
70
|
-
|
71
|
-
.OrderBy(d => d.Name)
|
37
|
+
var indirectDependencies = topLevelDependencies
|
38
|
+
.Where(d => !fileDependencies.ContainsKey(d.Name))
|
72
39
|
.ToImmutableArray();
|
40
|
+
var directDependencies = topLevelDependencies
|
41
|
+
.Where(d => fileDependencies.ContainsKey(d.Name))
|
42
|
+
.Select(d =>
|
43
|
+
{
|
44
|
+
var dependency = fileDependencies[d.Name];
|
45
|
+
return d with
|
46
|
+
{
|
47
|
+
Type = dependency.Type,
|
48
|
+
IsDirect = true
|
49
|
+
};
|
50
|
+
}).ToImmutableArray();
|
73
51
|
|
74
|
-
|
52
|
+
if (buildFile.GetFileType() == ProjectBuildFileType.Project)
|
75
53
|
{
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
54
|
+
// Collect information that is specific to the project file.
|
55
|
+
var properties = MSBuildHelper.GetProperties(buildFiles).Values
|
56
|
+
.Where(p => !p.SourceFilePath.StartsWith(".."))
|
57
|
+
.OrderBy(p => p.Name)
|
58
|
+
.ToImmutableArray();
|
59
|
+
var referencedProjectPaths = MSBuildHelper.GetProjectPathsFromProject(projectPath)
|
60
|
+
.Select(path => Path.GetRelativePath(workspacePath, path))
|
61
|
+
.OrderBy(p => p)
|
62
|
+
.ToImmutableArray();
|
63
|
+
|
64
|
+
// Get the complete set of dependencies including transitive dependencies.
|
65
|
+
var dependencies = indirectDependencies.Concat(directDependencies).ToImmutableArray();
|
66
|
+
dependencies = dependencies
|
67
|
+
.Select(d => d with { TargetFrameworks = tfms })
|
68
|
+
.ToImmutableArray();
|
69
|
+
var transitiveDependencies = await GetTransitiveDependencies(repoRootPath, projectPath, tfms, dependencies, logger);
|
70
|
+
ImmutableArray<Dependency> allDependencies = dependencies.Concat(transitiveDependencies).Concat(sdkDependencies)
|
89
71
|
.OrderBy(d => d.Name)
|
90
|
-
.ToImmutableArray()
|
91
|
-
|
72
|
+
.ToImmutableArray();
|
73
|
+
|
74
|
+
results.Add(new()
|
75
|
+
{
|
76
|
+
FilePath = Path.GetRelativePath(workspacePath, buildFile.Path),
|
77
|
+
Properties = properties,
|
78
|
+
TargetFrameworks = tfms,
|
79
|
+
ReferencedProjectPaths = referencedProjectPaths,
|
80
|
+
Dependencies = allDependencies,
|
81
|
+
});
|
82
|
+
}
|
83
|
+
else
|
84
|
+
{
|
85
|
+
results.Add(new()
|
86
|
+
{
|
87
|
+
FilePath = Path.GetRelativePath(workspacePath, buildFile.Path),
|
88
|
+
Dependencies = directDependencies.Concat(sdkDependencies)
|
89
|
+
.OrderBy(d => d.Name)
|
90
|
+
.ToImmutableArray(),
|
91
|
+
});
|
92
|
+
}
|
92
93
|
}
|
93
94
|
}
|
94
95
|
|
@@ -20,8 +20,7 @@ internal static class SdkPackageUpdater
|
|
20
20
|
// SDK-style project, modify the XML directly
|
21
21
|
logger.Log(" Running for SDK-style project");
|
22
22
|
|
23
|
-
|
24
|
-
var tfms = MSBuildHelper.GetTargetFrameworkMonikers(buildFiles);
|
23
|
+
(ImmutableArray<ProjectBuildFile> buildFiles, string[] tfms) = await MSBuildHelper.LoadBuildFilesAndTargetFrameworksAsync(repoRootPath, projectPath);
|
25
24
|
|
26
25
|
// Get the set of all top-level dependencies in the current project
|
27
26
|
var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray();
|
@@ -42,7 +41,7 @@ internal static class SdkPackageUpdater
|
|
42
41
|
return;
|
43
42
|
}
|
44
43
|
|
45
|
-
UpdateTopLevelDepdendency(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, peerDependencies, logger);
|
44
|
+
await UpdateTopLevelDepdendency(repoRootPath, buildFiles, tfms, dependencyName, previousDependencyVersion, newDependencyVersion, peerDependencies, logger);
|
46
45
|
}
|
47
46
|
|
48
47
|
if (!await AreDependenciesCoherentAsync(repoRootPath, projectPath, dependencyName, logger, buildFiles, tfms))
|
@@ -226,10 +225,10 @@ internal static class SdkPackageUpdater
|
|
226
225
|
logger.Log($" Adding [{dependencyName}/{newDependencyVersion}] as a top-level package reference.");
|
227
226
|
|
228
227
|
// see https://learn.microsoft.com/nuget/consume-packages/install-use-packages-dotnet-cli
|
229
|
-
var (exitCode,
|
228
|
+
var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet", $"add {projectPath} package {dependencyName} --version {newDependencyVersion}", workingDirectory: Path.GetDirectoryName(projectPath));
|
230
229
|
if (exitCode != 0)
|
231
230
|
{
|
232
|
-
logger.Log($" Transitive dependency [{dependencyName}/{newDependencyVersion}] was not added
|
231
|
+
logger.Log($" Transitive dependency [{dependencyName}/{newDependencyVersion}] was not added.\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}");
|
233
232
|
}
|
234
233
|
}
|
235
234
|
|
@@ -288,8 +287,10 @@ internal static class SdkPackageUpdater
|
|
288
287
|
return packagesAndVersions;
|
289
288
|
}
|
290
289
|
|
291
|
-
private static
|
290
|
+
private static async Task UpdateTopLevelDepdendency(
|
291
|
+
string repoRootPath,
|
292
292
|
ImmutableArray<ProjectBuildFile> buildFiles,
|
293
|
+
string[] targetFrameworks,
|
293
294
|
string dependencyName,
|
294
295
|
string previousDependencyVersion,
|
295
296
|
string newDependencyVersion,
|
@@ -307,6 +308,43 @@ internal static class SdkPackageUpdater
|
|
307
308
|
{
|
308
309
|
TryUpdateDependencyVersion(buildFiles, packageName, previousDependencyVersion: null, newDependencyVersion: packageVersion, logger);
|
309
310
|
}
|
311
|
+
|
312
|
+
// now make all dependency requirements coherent
|
313
|
+
Dependency[] updatedTopLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray();
|
314
|
+
foreach (ProjectBuildFile projectFile in buildFiles)
|
315
|
+
{
|
316
|
+
foreach (string tfm in targetFrameworks)
|
317
|
+
{
|
318
|
+
Dependency[]? resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRootPath, projectFile.Path, tfm, updatedTopLevelDependencies, logger);
|
319
|
+
if (resolvedDependencies is null)
|
320
|
+
{
|
321
|
+
logger.Log($" Unable to resolve dependency conflicts for {projectFile.Path}.");
|
322
|
+
continue;
|
323
|
+
}
|
324
|
+
|
325
|
+
// ensure the originally requested dependency was resolved to the correct version
|
326
|
+
var specificResolvedDependency = resolvedDependencies.Where(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
|
327
|
+
if (specificResolvedDependency is null)
|
328
|
+
{
|
329
|
+
logger.Log($" Unable resolve requested dependency for {dependencyName} in {projectFile.Path}.");
|
330
|
+
continue;
|
331
|
+
}
|
332
|
+
|
333
|
+
if (!newDependencyVersion.Equals(specificResolvedDependency.Version, StringComparison.OrdinalIgnoreCase))
|
334
|
+
{
|
335
|
+
logger.Log($" Inconsistent resolution for {dependencyName}; attempted upgrade to {newDependencyVersion} but resolved {specificResolvedDependency.Version}.");
|
336
|
+
continue;
|
337
|
+
}
|
338
|
+
|
339
|
+
// update all other dependencies
|
340
|
+
foreach (Dependency resolvedDependency in resolvedDependencies
|
341
|
+
.Where(d => !d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase))
|
342
|
+
.Where(d => d.Version is not null))
|
343
|
+
{
|
344
|
+
TryUpdateDependencyVersion(buildFiles, resolvedDependency.Name, previousDependencyVersion: null, newDependencyVersion: resolvedDependency.Version!, logger);
|
345
|
+
}
|
346
|
+
}
|
347
|
+
}
|
310
348
|
}
|
311
349
|
|
312
350
|
private static UpdateResult TryUpdateDependencyVersion(
|
@@ -12,7 +12,7 @@ public class UpdaterWorker
|
|
12
12
|
|
13
13
|
public async Task RunAsync(string repoRootPath, string workspacePath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive)
|
14
14
|
{
|
15
|
-
MSBuildHelper.RegisterMSBuild();
|
15
|
+
MSBuildHelper.RegisterMSBuild(Environment.CurrentDirectory, repoRootPath);
|
16
16
|
|
17
17
|
if (!Path.IsPathRooted(workspacePath) || !File.Exists(workspacePath))
|
18
18
|
{
|
@@ -2,6 +2,7 @@ using System.Collections.Immutable;
|
|
2
2
|
using System.Diagnostics.CodeAnalysis;
|
3
3
|
using System.Text;
|
4
4
|
using System.Text.Json;
|
5
|
+
using System.Text.Json.Nodes;
|
5
6
|
using System.Text.RegularExpressions;
|
6
7
|
using System.Xml;
|
7
8
|
|
@@ -13,6 +14,7 @@ using Microsoft.Build.Locator;
|
|
13
14
|
using Microsoft.Extensions.FileSystemGlobbing;
|
14
15
|
|
15
16
|
using NuGet.Configuration;
|
17
|
+
using NuGet.Versioning;
|
16
18
|
|
17
19
|
using NuGetUpdater.Core.Utilities;
|
18
20
|
|
@@ -24,106 +26,34 @@ internal static partial class MSBuildHelper
|
|
24
26
|
|
25
27
|
public static bool IsMSBuildRegistered => MSBuildPath.Length > 0;
|
26
28
|
|
27
|
-
static
|
28
|
-
{
|
29
|
-
RegisterMSBuild();
|
30
|
-
}
|
31
|
-
|
32
|
-
public static void RegisterMSBuild()
|
29
|
+
public static void RegisterMSBuild(string currentDirectory, string rootDirectory)
|
33
30
|
{
|
34
31
|
// Ensure MSBuild types are registered before calling a method that loads the types
|
35
32
|
if (!IsMSBuildRegistered)
|
36
33
|
{
|
37
|
-
var
|
38
|
-
var
|
39
|
-
var
|
40
|
-
try
|
34
|
+
var candidateDirectories = PathHelper.GetAllDirectoriesToRoot(currentDirectory, rootDirectory);
|
35
|
+
var globalJsonPaths = candidateDirectories.Select(d => Path.Combine(d, "global.json")).Where(File.Exists).Select(p => (p, p + Guid.NewGuid().ToString())).ToArray();
|
36
|
+
foreach (var (globalJsonPath, tempGlobalJsonPath) in globalJsonPaths)
|
41
37
|
{
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
File.Move(globalJsonPath, tempGlobalJsonPath);
|
46
|
-
}
|
38
|
+
Console.WriteLine($"Temporarily removing `global.json` from `{Path.GetDirectoryName(globalJsonPath)}` for MSBuild detection.");
|
39
|
+
File.Move(globalJsonPath, tempGlobalJsonPath);
|
40
|
+
}
|
47
41
|
|
42
|
+
try
|
43
|
+
{
|
48
44
|
var defaultInstance = MSBuildLocator.QueryVisualStudioInstances().First();
|
49
45
|
MSBuildPath = defaultInstance.MSBuildPath;
|
50
46
|
MSBuildLocator.RegisterInstance(defaultInstance);
|
51
47
|
}
|
52
48
|
finally
|
53
49
|
{
|
54
|
-
|
55
|
-
{
|
56
|
-
Console.WriteLine("Restoring `global.json` after MSBuild detection.");
|
57
|
-
File.Move(tempGlobalJsonPath, globalJsonPath);
|
58
|
-
}
|
59
|
-
}
|
60
|
-
}
|
61
|
-
}
|
62
|
-
|
63
|
-
public static string[] GetTargetFrameworkMonikers(ImmutableArray<ProjectBuildFile> buildFiles)
|
64
|
-
{
|
65
|
-
HashSet<string> targetFrameworkValues = new(StringComparer.OrdinalIgnoreCase);
|
66
|
-
Dictionary<string, Property> propertyInfo = new(StringComparer.OrdinalIgnoreCase);
|
67
|
-
|
68
|
-
foreach (var buildFile in buildFiles)
|
69
|
-
{
|
70
|
-
var projectRoot = CreateProjectRootElement(buildFile);
|
71
|
-
|
72
|
-
foreach (var property in projectRoot.Properties)
|
73
|
-
{
|
74
|
-
if (property.Name.Equals("TargetFramework", StringComparison.OrdinalIgnoreCase) ||
|
75
|
-
property.Name.Equals("TargetFrameworks", StringComparison.OrdinalIgnoreCase))
|
76
|
-
{
|
77
|
-
if (buildFile.IsOutsideBasePath)
|
78
|
-
{
|
79
|
-
continue;
|
80
|
-
}
|
81
|
-
|
82
|
-
foreach (var tfm in property.Value.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
83
|
-
{
|
84
|
-
targetFrameworkValues.Add(tfm);
|
85
|
-
}
|
86
|
-
}
|
87
|
-
else if (property.Name.Equals("TargetFrameworkVersion", StringComparison.OrdinalIgnoreCase))
|
50
|
+
foreach (var (globalJsonpath, tempGlobalJsonPath) in globalJsonPaths)
|
88
51
|
{
|
89
|
-
|
90
|
-
|
91
|
-
continue;
|
92
|
-
}
|
93
|
-
|
94
|
-
// For packages.config projects that use TargetFrameworkVersion, we need to convert it to TargetFramework
|
95
|
-
targetFrameworkValues.Add($"net{property.Value.TrimStart('v').Replace(".", "")}");
|
52
|
+
Console.WriteLine($"Restoring `global.json` to `{Path.GetDirectoryName(globalJsonpath)}` after MSBuild discovery.");
|
53
|
+
File.Move(tempGlobalJsonPath, globalJsonpath);
|
96
54
|
}
|
97
|
-
else
|
98
|
-
{
|
99
|
-
propertyInfo[property.Name] = new(property.Name, property.Value, buildFile.RelativePath);
|
100
|
-
}
|
101
|
-
}
|
102
|
-
}
|
103
|
-
|
104
|
-
HashSet<string> targetFrameworks = new(StringComparer.OrdinalIgnoreCase);
|
105
|
-
|
106
|
-
foreach (var targetFrameworkValue in targetFrameworkValues)
|
107
|
-
{
|
108
|
-
var (resultType, _, tfms, _, errorMessage) =
|
109
|
-
GetEvaluatedValue(targetFrameworkValue, propertyInfo, propertiesToIgnore: ["TargetFramework", "TargetFrameworks"]);
|
110
|
-
if (resultType != EvaluationResultType.Success)
|
111
|
-
{
|
112
|
-
continue;
|
113
|
-
}
|
114
|
-
|
115
|
-
if (string.IsNullOrEmpty(tfms))
|
116
|
-
{
|
117
|
-
continue;
|
118
|
-
}
|
119
|
-
|
120
|
-
foreach (var tfm in tfms.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
121
|
-
{
|
122
|
-
targetFrameworks.Add(tfm);
|
123
55
|
}
|
124
56
|
}
|
125
|
-
|
126
|
-
return targetFrameworks.ToArray();
|
127
57
|
}
|
128
58
|
|
129
59
|
public static IEnumerable<string> GetProjectPathsFromSolution(string solutionPath)
|
@@ -379,6 +309,124 @@ internal static partial class MSBuildHelper
|
|
379
309
|
}
|
380
310
|
}
|
381
311
|
|
312
|
+
internal static async Task<Dependency[]?> ResolveDependencyConflicts(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, Logger logger)
|
313
|
+
{
|
314
|
+
var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-coherence_");
|
315
|
+
try
|
316
|
+
{
|
317
|
+
var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages);
|
318
|
+
var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"restore \"{tempProjectPath}\"", workingDirectory: tempDirectory.FullName);
|
319
|
+
|
320
|
+
// simple cases first
|
321
|
+
// if restore failed, nothing we can do
|
322
|
+
if (exitCode != 0)
|
323
|
+
{
|
324
|
+
return null;
|
325
|
+
}
|
326
|
+
|
327
|
+
// if no problems found, just return the current set
|
328
|
+
if (!stdOut.Contains("NU1608"))
|
329
|
+
{
|
330
|
+
return packages;
|
331
|
+
}
|
332
|
+
|
333
|
+
// now it gets complicated; look for the packages with issues
|
334
|
+
MatchCollection matches = PackageIncompatibilityWarningPattern().Matches(stdOut);
|
335
|
+
(string, NuGetVersion)[] badPackagesAndVersions = matches.Select(m => (m.Groups["PackageName"].Value, NuGetVersion.Parse(m.Groups["PackageVersion"].Value))).ToArray();
|
336
|
+
Dictionary<string, HashSet<NuGetVersion>> badPackagesAndCandidateVersionsDictionary = new(StringComparer.OrdinalIgnoreCase);
|
337
|
+
|
338
|
+
// and for each of those packages, find all versions greater than the one that's currently installed
|
339
|
+
foreach ((string packageName, NuGetVersion packageVersion) in badPackagesAndVersions)
|
340
|
+
{
|
341
|
+
// this command dumps a JSON object with all versions of the specified package from all package sources
|
342
|
+
(exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"package search {packageName} --exact-match --format json", workingDirectory: tempDirectory.FullName);
|
343
|
+
if (exitCode != 0)
|
344
|
+
{
|
345
|
+
continue;
|
346
|
+
}
|
347
|
+
|
348
|
+
// ensure collection exists
|
349
|
+
if (!badPackagesAndCandidateVersionsDictionary.ContainsKey(packageName))
|
350
|
+
{
|
351
|
+
badPackagesAndCandidateVersionsDictionary.Add(packageName, new HashSet<NuGetVersion>());
|
352
|
+
}
|
353
|
+
|
354
|
+
HashSet<NuGetVersion> foundVersions = badPackagesAndCandidateVersionsDictionary[packageName];
|
355
|
+
|
356
|
+
var json = JsonHelper.ParseNode(stdOut);
|
357
|
+
if (json?["searchResult"] is JsonArray searchResults)
|
358
|
+
{
|
359
|
+
foreach (var searchResult in searchResults)
|
360
|
+
{
|
361
|
+
if (searchResult?["packages"] is JsonArray packagesArray)
|
362
|
+
{
|
363
|
+
foreach (var package in packagesArray)
|
364
|
+
{
|
365
|
+
// in 8.0.xxx SDKs, the package version is in the `latestVersion` property, but in 9.0.xxx, it's `version`
|
366
|
+
var packageVersionProperty = package?["version"] ?? package?["latestVersion"];
|
367
|
+
if (packageVersionProperty is JsonValue latestVersion &&
|
368
|
+
latestVersion.GetValueKind() == JsonValueKind.String &&
|
369
|
+
NuGetVersion.TryParse(latestVersion.ToString(), out var nugetVersion) &&
|
370
|
+
nugetVersion > packageVersion)
|
371
|
+
{
|
372
|
+
foundVersions.Add(nugetVersion);
|
373
|
+
}
|
374
|
+
}
|
375
|
+
}
|
376
|
+
}
|
377
|
+
}
|
378
|
+
}
|
379
|
+
|
380
|
+
// generate all possible combinations
|
381
|
+
(string Key, NuGetVersion v)[][] expandedLists = badPackagesAndCandidateVersionsDictionary.Select(kvp => kvp.Value.Order().Select(v => (kvp.Key, v)).ToArray()).ToArray();
|
382
|
+
IEnumerable<(string PackageName, NuGetVersion PackageVersion)>[] product = expandedLists.CartesianProduct().ToArray();
|
383
|
+
|
384
|
+
// FUTURE WORK: pre-filter individual known package incompatibilities to reduce the number of combinations, e.g., if Package.A v1.0.0
|
385
|
+
// is incompatible with Package.B v2.0.0, then remove _all_ combinations with that pair
|
386
|
+
|
387
|
+
// this is the slow part
|
388
|
+
foreach (IEnumerable<(string PackageName, NuGetVersion PackageVersion)> candidateSet in product)
|
389
|
+
{
|
390
|
+
// rebuild candidate dependency list with the relevant versions
|
391
|
+
Dictionary<string, NuGetVersion> packageVersions = candidateSet.ToDictionary(candidateSet => candidateSet.PackageName, candidateSet => candidateSet.PackageVersion);
|
392
|
+
Dependency[] candidatePackages = packages.Select(p =>
|
393
|
+
{
|
394
|
+
if (packageVersions.TryGetValue(p.Name, out var version))
|
395
|
+
{
|
396
|
+
// create a new dependency with the updated version
|
397
|
+
return new Dependency(p.Name, version.ToString(), p.Type, IsDevDependency: p.IsDevDependency, IsOverride: p.IsOverride, IsUpdate: p.IsUpdate);
|
398
|
+
}
|
399
|
+
|
400
|
+
// not the dependency we're looking for, use whatever it already was in this set
|
401
|
+
return p;
|
402
|
+
}).ToArray();
|
403
|
+
|
404
|
+
if (await DependenciesAreCoherentAsync(repoRoot, projectPath, targetFramework, candidatePackages, logger))
|
405
|
+
{
|
406
|
+
// return as soon as we find a coherent set
|
407
|
+
return candidatePackages;
|
408
|
+
}
|
409
|
+
}
|
410
|
+
|
411
|
+
// no package resolution set found
|
412
|
+
return null;
|
413
|
+
}
|
414
|
+
finally
|
415
|
+
{
|
416
|
+
tempDirectory.Delete(recursive: true);
|
417
|
+
}
|
418
|
+
}
|
419
|
+
|
420
|
+
// fully expand all possible combinations using the algorithm from here:
|
421
|
+
// https://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/
|
422
|
+
private static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
|
423
|
+
{
|
424
|
+
IEnumerable<IEnumerable<T>> emptyProduct = [[]];
|
425
|
+
return sequences.Aggregate(emptyProduct, (accumulator, sequence) => from accseq in accumulator
|
426
|
+
from item in sequence
|
427
|
+
select accseq.Concat([item]));
|
428
|
+
}
|
429
|
+
|
382
430
|
private static ProjectRootElement CreateProjectRootElement(ProjectBuildFile buildFile)
|
383
431
|
{
|
384
432
|
var xmlString = buildFile.Contents.ToFullString();
|
@@ -569,7 +617,7 @@ internal static partial class MSBuildHelper
|
|
569
617
|
return directoryPackagesPropsPath is not null;
|
570
618
|
}
|
571
619
|
|
572
|
-
internal static async Task<ImmutableArray<ProjectBuildFile
|
620
|
+
internal static async Task<(ImmutableArray<ProjectBuildFile> ProjectBuildFiles, string[] TargetFrameworks)> LoadBuildFilesAndTargetFrameworksAsync(string repoRootPath, string projectPath)
|
573
621
|
{
|
574
622
|
var buildFileList = new List<string>
|
575
623
|
{
|
@@ -579,6 +627,7 @@ internal static partial class MSBuildHelper
|
|
579
627
|
// a global.json file might cause problems with the dotnet msbuild command; create a safe version temporarily
|
580
628
|
TryGetGlobalJsonPath(repoRootPath, projectPath, out var globalJsonPath);
|
581
629
|
var safeGlobalJsonName = $"{globalJsonPath}{Guid.NewGuid()}";
|
630
|
+
HashSet<string> targetFrameworks = new(StringComparer.OrdinalIgnoreCase);
|
582
631
|
|
583
632
|
try
|
584
633
|
{
|
@@ -607,16 +656,51 @@ internal static partial class MSBuildHelper
|
|
607
656
|
// load the project even if it imports a file that doesn't exist (e.g. a file that's generated at restore
|
608
657
|
// or build time).
|
609
658
|
using var projectCollection = new ProjectCollection(); // do this in a one-off instance and don't pollute the global collection
|
610
|
-
|
659
|
+
Project project = Project.FromFile(projectPath, new ProjectOptions
|
611
660
|
{
|
612
661
|
LoadSettings = ProjectLoadSettings.IgnoreMissingImports,
|
613
662
|
ProjectCollection = projectCollection,
|
614
663
|
});
|
615
664
|
buildFileList.AddRange(project.Imports.Select(i => i.ImportedProject.FullPath.NormalizePathToUnix()));
|
665
|
+
|
666
|
+
// use the MSBuild-evaluated value so we don't have to try to manually parse XML
|
667
|
+
IEnumerable<ProjectProperty> targetFrameworkProperties = project.Properties.Where(p => p.Name.Equals("TargetFramework", StringComparison.OrdinalIgnoreCase)).ToList();
|
668
|
+
IEnumerable<ProjectProperty> targetFrameworksProperties = project.Properties.Where(p => p.Name.Equals("TargetFrameworks", StringComparison.OrdinalIgnoreCase)).ToList();
|
669
|
+
IEnumerable<ProjectProperty> targetFrameworkVersionProperties = project.Properties.Where(p => p.Name.Equals("TargetFrameworkVersion", StringComparison.OrdinalIgnoreCase)).ToList();
|
670
|
+
foreach (ProjectProperty tfm in targetFrameworkProperties)
|
671
|
+
{
|
672
|
+
if (!string.IsNullOrWhiteSpace(tfm.EvaluatedValue))
|
673
|
+
{
|
674
|
+
targetFrameworks.Add(tfm.EvaluatedValue);
|
675
|
+
}
|
676
|
+
}
|
677
|
+
|
678
|
+
foreach (ProjectProperty tfms in targetFrameworksProperties)
|
679
|
+
{
|
680
|
+
foreach (string tfmValue in tfms.EvaluatedValue.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
681
|
+
{
|
682
|
+
targetFrameworks.Add(tfmValue);
|
683
|
+
}
|
684
|
+
}
|
685
|
+
|
686
|
+
if (targetFrameworks.Count == 0)
|
687
|
+
{
|
688
|
+
// Only try this if we haven't been able to resolve anything yet. This is because deep in the SDK, a
|
689
|
+
// `TargetFramework` of `netstandard2.0` (eventually) gets turned into `v2.0` and we don't want to
|
690
|
+
// interpret that as a .NET Framework 2.0 project.
|
691
|
+
foreach (ProjectProperty tfvm in targetFrameworkVersionProperties)
|
692
|
+
{
|
693
|
+
// `v0.0` is an error case where no TFM could be evaluated
|
694
|
+
if (tfvm.EvaluatedValue != "v0.0")
|
695
|
+
{
|
696
|
+
targetFrameworks.Add($"net{tfvm.EvaluatedValue.TrimStart('v').Replace(".", "")}");
|
697
|
+
}
|
698
|
+
}
|
699
|
+
}
|
616
700
|
}
|
617
701
|
catch (InvalidProjectFileException)
|
618
702
|
{
|
619
|
-
return [];
|
703
|
+
return ([], []);
|
620
704
|
}
|
621
705
|
finally
|
622
706
|
{
|
@@ -627,18 +711,22 @@ internal static partial class MSBuildHelper
|
|
627
711
|
}
|
628
712
|
|
629
713
|
var repoRootPathPrefix = repoRootPath.NormalizePathToUnix() + "/";
|
630
|
-
var buildFiles =
|
631
|
-
|
632
|
-
|
633
|
-
.Where(f => f.StartsWith(repoRootPathPrefix, StringComparison.OrdinalIgnoreCase))
|
634
|
-
.Distinct();
|
714
|
+
var buildFiles = buildFileList
|
715
|
+
.Where(f => f.StartsWith(repoRootPathPrefix, StringComparison.OrdinalIgnoreCase))
|
716
|
+
.Distinct();
|
635
717
|
var result = buildFiles
|
636
718
|
.Where(File.Exists)
|
637
719
|
.Select(path => ProjectBuildFile.Open(repoRootPath, path))
|
638
720
|
.ToImmutableArray();
|
639
|
-
return result;
|
721
|
+
return (result, targetFrameworks.ToArray());
|
640
722
|
}
|
641
723
|
|
642
724
|
[GeneratedRegex("^\\s*NuGetData::Package=(?<PackageName>[^,]+), Version=(?<PackageVersion>.+)$")]
|
643
725
|
private static partial Regex PackagePattern();
|
726
|
+
|
727
|
+
// Example output:
|
728
|
+
// NU1608: Detected package version outside of dependency constraint: SpecFlow.Tools.MsBuild.Generation 3.3.30 requires SpecFlow(= 3.3.30) but version SpecFlow 3.9.74 was resolved.
|
729
|
+
// PackageName-|+++++++++++++++++++++++++++++++| |++++|-PackageVersion
|
730
|
+
[GeneratedRegex("NU1608: [^:]+: (?<PackageName>[^ ]+) (?<PackageVersion>[^ ]+)")]
|
731
|
+
private static partial Regex PackageIncompatibilityWarningPattern();
|
644
732
|
}
|
@@ -34,20 +34,11 @@ internal static class PathHelper
|
|
34
34
|
public static string GetFullPathFromRelative(string rootPath, string relativePath)
|
35
35
|
=> Path.GetFullPath(JoinPath(rootPath, relativePath.NormalizePathToUnix()));
|
36
36
|
|
37
|
-
|
38
|
-
/// Check in every directory from <paramref name="initialPath"/> up to <paramref name="rootPath"/> for the file specified in <paramref name="fileName"/>.
|
39
|
-
/// </summary>
|
40
|
-
/// <returns>The path of the found file or null.</returns>
|
41
|
-
public static string? GetFileInDirectoryOrParent(string initialPath, string rootPath, string fileName, bool caseSensitive = true)
|
37
|
+
public static string[] GetAllDirectoriesToRoot(string initialDirectoryPath, string rootDirectoryPath)
|
42
38
|
{
|
43
|
-
if (File.Exists(initialPath))
|
44
|
-
{
|
45
|
-
initialPath = Path.GetDirectoryName(initialPath)!;
|
46
|
-
}
|
47
|
-
|
48
39
|
var candidatePaths = new List<string>();
|
49
|
-
var rootDirectory = new DirectoryInfo(
|
50
|
-
var candidateDirectory = new DirectoryInfo(
|
40
|
+
var rootDirectory = new DirectoryInfo(rootDirectoryPath);
|
41
|
+
var candidateDirectory = new DirectoryInfo(initialDirectoryPath);
|
51
42
|
while (candidateDirectory.FullName != rootDirectory.FullName)
|
52
43
|
{
|
53
44
|
candidatePaths.Add(candidateDirectory.FullName);
|
@@ -58,8 +49,22 @@ internal static class PathHelper
|
|
58
49
|
}
|
59
50
|
}
|
60
51
|
|
61
|
-
candidatePaths.Add(
|
52
|
+
candidatePaths.Add(rootDirectoryPath);
|
53
|
+
return candidatePaths.ToArray();
|
54
|
+
}
|
55
|
+
|
56
|
+
/// <summary>
|
57
|
+
/// Check in every directory from <paramref name="initialPath"/> up to <paramref name="rootPath"/> for the file specified in <paramref name="fileName"/>.
|
58
|
+
/// </summary>
|
59
|
+
/// <returns>The path of the found file or null.</returns>
|
60
|
+
public static string? GetFileInDirectoryOrParent(string initialPath, string rootPath, string fileName, bool caseSensitive = true)
|
61
|
+
{
|
62
|
+
if (File.Exists(initialPath))
|
63
|
+
{
|
64
|
+
initialPath = Path.GetDirectoryName(initialPath)!;
|
65
|
+
}
|
62
66
|
|
67
|
+
var candidatePaths = GetAllDirectoriesToRoot(initialPath, rootPath);
|
63
68
|
foreach (var candidatePath in candidatePaths)
|
64
69
|
{
|
65
70
|
try
|
@@ -1,4 +1,5 @@
|
|
1
1
|
using System.Collections.Immutable;
|
2
|
+
using System.Diagnostics.CodeAnalysis;
|
2
3
|
using System.Text.Json;
|
3
4
|
|
4
5
|
using NuGetUpdater.Core.Discover;
|
@@ -29,7 +30,7 @@ public class DiscoveryWorkerTestBase
|
|
29
30
|
protected static void ValidateWorkspaceResult(ExpectedWorkspaceDiscoveryResult expectedResult, WorkspaceDiscoveryResult actualResult)
|
30
31
|
{
|
31
32
|
Assert.NotNull(actualResult);
|
32
|
-
Assert.Equal(expectedResult.FilePath, actualResult.FilePath);
|
33
|
+
Assert.Equal(expectedResult.FilePath.NormalizePathToUnix(), actualResult.FilePath.NormalizePathToUnix());
|
33
34
|
ValidateDirectoryPackagesProps(expectedResult.DirectoryPackagesProps, actualResult.DirectoryPackagesProps);
|
34
35
|
ValidateResultWithDependencies(expectedResult.GlobalJson, actualResult.GlobalJson);
|
35
36
|
ValidateResultWithDependencies(expectedResult.DotNetToolsJson, actualResult.DotNetToolsJson);
|
@@ -50,7 +51,7 @@ public class DiscoveryWorkerTestBase
|
|
50
51
|
Assert.NotNull(actualResult);
|
51
52
|
}
|
52
53
|
|
53
|
-
Assert.Equal(expectedResult.FilePath, actualResult.FilePath);
|
54
|
+
Assert.Equal(expectedResult.FilePath.NormalizePathToUnix(), actualResult.FilePath.NormalizePathToUnix());
|
54
55
|
ValidateDependencies(expectedResult.Dependencies, actualResult.Dependencies);
|
55
56
|
Assert.Equal(expectedResult.ExpectedDependencyCount ?? expectedResult.Dependencies.Length, actualResult.Dependencies.Length);
|
56
57
|
}
|
@@ -64,12 +65,12 @@ public class DiscoveryWorkerTestBase
|
|
64
65
|
|
65
66
|
foreach (var expectedProject in expectedProjects)
|
66
67
|
{
|
67
|
-
var actualProject = actualProjects.Single(p => p.FilePath == expectedProject.FilePath);
|
68
|
+
var actualProject = actualProjects.Single(p => p.FilePath.NormalizePathToUnix() == expectedProject.FilePath.NormalizePathToUnix());
|
68
69
|
|
69
|
-
Assert.Equal(expectedProject.FilePath, actualProject.FilePath);
|
70
|
-
AssertEx.Equal(expectedProject.Properties, actualProject.Properties);
|
70
|
+
Assert.Equal(expectedProject.FilePath.NormalizePathToUnix(), actualProject.FilePath.NormalizePathToUnix());
|
71
|
+
AssertEx.Equal(expectedProject.Properties, actualProject.Properties, PropertyComparer.Instance);
|
71
72
|
AssertEx.Equal(expectedProject.TargetFrameworks, actualProject.TargetFrameworks);
|
72
|
-
AssertEx.Equal(expectedProject.ReferencedProjectPaths, actualProject.ReferencedProjectPaths);
|
73
|
+
AssertEx.Equal(expectedProject.ReferencedProjectPaths.Select(PathHelper.NormalizePathToUnix), actualProject.ReferencedProjectPaths.Select(PathHelper.NormalizePathToUnix));
|
73
74
|
ValidateDependencies(expectedProject.Dependencies, actualProject.Dependencies);
|
74
75
|
Assert.Equal(expectedProject.ExpectedDependencyCount ?? expectedProject.Dependencies.Length, actualProject.Dependencies.Length);
|
75
76
|
}
|
@@ -114,4 +115,21 @@ public class DiscoveryWorkerTestBase
|
|
114
115
|
var resultJson = await File.ReadAllTextAsync(resultPath);
|
115
116
|
return JsonSerializer.Deserialize<WorkspaceDiscoveryResult>(resultJson, DiscoveryWorker.SerializerOptions)!;
|
116
117
|
}
|
118
|
+
|
119
|
+
internal class PropertyComparer : IEqualityComparer<Property>
|
120
|
+
{
|
121
|
+
public static PropertyComparer Instance { get; } = new();
|
122
|
+
|
123
|
+
public bool Equals(Property? x, Property? y)
|
124
|
+
{
|
125
|
+
return x?.Name == y?.Name &&
|
126
|
+
x?.Value == y?.Value &&
|
127
|
+
x?.SourceFilePath.NormalizePathToUnix() == y?.SourceFilePath.NormalizePathToUnix();
|
128
|
+
}
|
129
|
+
|
130
|
+
public int GetHashCode([DisallowNull] Property obj)
|
131
|
+
{
|
132
|
+
throw new NotImplementedException();
|
133
|
+
}
|
134
|
+
}
|
117
135
|
}
|
data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs
CHANGED
@@ -25,11 +25,14 @@ public partial class DiscoveryWorkerTests
|
|
25
25
|
<package id="NuGet.Core" version="2.11.1" targetFramework="net46" />
|
26
26
|
<package id="NuGet.Server" version="2.11.2" targetFramework="net46" />
|
27
27
|
<package id="RouteMagic" version="1.3" targetFramework="net46" />
|
28
|
-
<package id="WebActivatorEx" version="2.1.0" targetFramework="net46"
|
28
|
+
<package id="WebActivatorEx" version="2.1.0" targetFramework="net46" />
|
29
29
|
</packages>
|
30
30
|
"""),
|
31
31
|
("myproj.csproj", """
|
32
32
|
<Project>
|
33
|
+
<PropertyGroup>
|
34
|
+
<TargetFramework>net46</TargetFramework>
|
35
|
+
</PropertyGroup>
|
33
36
|
</Project>
|
34
37
|
""")
|
35
38
|
],
|
@@ -40,16 +43,21 @@ public partial class DiscoveryWorkerTests
|
|
40
43
|
new()
|
41
44
|
{
|
42
45
|
FilePath = "myproj.csproj",
|
46
|
+
Properties = [
|
47
|
+
new("TargetFramework", "net46", "myproj.csproj"),
|
48
|
+
],
|
49
|
+
TargetFrameworks = ["net46"],
|
43
50
|
Dependencies = [
|
44
|
-
new("Microsoft.
|
45
|
-
new("Microsoft.
|
46
|
-
new("Microsoft.
|
47
|
-
new("Microsoft.Web.
|
48
|
-
new("
|
49
|
-
new("
|
50
|
-
new("NuGet.
|
51
|
-
new("
|
52
|
-
new("
|
51
|
+
new("Microsoft.NETFramework.ReferenceAssemblies", "1.0.3", DependencyType.Unknown, TargetFrameworks: ["net46"], IsTransitive: true),
|
52
|
+
new("Microsoft.CodeDom.Providers.DotNetCompilerPlatform", "1.0.0", DependencyType.PackagesConfig, TargetFrameworks: ["net46"]),
|
53
|
+
new("Microsoft.Net.Compilers", "1.0.1", DependencyType.PackagesConfig, TargetFrameworks: ["net46"]),
|
54
|
+
new("Microsoft.Web.Infrastructure", "1.0.0.0", DependencyType.PackagesConfig, TargetFrameworks: ["net46"]),
|
55
|
+
new("Microsoft.Web.Xdt", "2.1.1", DependencyType.PackagesConfig, TargetFrameworks: ["net46"]),
|
56
|
+
new("Newtonsoft.Json", "8.0.3", DependencyType.PackagesConfig, TargetFrameworks: ["net46"]),
|
57
|
+
new("NuGet.Core", "2.11.1", DependencyType.PackagesConfig, TargetFrameworks: ["net46"]),
|
58
|
+
new("NuGet.Server", "2.11.2", DependencyType.PackagesConfig, TargetFrameworks: ["net46"]),
|
59
|
+
new("RouteMagic", "1.3", DependencyType.PackagesConfig, TargetFrameworks: ["net46"]),
|
60
|
+
new("WebActivatorEx", "2.1.0", DependencyType.PackagesConfig, TargetFrameworks: ["net46"]),
|
53
61
|
],
|
54
62
|
}
|
55
63
|
],
|
data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs
CHANGED
@@ -376,5 +376,30 @@ public partial class DiscoveryWorkerTests
|
|
376
376
|
],
|
377
377
|
});
|
378
378
|
}
|
379
|
+
|
380
|
+
[Fact]
|
381
|
+
|
382
|
+
public async Task NoDependenciesReturnedIfNoTargetFrameworkCanBeResolved()
|
383
|
+
{
|
384
|
+
await TestDiscoveryAsync(
|
385
|
+
workspacePath: "",
|
386
|
+
files: [
|
387
|
+
("myproj.csproj", """
|
388
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
389
|
+
<PropertyGroup>
|
390
|
+
<TargetFramework>$(SomeCommonTfmThatCannotBeResolved)</TargetFramework>
|
391
|
+
</PropertyGroup>
|
392
|
+
<ItemGroup>
|
393
|
+
<PackageReference Include="Some.Package" Version="1.2.3" />
|
394
|
+
</ItemGroup>
|
395
|
+
</Project>
|
396
|
+
""")
|
397
|
+
],
|
398
|
+
expectedResult: new()
|
399
|
+
{
|
400
|
+
FilePath = "",
|
401
|
+
Projects = []
|
402
|
+
});
|
403
|
+
}
|
379
404
|
}
|
380
405
|
}
|
@@ -1,16 +1,9 @@
|
|
1
|
-
using System.Collections.Generic;
|
2
|
-
|
3
1
|
using Xunit;
|
4
2
|
|
5
3
|
namespace NuGetUpdater.Core.Test.Update;
|
6
4
|
|
7
|
-
public class PackagesConfigUpdaterTests
|
5
|
+
public class PackagesConfigUpdaterTests : TestBase
|
8
6
|
{
|
9
|
-
public PackagesConfigUpdaterTests()
|
10
|
-
{
|
11
|
-
MSBuildHelper.RegisterMSBuild();
|
12
|
-
}
|
13
|
-
|
14
7
|
[Theory]
|
15
8
|
[MemberData(nameof(PackagesDirectoryPathTestData))]
|
16
9
|
public void PathToPackagesDirectoryCanBeDetermined(string projectContents, string dependencyName, string dependencyVersion, string expectedPackagesDirectoryPath)
|
@@ -10,7 +10,7 @@ namespace NuGetUpdater.Core.Test.Update;
|
|
10
10
|
using TestFile = (string Path, string Content);
|
11
11
|
using TestProject = (string Path, string Content, Guid ProjectId);
|
12
12
|
|
13
|
-
public abstract class UpdateWorkerTestBase
|
13
|
+
public abstract class UpdateWorkerTestBase : TestBase
|
14
14
|
{
|
15
15
|
protected static Task TestNoChange(
|
16
16
|
string dependencyName,
|
@@ -8,11 +8,6 @@ public partial class UpdateWorkerTests
|
|
8
8
|
{
|
9
9
|
public class Sdk : UpdateWorkerTestBase
|
10
10
|
{
|
11
|
-
public Sdk()
|
12
|
-
{
|
13
|
-
MSBuildHelper.RegisterMSBuild();
|
14
|
-
}
|
15
|
-
|
16
11
|
[Theory]
|
17
12
|
[InlineData("net472")]
|
18
13
|
[InlineData("netstandard2.0")]
|
@@ -175,24 +170,6 @@ public partial class UpdateWorkerTests
|
|
175
170
|
]);
|
176
171
|
}
|
177
172
|
|
178
|
-
[Fact]
|
179
|
-
public async Task NoChange_WhenPackageHasVersionConstraint()
|
180
|
-
{
|
181
|
-
// Dependency package has version constraint
|
182
|
-
await TestNoChangeforProject("AWSSDK.Core", "3.3.21.19", "3.7.300.20",
|
183
|
-
projectContents: $"""
|
184
|
-
<Project Sdk="Microsoft.NET.Sdk">
|
185
|
-
<PropertyGroup>
|
186
|
-
<TargetFramework>netstandard2.0</TargetFramework>
|
187
|
-
</PropertyGroup>
|
188
|
-
<ItemGroup>
|
189
|
-
<PackageReference Include="AWSSDK.S3" Version="3.3.17.3" />
|
190
|
-
<PackageReference Include="AWSSDK.Core" Version="3.3.21.19" />
|
191
|
-
</ItemGroup>
|
192
|
-
</Project>
|
193
|
-
""");
|
194
|
-
}
|
195
|
-
|
196
173
|
[Fact]
|
197
174
|
public async Task UpdateVersionAttribute_InProjectFile_ForPackageReferenceInclude_Windows()
|
198
175
|
{
|
@@ -2558,5 +2535,35 @@ public partial class UpdateWorkerTests
|
|
2558
2535
|
"""
|
2559
2536
|
);
|
2560
2537
|
}
|
2538
|
+
|
2539
|
+
[Fact]
|
2540
|
+
public async Task UpdatingPackageAlsoUpdatesAnythingWithADependencyOnTheUpdatedPackage()
|
2541
|
+
{
|
2542
|
+
// updating SpecFlow from 3.3.30 requires that SpecFlow.Tools.MsBuild.Generation also be updated
|
2543
|
+
await TestUpdateForProject("SpecFlow", "3.3.30", "3.4.3",
|
2544
|
+
projectContents: """
|
2545
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
2546
|
+
<PropertyGroup>
|
2547
|
+
<TargetFramework>netstandard2.0</TargetFramework>
|
2548
|
+
</PropertyGroup>
|
2549
|
+
<ItemGroup>
|
2550
|
+
<PackageReference Include="SpecFlow" Version="3.3.30" />
|
2551
|
+
<PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.3.30" />
|
2552
|
+
</ItemGroup>
|
2553
|
+
</Project>
|
2554
|
+
""",
|
2555
|
+
expectedProjectContents: """
|
2556
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
2557
|
+
<PropertyGroup>
|
2558
|
+
<TargetFramework>netstandard2.0</TargetFramework>
|
2559
|
+
</PropertyGroup>
|
2560
|
+
<ItemGroup>
|
2561
|
+
<PackageReference Include="SpecFlow" Version="3.4.3" />
|
2562
|
+
<PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.4.3" />
|
2563
|
+
</ItemGroup>
|
2564
|
+
</Project>
|
2565
|
+
"""
|
2566
|
+
);
|
2567
|
+
}
|
2561
2568
|
}
|
2562
2569
|
}
|
@@ -6,13 +6,8 @@ namespace NuGetUpdater.Core.Test.Utilities;
|
|
6
6
|
|
7
7
|
using TestFile = (string Path, string Content);
|
8
8
|
|
9
|
-
public class MSBuildHelperTests
|
9
|
+
public class MSBuildHelperTests : TestBase
|
10
10
|
{
|
11
|
-
public MSBuildHelperTests()
|
12
|
-
{
|
13
|
-
MSBuildHelper.RegisterMSBuild();
|
14
|
-
}
|
15
|
-
|
16
11
|
[Fact]
|
17
12
|
public void GetRootedValue_FindsValue()
|
18
13
|
{
|
@@ -110,15 +105,16 @@ public class MSBuildHelperTests
|
|
110
105
|
[InlineData("<Project><PropertyGroup><TargetFrameworks>netstandard2.0</TargetFrameworks></PropertyGroup></Project>", "netstandard2.0", null)]
|
111
106
|
[InlineData("<Project><PropertyGroup><TargetFrameworks> ; netstandard2.0 ; </TargetFrameworks></PropertyGroup></Project>", "netstandard2.0", null)]
|
112
107
|
[InlineData("<Project><PropertyGroup><TargetFrameworks>netstandard2.0 ; netstandard2.1 ; </TargetFrameworks></PropertyGroup></Project>", "netstandard2.0", "netstandard2.1")]
|
113
|
-
|
108
|
+
[InlineData("<Project><PropertyGroup><TargetFramework>netstandard2.0</TargetFramework><TargetFrameworkVersion Condition='False'>v4.7.2</TargetFrameworkVersion></PropertyGroup></Project>", "netstandard2.0", null)]
|
109
|
+
[InlineData("<Project><PropertyGroup><TargetFramework>$(PropertyThatCannotBeResolved)</TargetFramework></PropertyGroup></Project>", null, null)]
|
110
|
+
public async Task TfmsCanBeDeterminedFromProjectContents(string projectContents, string? expectedTfm1, string? expectedTfm2)
|
114
111
|
{
|
115
112
|
var projectPath = Path.GetTempFileName();
|
116
113
|
try
|
117
114
|
{
|
118
115
|
File.WriteAllText(projectPath, projectContents);
|
119
116
|
var expectedTfms = new[] { expectedTfm1, expectedTfm2 }.Where(tfm => tfm is not null).ToArray();
|
120
|
-
var
|
121
|
-
var actualTfms = MSBuildHelper.GetTargetFrameworkMonikers(ImmutableArray.Create(buildFile));
|
117
|
+
var (_buildFiles, actualTfms) = await MSBuildHelper.LoadBuildFilesAndTargetFrameworksAsync(Path.GetDirectoryName(projectPath)!, projectPath);
|
122
118
|
AssertEx.Equal(expectedTfms, actualTfms);
|
123
119
|
}
|
124
120
|
finally
|
@@ -493,6 +489,45 @@ public class MSBuildHelperTests
|
|
493
489
|
}
|
494
490
|
}
|
495
491
|
|
492
|
+
[Fact]
|
493
|
+
public async Task DependencyConflictsCanBeResolved()
|
494
|
+
{
|
495
|
+
// the package `SpecFlow` was already updated from 3.3.30 to 3.4.3, but this causes a conflict with
|
496
|
+
// `SpecFlow.Tools.MsBuild.Generation` that needs to be resolved
|
497
|
+
var repoRoot = Directory.CreateTempSubdirectory($"test_{nameof(DependencyConflictsCanBeResolved)}_");
|
498
|
+
try
|
499
|
+
{
|
500
|
+
var projectPath = Path.Join(repoRoot.FullName, "project.csproj");
|
501
|
+
await File.WriteAllTextAsync(projectPath, """
|
502
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
503
|
+
<PropertyGroup>
|
504
|
+
<TargetFramework>netstandard2.0</TargetFramework>
|
505
|
+
</PropertyGroup>
|
506
|
+
<ItemGroup>
|
507
|
+
<PackageReference Include="SpecFlow" Version="3.4.3" />
|
508
|
+
<PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.3.30" />
|
509
|
+
</ItemGroup>
|
510
|
+
</Project>
|
511
|
+
""");
|
512
|
+
var dependencies = new[]
|
513
|
+
{
|
514
|
+
new Dependency("SpecFlow", "3.4.3", DependencyType.PackageReference),
|
515
|
+
new Dependency("SpecFlow.Tools.MsBuild.Generation", "3.3.30", DependencyType.PackageReference),
|
516
|
+
};
|
517
|
+
var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "netstandard2.0", dependencies, new Logger(true));
|
518
|
+
Assert.NotNull(resolvedDependencies);
|
519
|
+
Assert.Equal(2, resolvedDependencies.Length);
|
520
|
+
Assert.Equal("SpecFlow", resolvedDependencies[0].Name);
|
521
|
+
Assert.Equal("3.4.3", resolvedDependencies[0].Version);
|
522
|
+
Assert.Equal("SpecFlow.Tools.MsBuild.Generation", resolvedDependencies[1].Name);
|
523
|
+
Assert.Equal("3.4.3", resolvedDependencies[1].Version);
|
524
|
+
}
|
525
|
+
finally
|
526
|
+
{
|
527
|
+
repoRoot.Delete(recursive: true);
|
528
|
+
}
|
529
|
+
}
|
530
|
+
|
496
531
|
public static IEnumerable<object[]> GetTopLevelPackageDependencyInfosTestData()
|
497
532
|
{
|
498
533
|
// simple case
|
data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterHelperTests.cs
CHANGED
@@ -167,7 +167,7 @@ namespace NuGetUpdater.Core.Test.Utilities
|
|
167
167
|
|
168
168
|
private static async Task<string[]> LoadBuildFilesFromTemp(TemporaryDirectory temporaryDirectory, string relativeProjectPath)
|
169
169
|
{
|
170
|
-
var buildFiles = await MSBuildHelper.
|
170
|
+
var (buildFiles, _tfms) = await MSBuildHelper.LoadBuildFilesAndTargetFrameworksAsync(temporaryDirectory.DirectoryPath, $"{temporaryDirectory.DirectoryPath}/{relativeProjectPath}");
|
171
171
|
var buildFilePaths = buildFiles.Select(f => f.RelativePath.NormalizePathToUnix()).ToArray();
|
172
172
|
return buildFilePaths;
|
173
173
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-nuget
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.253.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-04-
|
11
|
+
date: 2024-04-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dependabot-common
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.253.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.253.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rubyzip
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -184,20 +184,6 @@ dependencies:
|
|
184
184
|
- - "~>"
|
185
185
|
- !ruby/object:Gem::Version
|
186
186
|
version: 0.7.3
|
187
|
-
- !ruby/object:Gem::Dependency
|
188
|
-
name: stackprof
|
189
|
-
requirement: !ruby/object:Gem::Requirement
|
190
|
-
requirements:
|
191
|
-
- - "~>"
|
192
|
-
- !ruby/object:Gem::Version
|
193
|
-
version: 0.2.16
|
194
|
-
type: :development
|
195
|
-
prerelease: false
|
196
|
-
version_requirements: !ruby/object:Gem::Requirement
|
197
|
-
requirements:
|
198
|
-
- - "~>"
|
199
|
-
- !ruby/object:Gem::Version
|
200
|
-
version: 0.2.16
|
201
187
|
- !ruby/object:Gem::Dependency
|
202
188
|
name: turbo_tests
|
203
189
|
requirement: !ruby/object:Gem::Requirement
|
@@ -314,6 +300,7 @@ files:
|
|
314
300
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/SupportedFrameworkFacts.cs
|
315
301
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/NuGetUpdater.Core.Test.csproj
|
316
302
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs
|
303
|
+
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestBase.cs
|
317
304
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestExtensions.cs
|
318
305
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs
|
319
306
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs
|
@@ -417,7 +404,7 @@ licenses:
|
|
417
404
|
- Nonstandard
|
418
405
|
metadata:
|
419
406
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
420
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
407
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.253.0
|
421
408
|
post_install_message:
|
422
409
|
rdoc_options: []
|
423
410
|
require_paths:
|