dependabot-nuget 0.370.0 → 0.371.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 (23) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +14 -6
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +0 -1
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +31 -16
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/GetProperty.targets +9 -0
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +1 -0
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/FileWriters/XmlFileWriter.cs +41 -14
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DependencyConflictResolver.cs +55 -51
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +62 -12
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +0 -20
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +96 -5
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Proj.cs +0 -8
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +2 -49
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +0 -77
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +0 -1
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +0 -58
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/EndToEndTests.cs +0 -1
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdatedDependencyListTests.cs +0 -3
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/FileWriterTestsBase.cs +1 -1
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/XmlFileWriterTests.cs +64 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/XmlFileWriterTests_GetDocumentIndentationCharactersTests.cs +120 -0
  22. metadata +6 -5
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Property.cs +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a443457e2e8e24c70f4cb8a9e11a2fff6c3f9d15255b7e023c29a1ccdf4bd548
4
- data.tar.gz: 272f36da3e3a53a28cd748d21da99bf8b5c94ca94c854e549db6dc1ee8b2f40a
3
+ metadata.gz: 1d7916c7661919bee90276b975fe46847c6289fa45de9cc7ebe2d1dc12799685
4
+ data.tar.gz: c05e7678dab3e9baa32a75a38f535dca02fd692c5562e7898756c2ffc2acbfee
5
5
  SHA512:
6
- metadata.gz: 7e5806e6b08c76324c6ba174c1f0a4e085ab62ef8f6694b365932beefa770ea5cdcdf578574f73e61f3b08b1ff7fd2523fa7f23a9598a34f69ccf1f236397b44
7
- data.tar.gz: 7786d4b33b44eb4fe14cc7ba98d77c5ad45c9213ae80320d895de61db18e19592b72243c3a25b4bcdc182dbdb0f6e2787812b14dd8cf49c606503053c3612a3b
6
+ metadata.gz: fc5b580e70c49c5a2b0c13887cac6140be5e4bf642e4d6c4e3f43455b57cc0599c76cb6170f75ef949f76aab23fdefa66488f16cb948aa15fa7871c593ebe935
7
+ data.tar.gz: 9301c2888824bac12674380195add8516a43b247205329d2fb8524c9e2bfa5e03734c0ddd30afe91acf0f7fb3851181563b5c70b81a3e72ff4dbc70d17eb5292
@@ -318,7 +318,20 @@ public partial class DiscoveryWorker : IDiscoveryWorker
318
318
  private async Task<ImmutableArray<ProjectDiscoveryResult>> RunForProjectPathsAsync(string repoRootPath, string workspacePath, IEnumerable<string> projectPaths)
