dependabot-nuget 0.252.0 → 0.253.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.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:
|