dependabot-nuget 0.258.0 → 0.259.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/Directory.Packages.props +2 -0
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +2 -2
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +255 -191
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +63 -35
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +107 -14
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +9 -5
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +18 -0
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +6 -1
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.DotNetToolsJson.cs +6 -2
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs +6 -2
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +11 -21
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Proj.cs +95 -0
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +204 -62
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +64 -45
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +419 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/NuGetUpdater.Core.Test.csproj +1 -0
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs +7 -2
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +77 -19
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DirsProj.cs +120 -91
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs +132 -97
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +93 -75
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +45 -42
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +1089 -956
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +1624 -1291
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +296 -293
  27. data/helpers/lib/NuGetUpdater/global.json +6 -0
  28. data/lib/dependabot/nuget/file_parser.rb +4 -5
  29. data/lib/dependabot/nuget/file_updater.rb +1 -1
  30. data/lib/dependabot/nuget/update_checker/dependency_finder.rb +7 -2
  31. data/lib/dependabot/nuget/update_checker/property_updater.rb +1 -0
  32. data/lib/dependabot/nuget/update_checker/version_finder.rb +2 -3
  33. data/lib/dependabot/nuget/update_checker.rb +1 -0
  34. metadata +8 -5
@@ -1,7 +1,4 @@
1
- using System;
2
- using System.IO;
3
1
  using System.Text;
4
- using System.Threading.Tasks;
5
2
 
6
3
  using NuGetUpdater.Core;
7
4
  using NuGetUpdater.Core.Test;
@@ -26,12 +23,18 @@ public partial class EntryPointTests
26
23
  "--solution-or-project",
27
24
  Path.Combine(path, "path/to/solution.sln"),
28
25
  "--dependency",
29
- "Newtonsoft.Json",
26
+ "Some.package",
30
27
  "--new-version",
31
28
  "13.0.1",
32
29
  "--previous-version",
33
30
  "7.0.1",
34
31
  ],
32
+ packages:
33
+ [
34
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "7.0.1", "net45"),
35
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "13.0.1", "net45"),
36
+ ],
37
+ initialFiles:
35
38
  [
36
39
  ("path/to/solution.sln", """
37
40
  Microsoft Visual Studio Solution File, Format Version 12.00
@@ -66,8 +69,8 @@ public partial class EntryPointTests
66
69
  <None Include="packages.config" />
67
70
  </ItemGroup>
68
71
  <ItemGroup>
69
- <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
70
- <HintPath>packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
72
+ <Reference Include="Some.Package">
73
+ <HintPath>packages\Some.Package.7.0.1\lib\net45\Some.Package.dll</HintPath>
71
74
  <Private>True</Private>
72
75
  </Reference>
73
76
  </ItemGroup>
@@ -76,10 +79,11 @@ public partial class EntryPointTests
76
79
  """),
77
80
  ("path/to/packages.config", """
78
81
  <packages>
79
- <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
82
+ <package id="Some.Package" version="7.0.1" targetFramework="net45" />
80
83
  </packages>
81
84
  """)
82
85
  ],
