dependabot-nuget 0.287.0 → 0.289.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/.gitignore +1 -0
- data/helpers/lib/NuGetUpdater/Directory.Build.targets +17 -0
- data/helpers/lib/NuGetUpdater/Directory.Packages.props +26 -17
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Packaging/NuGet.Packaging.csproj +0 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +7 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +3 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +88 -47
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +31 -16
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementArrayConverter.cs +39 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Clone/ShellGitCommandHandler.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyDiscovery.props +7 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyDiscovery.targets +10 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +64 -53
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DotNetToolsJsonDiscovery.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/GlobalJsonDiscovery.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscovery.cs +17 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscoveryResult.cs +3 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +3 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +429 -12
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/WorkspaceDiscoveryResult.cs +0 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +12 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/JsonBuildFile.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/CompatabilityChecker.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +7 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Job.cs +23 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +43 -58
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/TargetFrameworkReporter.targets +13 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +13 -43
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/DotNetToolsJsonUpdater.cs +4 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/GlobalJsonUpdater.cs +5 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +3 -10
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +40 -33
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +12 -11
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +16 -12
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/CollectionExtensions.cs +17 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ConsoleLogger.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DependencyConflictResolver.cs +19 -19
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ILogger.cs +11 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +74 -20
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +1 -17
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathComparer.cs +31 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +46 -10
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProjectHelper.cs +96 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +135 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +71 -38
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +66 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Proj.cs +11 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +808 -222
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +477 -97
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +5 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +494 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +46 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/NuGetUpdater.Core.Test.csproj +0 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +401 -77
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +35 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdatedDependencyListTests.cs +60 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs +3 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestLogger.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/BindingRedirectsTests.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +8 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +40 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/AssertEx.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/LinuxOnlyAttribute.cs +12 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +8 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/PathHelperTests.cs +49 -3
- data/lib/dependabot/nuget/analysis/analysis_json_reader.rb +3 -1
- data/lib/dependabot/nuget/file_fetcher.rb +12 -393
- data/lib/dependabot/nuget/file_parser.rb +23 -54
- data/lib/dependabot/nuget/file_updater.rb +21 -16
- data/lib/dependabot/nuget/native_discovery/native_dependency_file_discovery.rb +2 -9
- data/lib/dependabot/nuget/native_discovery/native_discovery_json_reader.rb +183 -80
- data/lib/dependabot/nuget/native_discovery/native_project_discovery.rb +25 -3
- data/lib/dependabot/nuget/native_discovery/native_workspace_discovery.rb +1 -11
- data/lib/dependabot/nuget/native_helpers.rb +13 -4
- data/lib/dependabot/nuget/native_update_checker/native_update_checker.rb +17 -4
- metadata +15 -12
- data/helpers/lib/NuGetUpdater/NuGetProjects/Directory.Packages.props +0 -29
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementConverter.cs +0 -17
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DirectoryPackagesPropsDiscovery.cs +0 -69
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DirectoryPackagesPropsDiscoveryResult.cs +0 -11
- data/lib/dependabot/nuget/file_fetcher/import_paths_finder.rb +0 -73
- data/lib/dependabot/nuget/file_fetcher/sln_project_paths_finder.rb +0 -60
- data/lib/dependabot/nuget/native_discovery/native_directory_packages_props_discovery.rb +0 -44
@@ -1,12 +1,417 @@
|
|
1
1
|
using System.Collections.Immutable;
|
2
|
+
using System.Reflection;
|
3
|
+
using System.Xml.Linq;
|
4
|
+
using System.Xml.XPath;
|
5
|
+
|
6
|
+
using Microsoft.Build.Logging.StructuredLogger;
|
2
7
|
|
3
8
|
using NuGet.Versioning;
|
4
9
|
|
10
|
+
using NuGetUpdater.Core.Utilities;
|
11
|
+
|
12
|
+
using LoggerProperty = Microsoft.Build.Logging.StructuredLogger.Property;
|
13
|
+
|
5
14
|
namespace NuGetUpdater.Core.Discover;
|
6
15
|
|
7
16
|
internal static class SdkProjectDiscovery
|
8
17
|
{
|
9
|
-
|
18
|
+
private static readonly HashSet<string> TopLevelPackageItemNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
19
|
+
{
|
20
|
+
"PackageReference"
|
21
|
+
};
|
22
|
+
|
23
|
+
// the items listed below represent collection names that NuGet will resolve a package into, along with the metadata value names to get the package name and version
|
24
|
+
private static readonly Dictionary<string, (string NameMetadata, string VersionMetadata)> ResolvedPackageItemNames = new Dictionary<string, (string, string)>(StringComparer.OrdinalIgnoreCase)
|
25
|
+
{
|
26
|
+
["NativeCopyLocalItems"] = ("NuGetPackageId", "NuGetPackageVersion"),
|
27
|
+
["ResourceCopyLocalItems"] = ("NuGetPackageId", "NuGetPackageVersion"),
|
28
|
+
["RuntimeCopyLocalItems"] = ("NuGetPackageId", "NuGetPackageVersion"),
|
29
|
+
["ResolvedAnalyzers"] = ("NuGetPackageId", "NuGetPackageVersion"),
|
30
|
+
["_PackageDependenciesDesignTime"] = ("Name", "Version"),
|
31
|
+
};
|
32
|
+
|
33
|
+
// these packages are resolved during restore, but aren't really updatable and shouldn't be reported as dependencies
|
34
|
+
private static readonly HashSet<string> NonReportedPackgeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
35
|
+
{
|
36
|
+
"NETStandard.Library"
|
37
|
+
};
|
38
|
+
|
39
|
+
// these are additional files that are relevant to the project and need to be reported
|
40
|
+
private static readonly HashSet<string> AdditionalFileNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
41
|
+
{
|
42
|
+
"packages.config",
|
43
|
+
"app.config",
|
44
|
+
"web.config",
|
45
|
+
};
|
46
|
+
|
47
|
+
public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverAsync(string repoRootPath, string workspacePath, string startingProjectPath, ExperimentsManager experimentsManager, ILogger logger)
|
48
|
+
{
|
49
|
+
if (experimentsManager.UseDirectDiscovery)
|
50
|
+
{
|
51
|
+
return await DiscoverWithBinLogAsync(repoRootPath, workspacePath, startingProjectPath, logger);
|
52
|
+
}
|
53
|
+
else
|
54
|
+
{
|
55
|
+
return await DiscoverWithTempProjectAsync(repoRootPath, workspacePath, startingProjectPath, logger);
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverWithBinLogAsync(string repoRootPath, string workspacePath, string startingProjectPath, ILogger logger)
|
60
|
+
{
|
61
|
+
// N.b., there are many paths used in this function. The MSBuild binary log always reports fully qualified paths, so that's what will be used
|
62
|
+
// throughout until the very end when the appropriate kind of relative path is returned.
|
63
|
+
|
64
|
+
// step through the binlog one item at a time
|
65
|
+
var startingProjectDirectory = Path.GetDirectoryName(startingProjectPath)!;
|
66
|
+
|
67
|
+
// the following collection feature heavily; the shape is described as follows
|
68
|
+
|
69
|
+
Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesPerProject = new(PathComparer.Instance);
|
70
|
+
// projectPath tfm packageName, packageVersion
|
71
|
+
|
72
|
+
Dictionary<string, HashSet<string>> topLevelPackagesPerProject = new(PathComparer.Instance);
|
73
|
+
// projectPath, packageNames
|
74
|
+
|
75
|
+
Dictionary<string, Dictionary<string, string>> resolvedProperties = new(PathComparer.Instance);
|
76
|
+
// projectPath propertyName, propertyValue
|
77
|
+
|
78
|
+
Dictionary<string, HashSet<string>> importedFiles = new(PathComparer.Instance);
|
79
|
+
// projectPath, importedFiles
|
80
|
+
|
81
|
+
Dictionary<string, HashSet<string>> referencedProjects = new(PathComparer.Instance);
|
82
|
+
// projectPath, referencedProjects
|
83
|
+
|
84
|
+
Dictionary<string, HashSet<string>> additionalFiles = new(PathComparer.Instance);
|
85
|
+
// projectPath, additionalFiles
|
86
|
+
|
87
|
+
var tfms = await MSBuildHelper.GetTargetFrameworkValuesFromProject(repoRootPath, startingProjectPath, logger);
|
88
|
+
foreach (var tfm in tfms)
|
89
|
+
{
|
90
|
+
// create a binlog
|
91
|
+
var binLogPath = Path.Combine(Path.GetTempPath(), $"msbuild_{Guid.NewGuid():d}.binlog");
|
92
|
+
try
|
93
|
+
{
|
94
|
+
// TODO: once the updater image has all relevant SDKs installed, we won't have to sideline global.json anymore
|
95
|
+
var (exitCode, stdOut, stdErr) = await MSBuildHelper.SidelineGlobalJsonAsync(startingProjectDirectory, repoRootPath, async () =>
|
96
|
+
{
|
97
|
+
// the built-in target `GenerateBuildDependencyFile` forces resolution of all NuGet packages, but doesn't invoke a full build
|
98
|
+
var dependencyDiscoveryTargetsPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "DependencyDiscovery.targets");
|
99
|
+
var args = new string[]
|
100
|
+
{
|
101
|
+
"build",
|
102
|
+
startingProjectPath,
|
103
|
+
"/t:_DiscoverDependencies",
|
104
|
+
$"/p:TargetFramework={tfm}",
|
105
|
+
$"/p:CustomAfterMicrosoftCommonCrossTargetingTargets={dependencyDiscoveryTargetsPath};CustomAfterMicrosoftCommonTargets={dependencyDiscoveryTargetsPath}",
|
106
|
+
$"/bl:{binLogPath}"
|
107
|
+
};
|
108
|
+
var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", args, workingDirectory: startingProjectDirectory);
|
109
|
+
return (exitCode, stdOut, stdErr);
|
110
|
+
}, logger, retainMSBuildSdks: true);
|
111
|
+
MSBuildHelper.ThrowOnUnauthenticatedFeed(stdOut);
|
112
|
+
if (stdOut.Contains("""error MSB4057: The target "GenerateBuildDependencyFile" does not exist in the project."""))
|
113
|
+
{
|
114
|
+
// this can happen if it's a non-SDK-style project; totally normal, not worth examining the binlog
|
115
|
+
return [];
|
116
|
+
}
|
117
|
+
if (exitCode != 0)
|
118
|
+
{
|
119
|
+
// log error, but still try to resolve what we can
|
120
|
+
logger.Warn($" Error determining dependencies from `{startingProjectPath}`:\nSTDOUT:\n{stdOut}\nSTDERR:\n{stdErr}");
|
121
|
+
}
|
122
|
+
|
123
|
+
var buildRoot = BinaryLog.ReadBuild(binLogPath);
|
124
|
+
buildRoot.VisitAllChildren<BaseNode>(node =>
|
125
|
+
{
|
126
|
+
switch (node)
|
127
|
+
{
|
128
|
+
case LoggerProperty property:
|
129
|
+
{
|
130
|
+
var projectEvaluation = property.GetNearestParent<ProjectEvaluation>();
|
131
|
+
if (projectEvaluation is not null)
|
132
|
+
{
|
133
|
+
var properties = resolvedProperties.GetOrAdd(projectEvaluation.ProjectFile, () => new(StringComparer.OrdinalIgnoreCase));
|
134
|
+
properties[property.Name] = property.Value;
|
135
|
+
}
|
136
|
+
}
|
137
|
+
break;
|
138
|
+
case Import import:
|
139
|
+
{
|
140
|
+
var projectEvaluation = GetNearestProjectEvaluation(import);
|
141
|
+
if (projectEvaluation is not null)
|
142
|
+
{
|
143
|
+
// props and targets files might have been imported from these, but they're not to be considered as dependency files
|
144
|
+
var forbiddenDirectories = new[]
|
145
|
+
{
|
146
|
+
GetPropertyValueFromProjectEvaluation(projectEvaluation, "BaseIntermediateOutputPath"), // e.g., "obj/"
|
147
|
+
GetPropertyValueFromProjectEvaluation(projectEvaluation, "BaseOutputPath"), // e.g., "bin/"
|
148
|
+
}
|
149
|
+
.Where(p => !string.IsNullOrEmpty(p))
|
150
|
+
.Select(p => Path.Combine(Path.GetDirectoryName(projectEvaluation.ProjectFile)!, p!))
|
151
|
+
.Select(p => p.NormalizePathToUnix())
|
152
|
+
.Select(p => new DirectoryInfo(p))
|
153
|
+
.ToArray();
|
154
|
+
if (PathHelper.IsFileUnderDirectory(new DirectoryInfo(repoRootPath), new FileInfo(import.ImportedProjectFilePath)))
|
155
|
+
{
|
156
|
+
if (!forbiddenDirectories.Any(f => PathHelper.IsFileUnderDirectory(f, new FileInfo(import.ImportedProjectFilePath))))
|
157
|
+
{
|
158
|
+
var imports = importedFiles.GetOrAdd(projectEvaluation.ProjectFile, () => new(PathComparer.Instance));
|
159
|
+
imports.Add(import.ImportedProjectFilePath);
|
160
|
+
}
|
161
|
+
}
|
162
|
+
}
|
163
|
+
}
|
164
|
+
break;
|
165
|
+
case NamedNode namedNode when namedNode is AddItem or RemoveItem:
|
166
|
+
ProcessResolvedPackageReference(namedNode, packagesPerProject, topLevelPackagesPerProject);
|
167
|
+
|
168
|
+
if (namedNode is AddItem addItem)
|
169
|
+
{
|
170
|
+
// maintain list of project references
|
171
|
+
if (addItem.Name.Equals("ProjectReference", StringComparison.OrdinalIgnoreCase))
|
172
|
+
{
|
173
|
+
var projectEvaluation = GetNearestProjectEvaluation(addItem);
|
174
|
+
if (projectEvaluation is not null)
|
175
|
+
{
|
176
|
+
foreach (var referencedProject in addItem.Children.OfType<Item>())
|
177
|
+
{
|
178
|
+
var referencedProjectPaths = referencedProjects.GetOrAdd(projectEvaluation.ProjectFile, () => new(PathComparer.Instance));
|
179
|
+
var referencedProjectPath = new FileInfo(Path.Combine(Path.GetDirectoryName(projectEvaluation.ProjectFile)!, referencedProject.Name)).FullName;
|
180
|
+
referencedProjectPaths.Add(referencedProjectPath);
|
181
|
+
}
|
182
|
+
}
|
183
|
+
}
|
184
|
+
|
185
|
+
// maintain list of additional files
|
186
|
+
if (addItem.Name.Equals("None", StringComparison.OrdinalIgnoreCase) ||
|
187
|
+
addItem.Name.Equals("Content", StringComparison.OrdinalIgnoreCase))
|
188
|
+
{
|
189
|
+
var projectEvaluation = GetNearestProjectEvaluation(addItem);
|
190
|
+
if (projectEvaluation is not null)
|
191
|
+
{
|
192
|
+
foreach (var additionalItem in addItem.Children.OfType<Item>())
|
193
|
+
{
|
194
|
+
if (AdditionalFileNames.Contains(additionalItem.Name))
|
195
|
+
{
|
196
|
+
var additionalFilesForProject = additionalFiles.GetOrAdd(projectEvaluation.ProjectFile, () => new(PathComparer.Instance));
|
197
|
+
var additionalFilePath = new FileInfo(Path.Combine(Path.GetDirectoryName(projectEvaluation.ProjectFile)!, additionalItem.Name)).FullName;
|
198
|
+
additionalFilesForProject.Add(additionalFilePath);
|
199
|
+
}
|
200
|
+
}
|
201
|
+
}
|
202
|
+
}
|
203
|
+
}
|
204
|
+
break;
|
205
|
+
}
|
206
|
+
}, takeChildrenSnapshot: true);
|
207
|
+
}
|
208
|
+
catch (HttpRequestException)
|
209
|
+
{
|
210
|
+
// likely an unauthenticated feed; this needs to be sent further up the chain
|
211
|
+
throw;
|
212
|
+
}
|
213
|
+
finally
|
214
|
+
{
|
215
|
+
try
|
216
|
+
{
|
217
|
+
File.Delete(binLogPath);
|
218
|
+
}
|
219
|
+
catch
|
220
|
+
{
|
221
|
+
}
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
// and done
|
226
|
+
var projectDiscoveryResults = packagesPerProject.Keys.OrderBy(p => p).Select(projectPath =>
|
227
|
+
{
|
228
|
+
// gather some project-level information
|
229
|
+
var packagesByTfm = packagesPerProject[projectPath];
|
230
|
+
var projectFullDirectory = Path.GetDirectoryName(projectPath)!;
|
231
|
+
var doc = XDocument.Load(projectPath);
|
232
|
+
var localPropertyDefinitionElements = doc.Root!.XPathSelectElements("/Project/PropertyGroup/*");
|
233
|
+
var projectPropertyNames = localPropertyDefinitionElements.Select(e => e.Name.LocalName).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
234
|
+
var projectRelativePath = Path.GetRelativePath(workspacePath, projectPath);
|
235
|
+
var topLevelPackageNames = topLevelPackagesPerProject.GetOrAdd(projectPath, () => new(StringComparer.OrdinalIgnoreCase));
|
236
|
+
|
237
|
+
// create dependencies
|
238
|
+
var tfms = packagesByTfm.Keys.OrderBy(tfm => tfm).ToImmutableArray();
|
239
|
+
var dependencies = tfms.SelectMany(tfm =>
|
240
|
+
{
|
241
|
+
return packagesByTfm[tfm].Keys.OrderBy(p => p).Select(packageName =>
|
242
|
+
{
|
243
|
+
var packageVersion = packagesByTfm[tfm][packageName]!;
|
244
|
+
var isTopLevel = topLevelPackageNames.Contains(packageName);
|
245
|
+
var dependencyType = isTopLevel ? DependencyType.PackageReference : DependencyType.Unknown;
|
246
|
+
return new Dependency(packageName, packageVersion, dependencyType, TargetFrameworks: [tfm], IsDirect: isTopLevel, IsTransitive: !isTopLevel);
|
247
|
+
});
|
248
|
+
}).ToImmutableArray();
|
249
|
+
|
250
|
+
// others
|
251
|
+
var properties = resolvedProperties[projectPath]
|
252
|
+
.Where(pkvp => projectPropertyNames.Contains(pkvp.Key))
|
253
|
+
.Select(pkvp => new Property(pkvp.Key, pkvp.Value, Path.GetRelativePath(repoRootPath, projectPath).NormalizePathToUnix()))
|
254
|
+
.OrderBy(p => p.Name)
|
255
|
+
.ToImmutableArray();
|
256
|
+
var referenced = referencedProjects.GetOrAdd(projectPath, () => new(PathComparer.Instance))
|
257
|
+
.Select(p => Path.GetRelativePath(projectFullDirectory, p).NormalizePathToUnix())
|
258
|
+
.OrderBy(p => p)
|
259
|
+
.ToImmutableArray();
|
260
|
+
var imported = importedFiles.GetOrAdd(projectPath, () => new(PathComparer.Instance))
|
261
|
+
.Select(p => Path.GetRelativePath(projectFullDirectory, p))
|
262
|
+
.Select(p => p.NormalizePathToUnix())
|
263
|
+
.OrderBy(p => p)
|
264
|
+
.ToImmutableArray();
|
265
|
+
var additionalFromLocation = ProjectHelper.GetAdditionalFilesFromProjectLocation(projectPath, ProjectHelper.PathFormat.Full);
|
266
|
+
var additional = additionalFiles.GetOrAdd(projectPath, () => new(PathComparer.Instance))
|
267
|
+
.Concat(additionalFromLocation)
|
268
|
+
.Select(p => Path.GetRelativePath(projectFullDirectory, p))
|
269
|
+
.Select(p => p.NormalizePathToUnix())
|
270
|
+
.OrderBy(p => p)
|
271
|
+
.ToImmutableArray();
|
272
|
+
|
273
|
+
return new ProjectDiscoveryResult()
|
274
|
+
{
|
275
|
+
FilePath = projectRelativePath,
|
276
|
+
Dependencies = dependencies,
|
277
|
+
TargetFrameworks = tfms,
|
278
|
+
Properties = properties,
|
279
|
+
ReferencedProjectPaths = referenced,
|
280
|
+
ImportedFiles = imported,
|
281
|
+
AdditionalFiles = additional,
|
282
|
+
};
|
283
|
+
}).ToImmutableArray();
|
284
|
+
return projectDiscoveryResults;
|
285
|
+
}
|
286
|
+
|
287
|
+
private static void ProcessResolvedPackageReference(
|
288
|
+
NamedNode node,
|
289
|
+
Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesPerProject, // projectPath -> tfm -> (packageName, packageVersion)
|
290
|
+
Dictionary<string, HashSet<string>> topLevelPackagesPerProject
|
291
|
+
)
|
292
|
+
{
|
293
|
+
var doRemoveOperation = node is RemoveItem;
|
294
|
+
var doAddOperation = node is AddItem;
|
295
|
+
|
296
|
+
if (TopLevelPackageItemNames.Contains(node.Name))
|
297
|
+
{
|
298
|
+
foreach (var child in node.Children.OfType<Item>())
|
299
|
+
{
|
300
|
+
var projectEvaluation = GetNearestProjectEvaluation(node);
|
301
|
+
if (projectEvaluation is not null)
|
302
|
+
{
|
303
|
+
var packageName = child.Name;
|
304
|
+
if (NonReportedPackgeNames.Contains(packageName))
|
305
|
+
{
|
306
|
+
continue;
|
307
|
+
}
|
308
|
+
|
309
|
+
var topLevelPackages = topLevelPackagesPerProject.GetOrAdd(projectEvaluation.ProjectFile, () => new(StringComparer.OrdinalIgnoreCase));
|
310
|
+
|
311
|
+
if (doRemoveOperation)
|
312
|
+
{
|
313
|
+
topLevelPackages.Remove(packageName);
|
314
|
+
}
|
315
|
+
|
316
|
+
if (doAddOperation)
|
317
|
+
{
|
318
|
+
topLevelPackages.Add(packageName);
|
319
|
+
}
|
320
|
+
}
|
321
|
+
}
|
322
|
+
}
|
323
|
+
else if (ResolvedPackageItemNames.TryGetValue(node.Name, out var metadataNames))
|
324
|
+
{
|
325
|
+
var nameMetadata = metadataNames.NameMetadata;
|
326
|
+
var versionMetadata = metadataNames.VersionMetadata;
|
327
|
+
var projectEvaluation = GetNearestProjectEvaluation(node);
|
328
|
+
if (projectEvaluation is not null)
|
329
|
+
{
|
330
|
+
// without a tfm we can't do anything meaningful with the package reference
|
331
|
+
var tfm = GetPropertyValueFromProjectEvaluation(projectEvaluation, "TargetFramework");
|
332
|
+
if (tfm is not null)
|
333
|
+
{
|
334
|
+
foreach (var child in node.Children.OfType<Item>())
|
335
|
+
{
|
336
|
+
var packageName = GetChildMetadataValue(child, nameMetadata);
|
337
|
+
var packageVersion = GetChildMetadataValue(child, versionMetadata);
|
338
|
+
if (packageName is not null && packageVersion is not null)
|
339
|
+
{
|
340
|
+
if (NonReportedPackgeNames.Contains(packageName))
|
341
|
+
{
|
342
|
+
continue;
|
343
|
+
}
|
344
|
+
|
345
|
+
var tfmsPerProject = packagesPerProject.GetOrAdd(projectEvaluation.ProjectFile, () => new(StringComparer.OrdinalIgnoreCase));
|
346
|
+
var packagesPerTfm = tfmsPerProject.GetOrAdd(tfm, () => new(StringComparer.OrdinalIgnoreCase));
|
347
|
+
|
348
|
+
if (doRemoveOperation)
|
349
|
+
{
|
350
|
+
packagesPerTfm.Remove(packageName);
|
351
|
+
}
|
352
|
+
|
353
|
+
if (doAddOperation)
|
354
|
+
{
|
355
|
+
packagesPerTfm[packageName] = packageVersion;
|
356
|
+
}
|
357
|
+
}
|
358
|
+
}
|
359
|
+
}
|
360
|
+
}
|
361
|
+
}
|
362
|
+
}
|
363
|
+
|
364
|
+
private static string? GetChildMetadataValue(TreeNode node, string metadataItemName)
|
365
|
+
{
|
366
|
+
var metadata = node.Children.OfType<Metadata>();
|
367
|
+
var metadataValue = metadata.FirstOrDefault(m => m.Name.Equals(metadataItemName, StringComparison.OrdinalIgnoreCase))?.Value;
|
368
|
+
return metadataValue;
|
369
|
+
}
|
370
|
+
|
371
|
+
private static ProjectEvaluation? GetNearestProjectEvaluation(BaseNode node)
|
372
|
+
{
|
373
|
+
// we need to find the containing project evaluation
|
374
|
+
// if this is a <PackageReference>, one of the parents is it
|
375
|
+
// otherwise, we need to find the parent `Project` and the corresponding evaluation from the build
|
376
|
+
var projectEvaluation = node.GetNearestParent<ProjectEvaluation>();
|
377
|
+
if (projectEvaluation is null)
|
378
|
+
{
|
379
|
+
var project = node.GetNearestParent<Project>();
|
380
|
+
if (project is null)
|
381
|
+
{
|
382
|
+
return null;
|
383
|
+
}
|
384
|
+
|
385
|
+
var build = project.GetNearestParent<Build>();
|
386
|
+
if (build is null)
|
387
|
+
{
|
388
|
+
return null;
|
389
|
+
}
|
390
|
+
|
391
|
+
projectEvaluation = build.FindEvaluation(project.EvaluationId);
|
392
|
+
}
|
393
|
+
|
394
|
+
return projectEvaluation;
|
395
|
+
}
|
396
|
+
|
397
|
+
private static string? GetPropertyValueFromProjectEvaluation(ProjectEvaluation projectEvaluation, string propertyName)
|
398
|
+
{
|
399
|
+
var propertiesFolder = projectEvaluation.Children.OfType<Folder>().FirstOrDefault(f => f.Name == "Properties");
|
400
|
+
if (propertiesFolder is null)
|
401
|
+
{
|
402
|
+
return null;
|
403
|
+
}
|
404
|
+
|
405
|
+
var property = propertiesFolder.Children.OfType<LoggerProperty>().FirstOrDefault(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
|
406
|
+
if (property is null)
|
407
|
+
{
|
408
|
+
return null;
|
409
|
+
}
|
410
|
+
|
411
|
+
return property.Value;
|
412
|
+
}
|
413
|
+
|
414
|
+
public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverWithTempProjectAsync(string repoRootPath, string workspacePath, string projectPath, ILogger logger)
|
10
415
|
{
|
11
416
|
// Determine which targets and props files contribute to the build.
|
12
417
|
var (buildFiles, projectTargetFrameworks) = await MSBuildHelper.LoadBuildFilesAndTargetFrameworksAsync(repoRootPath, projectPath);
|
@@ -30,6 +435,10 @@ internal static class SdkProjectDiscovery
|
|
30
435
|
// The build file dependencies have the correct DependencyType and the TopLevelDependencies have the evaluated version.
|
31
436
|
// Combine them to have the set of dependencies that are directly referenced from the build file.
|
32
437
|
var fileDependencies = BuildFile.GetDependencies(buildFile).ToImmutableArray();
|
438
|
+
|
439
|
+
// this is new-ish behavior; don't ever report this dependency because there's no meaningful way to update it
|
440
|
+
fileDependencies = fileDependencies.Where(d => !d.Name.Equals("Microsoft.NET.Sdk", StringComparison.OrdinalIgnoreCase)).ToImmutableArray();
|
441
|
+
|
33
442
|
var fileDependencyLookup = fileDependencies
|
34
443
|
.ToLookup(d => d.Name, StringComparer.OrdinalIgnoreCase);
|
35
444
|
var sdkDependencies = fileDependencies
|
@@ -58,7 +467,7 @@ internal static class SdkProjectDiscovery
|
|
58
467
|
.OrderBy(p => p.Name)
|
59
468
|
.ToImmutableArray();
|
60
469
|
var referencedProjectPaths = MSBuildHelper.GetProjectPathsFromProject(projectPath)
|
61
|
-
.Select(path => Path.GetRelativePath(workspacePath, path))
|
470
|
+
.Select(path => Path.GetRelativePath(workspacePath, path).NormalizePathToUnix())
|
62
471
|
.OrderBy(p => p)
|
63
472
|
.ToImmutableArray();
|
64
473
|
|
@@ -72,23 +481,31 @@ internal static class SdkProjectDiscovery
|
|
72
481
|
.OrderBy(d => d.Name)
|
73
482
|
.ToImmutableArray();
|
74
483
|
|
484
|
+
// for the temporary project, these directories correspond to $(OutputPath) and $(IntermediateOutputPath) and files from
|
485
|
+
// these directories should not be reported
|
486
|
+
var intermediateDirectories = new string[]
|
487
|
+
{
|
488
|
+
Path.Join(Path.GetDirectoryName(buildFile.Path), "bin"),
|
489
|
+
Path.Join(Path.GetDirectoryName(buildFile.Path), "obj"),
|
490
|
+
};
|
491
|
+
var projectDirectory = Path.GetDirectoryName(buildFile.Path)!;
|
492
|
+
var additionalFiles = ProjectHelper.GetAllAdditionalFilesFromProject(buildFile.Path, ProjectHelper.PathFormat.Relative);
|
75
493
|
results.Add(new()
|
76
494
|
{
|
77
|
-
FilePath = Path.GetRelativePath(workspacePath, buildFile.Path),
|
495
|
+
FilePath = Path.GetRelativePath(workspacePath, buildFile.Path).NormalizePathToUnix(),
|
78
496
|
Properties = properties,
|
79
497
|
TargetFrameworks = tfms,
|
80
498
|
ReferencedProjectPaths = referencedProjectPaths,
|
81
499
|
Dependencies = allDependencies,
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
Dependencies = directDependencies.Concat(sdkDependencies)
|
90
|
-
.OrderBy(d => d.Name)
|
500
|
+
ImportedFiles = buildFiles.Where(b =>
|
501
|
+
{
|
502
|
+
var fileType = b.GetFileType();
|
503
|
+
return fileType == ProjectBuildFileType.Props || fileType == ProjectBuildFileType.Targets;
|
504
|
+
})
|
505
|
+
.Where(b => !intermediateDirectories.Any(i => PathHelper.IsFileUnderDirectory(new DirectoryInfo(i), new FileInfo(b.Path))))
|
506
|
+
.Select(b => Path.GetRelativePath(projectDirectory, b.Path).NormalizePathToUnix())
|
91
507
|
.ToImmutableArray(),
|
508
|
+
AdditionalFiles = additionalFiles,
|
92
509
|
});
|
93
510
|
}
|
94
511
|
}
|
@@ -7,7 +7,6 @@ public sealed record WorkspaceDiscoveryResult : NativeResult
|
|
7
7
|
public required string Path { get; init; }
|
8
8
|
public bool IsSuccess { get; init; } = true;
|
9
9
|
public ImmutableArray<ProjectDiscoveryResult> Projects { get; init; }
|
10
|
-
public DirectoryPackagesPropsDiscoveryResult? DirectoryPackagesProps { get; init; }
|
11
10
|
public GlobalJsonDiscoveryResult? GlobalJson { get; init; }
|
12
11
|
public DotNetToolsJsonDiscoveryResult? DotNetToolsJson { get; init; }
|
13
12
|
}
|
@@ -7,12 +7,23 @@ namespace NuGetUpdater.Core;
|
|
7
7
|
public record ExperimentsManager
|
8
8
|
{
|
9
9
|
public bool UseLegacyDependencySolver { get; init; } = false;
|
10
|
+
public bool UseDirectDiscovery { get; init; } = false;
|
11
|
+
|
12
|
+
public Dictionary<string, object> ToDictionary()
|
13
|
+
{
|
14
|
+
return new()
|
15
|
+
{
|
16
|
+
["nuget_legacy_dependency_solver"] = UseLegacyDependencySolver,
|
17
|
+
["nuget_use_direct_discovery"] = UseDirectDiscovery,
|
18
|
+
};
|
19
|
+
}
|
10
20
|
|
11
21
|
public static ExperimentsManager GetExperimentsManager(Dictionary<string, object>? experiments)
|
12
22
|
{
|
13
23
|
return new ExperimentsManager()
|
14
24
|
{
|
15
25
|
UseLegacyDependencySolver = IsEnabled(experiments, "nuget_legacy_dependency_solver"),
|
26
|
+
UseDirectDiscovery = IsEnabled(experiments, "nuget_use_direct_discovery"),
|
16
27
|
};
|
17
28
|
}
|
18
29
|
|
@@ -26,8 +37,7 @@ public record ExperimentsManager
|
|
26
37
|
}
|
27
38
|
catch (JsonException ex)
|
28
39
|
{
|
29
|
-
|
30
|
-
logger.Log($"{DateTime.UtcNow:yyyy/MM/dd HH:mm:ss} INFO Error deserializing job file: {ex.ToString()}: {jobFileContent}");
|
40
|
+
logger.Info($"Error deserializing job file: {ex.ToString()}: {jobFileContent}");
|
31
41
|
return new ExperimentsManager();
|
32
42
|
}
|
33
43
|
}
|
@@ -39,7 +39,7 @@ internal abstract class JsonBuildFile : BuildFile<string>
|
|
39
39
|
{
|
40
40
|
// We can't police that people have legal JSON files.
|
41
41
|
// If they don't, we just return null.
|
42
|
-
logger.
|
42
|
+
logger.Warn($"Failed to parse JSON file: {RelativePath}, got {ex}");
|
43
43
|
FailedToParse = true;
|
44
44
|
return null;
|
45
45
|
}
|
@@ -15,11 +15,11 @@ public class CompatibilityChecker
|
|
15
15
|
var incompatibleFrameworks = projectFrameworks.Where(f => !compatibleFrameworks.Contains(f)).ToArray();
|
16
16
|
if (incompatibleFrameworks.Length > 0)
|
17
17
|
{
|
18
|
-
logger.
|
18
|
+
logger.Warn($"The package is not compatible. Incompatible project frameworks: {string.Join(", ", incompatibleFrameworks.Select(f => f.GetShortFolderName()))}");
|
19
19
|
return false;
|
20
20
|
}
|
21
21
|
|
22
|
-
logger.
|
22
|
+
logger.Info("The package is compatible.");
|
23
23
|
return true;
|
24
24
|
|
25
25
|
static NuGetFramework ParseFramework(string tfm)
|
@@ -7,6 +7,12 @@
|
|
7
7
|
<GeneratePathProperty>true</GeneratePathProperty>
|
8
8
|
</PropertyGroup>
|
9
9
|
|
10
|
+
<ItemGroup>
|
11
|
+
<None Include="DependencyDiscovery.props" CopyToOutputDirectory="PreserveNewest" />
|
12
|
+
<None Include="DependencyDiscovery.targets" CopyToOutputDirectory="PreserveNewest" />
|
13
|
+
<None Include="TargetFrameworkReporter.targets" CopyToOutputDirectory="PreserveNewest" />
|
14
|
+
</ItemGroup>
|
15
|
+
|
10
16
|
<ItemGroup>
|
11
17
|
<ProjectReference Include="..\NuGetProjects\NuGet.CommandLine\NuGet.CommandLine.csproj" />
|
12
18
|
</ItemGroup>
|
@@ -15,8 +21,7 @@
|
|
15
21
|
<PackageReference Include="GuiLabs.Language.Xml" />
|
16
22
|
<PackageReference Include="DiffPlex" />
|
17
23
|
<PackageReference Include="Microsoft.Build.Locator" />
|
18
|
-
<PackageReference Include="
|
19
|
-
<PackageReference Include="Microsoft.Build.Utilities.Core" ExcludeAssets="Runtime" PrivateAssets="All" />
|
24
|
+
<PackageReference Include="MSBuild.StructuredLogger" />
|
20
25
|
<PackageReference Include="NuGet.Core" Aliases="CoreV2" />
|
21
26
|
</ItemGroup>
|
22
27
|
|
@@ -1,9 +1,14 @@
|
|
1
|
+
using System.Text.Json;
|
2
|
+
using System.Text.Json.Serialization;
|
3
|
+
|
1
4
|
namespace NuGetUpdater.Core.Run.ApiModel;
|
2
5
|
|
3
6
|
public sealed record Job
|
4
7
|
{
|
5
8
|
public string PackageManager { get; init; } = "nuget";
|
6
9
|
public AllowedUpdate[]? AllowedUpdates { get; init; } = null;
|
10
|
+
|
11
|
+
[JsonConverter(typeof(NullAsBoolConverter))]
|
7
12
|
public bool Debug { get; init; } = false;
|
8
13
|
public object[]? DependencyGroups { get; init; } = null;
|
9
14
|
public object[]? Dependencies { get; init; } = null;
|
@@ -47,3 +52,21 @@ public sealed record Job
|
|
47
52
|
}
|
48
53
|
}
|
49
54
|
}
|
55
|
+
|
56
|
+
public class NullAsBoolConverter : JsonConverter<bool>
|
57
|
+
{
|
58
|
+
public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
59
|
+
{
|
60
|
+
if (reader.TokenType == JsonTokenType.Null)
|
61
|
+
{
|
62
|
+
return false;
|
63
|
+
}
|
64
|
+
|
65
|
+
return reader.GetBoolean();
|
66
|
+
}
|
67
|
+
|
68
|
+
public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options)
|
69
|
+
{
|
70
|
+
writer.WriteBooleanValue(value);
|
71
|
+
}
|
72
|
+
}
|