dependabot-nuget 0.285.0 → 0.287.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/helpers/lib/NuGetUpdater/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>
|