86
+ expectedFiles:
83
87
  [
84
88
  ("path/to/my.csproj", """
85
89
  <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
@@ -91,8 +95,8 @@ public partial class EntryPointTests
91
95
  <None Include="packages.config" />
92
96
  </ItemGroup>
93
97
  <ItemGroup>
94
- <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
95
- <HintPath>packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
98
+ <Reference Include="Some.Package">
99
+ <HintPath>packages\Some.Package.13.0.1\lib\net45\Some.Package.dll</HintPath>
96
100
  <Private>True</Private>
97
101
  </Reference>
98
102
  </ItemGroup>
@@ -102,10 +106,11 @@ public partial class EntryPointTests
102
106
  ("path/to/packages.config", """
103
107
  <?xml version="1.0" encoding="utf-8"?>
104
108
  <packages>
105
- <package id="Newtonsoft.Json" version="13.0.1" targetFramework="net45" />
109
+ <package id="Some.Package" version="13.0.1" targetFramework="net45" />
106
110
  </packages>
107
111
  """)
108
- ]);
112
+ ]
113
+ );
109
114
  }
110
115
 
111
116
  [Fact]
@@ -119,13 +124,19 @@ public partial class EntryPointTests
119
124
  "--solution-or-project",
120
125
  Path.Combine(path, "path/to/my.csproj"),
121
126
  "--dependency",
122
- "Newtonsoft.Json",
127
+ "Some.Package",
123
128
  "--new-version",
124
129
  "13.0.1",
125
130
  "--previous-version",
126
131
  "7.0.1",
127
132
  "--verbose"
128
133
  ],
134
+ packages:
135
+ [
136
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "7.0.1", "net45"),
137
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "13.0.1", "net45"),
138
+ ],
139
+ initialFiles:
129
140
  [
130
141
  ("path/to/my.csproj", """
131
142
  <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
@@ -137,8 +148,8 @@ public partial class EntryPointTests
137
148
  <None Include="packages.config" />
138
149
  </ItemGroup>
139
150
  <ItemGroup>
140
- <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
141
- <HintPath>packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
151
+ <Reference Include="Some.Package">
152
+ <HintPath>packages\Some.Package.7.0.1\lib\net45\Some.Package.dll</HintPath>
142
153
  <Private>True</Private>
143
154
  </Reference>
144
155
  </ItemGroup>
@@ -147,10 +158,11 @@ public partial class EntryPointTests
147
158
  """),
148
159
  ("path/to/packages.config", """
149
160
  <packages>
150
- <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
161
+ <package id="Some.Package" version="7.0.1" targetFramework="net45" />
151
162
  </packages>
152
163
  """)
153
164
  ],
165
+ expectedFiles:
154
166
  [
155
167
  ("path/to/my.csproj", """
156
168
  <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
@@ -162,8 +174,8 @@ public partial class EntryPointTests
162
174
  <None Include="packages.config" />
163
175
  </ItemGroup>
164
176
  <ItemGroup>
165
- <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
166
- <HintPath>packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
177
+ <Reference Include="Some.Package">
178
+ <HintPath>packages\Some.Package.13.0.1\lib\net45\Some.Package.dll</HintPath>
167
179
  <Private>True</Private>
168
180
  </Reference>
169
181
  </ItemGroup>
@@ -173,10 +185,11 @@ public partial class EntryPointTests
173
185
  ("path/to/packages.config", """
174
186
  <?xml version="1.0" encoding="utf-8"?>
175
187
  <packages>
176
- <package id="Newtonsoft.Json" version="13.0.1" targetFramework="net45" />
188
+ <package id="Some.Package" version="13.0.1" targetFramework="net45" />
177
189
  </packages>
178
190
  """)
179
- ]);
191
+ ]
192
+ );
180
193
  }
181
194
 
182
195
  [Fact]
@@ -190,13 +203,18 @@ public partial class EntryPointTests
190
203
  "--solution-or-project",
191
204
  $"{path}/some-dir/dirs.proj",
192
205
  "--dependency",
193
- "NuGet.Versioning",
206
+ "Some.Package",
194
207
  "--new-version",
195
208
  "6.6.1",
196
209
  "--previous-version",
197
210
  "6.1.0",
198
211
  "--verbose"
199
212
  ],
213
+ packages:
214
+ [
215
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "6.1.0", "net8.0"),
216
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "6.6.1", "net8.0"),
217
+ ],
200
218
  initialFiles:
