dependabot-nuget 0.258.0 → 0.259.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.
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
  }