dependabot-nuget 0.285.0 → 0.287.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/NuGetProjects/Directory.Build.props +5 -1
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.CommandLine/NuGet.CommandLine.csproj +1 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Configuration/NuGet.Configuration.csproj +1 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.LibraryModel/NuGet.LibraryModel.csproj +1 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Packaging/NuGet.Packaging.csproj +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +8 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +7 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +11 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +24 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/NuGetContext.cs +15 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +9 -38
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +10 -8
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +52 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/ProjectBuildFile.cs +15 -8
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/IAnalyzeWorker.cs +9 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/IDiscoveryWorker.cs +8 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/IUpdaterWorker.cs +9 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +78 -61
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +21 -10
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +37 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +5 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +5 -6
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +51 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +302 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +269 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +577 -43
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +168 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestAnalyzeWorker.cs +37 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestDiscoveryWorker.cs +35 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestUpdaterWorker.cs +39 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +104 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +51 -13
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DirsProj.cs +4 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +62 -18
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/PathHelperTests.cs +14 -0
- data/helpers/lib/NuGetUpdater/global.json +1 -1
- data/lib/dependabot/nuget/file_updater.rb +8 -3
- data/lib/dependabot/nuget/native_helpers.rb +11 -12
- metadata +12 -6
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/DependencySolverEnvironment.cs +0 -12
@@ -13,6 +13,9 @@ public class RunWorker
|
|
13
13
|
{
|
14
14
|
private readonly IApiHandler _apiHandler;
|
15
15
|
private readonly ILogger _logger;
|
16
|
+
private readonly IDiscoveryWorker _discoveryWorker;
|
17
|
+
private readonly IAnalyzeWorker _analyzeWorker;
|
18
|
+
private readonly IUpdaterWorker _updaterWorker;
|
16
19
|
|
17
20
|
internal static readonly JsonSerializerOptions SerializerOptions = new()
|
18
21
|
{
|
@@ -21,10 +24,13 @@ public class RunWorker
|
|
21
24
|
Converters = { new JsonStringEnumConverter() },
|
22
25
|
};
|
23
26
|
|
24
|
-
public RunWorker(IApiHandler apiHandler, ILogger logger)
|
27
|
+
public RunWorker(IApiHandler apiHandler, IDiscoveryWorker discoverWorker, IAnalyzeWorker analyzeWorker, IUpdaterWorker updateWorker, ILogger logger)
|
25
28
|
{
|
26
29
|
_apiHandler = apiHandler;
|
27
30
|
_logger = logger;
|
31
|
+
_discoveryWorker = discoverWorker;
|
32
|
+
_analyzeWorker = analyzeWorker;
|
33
|
+
_updaterWorker = updateWorker;
|
28
34
|
}
|
29
35
|
|
30
36
|
public async Task RunAsync(FileInfo jobFilePath, DirectoryInfo repoContentsPath, string baseCommitSha, FileInfo outputFilePath)
|
@@ -55,12 +61,13 @@ public class RunWorker
|
|
55
61
|
{
|
56
62
|
MSBuildHelper.RegisterMSBuild(repoContentsPath.FullName, repoContentsPath.FullName);
|
57
63
|
|
64
|
+
var experimentsManager = ExperimentsManager.GetExperimentsManager(job.Experiments);
|
58
65
|
var allDependencyFiles = new Dictionary<string, DependencyFile>();
|
59
66
|
foreach (var directory in job.GetAllDirectories())
|
60
67
|
{
|
61
68
|
var localPath = PathHelper.JoinPath(repoContentsPath.FullName, directory);
|
62
69
|
lastUsedPackageSourceUrls = NuGetContext.GetPackageSourceUrls(localPath);
|
63
|
-
var result = await RunForDirectory(job, repoContentsPath, directory, baseCommitSha);
|
70
|
+
var result = await RunForDirectory(job, repoContentsPath, directory, baseCommitSha, experimentsManager);
|
64
71
|
foreach (var dependencyFile in result.Base64DependencyFiles)
|
65
72
|
{
|
66
73
|
var uniqueKey = Path.GetFullPath(Path.Join(dependencyFile.Directory, dependencyFile.Name)).NormalizePathToUnix().EnsurePrefix("/");
|
@@ -102,10 +109,9 @@ public class RunWorker
|
|
102
109
|
return runResult;
|
103
110
|
}
|
104
111
|
|
105
|
-
private async Task<RunResult> RunForDirectory(Job job, DirectoryInfo repoContentsPath, string repoDirectory, string baseCommitSha)
|
112
|
+
private async Task<RunResult> RunForDirectory(Job job, DirectoryInfo repoContentsPath, string repoDirectory, string baseCommitSha, ExperimentsManager experimentsManager)
|
106
113
|
{
|
107
|
-
var
|
108
|
-
var discoveryResult = await discoveryWorker.RunAsync(repoContentsPath.FullName, repoDirectory);
|
114
|
+
var discoveryResult = await _discoveryWorker.RunAsync(repoContentsPath.FullName, repoDirectory);
|
109
115
|
|
110
116
|
_logger.Log("Discovery JSON content:");
|
111
117
|
_logger.Log(JsonSerializer.Serialize(discoveryResult, DiscoveryWorker.SerializerOptions));
|
@@ -128,23 +134,31 @@ public class RunWorker
|
|
128
134
|
});
|
129
135
|
|
130
136
|
// track original contents for later handling
|
131
|
-
|
137
|
+
async Task TrackOriginalContentsAsync(string directory, string fileName, string? replacementFileName = null)
|
132
138
|
{
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
var
|
141
|
-
|
142
|
-
|
143
|
-
if (File.Exists(packagesConfigPath))
|
139
|
+
var repoFullPath = Path.Join(directory, fileName);
|
140
|
+
if (replacementFileName is not null)
|
141
|
+
{
|
142
|
+
repoFullPath = Path.Join(Path.GetDirectoryName(repoFullPath)!, replacementFileName);
|
143
|
+
}
|
144
|
+
|
145
|
+
repoFullPath = repoFullPath.FullyNormalizedRootedPath();
|
146
|
+
var localFullPath = Path.Join(repoContentsPath.FullName, repoFullPath);
|
147
|
+
|
148
|
+
if (!File.Exists(localFullPath))
|
144
149
|
{
|
145
|
-
|
146
|
-
originalDependencyFileContents[normalizedPackagesConfigPath] = packagesConfigContent;
|
150
|
+
return;
|
147
151
|
}
|
152
|
+
|
153
|
+
var content = await File.ReadAllTextAsync(localFullPath);
|
154
|
+
originalDependencyFileContents[repoFullPath] = content;
|
155
|
+
}
|
156
|
+
|
157
|
+
foreach (var project in discoveryResult.Projects)
|
158
|
+
{
|
159
|
+
await TrackOriginalContentsAsync(discoveryResult.Path, project.FilePath);
|
160
|
+
await TrackOriginalContentsAsync(discoveryResult.Path, project.FilePath, replacementFileName: "packages.config");
|
161
|
+
// TODO: include global.json, etc.
|
148
162
|
}
|
149
163
|
|
150
164
|
// do update
|
@@ -166,7 +180,6 @@ public class RunWorker
|
|
166
180
|
continue;
|
167
181
|
}
|
168
182
|
|
169
|
-
var analyzeWorker = new AnalyzeWorker(_logger);
|
170
183
|
var dependencyInfo = new DependencyInfo()
|
171
184
|
{
|
172
185
|
Name = dependency.Name,
|
@@ -175,16 +188,18 @@ public class RunWorker
|
|
175
188
|
IgnoredVersions = [],
|
176
189
|
Vulnerabilities = [],
|
177
190
|
};
|
178
|
-
var analysisResult = await
|
191
|
+
var analysisResult = await _analyzeWorker.RunAsync(repoContentsPath.FullName, discoveryResult, dependencyInfo);
|
179
192
|
// TODO: log analysisResult
|
180
193
|
if (analysisResult.CanUpdate)
|
181
194
|
{
|
182
|
-
var dependencyLocation = Path.
|
195
|
+
var dependencyLocation = Path.Join(discoveryResult.Path, project.FilePath);
|
183
196
|
if (dependency.Type == DependencyType.PackagesConfig)
|
184
197
|
{
|
185
|
-
dependencyLocation = Path.
|
198
|
+
dependencyLocation = Path.Join(Path.GetDirectoryName(dependencyLocation)!, "packages.config");
|
186
199
|
}
|
187
200
|
|
201
|
+
dependencyLocation = dependencyLocation.FullyNormalizedRootedPath();
|
202
|
+
|
188
203
|
// TODO: this is inefficient, but not likely causing a bottleneck
|
189
204
|
var previousDependency = discoveredUpdatedDependencies.Dependencies
|
190
205
|
.Single(d => d.Name == dependency.Name && d.Requirements.Single().File == dependencyLocation);
|
@@ -201,7 +216,7 @@ public class RunWorker
|
|
201
216
|
Groups = previousDependency.Requirements.Single().Groups,
|
202
217
|
Source = new RequirementSource()
|
203
218
|
{
|
204
|
-
SourceUrl = analysisResult.UpdatedDependencies.
|
219
|
+
SourceUrl = analysisResult.UpdatedDependencies.FirstOrDefault(d => d.Name == dependency.Name)?.InfoUrl,
|
205
220
|
},
|
206
221
|
}
|
207
222
|
],
|
@@ -209,9 +224,8 @@ public class RunWorker
|
|
209
224
|
PreviousRequirements = previousDependency.Requirements,
|
210
225
|
};
|
211
226
|
|
212
|
-
var
|
213
|
-
var
|
214
|
-
var updateResult = await updateWorker.RunAsync(repoContentsPath.FullName, dependencyFilePath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, isTransitive: false);
|
227
|
+
var dependencyFilePath = Path.Join(discoveryResult.Path, project.FilePath).FullyNormalizedRootedPath();
|
228
|
+
var updateResult = await _updaterWorker.RunAsync(repoContentsPath.FullName, dependencyFilePath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, isTransitive: false);
|
215
229
|
// TODO: need to report if anything was actually updated
|
216
230
|
if (updateResult.ErrorType is null || updateResult.ErrorType == ErrorType.None)
|
217
231
|
{
|
@@ -228,44 +242,41 @@ public class RunWorker
|
|
228
242
|
|
229
243
|
// create PR - we need to manually check file contents; we can't easily use `git status` in tests
|
230
244
|
var updatedDependencyFiles = new List<DependencyFile>();
|
231
|
-
|
245
|
+
async Task AddUpdatedFileIfDifferentAsync(string directory, string fileName, string? replacementFileName = null)
|
232
246
|
{
|
233
|
-
var
|
234
|
-
|
235
|
-
var updatedProjectContent = await File.ReadAllTextAsync(localProjectPath);
|
236
|
-
var originalProjectContent = originalDependencyFileContents[projectPath];
|
237
|
-
|
238
|
-
if (updatedProjectContent != originalProjectContent)
|
247
|
+
var repoFullPath = Path.Join(directory, fileName);
|
248
|
+
if (replacementFileName is not null)
|
239
249
|
{
|
240
|
-
|
241
|
-
{
|
242
|
-
Name = project.FilePath,
|
243
|
-
Content = updatedProjectContent,
|
244
|
-
Directory = Path.GetDirectoryName(projectPath)!.NormalizeUnixPathParts(),
|
245
|
-
});
|
250
|
+
repoFullPath = Path.Join(Path.GetDirectoryName(repoFullPath)!, replacementFileName);
|
246
251
|
}
|
247
252
|
|
248
|
-
|
249
|
-
var
|
250
|
-
var normalizedPackagesConfigPath = Path.Join(discoveryResult.Path, projectDirectory, "packages.config").NormalizePathToUnix().EnsurePrefix("/");
|
253
|
+
repoFullPath = repoFullPath.FullyNormalizedRootedPath();
|
254
|
+
var localFullPath = Path.Join(repoContentsPath.FullName, repoFullPath);
|
251
255
|
|
252
|
-
if (File.Exists(
|
256
|
+
if (!File.Exists(localFullPath))
|
253
257
|
{
|
254
|
-
|
255
|
-
|
258
|
+
return;
|
259
|
+
}
|
256
260
|
|
257
|
-
|
261
|
+
var originalContent = originalDependencyFileContents[repoFullPath];
|
262
|
+
var updatedContent = await File.ReadAllTextAsync(localFullPath);
|
263
|
+
if (updatedContent != originalContent)
|
264
|
+
{
|
265
|
+
updatedDependencyFiles.Add(new DependencyFile()
|
258
266
|
{
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
Directory = Path.GetDirectoryName(normalizedPackagesConfigPath)!.NormalizeUnixPathParts(),
|
264
|
-
});
|
265
|
-
}
|
267
|
+
Name = Path.GetFileName(repoFullPath),
|
268
|
+
Directory = Path.GetDirectoryName(repoFullPath)!.NormalizePathToUnix(),
|
269
|
+
Content = updatedContent,
|
270
|
+
});
|
266
271
|
}
|
267
272
|
}
|
268
273
|
|
274
|
+
foreach (var project in discoveryResult.Projects)
|
275
|
+
{
|
276
|
+
await AddUpdatedFileIfDifferentAsync(discoveryResult.Path, project.FilePath);
|
277
|
+
await AddUpdatedFileIfDifferentAsync(discoveryResult.Path, project.FilePath, replacementFileName: "packages.config");
|
278
|
+
}
|
279
|
+
|
269
280
|
if (updatedDependencyFiles.Count > 0)
|
270
281
|
{
|
271
282
|
var createPullRequest = new CreatePullRequest()
|
@@ -292,11 +303,15 @@ public class RunWorker
|
|
292
303
|
|
293
304
|
var result = new RunResult()
|
294
305
|
{
|
295
|
-
Base64DependencyFiles = originalDependencyFileContents.Select(kvp =>
|
306
|
+
Base64DependencyFiles = originalDependencyFileContents.Select(kvp =>
|
296
307
|
{
|
297
|
-
|
298
|
-
|
299
|
-
|
308
|
+
var fullPath = kvp.Key.FullyNormalizedRootedPath();
|
309
|
+
return new DependencyFile()
|
310
|
+
{
|
311
|
+
Name = Path.GetFileName(fullPath),
|
312
|
+
Content = Convert.ToBase64String(Encoding.UTF8.GetBytes(kvp.Value)),
|
313
|
+
Directory = Path.GetDirectoryName(fullPath)!.NormalizePathToUnix(),
|
314
|
+
};
|
300
315
|
}).ToArray(),
|
301
316
|
BaseCommitSha = baseCommitSha,
|
302
317
|
};
|
@@ -308,7 +323,7 @@ public class RunWorker
|
|
308
323
|
string GetFullRepoPath(string path)
|
309
324
|
{
|
310
325
|
// ensures `path\to\file` is `/path/to/file`
|
311
|
-
return Path.Join(discoveryResult.Path, path).
|
326
|
+
return Path.Join(discoveryResult.Path, path).FullyNormalizedRootedPath();
|
312
327
|
}
|
313
328
|
|
314
329
|
var auxiliaryFiles = new List<string>();
|
@@ -328,7 +343,7 @@ public class RunWorker
|
|
328
343
|
foreach (var project in discoveryResult.Projects)
|
329
344
|
{
|
330
345
|
var projectDirectory = Path.GetDirectoryName(project.FilePath);
|
331
|
-
var pathToPackagesConfig = Path.Join(pathToContents, discoveryResult.Path, projectDirectory, "packages.config")
|
346
|
+
var pathToPackagesConfig = Path.Join(pathToContents, discoveryResult.Path, projectDirectory, "packages.config");
|
332
347
|
|
333
348
|
if (File.Exists(pathToPackagesConfig))
|
334
349
|
{
|
@@ -346,7 +361,9 @@ public class RunWorker
|
|
346
361
|
Name = d.Name,
|
347
362
|
Requirements = d.IsTransitive ? [] : [new ReportedRequirement()
|
348
363
|
{
|
349
|
-
File = d.Type == DependencyType.PackagesConfig
|
364
|
+
File = d.Type == DependencyType.PackagesConfig
|
365
|
+
? Path.Join(Path.GetDirectoryName(GetFullRepoPath(p.FilePath))!, "packages.config").FullyNormalizedRootedPath()
|
366
|
+
: GetFullRepoPath(p.FilePath),
|
350
367
|
Requirement = d.Version!,
|
351
368
|
Groups = ["dependencies"],
|
352
369
|
}],
|
@@ -26,6 +26,7 @@ internal static class PackageReferenceUpdater
|
|
26
26
|
string previousDependencyVersion,
|
27
27
|
string newDependencyVersion,
|
28
28
|
bool isTransitive,
|
29
|
+
ExperimentsManager experimentsManager,
|
29
30
|
ILogger logger)
|
30
31
|
{
|
31
32
|
// PackageReference project; modify the XML directly
|
@@ -42,11 +43,7 @@ internal static class PackageReferenceUpdater
|
|
42
43
|
}
|
43
44
|
|
44
45
|
var peerDependencies = await GetUpdatedPeerDependenciesAsync(repoRootPath, projectPath, tfms, dependencyName, newDependencyVersion, logger);
|
45
|
-
if (
|
46
|
-
{
|
47
|
-
await UpdateDependencyWithConflictResolution(repoRootPath, buildFiles, tfms, projectPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive, peerDependencies, logger);
|
48
|
-
}
|
49
|
-
else
|
46
|
+
if (experimentsManager.UseLegacyDependencySolver)
|
50
47
|
{
|
51
48
|
if (isTransitive)
|
52
49
|
{
|
@@ -62,6 +59,10 @@ internal static class PackageReferenceUpdater
|
|
62
59
|
await UpdateTopLevelDepdendency(repoRootPath, buildFiles, tfms, dependencyName, previousDependencyVersion, newDependencyVersion, peerDependencies, logger);
|
63
60
|
}
|
64
61
|
}
|
62
|
+
else
|
63
|
+
{
|
64
|
+
await UpdateDependencyWithConflictResolution(repoRootPath, buildFiles, tfms, projectPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive, peerDependencies, logger);
|
65
|
+
}
|
65
66
|
|
66
67
|
if (!await AreDependenciesCoherentAsync(repoRootPath, projectPath, dependencyName, logger, buildFiles, tfms))
|
67
68
|
{
|
@@ -673,11 +674,21 @@ internal static class PackageReferenceUpdater
|
|
673
674
|
ProjectBuildFile buildFile,
|
674
675
|
string packageName)
|
675
676
|
=> buildFile.PackageItemNodes.Where(e =>
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
677
|
+
{
|
678
|
+
// Attempt to get "Include" or "Update" attribute values
|
679
|
+
var includeOrUpdateValue = e.GetAttributeOrSubElementValue("Include", StringComparison.OrdinalIgnoreCase)
|
680
|
+
?? e.GetAttributeOrSubElementValue("Update", StringComparison.OrdinalIgnoreCase);
|
681
|
+
// Trim and split if there's a valid value
|
682
|
+
var packageNames = includeOrUpdateValue?
|
683
|
+
.Trim()
|
684
|
+
.Split(';', StringSplitOptions.RemoveEmptyEntries)
|
685
|
+
.Select(t => t.Trim())
|
686
|
+
.Where(t => t.Equals(packageName.Trim(), StringComparison.OrdinalIgnoreCase));
|
687
|
+
// Check if there's a matching package name and a non-null version attribute
|
688
|
+
return packageNames?.Any() == true &&
|
689
|
+
(e.GetAttributeOrSubElementValue("Version", StringComparison.OrdinalIgnoreCase)
|
690
|
+
?? e.GetAttributeOrSubElementValue("VersionOverride", StringComparison.OrdinalIgnoreCase)) is not null;
|
691
|
+
});
|
681
692
|
|
682
693
|
private static async Task<bool> AreDependenciesCoherentAsync(string repoRootPath, string projectPath, string dependencyName, ILogger logger, ImmutableArray<ProjectBuildFile> buildFiles, string[] tfms)
|
683
694
|
{
|
@@ -1,5 +1,6 @@
|
|
1
1
|
using System.Diagnostics;
|
2
2
|
using System.Text;
|
3
|
+
using System.Text.RegularExpressions;
|
3
4
|
using System.Xml.Linq;
|
4
5
|
using System.Xml.XPath;
|
5
6
|
|
@@ -213,8 +214,8 @@ internal static class PackagesConfigUpdater
|
|
213
214
|
var hintPathSubString = $"{dependencyName}.{dependencyVersion}";
|
214
215
|
|
215
216
|
string? partialPathMatch = null;
|
216
|
-
var
|
217
|
-
foreach (var hintPathNode in
|
217
|
+
var specificHintPathNodes = projectBuildFile.Contents.Descendants().Where(e => e.IsHintPathNodeForDependency(dependencyName)).ToArray();
|
218
|
+
foreach (var hintPathNode in specificHintPathNodes)
|
218
219
|
{
|
219
220
|
var hintPath = hintPathNode.GetContentValue();
|
220
221
|
var hintPathSubStringLocation = hintPath.IndexOf(hintPathSubString, StringComparison.OrdinalIgnoreCase);
|
@@ -255,18 +256,49 @@ internal static class PackagesConfigUpdater
|
|
255
256
|
if (hasPackage)
|
256
257
|
{
|
257
258
|
// the dependency exists in the packages.config file, so it must be the second case
|
258
|
-
//
|
259
|
-
|
259
|
+
// at this point there's no perfect way to determine what the packages path is, but there's a really good chance that
|
260
|
+
// for any given package it looks something like this:
|
261
|
+
// ..\..\packages\Package.Name.[version]\lib\Tfm\Package.Name.dll
|
262
|
+
var genericHintPathNodes = projectBuildFile.Contents.Descendants().Where(IsHintPathNode).ToArray();
|
263
|
+
if (genericHintPathNodes.Length > 0)
|
264
|
+
{
|
265
|
+
foreach (var hintPathNode in genericHintPathNodes)
|
266
|
+
{
|
267
|
+
var hintPath = hintPathNode.GetContentValue();
|
268
|
+
var match = Regex.Match(hintPath, @"^(?<PackagesPath>.*)[/\\](?<PackageNameAndVersion>[^/\\]+)[/\\]lib[/\\](?<Tfm>[^/\\]+)[/\\](?<AssemblyName>[^/\\]+)$");
|
269
|
+
// e.g., ..\..\packages \ Some.Package.1.2.3 \ lib\ net45 \ Some.Package.dll
|
270
|
+
if (match.Success)
|
271
|
+
{
|
272
|
+
partialPathMatch = match.Groups["PackagesPath"].Value;
|
273
|
+
break;
|
274
|
+
}
|
275
|
+
}
|
276
|
+
}
|
277
|
+
else
|
278
|
+
{
|
279
|
+
// we know the dependency is used, but we have absolutely no idea where the packages path is, so we'll default to something reasonable
|
280
|
+
partialPathMatch = "../packages";
|
281
|
+
}
|
260
282
|
}
|
261
283
|
}
|
262
284
|
|
263
285
|
return partialPathMatch?.NormalizePathToUnix();
|
264
286
|
}
|
265
287
|
|
266
|
-
private static bool
|
288
|
+
private static bool IsHintPathNode(this IXmlElementSyntax element)
|
267
289
|
{
|
268
290
|
if (element.Name.Equals("HintPath", StringComparison.OrdinalIgnoreCase) &&
|
269
291
|
element.Parent.Name.Equals("Reference", StringComparison.OrdinalIgnoreCase))
|
292
|
+
{
|
293
|
+
return true;
|
294
|
+
}
|
295
|
+
|
296
|
+
return false;
|
297
|
+
}
|
298
|
+
|
299
|
+
private static bool IsHintPathNodeForDependency(this IXmlElementSyntax element, string dependencyName)
|
300
|
+
{
|
301
|
+
if (element.IsHintPathNode())
|
270
302
|
{
|
271
303
|
// the include attribute will look like one of the following:
|
272
304
|
// <Reference Include="Some.Dependency, Version=1.0.0.0, Culture=neutral, PublicKeyToken=abcd">
|
@@ -7,8 +7,9 @@ using NuGetUpdater.Core.Updater;
|
|
7
7
|
|
8
8
|
namespace NuGetUpdater.Core;
|
9
9
|
|
10
|
-
public class UpdaterWorker
|
10
|
+
public class UpdaterWorker : IUpdaterWorker
|
11
11
|
{
|
12
|
+
private readonly ExperimentsManager _experimentsManager;
|
12
13
|
private readonly ILogger _logger;
|
13
14
|
private readonly HashSet<string> _processedProjectPaths = new(StringComparer.OrdinalIgnoreCase);
|
14
15
|
|
@@ -18,8 +19,9 @@ public class UpdaterWorker
|
|
18
19
|
Converters = { new JsonStringEnumConverter() },
|
19
20
|
};
|
20
21
|
|
21
|
-
public UpdaterWorker(ILogger logger)
|
22
|
+
public UpdaterWorker(ExperimentsManager experimentsManager, ILogger logger)
|
22
23
|
{
|
24
|
+
_experimentsManager = experimentsManager;
|
23
25
|
_logger = logger;
|
24
26
|
}
|
25
27
|
|
@@ -221,7 +223,7 @@ public class UpdaterWorker
|
|
221
223
|
}
|
222
224
|
|
223
225
|
// Some repos use a mix of packages.config and PackageReference
|
224
|
-
await PackageReferenceUpdater.UpdateDependencyAsync(repoRootPath, projectPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive, _logger);
|
226
|
+
await PackageReferenceUpdater.UpdateDependencyAsync(repoRootPath, projectPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive, _experimentsManager, _logger);
|
225
227
|
|
226
228
|
// Update lock file if exists
|
227
229
|
if (File.Exists(Path.Combine(Path.GetDirectoryName(projectPath), "packages.lock.json")))
|
@@ -261,7 +261,11 @@ internal static partial class MSBuildHelper
|
|
261
261
|
? evaluationResult.EvaluatedValue.TrimStart('[', '(').TrimEnd(']', ')')
|
262
262
|
: evaluationResult.EvaluatedValue;
|
263
263
|
|
264
|
-
|
264
|
+
// If at this point we have a semicolon in the name then split it and yield multiple dependencies.
|
265
|
+
foreach (var splitName in name.Split(';', StringSplitOptions.RemoveEmptyEntries))
|
266
|
+
{
|
267
|
+
yield return new Dependency(splitName.Trim(), packageVersion, dependencyType, EvaluationResult: evaluationResult, IsUpdate: isUpdate);
|
268
|
+
}
|
265
269
|
}
|
266
270
|
}
|
267
271
|
|
@@ -335,11 +339,6 @@ internal static partial class MSBuildHelper
|
|
335
339
|
}
|
336
340
|
}
|
337
341
|
|
338
|
-
internal static bool UseNewDependencySolver()
|
339
|
-
{
|
340
|
-
return Environment.GetEnvironmentVariable("UseNewNugetPackageResolver") == "true";
|
341
|
-
}
|
342
|
-
|
343
342
|
internal static async Task<Dependency[]?> ResolveDependencyConflicts(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, Dependency[] update, ILogger logger)
|
344
343
|
{
|
345
344
|
var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-coherence_");
|
@@ -63,6 +63,8 @@ internal static class PathHelper
|
|
63
63
|
return result;
|
64
64
|
}
|
65
65
|
|
66
|
+
public static string FullyNormalizedRootedPath(this string path) => path.NormalizePathToUnix().NormalizeUnixPathParts().EnsurePrefix("/");
|
67
|
+
|
66
68
|
public static string GetFullPathFromRelative(string rootPath, string relativePath)
|
67
69
|
=> Path.GetFullPath(JoinPath(rootPath, relativePath.NormalizePathToUnix()));
|
68
70
|
|
@@ -85,6 +87,55 @@ internal static class PathHelper
|
|
85
87
|
return candidatePaths.ToArray();
|
86
88
|
}
|
87
89
|
|
90
|
+
/// <summary>
|
91
|
+
/// Resolves the case of the file path in a case-insensitive manner. Returns null if the file path is not found. file path must be a full path inside the repoRootPath.
|
92
|
+
/// </summary>
|
93
|
+
/// <param name="filePath">The file path to resolve.</param>
|
94
|
+
/// <param name="repoRootPath">The root path of the repository.</param>
|
95
|
+
public static string? ResolveCaseInsensitivePathInsideRepoRoot(string filePath, string repoRootPath)
|
96
|
+
{
|
97
|
+
if (string.IsNullOrEmpty(filePath) || string.IsNullOrEmpty(repoRootPath))
|
98
|
+
{
|
99
|
+
return null; // Invalid input
|
100
|
+
}
|
101
|
+
|
102
|
+
// Normalize paths
|
103
|
+
var normalizedFilePath = filePath.FullyNormalizedRootedPath();
|
104
|
+
var normalizedRepoRoot = repoRootPath.FullyNormalizedRootedPath();
|
105
|
+
|
106
|
+
// Ensure the file path starts with the repo root path
|
107
|
+
if (!normalizedFilePath.StartsWith(normalizedRepoRoot + "/", StringComparison.OrdinalIgnoreCase))
|
108
|
+
{
|
109
|
+
return null; // filePath is outside of repoRootPath
|
110
|
+
}
|
111
|
+
|
112
|
+
// Start resolving from the root path
|
113
|
+
var currentPath = normalizedRepoRoot;
|
114
|
+
var relativePath = normalizedFilePath.Substring(normalizedRepoRoot.Length).TrimStart('/');
|
115
|
+
|
116
|
+
foreach (var part in relativePath.Split('/'))
|
117
|
+
{
|
118
|
+
if (string.IsNullOrEmpty(part))
|
119
|
+
{
|
120
|
+
continue;
|
121
|
+
}
|
122
|
+
|
123
|
+
// Enumerate the current directory to find a case-insensitive match
|
124
|
+
var nextPath = Directory
|
125
|
+
.EnumerateFileSystemEntries(currentPath)
|
126
|
+
.FirstOrDefault(entry => string.Equals(Path.GetFileName(entry), part, StringComparison.OrdinalIgnoreCase));
|
127
|
+
|
128
|
+
if (nextPath == null)
|
129
|
+
{
|
130
|
+
return null; // Part of the path does not exist
|
131
|
+
}
|
132
|
+
|
133
|
+
currentPath = nextPath;
|
134
|
+
}
|
135
|
+
|
136
|
+
return currentPath; // Fully resolved path with correct casing
|
137
|
+
}
|
138
|
+
|
88
139
|
/// <summary>
|
89
140
|
/// Check in every directory from <paramref name="initialPath"/> up to <paramref name="rootPath"/> for the file specified in <paramref name="fileName"/>.
|
90
141
|
/// </summary>
|