201
219
  [
202
220
  ("some-dir/dirs.proj", """
@@ -211,12 +229,12 @@ public partial class EntryPointTests
211
229
  <Project Sdk="Microsoft.NET.Sdk">
212
230
  <PropertyGroup>
213
231
  <OutputType>Exe</OutputType>
214
- <TargetFramework>net6.0</TargetFramework>
232
+ <TargetFramework>net8.0</TargetFramework>
215
233
  <ImplicitUsings>enable</ImplicitUsings>
216
234
  <Nullable>enable</Nullable>
217
235
  </PropertyGroup>
218
236
  <ItemGroup>
219
- <PackageReference Include="NuGet.Versioning" Version="6.1.0" />
237
+ <PackageReference Include="Some.Package" Version="6.1.0" />
220
238
  </ItemGroup>
221
239
  </Project>
222
240
  """),
@@ -224,12 +242,12 @@ public partial class EntryPointTests
224
242
  <Project Sdk="Microsoft.NET.Sdk">
225
243
  <PropertyGroup>
226
244
  <OutputType>Exe</OutputType>
227
- <TargetFramework>net6.0</TargetFramework>
245
+ <TargetFramework>net8.0</TargetFramework>
228
246
  <ImplicitUsings>enable</ImplicitUsings>
229
247
  <Nullable>enable</Nullable>
230
248
  </PropertyGroup>
231
249
  <ItemGroup>
232
- <PackageReference Include="NuGet.Versioning" Version="6.1.0" />
250
+ <PackageReference Include="Some.Package" Version="6.1.0" />
233
251
  </ItemGroup>
234
252
  </Project>
235
253
  """),
@@ -237,7 +255,7 @@ public partial class EntryPointTests
237
255
  <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
238
256
 
239
257
  <ItemGroup>
240
- <PackageReference Include="NuGet.Versioning" Version="6.1.0" />
258
+ <PackageReference Include="Some.Package" Version="6.1.0" />
241
259
  </ItemGroup>
242
260
 
243
261
  </Project>
@@ -258,12 +276,12 @@ public partial class EntryPointTests
258
276
  <Project Sdk="Microsoft.NET.Sdk">
259
277
  <PropertyGroup>
260
278
  <OutputType>Exe</OutputType>
261
- <TargetFramework>net6.0</TargetFramework>
279
+ <TargetFramework>net8.0</TargetFramework>
262
280
  <ImplicitUsings>enable</ImplicitUsings>
263
281
  <Nullable>enable</Nullable>
264
282
  </PropertyGroup>
265
283
  <ItemGroup>
266
- <PackageReference Include="NuGet.Versioning" Version="6.6.1" />
284
+ <PackageReference Include="Some.Package" Version="6.6.1" />
267
285
  </ItemGroup>
268
286
  </Project>
269
287
  """),
@@ -271,12 +289,12 @@ public partial class EntryPointTests
271
289
  <Project Sdk="Microsoft.NET.Sdk">
272
290
  <PropertyGroup>
273
291
  <OutputType>Exe</OutputType>
274
- <TargetFramework>net6.0</TargetFramework>
292
+ <TargetFramework>net8.0</TargetFramework>
275
293
  <ImplicitUsings>enable</ImplicitUsings>
276
294
  <Nullable>enable</Nullable>
277
295
  </PropertyGroup>
278
296
  <ItemGroup>
279
- <PackageReference Include="NuGet.Versioning" Version="6.6.1" />
297
+ <PackageReference Include="Some.Package" Version="6.6.1" />
280
298
  </ItemGroup>
281
299
  </Project>
282
300
  """),
@@ -284,7 +302,7 @@ public partial class EntryPointTests
284
302
  <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
285
303
 
286
304
  <ItemGroup>
287
- <PackageReference Include="NuGet.Versioning" Version="6.1.0" />
305
+ <PackageReference Include="Some.Package" Version="6.1.0" />
288
306
  </ItemGroup>
289
307
 
290
308
  </Project>
@@ -303,6 +321,14 @@ public partial class EntryPointTests
303
321
  // test this, it must be tested in a new process where MSBuild has not been loaded yet and the runner tool
304
322
  // must be started with its working directory at the test repo's root.
305
323
  using var tempDir = new TemporaryDirectory();
324
+
325
+ MockNuGetPackage[] testPackages =
326
+ [
327
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "7.0.1", "net8.0"),
328
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "13.0.1", "net8.0"),
329
+ ];
330
+ await MockNuGetPackagesInDirectory(testPackages, tempDir.DirectoryPath);
331
+
306
332
  var globalJsonPath = Path.Join(tempDir.DirectoryPath, "global.json");
307
333
  var srcGlobalJsonPath = Path.Join(tempDir.DirectoryPath, "src", "global.json");
308
334
  string globalJsonContent = """
@@ -322,20 +348,21 @@ public partial class EntryPointTests
322
348
  <TargetFramework>net8.0</TargetFramework>
323
349
  </PropertyGroup>
324
350
  <ItemGroup>
325
- <PackageReference Include="Newtonsoft.Json" Version="7.0.1" />
351
+ <PackageReference Include="Some.Package" Version="7.0.1" />
326
352
  </ItemGroup>