319
319
  {
320
320
  var normalizedProjectPaths = projectPaths.SelectMany(p => PathHelper.ResolveCaseInsensitivePathsInsideRepoRoot(p, repoRootPath) ?? []).Distinct().ToImmutableArray();
321
- var disposables = normalizedProjectPaths.Select(p => new SpecialImportsConditionPatcher(p)).ToImmutableArray();
321
+
322
+ // Find all MSBuild files that may contain special imports
323
+ var enumerationOptions = new EnumerationOptions()
324
+ {
325
+ RecurseSubdirectories = true,
326
+ IgnoreInaccessible = true,
327
+ AttributesToSkip = FileAttributes.ReparsePoint,
328
+ };
329
+ var msbuildExtensions = new[] { ".props", ".targets", ".proj", ".csproj", ".vbproj", ".fsproj" };
330
+ var filesToPatch = Directory.GetFiles(repoRootPath, "*.*", enumerationOptions)
331
+ .Where(f => msbuildExtensions.Contains(Path.GetExtension(f), StringComparer.OrdinalIgnoreCase))
332
+ .ToImmutableArray();
333
+
334
+ var disposables = filesToPatch.Select(p => new SpecialImportsConditionPatcher(p)).ToImmutableArray();
322
335
  var results = new Dictionary<string, ProjectDiscoveryResult>(StringComparer.Ordinal);
323
336
 
324
337
  try
@@ -411,10 +424,6 @@ public partial class DiscoveryWorker : IDiscoveryWorker
411
424
  var mergedDependencies = mergedDependenciesSet.Values
412
425
  .OrderBy(d => d.Name, StringComparer.OrdinalIgnoreCase)
413
426
  .ToImmutableArray();
414
- var mergedProperties = result1.Properties.Concat(result2.Properties)
415
- .DistinctBy(p => p.Name, StringComparer.OrdinalIgnoreCase)
416
- .OrderBy(p => p.Name)
417
- .ToImmutableArray();
418
427
  var mergedTargetFrameworks = result1.TargetFrameworks.Concat(result2.TargetFrameworks)
419
428
  .Select(t =>
420
429
  {
@@ -450,7 +459,6 @@ public partial class DiscoveryWorker : IDiscoveryWorker
450
459
  Dependencies = mergedDependencies,
451
460
  IsSuccess = result1.IsSuccess && result2.IsSuccess,
452
461
  Error = result1.Error ?? result2.Error,
453
- Properties = mergedProperties,
454
462
  TargetFrameworks = mergedTargetFrameworks,
455
463
  ReferencedProjectPaths = mergedReferencedProjects,
456
464
  ImportedFiles = mergedImportedFiles,
@@ -10,7 +10,6 @@ public record ProjectDiscoveryResult : IDiscoveryResultWithDependencies
10
10
  public required ImmutableArray<Dependency> Dependencies { get; init; }
11
11
  public bool IsSuccess { get; init; } = true;
12
12
  public JobErrorBase? Error { get; init; } = null;
13
- public ImmutableArray<Property> Properties { get; init; } = [];
14
13
  public ImmutableArray<string> TargetFrameworks { get; init; } = [];
15
14
  public ImmutableArray<string> ReferencedProjectPaths { get; init; } = [];
16
15
  public required ImmutableArray<string> ImportedFiles { get; init; }
@@ -1,7 +1,6 @@
1
1
  using System.Collections.Immutable;
2
2
  using System.Text.Json;
3
3
  using System.Xml.Linq;
4
- using System.Xml.XPath;
5
4
 
6
5
  using Microsoft.Build.Logging.StructuredLogger;
7
6
 
@@ -63,6 +62,12 @@ internal static class SdkProjectDiscovery
63
62
  "GenerateBuildDependencyFile"
64
63
  ];
65
64
 
65
+ // these targets are required to evaluate a legacy project with a single operation
66
+ private static readonly ImmutableArray<string> LegacyProjectSingleRestoreTargetNames = ["ResolveProjectReferences"];
67
+
68
+ // this property evaluates to a version number in an SDK-style project and is unset or empty otherwise
69
+ private const string NETCoreSdkVersionPropertyName = "NETCoreSdkVersion";
70
+
66
71
  // this seems to be the maximum number of TFMs that can be restored in parallel without running into race conditions
67
72
  private const int MaximumParallelTargetFrameworkRestores = 2;
68
73
 
@@ -138,20 +143,39 @@ internal static class SdkProjectDiscovery
138
143
  var binLogPath = Path.Combine(Path.GetTempPath(), $"msbuild_{Guid.NewGuid():d}.binlog");
139
144
  try
140
145
  {
141
- // when using single restore, we can directly invoke the relevant targets
146
+ // when using single restore, we can directly invoke the relevant targets...
142
147
  var args = new List<string>() { "msbuild", startingProjectPath };
143
- var targets = await MSBuildHelper.GetProjectTargetsAsync(startingProjectPath, logger);
144
- var useDirectRestore = SingleRestoreTargetNames.All(targets.Contains);
148
+
149
+ // ...but determining what the relevant targets are can be complicated
150
+
151
+ // For SDK-style projects the targets `Restore`, `ResolveProjectReferences`, and `GenerateBuildDependencyFile`
152
+ // are necessary. If the project has a single target framework, those magic targets will all be present and can
153
+ // be directly invoked, but if the project has multiple target frameworks, those targets will _NOT_ be directly
154
+ // present and we instead have to invoke the `Build` target and specify the three magic values as `InnerTargets`.
155
+
156
+ // If the project is legacy then those three magic targets will not be present, but we shouldn't use the `Build`
157
+ // and `InnerTargets` trick because that's an SDK-only mechanism, so we instead only need to invoke
158
+ // `ResolveProjectReferences` to gather all `PackageReference` items and further down we re-build the transitive
159
+ // dependency set. Without the legacy project check, we could incorrectly invoke `Build` which eventually tries
160
+ // to call `csc.exe` which is unnecessary and can be slow.
161
+ var netCoreSdkVersionValue = await MSBuildHelper.GetProjectPropertyAsync(startingProjectPath, NETCoreSdkVersionPropertyName, logger);
162
+ var isLegacyProject = string.IsNullOrEmpty(netCoreSdkVersionValue);
163
+ var requiredTargets = isLegacyProject
164
+ ? LegacyProjectSingleRestoreTargetNames
165
+ : SingleRestoreTargetNames;
166
+
167
+ var actualTargets = await MSBuildHelper.GetProjectTargetsAsync(startingProjectPath, logger);
168
+ var useDirectRestore = requiredTargets.All(actualTargets.Contains);
145
169
  if (useDirectRestore || isIndividualTfmRestore)
146
170
  {
147
171
  // directly call the required targets
148
- args.Add($"/t:{string.Join(",", SingleRestoreTargetNames)}");
172
+ args.Add($"/t:{string.Join(",", requiredTargets)}");
149
173
  }
150
174
  else
151
175
  {
152
176
  // delegate to the inner build and call those targets
153
177
  args.Add("/t:Build");
154
- args.Add($"/p:InnerTargets=\"{string.Join(";", SingleRestoreTargetNames)}\"");
178
+ args.Add($"/p:InnerTargets=\"{string.Join(";", requiredTargets)}\"");
155
179
  }
156
180
 
157
181
  // inject various props and targets to help with discovery
@@ -385,7 +409,7 @@ internal static class SdkProjectDiscovery
385
409
  foreach (var projectPath in resolvedProperties.Keys)
386
410
  {
387
411
  var projectProperties = resolvedProperties[projectPath];
388
- var isProjectLegacy = !projectProperties.ContainsKey("NETCoreSdkVersion"); // legacy projects don't contain this property
412
+ var isProjectLegacy = !projectProperties.ContainsKey(NETCoreSdkVersionPropertyName); // legacy projects don't contain this property
389
413
  if (isProjectLegacy)
390
414
  {
391
415
  logger.Info($"Project {projectPath} is legacy");
@@ -482,9 +506,6 @@ internal static class SdkProjectDiscovery
482
506
  }
483
507
 
484
508
  var projectFullDirectory = Path.GetDirectoryName(projectPath)!;
485
- var doc = XDocument.Load(projectPath);
486
- var localPropertyDefinitionElements = doc.Root!.XPathSelectElements("/Project/PropertyGroup/*");
487
- var projectPropertyNames = localPropertyDefinitionElements.Select(e => e.Name.LocalName).ToHashSet(StringComparer.OrdinalIgnoreCase);
488
509
  var projectRelativePath = Path.GetRelativePath(workspacePath, projectPath);
489
510
 
490
511
  var propertiesForProject = resolvedProperties.GetOrAdd(projectPath, () => new(StringComparer.OrdinalIgnoreCase));
@@ -611,11 +632,6 @@ internal static class SdkProjectDiscovery
611
632
 
612
633
  // others
613
634
  var projectProperties = resolvedProperties[projectPath];
614
- var properties = projectProperties
615
- .Where(pkvp => projectPropertyNames.Contains(pkvp.Key))
616
- .Select(pkvp => new Property(pkvp.Key, pkvp.Value, Path.GetRelativePath(repoRootPath, projectPath).NormalizePathToUnix()))
617
- .OrderBy(p => p.Name)
618
- .ToImmutableArray();
619
635
  var referenced = referencedProjects.GetOrAdd(projectPath, () => new(PathComparer.Instance))
620
636
  .Select(p => Path.GetRelativePath(projectFullDirectory, p).NormalizePathToUnix())
621
637
  .OrderBy(p => p)
@@ -640,7 +656,6 @@ internal static class SdkProjectDiscovery
640
656
  FilePath = projectRelativePath,
641
657
  Dependencies = dependencies,
642
658
  TargetFrameworks = tfms,
643
- Properties = properties,
644
659
  ReferencedProjectPaths = referenced,
645
660
  ImportedFiles = imported,
646
661
  AdditionalFiles = additional,
@@ -0,0 +1,9 @@
1
+ <Project>
2
+ <Target Name="_Dependabot_GetProperty">
3
+ <PropertyGroup>
4
+ <!-- the following value will get copied and replaced at runtime -->
5
+ <_DependabotPropertyValue>%RequestedPropertyName%</_DependabotPropertyValue>
6
+ </PropertyGroup>
7
+ <Message Importance="High" Text="__PROPERTY_VALUE:$(_DependabotPropertyValue)" />
8
+ </Target>
9
+ </Project>
@@ -11,6 +11,7 @@
11
11
  <None Include="DependencyDiscovery.props" CopyToOutputDirectory="PreserveNewest" />
12
12
  <None Include="DependencyDiscoveryTargetingPacks.props" CopyToOutputDirectory="PreserveNewest" />
13
13
  <None Include="DependencyDiscovery.targets" CopyToOutputDirectory="PreserveNewest" />
14
+ <None Include="GetProperty.targets" CopyToOutputDirectory="PreserveNewest" />
14
15
  <None Include="TargetFrameworkReporter.targets" CopyToOutputDirectory="PreserveNewest" />
15
16
  </ItemGroup>
16
17
 
@@ -150,29 +150,35 @@ public class XmlFileWriter : IFileWriter
150
150
  // find last `<ItemGroup>` in the project...
151
151
  Action addItemGroup = () => { }; // adding an ItemGroup to the project isn't always necessary, but it's much easier to prepare for it here
152
152
  var projectDocument = filesAndContents[projectRelativePath];
153
- var lastItemGroup = projectDocument.RootSyntax.Elements
154
- .LastOrDefault(e => e.Name.Equals(ItemGroupElementName, StringComparison.OrdinalIgnoreCase));
155
- if (lastItemGroup is null)
153
+ var indentation = GetDocumentIndentationCharacters(projectDocument);
154
+ var itemGroups = projectDocument.RootSyntax.Elements
155
+ .Where(e => e.Name.Equals(ItemGroupElementName, StringComparison.OrdinalIgnoreCase))
156
+ .ToArray();
157
+ var itemGroupsWithPackageReferences = itemGroups
158
+ .Where(e => e.Elements.Any(c => c.Name.Equals(PackageReferenceElementName, StringComparison.OrdinalIgnoreCase)))
159
+ .ToArray();
160
+ var itemGroupForInsertion = itemGroupsWithPackageReferences.LastOrDefault() ?? itemGroups.LastOrDefault();
161
+ if (itemGroupForInsertion is null)
156
162
  {
157
163
  _logger.Info($"No `<{ItemGroupElementName}>` element found in project; adding one.");
158
- lastItemGroup = XmlExtensions.CreateOpenCloseXmlElementSyntax(ItemGroupElementName,
159
- new SyntaxList<SyntaxNode>([SyntaxFactory.EndOfLineTrivia("\n"), SyntaxFactory.WhitespaceTrivia(" ")]),
164
+ itemGroupForInsertion = XmlExtensions.CreateOpenCloseXmlElementSyntax(ItemGroupElementName,
165
+ new SyntaxList<SyntaxNode>([SyntaxFactory.EndOfLineTrivia("\n"), SyntaxFactory.WhitespaceTrivia(indentation)]),
160
166
  insertIntermediateNewline: false);
161
167
  addItemGroup = () =>
162
168
  {
163
169
  // add the new element
164
- var updatedRootSyntax = projectDocument.RootSyntax.AddChild(lastItemGroup);
170
+ var updatedRootSyntax = projectDocument.RootSyntax.AddChild(itemGroupForInsertion);
165
171
  var updatedProjectDocument = projectDocument.ReplaceNode(projectDocument.RootSyntax.AsNode, updatedRootSyntax.AsNode);
166
172
 
167
173
  // reset well-known variables
168
174
  projectDocument = updatedProjectDocument;
169
175
  filesAndContents[projectRelativePath] = updatedProjectDocument;
170
- lastItemGroup = updatedProjectDocument.RootSyntax.Elements.Last(e => e.Name.Equals(ItemGroupElementName, StringComparison.OrdinalIgnoreCase));
176
+ itemGroupForInsertion = updatedProjectDocument.RootSyntax.Elements.Last(e => e.Name.Equals(ItemGroupElementName, StringComparison.OrdinalIgnoreCase));
171
177
  };
172
178
  }
173
179
 
174
180
  // ...find where the new item should go...
175
- var elementsBeforeNew = GetOrderedElementsBeforeSpecified(lastItemGroup, PackageReferenceElementName, [IncludeAttributeName, UpdateAttributeName], requiredPackageVersion.Name);
181
+ var elementsBeforeNew = GetOrderedElementsBeforeSpecified(itemGroupForInsertion, PackageReferenceElementName, [IncludeAttributeName, UpdateAttributeName], requiredPackageVersion.Name);
176
182
 
177
183
  // ...prepare a new `<PackageReference>` element...
178
184
  var newElement = XmlExtensions.CreateSingleLineXmlElementSyntax(PackageReferenceElementName, leadingTrivia: new SyntaxList<SyntaxNode>())
@@ -221,18 +227,18 @@ public class XmlFileWriter : IFileWriter
221
227
  else
222
228
  {
223
229
  // no prior package references; add to the front
224
- var itemGroupTrivia = lastItemGroup.AsNode.GetLeadingTrivia().ToList();
230
+ var itemGroupTrivia = itemGroupForInsertion.AsNode.GetLeadingTrivia().ToList();
225
231
  var priorEolIndex = itemGroupTrivia.FindLastIndex(t => t.Kind == SyntaxKind.EndOfLineTrivia);
226
232
  var indentTrivia = itemGroupTrivia
227
233
  .Skip(priorEolIndex + 1)
228
234
  .Select(t => SyntaxFactory.WhitespaceTrivia(t.ToFullString()))
229
235
  .ToArray();
230
- var newTrivia = new SyntaxTriviaList([SyntaxFactory.EndOfLineTrivia("\n"), SyntaxFactory.WhitespaceTrivia(" "), .. indentTrivia]);
236
+ var newTrivia = new SyntaxTriviaList([SyntaxFactory.EndOfLineTrivia("\n"), SyntaxFactory.WhitespaceTrivia(indentation), .. indentTrivia]);
231
237
  newElement = (IXmlElementSyntax)newElement.AsNode.WithLeadingTrivia(newTrivia);
232
238
  var updatedItemGroup = (IXmlElementSyntax)ReplaceNode(
233
239
  projectRelativePath,
234
- lastItemGroup.AsNode,
235
- lastItemGroup.InsertChild(newElement, 0).AsNode
240
+ itemGroupForInsertion.AsNode,
241
+ itemGroupForInsertion.InsertChild(newElement, 0).AsNode
236
242
  );
237
243
  newElement = (IXmlElementSyntax)updatedItemGroup.Content[0];
238
244
  }
@@ -353,7 +359,9 @@ public class XmlFileWriter : IFileWriter
353
359
  .Skip(priorEolIndex + 1)
354
360
  .Select(t => SyntaxFactory.WhitespaceTrivia(t.ToFullString()))
355
361
  .ToArray();
356
- var newTrivia = new SyntaxTriviaList([SyntaxFactory.EndOfLineTrivia("\n"), SyntaxFactory.WhitespaceTrivia(" "), .. indentTrivia]);
362
+ var packageVersionDocument = filesAndContents[filePath];
363
+ var packageVersionIndentation = GetDocumentIndentationCharacters(packageVersionDocument);
364
+ var newTrivia = new SyntaxTriviaList([SyntaxFactory.EndOfLineTrivia("\n"), SyntaxFactory.WhitespaceTrivia(packageVersionIndentation), .. indentTrivia]);
357
365
  newVersionElement = (IXmlElementSyntax)newVersionElement.AsNode.WithLeadingTrivia(newTrivia).WithoutTrailingTrivia();
358
366
  var insertionIndex = 0;
359
367
  var replacementPackageVersionGroup = packageVersionGroup
@@ -581,7 +589,7 @@ public class XmlFileWriter : IFileWriter
581
589
  return elementsBeforeNew;
582
590
  }
583
591
 
584
- private static async Task<XmlDocumentSyntax> ReadFileContentsAsync(DirectoryInfo repoContentsPath, string path)
592
+ internal static async Task<XmlDocumentSyntax> ReadFileContentsAsync(DirectoryInfo repoContentsPath, string path)
585
593
  {
586
594
  var fullPath = Path.Join(repoContentsPath.FullName, path);
587
595
  var contents = await File.ReadAllTextAsync(fullPath);
@@ -696,4 +704,23 @@ public class XmlFileWriter : IFileWriter
696
704
 
697
705
  return newRange.ToString();
698
706
  }
707
+
708
+ public static string GetDocumentIndentationCharacters(XmlDocumentSyntax document)
709
+ {
710
+ // find the first element with leading whitespace and assume that's the document indentation
711
+ var nodeLeadingLineTrivias = document.DescendantNodes()
712
+ .Select(n => n.GetLeadingTrivia().ToList())
713
+ .Select(l =>
714
+ {
715
+ var priorEolIndex = l.FindLastIndex(t => t.Kind == SyntaxKind.EndOfLineTrivia);
716
+ var leadingTriviaParts = l.Skip(priorEolIndex + 1)
717
+ .Select(t => t.ToFullString());
718
+ var leadingTrivia = string.Concat(leadingTriviaParts);
719
+ return leadingTrivia;
720
+ })
721
+ .ToArray();
722
+ var nodeLeadingLineTrivia = nodeLeadingLineTrivias
723
+ .FirstOrDefault(t => !string.IsNullOrEmpty(t));
724
+ return nodeLeadingLineTrivia ?? " ";
725
+ }
699
726
  }
@@ -19,12 +19,12 @@ namespace NuGetUpdater.Core;
19
19
  // Data type to store information of a given package
20
20
  public class PackageToUpdate
21
21
  {
22
- public string PackageName { get; set; }
23
- public string CurrentVersion { get; set; }
24
- public string NewVersion { get; set; }
22
+ public required string PackageName { get; set; }
23
+ public string? CurrentVersion { get; set; }
24
+ public string? NewVersion { get; set; }
25
25
 
26
26
  // Second version in case there's a "bounds" on the package version
27
- public string SecondVersion { get; set; }
27
+ public string? SecondVersion { get; set; }
28
28
 
29
29
  // Bool to determine if a package has to be a specific version
30
30
  public bool IsSpecific { get; set; }
@@ -122,7 +122,7 @@ public class PackageManager
122
122
  }
123
123
 
124
124
  // Method to find the best match framework of a given package's target framework availability
125
- public static NuGetFramework FindBestMatchFramework(IEnumerable<NuGet.Packaging.PackageDependencyGroup> dependencySet, string targetFrameworkString)
125
+ public static NuGetFramework? FindBestMatchFramework(IEnumerable<NuGet.Packaging.PackageDependencyGroup> dependencySet, string targetFrameworkString)
126
126
  {
127
127
  // Parse the given target framework string into a NuGetFramework object
128
128
  var targetFramework = NuGetFramework.ParseFolder(targetFrameworkString);
@@ -136,7 +136,7 @@ public class PackageManager
136
136
  }
137
137
 
138
138
  // Method to get the dependencies of a package
139
- public async Task<List<PackageToUpdate>> GetDependenciesAsync(PackageToUpdate package, string targetFramework, string projectDirectory, ILogger logger)
139
+ public async Task<List<PackageToUpdate>?> GetDependenciesAsync(PackageToUpdate package, string targetFramework, string projectDirectory, ILogger logger)
140
140
  {
141
141
  if (!NuGetVersion.TryParse(package.NewVersion, out var otherVersion))
142
142
  {
@@ -166,47 +166,46 @@ public class PackageManager
166
166
 
167
167
  foreach (var packageDependency in bestMatchGroup.Packages)
168
168
  {
169
- string version = packageDependency.VersionRange.OriginalString;
170
- string firstVersion = null;
171
- string SecondVersion = null;
169
+ string? version = packageDependency.VersionRange.OriginalString;
170
+ string? SecondVersion = null;
172
171
 
173
172
  // Conditions to check if the version has bounds specified
174
- if (version.StartsWith("[") && version.EndsWith("]"))
173
+ if (version is not null && version.StartsWith("[") && version.EndsWith("]"))
175
174
  {
176
175
  version = version.Trim('[', ']');
177
176
  var versions = version.Split(',');
178
- version = versions.FirstOrDefault().Trim();
177
+ version = versions[0].Trim();
179
178
  if (versions.Length > 1)
180
179
  {
181
180
  SecondVersion = versions.LastOrDefault()?.Trim();
182
181
  }
183
182
  specific = true;
184
183
  }
185
- else if (version.StartsWith("[") && version.EndsWith(")"))
184
+ else if (version is not null && version.StartsWith("[") && version.EndsWith(")"))
186
185
  {
187
186
  version = version.Trim('[', ')');
188
187
  var versions = version.Split(',');
189
- version = versions.FirstOrDefault().Trim();
188
+ version = versions[0].Trim();
190
189
  if (versions.Length > 1)
191
190
  {
192
191
  SecondVersion = versions.LastOrDefault()?.Trim();
193
192
  }
194
193
  }
195
- else if (version.StartsWith("(") && version.EndsWith("]"))
194
+ else if (version is not null && version.StartsWith("(") && version.EndsWith("]"))
196
195
  {
197
196
  version = version.Trim('(', ']');
198
197
  var versions = version.Split(',');
199
- version = versions.FirstOrDefault().Trim();
198
+ version = versions[0].Trim();
200
199
  if (versions.Length > 1)
201
200
  {
202
201
  SecondVersion = versions.LastOrDefault()?.Trim();
203
202
  }
204
203
  }
205
- else if (version.StartsWith("(") && version.EndsWith(")"))
204
+ else if (version is not null && version.StartsWith("(") && version.EndsWith(")"))
206
205
  {
207
206
  version = version.Trim('(', ')');
208
207
  var versions = version.Split(',');
209
- version = versions.FirstOrDefault().Trim();
208
+ version = versions[0].Trim();
210
209
  if (versions.Length > 1)
211
210
  {
212
211
  SecondVersion = versions.LastOrDefault()?.Trim();
@@ -292,7 +291,7 @@ public class PackageManager
292
291
  // Loop through each package and get their dependencies
293
292
  foreach (PackageToUpdate package in packages)
294
293
  {
295
- List<PackageToUpdate> dependencies = await GetDependenciesAsync(package, targetFramework, projectDirectory, logger);
294
+ List<PackageToUpdate>? dependencies = await GetDependenciesAsync(package, targetFramework, projectDirectory, logger);
296
295
 
297
296
  if (dependencies == null)
298
297
  {
@@ -302,7 +301,7 @@ public class PackageManager
302
301
  // Add each dependency based off if it exists or not
303
302
  foreach (PackageToUpdate dependency in dependencies)
304
303
  {
305
- PackageToUpdate checkInExisting = packages.FirstOrDefault(p => string.Compare(p.PackageName, dependency.PackageName, StringComparison.OrdinalIgnoreCase) == 0);
304
+ PackageToUpdate? checkInExisting = packages.FirstOrDefault(p => string.Compare(p.PackageName, dependency.PackageName, StringComparison.OrdinalIgnoreCase) == 0);
306
305
  if (checkInExisting != null)
307
306
  {
308
307
  checkInExisting.IsSpecific = dependency.IsSpecific;
@@ -356,17 +355,17 @@ public class PackageManager
356
355
 
357
356
  try
358
357
  {
359
- NuGetVersion CurrentVersion = new NuGetVersion(package.CurrentVersion);
360
- NuGetVersion newerVersion = new NuGetVersion(package.NewVersion);
358
+ NuGetVersion CurrentVersion = new NuGetVersion(package.CurrentVersion!);
359
+ NuGetVersion newerVersion = new NuGetVersion(package.NewVersion!);
361
360
 
362
361
  // If the CurrentVersion is less than or equal to the newerVersion, proceed with the update
363
362
  if (CurrentVersion <= newerVersion)
364
363
  {
365
- string currentVersiontemp = package.CurrentVersion;
364
+ string? currentVersiontemp = package.CurrentVersion;
366
365
  package.CurrentVersion = package.NewVersion;
367
366
 
368
367
  // Check if the current package has dependencies
369
- List<PackageToUpdate> dependencyList = await GetDependenciesAsync(package, targetFramework, projectDirectory, logger);
368
+ List<PackageToUpdate>? dependencyList = await GetDependenciesAsync(package, targetFramework, projectDirectory, logger);
370
369
 
371
370
  // If there are dependencies
372
371
  if (dependencyList != null)
@@ -379,14 +378,14 @@ public class PackageManager
379
378
  // If you find the dependency
380
379
  if (string.Equals(dependency.PackageName, existingPackage.PackageName, StringComparison.OrdinalIgnoreCase))
381
380
  {
382
- NuGetVersion existingCurrentVersion = new NuGetVersion(existingPackage.CurrentVersion);
383
- NuGetVersion dependencyCurrentVersion = new NuGetVersion(dependency.CurrentVersion);
381
+ NuGetVersion existingCurrentVersion = new NuGetVersion(existingPackage.CurrentVersion!);
382
+ NuGetVersion dependencyCurrentVersion = new NuGetVersion(dependency.CurrentVersion!);
384
383
 
385
384
  // Check if the existing version is less than the dependency's existing version
386
385
  if (existingCurrentVersion < dependencyCurrentVersion)
387
386
  {
388
387
  // Create temporary copy of the current version and of the existing package
389
- string dependencyOldVersion = existingPackage.CurrentVersion;
388
+ string? dependencyOldVersion = existingPackage.CurrentVersion;
390
389
 
391
390
  // Susbtitute the current version of the existingPackage with the dependency current version
392
391
  existingPackage.CurrentVersion = dependency.CurrentVersion;
@@ -411,7 +410,7 @@ public class PackageManager
411
410
  }
412
411
 
413
412
  // If the dependency has brackets or parenthesis, it's a specific version
414
- if (dependency.CurrentVersion.Contains('[') || dependency.CurrentVersion.Contains(']') || dependency.CurrentVersion.Contains('{') || dependency.CurrentVersion.Contains('}'))
413
+ if (dependency.CurrentVersion is not null && (dependency.CurrentVersion.Contains('[') || dependency.CurrentVersion.Contains(']') || dependency.CurrentVersion.Contains('{') || dependency.CurrentVersion.Contains('}')))
415
414
  {
416
415
  dependency.IsSpecific = true;
417
416
  }
@@ -433,7 +432,7 @@ public class PackageManager
433
432
  if (!isCompatible)
434
433
  {
435
434
  // Attempt to find and update to a compatible version between the two
436
- NuGetVersion compatibleVersion = await FindCompatibleVersionAsync(existingPackages, parent, package, targetFramework, logger);
435
+ NuGetVersion? compatibleVersion = await FindCompatibleVersionAsync(existingPackages, parent, package, targetFramework, logger);
437
436
  if (compatibleVersion == null)
438
437
  {
439
438
  return "Failed to update";
@@ -447,32 +446,32 @@ public class PackageManager
447
446
  // If it's compatible and the package you updated wasn't in the existing package, check if the parent's dependencies version is the same as the current version
448
447
  else if (isCompatible == true && inExisting == false)
449
448
  {
450
- List<PackageToUpdate> dependencyListParent = await GetDependenciesAsync(parent, targetFramework, projectDirectory, logger);
449
+ List<PackageToUpdate>? dependencyListParent = await GetDependenciesAsync(parent, targetFramework, projectDirectory, logger);
451
450
 
452
- PackageToUpdate parentDependency = dependencyListParent.FirstOrDefault(p => string.Compare(p.PackageName, package.PackageName, StringComparison.OrdinalIgnoreCase) == 0);
451
+ PackageToUpdate? parentDependency = dependencyListParent?.FirstOrDefault(p => string.Compare(p.PackageName, package.PackageName, StringComparison.OrdinalIgnoreCase) == 0);
453
452
 
454
453
  // If the parent's dependency current version is not the same as the current version of the package
455
- if (parentDependency.CurrentVersion != package.CurrentVersion)
454
+ if (parentDependency is not null && parentDependency.CurrentVersion != package.CurrentVersion)
456
455
  {
457
456
  // Create a NugetContext instance to get the latest versions of the parent
458
457
  NuGetContext nugetContext = new NuGetContext(Path.GetDirectoryName(projectPath));
459
458
 
460
- string currentVersionString = parent.CurrentVersion;
461
- NuGetVersion currentVersionParent = NuGetVersion.Parse(currentVersionString);
459
+ string? currentVersionString = parent.CurrentVersion;
460
+ NuGetVersion currentVersionParent = NuGetVersion.Parse(currentVersionString!);
462
461
 
463
462
  var result = await VersionFinder.GetVersionsByNameAsync([projectFramework], parent.PackageName, currentVersionParent, nugetContext, logger, CancellationToken.None);
464
463
  var versions = result.GetVersions();
465
- NuGetVersion latestVersion = versions.Where(v => !v.IsPrerelease).Max();
464
+ NuGetVersion? latestVersion = versions.Where(v => !v.IsPrerelease).Max();
466
465
 
467
466
  // Loop from the current version to the latest version, use next patch as a limit (unless there's a limit) so it doesn't look for versions that don't exist
468
- for (NuGetVersion? version = currentVersionParent; version is not null && version <= latestVersion; version = NextPatch(version, versions))
467
+ for (NuGetVersion? version = currentVersionParent; version is not null && latestVersion is not null && version <= latestVersion; version = NextPatch(version, versions))
469
468
  {
470
469
  // Check if the parent needs to be updated since the child isn't in the existing package list and the parent can update to a newer version to remove the dependency
471
- List<PackageToUpdate> dependencyListParentTemp = await GetDependenciesAsync(parent, targetFramework, projectDirectory, logger);
472
- PackageToUpdate parentDependencyTemp = dependencyListParentTemp.FirstOrDefault(p => string.Compare(p.PackageName, package.PackageName, StringComparison.OrdinalIgnoreCase) == 0);
470
+ List<PackageToUpdate>? dependencyListParentTemp = await GetDependenciesAsync(parent, targetFramework, projectDirectory, logger);
471
+ PackageToUpdate? parentDependencyTemp = dependencyListParentTemp?.FirstOrDefault(p => string.Compare(p.PackageName, package.PackageName, StringComparison.OrdinalIgnoreCase) == 0);
473
472
 
474
473
  // If the newer package version of the parent has the same version as the parent's previous dependency, update
475
- if (parentDependencyTemp.CurrentVersion == package.CurrentVersion)
474
+ if (parentDependencyTemp is not null && parentDependencyTemp.CurrentVersion == package.CurrentVersion)
476
475
  {
477
476
  parent.NewVersion = version.ToString();
478
477
  parent.CurrentVersion = null;
@@ -504,7 +503,12 @@ public class PackageManager
504
503
  public async Task<bool> IsCompatibleAsync(PackageToUpdate parent, PackageToUpdate child, string targetFramework, string projectDirectory, ILogger logger)
505
504
  {
506
505
  // Get the dependencies of the parent
507
- List<PackageToUpdate> dependencies = await GetDependenciesAsync(parent, targetFramework, projectDirectory, logger);
506
+ List<PackageToUpdate>? dependencies = await GetDependenciesAsync(parent, targetFramework, projectDirectory, logger);
507
+
508
+ if (dependencies is null)
509
+ {
510
+ return false;
511
+ }
508
512
 
509
513
  foreach (PackageToUpdate dependency in dependencies)
510
514
  {
@@ -512,8 +516,8 @@ public class PackageManager
512
516
  // If the child is present
513
517
  if (string.Equals(dependency.PackageName, child.PackageName, StringComparison.OrdinalIgnoreCase))
514
518
  {
515
- NuGetVersion dependencyVersion = new NuGetVersion(dependency.CurrentVersion);
516
- NuGetVersion childVersion = new NuGetVersion(child.CurrentVersion);
519
+ NuGetVersion dependencyVersion = new NuGetVersion(dependency.CurrentVersion!);
520
+ NuGetVersion childVersion = new NuGetVersion(child.CurrentVersion!);
517
521
 
518
522
  // If the dependency version of the parent and the childversion is the same, or if the child version can be >=
519
523
  if (dependencyVersion == childVersion || (childVersion > dependencyVersion && dependency.IsSpecific != true))
@@ -538,13 +542,13 @@ public class PackageManager
538
542
  }
539
543
 
540
544
  // Method to find a compatible version with the child for the parent to update to
541
- public async Task<NuGetVersion> FindCompatibleVersionAsync(List<PackageToUpdate> existingPackages, PackageToUpdate possibleParent, PackageToUpdate possibleDependency, string targetFramework, ILogger logger)
545
+ public async Task<NuGetVersion?> FindCompatibleVersionAsync(List<PackageToUpdate> existingPackages, PackageToUpdate possibleParent, PackageToUpdate possibleDependency, string targetFramework, ILogger logger)
542
546
  {
543
547
  string packageId = possibleParent.PackageName;
544
- string currentVersionString = possibleParent.CurrentVersion;
545
- NuGetVersion CurrentVersion = NuGetVersion.Parse(currentVersionString);
546
- string currentVersionStringDependency = possibleDependency.CurrentVersion;
547
- NuGetVersion currentVersionDependency = NuGetVersion.Parse(currentVersionStringDependency);
548
+ string? currentVersionString = possibleParent.CurrentVersion;
549
+ NuGetVersion CurrentVersion = NuGetVersion.Parse(currentVersionString!);
550
+ string? currentVersionStringDependency = possibleDependency.CurrentVersion;
551
+ NuGetVersion currentVersionDependency = NuGetVersion.Parse(currentVersionStringDependency!);
548
552
 
549
553
  // Create a NugetContext instance to get the latest versions of the parent
550
554
  NuGetContext nugetContext = new NuGetContext(Path.GetDirectoryName(projectPath));
@@ -559,7 +563,7 @@ public class PackageManager
559
563
  return null;
560
564
  }
561
565
 
562
- NuGetVersion latestVersion = versions
566
+ NuGetVersion? latestVersion = versions
563
567
  .Where(v => !v.IsPrerelease)
564
568
  .Max();
565
569
 
@@ -577,7 +581,7 @@ public class PackageManager
577
581
  }
578
582
 
579
583
  // Loop from the current version to the latest version, use next patch as a limit (unless there's a limit) so it doesn't look for versions that don't exist
580
- for (NuGetVersion? version = CurrentVersion; version is not null && version <= latestVersion; version = NextPatch(version, versions))
584
+ for (NuGetVersion? version = CurrentVersion; version is not null && latestVersion is not null && version <= latestVersion; version = NextPatch(version, versions))
581
585
  {
582
586
  possibleParent.NewVersion = version.ToString();
583
587
 
@@ -588,7 +592,7 @@ public class PackageManager
588
592
  if (await AreAllParentsCompatibleAsync(existingPackages, possibleParent, targetFramework, nugetContext.CurrentDirectory, logger))
589
593
  {
590
594
  // If compatible, return the new version
591
- if (Regex.IsMatch(possibleParent.NewVersion, @"[a-zA-Z]"))
595
+ if (Regex.IsMatch(possibleParent.NewVersion!, @"[a-zA-Z]"))
592
596
  {
593
597
  possibleParent.IsSpecific = true;
594
598
  }
@@ -616,7 +620,7 @@ public class PackageManager
616
620
  if (!isCompatible)
617
621
  {
618
622
  // Find a compatible version if possible
619
- NuGetVersion compatibleVersion = await FindCompatibleVersionAsync(existingPackages, parent, possibleParent, targetFramework, logger);
623
+ NuGetVersion? compatibleVersion = await FindCompatibleVersionAsync(existingPackages, parent, possibleParent, targetFramework, logger);
620
624
  if (compatibleVersion == null)
621
625
  {
622
626
  return false;
@@ -641,7 +645,7 @@ public class PackageManager
641
645
  {
642
646
  foreach (PackageToUpdate packageToUpdate in packagesToUpdate)
643
647
  {
644
- PackageToUpdate existingPackage = existingPackages.FirstOrDefault(p => string.Compare(p.PackageName, packageToUpdate.PackageName, StringComparison.OrdinalIgnoreCase) == 0);
648
+ PackageToUpdate? existingPackage = existingPackages.FirstOrDefault(p => string.Compare(p.PackageName, packageToUpdate.PackageName, StringComparison.OrdinalIgnoreCase) == 0);
645
649
 
646
650
  if (existingPackage != null)
647
651
  {