327
353
  </Project>
328
354
  """);
329
- var executableName = $"NuGetUpdater.Cli{(Environment.OSVersion.Platform == PlatformID.Win32NT ? ".exe" : "")}";
355
+ var executableName = Path.Join(Path.GetDirectoryName(GetType().Assembly.Location), "NuGetUpdater.Cli.dll");
330
356
  var executableArgs = string.Join(" ",
331
357
  [
358
+ executableName,
332
359
  "update",
333
360
  "--repo-root",
334
361
  tempDir.DirectoryPath,
335
362
  "--solution-or-project",
336
363
  projectPath,
337
364
  "--dependency",
338
- "Newtonsoft.Json",
365
+ "Some.Package",
339
366
  "--new-version",
340
367
  "13.0.1",
341
368
  "--previous-version",
@@ -350,7 +377,7 @@ public partial class EntryPointTests
350
377
  workingDirectory = Path.Join(workingDirectory, workingDirectoryPath);
351
378
  }
352
379
 
353
- var (exitCode, output, error) = await ProcessEx.RunAsync(executableName, executableArgs, workingDirectory: workingDirectory);
380
+ var (exitCode, output, error) = await ProcessEx.RunAsync("dotnet", executableArgs, workingDirectory: workingDirectory);
354
381
  Assert.True(exitCode == 0, $"Error running update on unsupported SDK.\nSTDOUT:\n{output}\nSTDERR:\n{error}");
355
382
 
356
383
  // verify project update
@@ -366,7 +393,7 @@ public partial class EntryPointTests
366
393
  Assert.Contains("99.99.99", updatedGlobalJsonContents);
367
394
  }
368
395
 
369
- private static async Task Run(Func<string, string[]> getArgs, (string Path, string Content)[] initialFiles, (string, string)[] expectedFiles)
396
+ private static async Task Run(Func<string, string[]> getArgs, (string Path, string Content)[] initialFiles, (string, string)[] expectedFiles, MockNuGetPackage[]? packages = null)
370
397
  {
371
398
  var actualFiles = await RunUpdate(initialFiles, async path =>
372
399
  {
@@ -380,6 +407,7 @@ public partial class EntryPointTests
380
407
 
381
408
  try
382
409
  {
410
+ await MockNuGetPackagesInDirectory(packages, path);
383
411
  var args = getArgs(path);
384
412
  var result = await Program.Main(args);
385
413
  if (result != 0)
@@ -2,6 +2,10 @@ using System.Collections.Immutable;
2
2
  using System.Text.Json;
3
3
  using System.Text.Json.Serialization;
4
4
 
5
+ using Microsoft.Build.Construction;
6
+ using Microsoft.Build.Definition;
7
+ using Microsoft.Build.Evaluation;
8
+
5
9
  using NuGetUpdater.Core.Utilities;
6
10
 
7
11
  namespace NuGetUpdater.Core.Discover;
@@ -28,16 +32,15 @@ public partial class DiscoveryWorker
28
32
  {
29
33
  MSBuildHelper.RegisterMSBuild(Environment.CurrentDirectory, repoRootPath);
30
34
 
31
- // When running under unit tests, the workspace path may not be rooted.
32
- if (!Path.IsPathRooted(workspacePath) || !Directory.Exists(workspacePath))
33
- {
34
- workspacePath = Path.GetFullPath(Path.Join(repoRootPath, workspacePath));
35
- }
36
- else if (workspacePath == "/")
35
+ // the `workspacePath` variable is relative to a repository root, so a rooted path actually isn't rooted; the
36
+ // easy way to deal with this is to just trim the leading "/" if it exists
37
+ if (workspacePath.StartsWith("/"))
37
38
  {
38
- workspacePath = repoRootPath;
39
+ workspacePath = workspacePath[1..];
39
40
  }
40
41
 
42
+ workspacePath = Path.Combine(repoRootPath, workspacePath);
43
+
41
44
  DotNetToolsJsonDiscoveryResult? dotNetToolsJsonDiscovery = null;
42
45
  GlobalJsonDiscoveryResult? globalJsonDiscovery = null;
43
46
  DirectoryPackagesPropsDiscoveryResult? directoryPackagesPropsDiscovery = null;
@@ -115,27 +118,117 @@ public partial class DiscoveryWorker
115
118
  private async Task<ImmutableArray<ProjectDiscoveryResult>> RunForDirectoryAsnyc(string repoRootPath, string workspacePath)
116
119
  {
117
120
  _logger.Log($" Discovering projects beneath [{Path.GetRelativePath(repoRootPath, workspacePath)}].");
118
- var projectPaths = FindProjectFiles(workspacePath);
119
- if (projectPaths.IsEmpty)
121
+ var entryPoints = FindEntryPoints(workspacePath);
122
+ var projects = ExpandEntryPointsIntoProjects(entryPoints);
123
+ if (projects.IsEmpty)
120
124
  {
121
125
  _logger.Log(" No project files found.");
122
126
  return [];
123
127
  }
124
128
 
125
- return await RunForProjectPathsAsync(repoRootPath, workspacePath, projectPaths);
129
+ return await RunForProjectPathsAsync(repoRootPath, workspacePath, projects);
126
130
  }
127
131
 
128
- private static ImmutableArray<string> FindProjectFiles(string workspacePath)
132
+ private static ImmutableArray<string> FindEntryPoints(string workspacePath)
129
133
  {
130
- return Directory.EnumerateFiles(workspacePath, "*.*proj", SearchOption.AllDirectories)
134
+ return Directory.EnumerateFiles(workspacePath)
131
135
  .Where(path =>
132
136
  {
133
- var extension = Path.GetExtension(path).ToLowerInvariant();
134
- return extension == ".proj" || extension == ".csproj" || extension == ".fsproj" || extension == ".vbproj";
137
+ string extension = Path.GetExtension(path).ToLowerInvariant();
138
+ switch (extension)
139
+ {
140
+ case ".sln":
141
+ case ".proj":
142
+ case ".csproj":
143
+ case ".fsproj":
144
+ case ".vbproj":
145
+ return true;
146
+ default:
147
+ return false;
148
+ }
135
149
  })
136
150
  .ToImmutableArray();
137
151
  }
138
152
 
153
+ private static ImmutableArray<string> ExpandEntryPointsIntoProjects(IEnumerable<string> entryPoints)
154
+ {
155
+ HashSet<string> expandedProjects = new();
156
+ HashSet<string> seenProjects = new();
157
+ Stack<string> filesToExpand = new(entryPoints);
158
+ while (filesToExpand.Count > 0)
159
+ {
160
+ string candidateEntryPoint = filesToExpand.Pop();
161
+ if (seenProjects.Add(candidateEntryPoint))
162
+ {
163
+ string extension = Path.GetExtension(candidateEntryPoint).ToLowerInvariant();
164
+ if (extension == ".sln")
165
+ {
166
+ SolutionFile solution = SolutionFile.Parse(candidateEntryPoint);
167
+ foreach (ProjectInSolution project in solution.ProjectsInOrder)
168
+ {
169
+ filesToExpand.Push(project.AbsolutePath);
170
+ }
171
+ }
172
+ else if (extension == ".proj")
173
+ {
174
+ IEnumerable<string> foundProjects = ExpandItemGroupFilesFromProject(candidateEntryPoint, "ProjectFile", "ProjectReference");
175
+ foreach (string foundProject in foundProjects)
176
+ {
177
+ filesToExpand.Push(foundProject);
178
+ }
179
+ }
180
+ else
181
+ {
182
+ // .csproj, .fsproj, .vbproj
183
+ // keep this project and check for references
184
+ expandedProjects.Add(candidateEntryPoint);
185
+ IEnumerable<string> referencedProjects = ExpandItemGroupFilesFromProject(candidateEntryPoint, "ProjectReference");
186
+ foreach (string referencedProject in referencedProjects)
187
+ {
188
+ filesToExpand.Push(referencedProject);
189
+ }
190
+ }
191
+ }
192
+ }
193
+
194
+ return expandedProjects.ToImmutableArray();
195
+ }
196
+
197
+ private static IEnumerable<string> ExpandItemGroupFilesFromProject(string projectPath, params string[] itemTypes)
198
+ {
199
+ if (!File.Exists(projectPath))
200
+ {
201
+ return [];
202
+ }
203
+
204
+ using ProjectCollection projectCollection = new();
205
+ Project project = Project.FromFile(projectPath, new ProjectOptions
206
+ {
207
+ LoadSettings = ProjectLoadSettings.IgnoreMissingImports | ProjectLoadSettings.IgnoreEmptyImports | ProjectLoadSettings.IgnoreInvalidImports,
208
+ ProjectCollection = projectCollection,
209
+ });
210
+
211
+ HashSet<string> allowableItemTypes = new(itemTypes, StringComparer.OrdinalIgnoreCase);
212
+ List<ProjectItem> projectItems = project.Items.Where(i => allowableItemTypes.Contains(i.ItemType)).ToList();
213
+ string projectDir = Path.GetDirectoryName(projectPath)!;
214
+ HashSet<string> seenItems = new(StringComparer.OrdinalIgnoreCase);
215
+ List<string> foundItems = new();
216
+ foreach (ProjectItem projectItem in projectItems)
217
+ {
218
+ // referenced projects commonly use the Windows-style directory separator which can cause problems on Unix
219
+ // but Windows is able to handle a Unix-style path, so we normalize everything to that then normalize again
220
+ // with regards to relative paths, e.g., "some/path/" + "..\other\file" => "some/other/file"
221
+ string referencedProjectPath = Path.Join(projectDir, projectItem.EvaluatedInclude.NormalizePathToUnix());
222
+ string normalizedReferenceProjectPath = new FileInfo(referencedProjectPath).FullName;
223
+ if (seenItems.Add(normalizedReferenceProjectPath))
224
+ {
225
+ foundItems.Add(normalizedReferenceProjectPath);
226
+ }
227
+ }
228
+
229
+ return foundItems;
230
+ }
231
+
139
232
  private async Task<ImmutableArray<ProjectDiscoveryResult>> RunForProjectPathsAsync(string repoRootPath, string workspacePath, IEnumerable<string> projectPaths)
140
233
  {
141
234
  var results = new Dictionary<string, ProjectDiscoveryResult>(StringComparer.OrdinalIgnoreCase);
@@ -480,11 +480,15 @@ internal static partial class MSBuildHelper
480
480
  // We need to copy local package sources from the NuGet.Config file to the temp directory
481
481
  foreach (var localSource in packageSources.Where(p => p.IsLocal))
482
482
  {
483
- var subDir = localSource.Source.Split(nugetConfigDir)[1];
484
- var destPath = Path.Join(tempDir.FullName, subDir);
485
- if (Directory.Exists(localSource.Source))
483
+ // if the source is relative to the original location, copy it to the temp directory
484
+ if (PathHelper.IsSubdirectoryOf(nugetConfigDir!, localSource.Source))
486
485
  {
487
- PathHelper.CopyDirectory(localSource.Source, destPath);
486
+ string sourceRelativePath = Path.GetRelativePath(nugetConfigDir!, localSource.Source);
487
+ string destPath = Path.Join(tempDir.FullName, sourceRelativePath);
488
+ if (Directory.Exists(localSource.Source))
489
+ {
490
+ PathHelper.CopyDirectory(localSource.Source, destPath);
491
+ }
488
492
  }
489
493
  }
490
494
  }
@@ -538,12 +542,12 @@ internal static partial class MSBuildHelper
538
542
  <PropertyGroup>
539
543
  <!-- For Windows-specific apps -->
540
544
  <EnableWindowsTargeting>true</EnableWindowsTargeting>
545
+ <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
541
546
  </PropertyGroup>
542
547
  </Project>
543
548
  """);
544
549
 
545
550
  await File.WriteAllTextAsync(Path.Combine(tempDir.FullName, "Directory.Build.targets"), "<Project />");
546
- await File.WriteAllTextAsync(Path.Combine(tempDir.FullName, "Directory.Packages.props"), "<Project />");
547
551
 
548
552
  return tempProjectPath;
549
553
  }
@@ -111,4 +111,22 @@ internal static class PathHelper
111
111
  CopyDirectory(subDir.FullName, newDestinationDir);
112
112
  }
113
113
  }
114
+
115
+ public static bool IsSubdirectoryOf(string parentDirectory, string childDirectory)
116
+ {
117
+ var parentDirInfo = new DirectoryInfo(parentDirectory);
118
+ var childDirInfo = new DirectoryInfo(childDirectory);
119
+
120
+ while (childDirInfo.Parent is not null)
121
+ {
122
+ if (childDirInfo.Parent.FullName == parentDirInfo.FullName)
123
+ {
124
+ return true;
125
+ }
126
+
127
+ childDirInfo = childDirInfo.Parent;
128
+ }
129
+
130
+ return false;
131
+ }
114
132
  }
@@ -3,9 +3,11 @@ using System.Diagnostics.CodeAnalysis;
3
3
  using System.Text.Json;
4
4
 
5
5
  using NuGetUpdater.Core.Discover;
6
+ using NuGetUpdater.Core.Test.Update;
6
7
  using NuGetUpdater.Core.Test.Utilities;
7
8
 
8
9
  using Xunit;
10
+ using Xunit.Sdk;
9
11
 
10
12
  namespace NuGetUpdater.Core.Test.Discover;
11
13
 
@@ -16,10 +18,13 @@ public class DiscoveryWorkerTestBase
16
18
  protected static async Task TestDiscoveryAsync(
17
19
  string workspacePath,
18
20
  TestFile[] files,
19
- ExpectedWorkspaceDiscoveryResult expectedResult)
21
+ ExpectedWorkspaceDiscoveryResult expectedResult,
22
+ MockNuGetPackage[]? packages = null)
20
23
  {
21
24
  var actualResult = await RunDiscoveryAsync(files, async directoryPath =>
22
25
  {
26
+ await UpdateWorkerTestBase.MockNuGetPackagesInDirectory(packages, directoryPath);
27
+
23
28
  var worker = new DiscoveryWorker(new Logger(verbose: true));
24
29
  await worker.RunAsync(directoryPath, workspacePath, DiscoveryWorker.DiscoveryResultFileName);
25
30
  });
@@ -10,6 +10,7 @@ public partial class DiscoveryWorkerTests
10
10
  public async Task DiscoversDependencies()
11
11
  {
12
12
  await TestDiscoveryAsync(
13
+ packages: [],
13
14
  workspacePath: "",
14
15
  files: [
15
16
  (".config/dotnet-tools.json", """
@@ -45,13 +46,15 @@ public partial class DiscoveryWorkerTests
45
46
  ]
46
47
  },
47
48
  ExpectedProjectCount = 0,
48
- });
49
+ }
50
+ );
49
51
  }
50
52
 
51
53
  [Fact]
52
54
  public async Task ReportsFailure()
53
55
  {
54
56
  await TestDiscoveryAsync(
57
+ packages: [],
55
58
  workspacePath: "",
56
59
  files: [
57
60
  (".config/dotnet-tools.json", """
@@ -85,7 +88,8 @@ public partial class DiscoveryWorkerTests
85
88
  ExpectedDependencyCount = 0,
86
89
  },
87
90
  ExpectedProjectCount = 0,
88
- });
91
+ }
92
+ );
89
93
  }
90
94
  }
91
95
  }
@@ -10,6 +10,7 @@ public partial class DiscoveryWorkerTests
10
10
  public async Task DiscoversDependencies()
11
11
  {
12
12
  await TestDiscoveryAsync(
13
+ packages: [],
13
14
  workspacePath: "",
14
15
  files: [
15
16
  ("global.json", """
@@ -35,13 +36,15 @@ public partial class DiscoveryWorkerTests
35
36
  ]
36
37
  },
37
38
  ExpectedProjectCount = 0,
38
- });
39
+ }
40
+ );
39
41
  }
40
42
 
41
43
  [Fact]
42
44
  public async Task ReportsFailure()
43
45
  {
44
46
  await TestDiscoveryAsync(
47
+ packages: [],
45
48
  workspacePath: "",
46
49
  files: [
47
50
  ("global.json", """
@@ -65,7 +68,8 @@ public partial class DiscoveryWorkerTests
65
68
  ExpectedDependencyCount = 0,
66
69
  },
67
70
  ExpectedProjectCount = 0,
68
- });
71
+ }
72
+ );
69
73
  }
70
74
  }
71
75
  }