dependabot-nuget 0.394.0 → 0.396.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f87265e6a1903fa2d9a3ebf527ce5a2011050a3166b4dc38e0e5ed973047ed3
4
- data.tar.gz: a34d83e590e6a578617e53ac28407412492c01c62c198f910929246c3af3b405
3
+ metadata.gz: 3c8d2c369fea3791d04cedce39111d000bc6d0052f48a6df16116b9c811b1f4c
4
+ data.tar.gz: '08c4cec0fef16173ca1d28eb497127064b4ecc5daeb2a2d84f3ab8ee72cc6852'
5
5
  SHA512:
6
- metadata.gz: d3d30fb0420935c01ec7fa4d8d08538994cd51bc4c4e4875b717f85ede40bcbec970b0e127bc9d830c6bdc9de8ef3048340d49bc36efcd307c4d2c57a5e82e4c
7
- data.tar.gz: 54406d1b180a7da9baf7fd61f9aba249d8eae142eda7404f96c6f9c364184f600d49b3ea61daa756f86f7ad005026560623522003712801a149a176c8863ec4b
6
+ metadata.gz: 220207aba4f007a902a08030847f865afad86e57de5dfcbfe81d9a6ef5bdaa5284ace3c4259f122778a69ad6401e42645858dd09516edf75055b14c48f3c4f59
7
+ data.tar.gz: c309c50fd55d24516bca819ff47c5b3981b867fe7d15a5990505c2ad0e527e999d0aa988fcb81c359e98291eb8da983edae2a7d803e7677530bf0dfef8fc85da
@@ -27,20 +27,20 @@
27
27
  <PackageVersion Include="MSBuild.StructuredLogger" Version="2.3.246" />
28
28
  <PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
29
29
  <PackageVersion Include="OpenTelemetry" Version="1.18.0" />
30
- <PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.15.3" />
30
+ <PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.18.0" />
31
31
  <PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.18.0" />
32
32
  <PackageVersion Include="packageurl-dotnet" Version="2.0.1" />
33
33
  <PackageVersion Include="Semver" Version="3.0.0" />
34
- <PackageVersion Include="System.CommandLine" Version="2.0.3" />
35
- <PackageVersion Include="System.ComponentModel.Composition" Version="10.0.3" />
34
+ <PackageVersion Include="System.CommandLine" Version="2.0.11" />
35
+ <PackageVersion Include="System.ComponentModel.Composition" Version="10.0.11" />
36
36
  <PackageVersion Include="System.Net.Http" Version="4.3.4" />
37
37
  <PackageVersion Include="System.Formats.Asn1" Version="10.0.3" />
38
- <PackageVersion Include="System.Security.Cryptography.Pkcs" Version="10.0.10" />
39
- <PackageVersion Include="System.Security.Cryptography.ProtectedData" Version="10.0.8" />
40
- <PackageVersion Include="System.Text.Json" Version="10.0.3" />
38
+ <PackageVersion Include="System.Security.Cryptography.Pkcs" Version="10.0.11" />
39
+ <PackageVersion Include="System.Security.Cryptography.ProtectedData" Version="10.0.11" />
40
+ <PackageVersion Include="System.Text.Json" Version="10.0.11" />
41
41
  <PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
42
- <PackageVersion Include="System.Threading.Tasks.Dataflow" Version="10.0.3" />
42
+ <PackageVersion Include="System.Threading.Tasks.Dataflow" Version="10.0.11" />
43
43
  <PackageVersion Include="xunit.v3" Version="3.2.2" />
44
- <PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
44
+ <PackageVersion Include="xunit.runner.visualstudio" Version="4.0.0" />
45
45
  </ItemGroup>
46
46
  </Project>
@@ -16,6 +16,7 @@
16
16
  </ItemGroup>
17
17
 
18
18
  <ItemGroup>
19
+ <PackageReference Include="System.Security.Cryptography.Pkcs" />
19
20
  <EmbeddedResource Include="$(NuGetSourceLocation)\src\NuGet.Core\NuGet.Protocol\**\*.resx" />
20
21
  </ItemGroup>
21
22
 
@@ -65,6 +65,8 @@ internal static class SdkProjectDiscovery
65
65
  // these targets are required to evaluate a legacy project with a single operation
66
66
  private static readonly ImmutableArray<string> LegacyProjectSingleRestoreTargetNames = ["ResolveProjectReferences"];
67
67
 
68
+ private const string TargetFrameworksPropertyName = "TargetFrameworks";
69
+
68
70
  // this property evaluates to a version number in an SDK-style project and is unset or empty otherwise
69
71
  private const string NETCoreSdkVersionPropertyName = "NETCoreSdkVersion";
70
72
 
@@ -119,16 +121,30 @@ internal static class SdkProjectDiscovery
119
121
  Dictionary<string, HashSet<string>> additionalFiles = new(PathComparer.Instance);
120
122
  // projectPath additionalFiles
121
123
 
124
+ var startingProjectProperties = await MSBuildHelper.GetProjectPropertiesAsync(
125
+ startingProjectPath,
126
+ [TargetFrameworksPropertyName, NETCoreSdkVersionPropertyName],
127
+ logger
128
+ );
129
+ var projectTfms = MSBuildHelper.ParseProjectTargetFrameworks(startingProjectProperties?.GetValueOrDefault(TargetFrameworksPropertyName));
130
+ var netCoreSdkVersionValue = startingProjectProperties?.GetValueOrDefault(NETCoreSdkVersionPropertyName);
131
+ var isLegacyProject = string.IsNullOrEmpty(netCoreSdkVersionValue);
132
+ var requiredTargets = isLegacyProject
133
+ ? LegacyProjectSingleRestoreTargetNames
134
+ : SingleRestoreTargetNames;
135
+
122
136
  // due to how MSBuild handles multi-TFM projects with target platforms we may need to process each TFM separately
123
137
  // we detect that by determining if there are multiple target frameworks specified and if any of them have a platform suffix (e.g., `-windows`, `-android`, etc)
124
138
  // alternately, if there are too many target frameworks specified, they must be handled individually
125
- var projectTfms = await MSBuildHelper.GetProjectTargetFrameworksAsync(startingProjectPath, logger);
126
139
  var hasPlatformTfms = projectTfms.Any(tfm => tfm.Contains('-'));
127
140
  var requiresIndividualRestores = hasPlatformTfms || projectTfms.Length > MaximumParallelTargetFrameworkRestores;
141
+ var useDirectRestore = requiresIndividualRestores;
128
142
  if (!requiresIndividualRestores)
129
143
  {
130
144
  logger.Info($"Performing single restore for project {startingProjectPath}");
131
145
  projectTfms = [string.Empty]; // a single restore can handle everything, but we need to loop at least once and an empty TFM is our signal to not specify anything
146
+ var actualTargets = await MSBuildHelper.GetProjectTargetsAsync(startingProjectPath, logger);
147
+ useDirectRestore = requiredTargets.All(actualTargets.Contains);
132
148
  }
133
149
  else
134
150
  {
@@ -158,15 +174,7 @@ internal static class SdkProjectDiscovery
158
174
  // `ResolveProjectReferences` to gather all `PackageReference` items and further down we re-build the transitive
159
175
  // dependency set. Without the legacy project check, we could incorrectly invoke `Build` which eventually tries
160
176
  // 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);
169
- if (useDirectRestore || isIndividualTfmRestore)
177
+ if (useDirectRestore)
170
178
  {
171
179
  // directly call the required targets
172
180
  args.Add($"/t:{string.Join(",", requiredTargets)}");
@@ -4,7 +4,7 @@ using System.Text.Json.Serialization;
4
4
 
5
5
  namespace NuGetUpdater.Core.Run;
6
6
 
7
- public class HttpApiHandler : IApiHandler
7
+ public class HttpApiHandler : IApiHandler, IApiRetryDelayProvider
8
8
  {
9
9
  private static readonly HttpClient HttpClient = new();
10
10
 
@@ -16,13 +16,22 @@ public class HttpApiHandler : IApiHandler
16
16
 
17
17
  private readonly string _apiUrl;
18
18
  private readonly string _jobId;
19
+ private readonly Func<TimeSpan, Task> _retryDelay;
19
20
 
20
21
  public HttpApiHandler(string apiUrl, string jobId)
22
+ : this(apiUrl, jobId, Task.Delay)
23
+ {
24
+ }
25
+
26
+ internal HttpApiHandler(string apiUrl, string jobId, Func<TimeSpan, Task> retryDelay)
21
27
  {
22
28
  _apiUrl = apiUrl.TrimEnd('/');
23
29
  _jobId = jobId;
30
+ _retryDelay = retryDelay;
24
31
  }
25
32
 
33
+ Task IApiRetryDelayProvider.DelayAsync(TimeSpan delay) => _retryDelay(delay);
34
+
26
35
  public async Task SendAsync(string endpoint, object body, string method)
27
36
  {
28
37
  var uri = $"{_apiUrl}/update_jobs/{_jobId}/{endpoint}";
@@ -7,6 +7,11 @@ public interface IApiHandler
7
7
  Task SendAsync(string endpoint, object body, string method);
8
8
  }
9
9
 
10
+ internal interface IApiRetryDelayProvider
11
+ {
12
+ Task DelayAsync(TimeSpan delay);
13
+ }
14
+
10
15
  public static class IApiHandlerExtensions
11
16
  {
12
17
  public static async Task RecordUpdateJobError(this IApiHandler handler, JobErrorBase error, ILogger logger)
@@ -70,7 +75,15 @@ public static class IApiHandlerExtensions
70
75
  (ex.StatusCode is null || ((int)ex.StatusCode) / 100 == 5))
71
76
  {
72
77
  retryCount++;
73
- await Task.Delay(TimeSpan.FromSeconds(Random.Shared.Next(MinRetryDelay, MaxRetryDelay)));
78
+ var delay = TimeSpan.FromSeconds(Random.Shared.Next(MinRetryDelay, MaxRetryDelay));
79
+ if (handler is IApiRetryDelayProvider delayProvider)
80
+ {
81
+ await delayProvider.DelayAsync(delay);
82
+ }
83
+ else
84
+ {
85
+ await Task.Delay(delay);
86
+ }
74
87
  }
75
88
  }
76
89
  }
@@ -1,6 +1,7 @@
1
1
  using System.Collections.Immutable;
2
2
  using System.Diagnostics.CodeAnalysis;
3
3
  using System.Reflection;
4
+ using System.Text.Json;
4
5
  using System.Text.Json.Nodes;
5
6
  using System.Text.RegularExpressions;
6
7
  using System.Xml.Linq;
@@ -563,7 +564,11 @@ internal static partial class MSBuildHelper
563
564
  return targets;
564
565
  }
565
566
 
566
- internal static async Task<string?> GetProjectPropertyAsync(string projectPath, string propertyName, ILogger logger)
567
+ internal static async Task<ImmutableDictionary<string, string>?> GetProjectPropertiesAsync(
568
+ string projectPath,
569
+ IReadOnlyCollection<string> propertyNames,
570
+ ILogger logger
571
+ )
567
572
  {
568
573
  var extension = Path.GetExtension(projectPath)?.ToLowerInvariant();
569
574
  if (extension == ".sln" || extension == ".slnx")
@@ -572,51 +577,128 @@ internal static partial class MSBuildHelper
572
577
  return null;
573
578
  }
574
579
 
575
- var projectDirectory = Path.GetDirectoryName(projectPath)!;
576
- var args = new[]
580
+ var requestedPropertyNames = propertyNames
581
+ .Distinct(StringComparer.OrdinalIgnoreCase)
582
+ .ToImmutableArray();
583
+ if (requestedPropertyNames.Length == 0)
577
584
  {
578
- projectPath,
579
- $"-getProperty:{propertyName}"
580
- };
585
+ return ImmutableDictionary.Create<string, string>(StringComparer.OrdinalIgnoreCase);
586
+ }
581
587
 
582
- var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetMSBuildSafelyAsync(args, projectDirectory);
583
- if (exitCode != 0)
588
+ if (requestedPropertyNames.Length == 1)
589
+ {
590
+ var propertyName = requestedPropertyNames[0];
591
+ var propertyValue = await GetProjectPropertyAsync(projectPath, propertyName, logger);
592
+ return propertyValue is null
593
+ ? null
594
+ : ImmutableDictionary.Create<string, string>(StringComparer.OrdinalIgnoreCase).Add(propertyName, propertyValue);
595
+ }
596
+
597
+ var projectDirectory = Path.GetDirectoryName(projectPath)!;
598
+ var tempDir = Directory.CreateTempSubdirectory("__get_msbuild_properties_");
599
+ try
584
600
  {
585
- if (stdOut.Contains("error MSB1001: Unknown switch."))
601
+ var resultOutputPath = Path.Combine(tempDir.FullName, "result.json");
602
+ var args = new[]
586
603
  {
587
- // older versions of MSBuild don't allow `-getProperty` so we go a different route
588
- // we can't force an indirect property evaluation, but we can force import a custom targets file that effectively renames the property
589
- var tempDir = Directory.CreateTempSubdirectory("__get_msbuild_property_");
590
- try
604
+ projectPath,
605
+ $"-getProperty:{string.Join(",", requestedPropertyNames)}",
606
+ $"-getResultOutputFile:{resultOutputPath}",
607
+ };
608
+
609
+ var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetMSBuildSafelyAsync(args, projectDirectory);
610
+ if (exitCode != 0)
611
+ {
612
+ if (IsGetPropertyUnsupported(stdOut, stdErr))
591
613
  {
592
- // prepare magic contents
593
- var targetsTemplateContents = await File.ReadAllTextAsync(GetFileFromRuntimeDirectory("GetProperty.targets"));
594
- var targetContents = targetsTemplateContents.Replace("%RequestedPropertyName%", $"$({propertyName})");
595
-
596
- // write magic contents
597
- var tempTargetsPath = Path.Combine(tempDir.FullName, $"GetProperty_{propertyName}.targets");
598
- await File.WriteAllTextAsync(tempTargetsPath, targetContents);
599
-
600
- // do it
601
- args = [
602
- projectPath,
603
- $"/p:CustomAfterMicrosoftCommonTargets={tempTargetsPath}",
604
- "/t:_Dependabot_GetProperty",
605
- ];
606
- (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetMSBuildSafelyAsync(args, projectDirectory);
607
- if (exitCode == 0)
614
+ var fallbackProperties = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.OrdinalIgnoreCase);
615
+ foreach (var propertyName in requestedPropertyNames)
608
616
  {
609
- var match = Regex.Match(stdOut, "__PROPERTY_VALUE:(?<PropertyValue>[^$]*)$", RegexOptions.Multiline);
610
- if (match.Success)
617
+ var propertyValue = await GetProjectPropertyUsingCompatibilityTargetAsync(projectPath, propertyName, logger);
618
+ if (propertyValue is null)
611
619
  {
612
- return match.Groups["PropertyValue"].Value.Trim();
620
+ return null;
613
621
  }
622
+
623
+ fallbackProperties[propertyName] = propertyValue;
614
624
  }
625
+
626
+ return fallbackProperties.ToImmutable();
615
627
  }
616
- finally
628
+
629
+ logger.Warn($"Unable to determine properties '{string.Join("', '", requestedPropertyNames)}' for project [{projectPath}]:\nSTDOUT:\n{stdOut}\nSTDERR:\n{stdErr}\n");
630
+ return null;
631
+ }
632
+
633
+ if (!File.Exists(resultOutputPath))
634
+ {
635
+ logger.Warn($"Unable to determine properties '{string.Join("', '", requestedPropertyNames)}' for project [{projectPath}]: MSBuild did not produce a result output file.\nSTDOUT:\n{stdOut}\nSTDERR:\n{stdErr}\n");
636
+ return null;
637
+ }
638
+
639
+ var resultOutput = await File.ReadAllTextAsync(resultOutputPath);
640
+ try
641
+ {
642
+ using var resultDocument = JsonDocument.Parse(resultOutput);
643
+ var root = resultDocument.RootElement;
644
+ if (root.ValueKind != JsonValueKind.Object ||
645
+ !root.TryGetProperty("Properties", out var propertiesElement) ||
646
+ propertiesElement.ValueKind != JsonValueKind.Object)
617
647
  {
618
- tempDir.Delete(recursive: true);
648
+ logger.Warn($"Unable to determine properties '{string.Join("', '", requestedPropertyNames)}' for project [{projectPath}]: MSBuild result output did not contain a properties object.");
649
+ return null;
619
650
  }
651
+
652
+ var properties = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.OrdinalIgnoreCase);
653
+ foreach (var propertyName in requestedPropertyNames)
654
+ {
655
+ if (!propertiesElement.TryGetProperty(propertyName, out var propertyElement) ||
656
+ propertyElement.ValueKind != JsonValueKind.String ||
657
+ propertyElement.GetString() is not string propertyValue)
658
+ {
659
+ logger.Warn($"Unable to determine property '{propertyName}' for project [{projectPath}]: MSBuild result output did not contain a string value for the property.");
660
+ return null;
661
+ }
662
+
663
+ properties[propertyName] = propertyValue;
664
+ }
665
+
666
+ return properties.ToImmutable();
667
+ }
668
+ catch (JsonException ex)
669
+ {
670
+ logger.Warn($"Unable to determine properties '{string.Join("', '", requestedPropertyNames)}' for project [{projectPath}]: MSBuild produced invalid JSON result output: {ex.Message}");
671
+ return null;
672
+ }
673
+ }
674
+ finally
675
+ {
676
+ tempDir.Delete(recursive: true);
677
+ }
678
+ }
679
+
680
+ internal static async Task<string?> GetProjectPropertyAsync(string projectPath, string propertyName, ILogger logger)
681
+ {
682
+ var extension = Path.GetExtension(projectPath)?.ToLowerInvariant();
683
+ if (extension == ".sln" || extension == ".slnx")
684
+ {
685
+ // solution files don't specify properties, so we can skip the process invocation
686
+ return null;
687
+ }
688
+
689
+ var projectDirectory = Path.GetDirectoryName(projectPath)!;
690
+ var args = new[]
691
+ {
692
+ projectPath,
693
+ $"-getProperty:{propertyName}"
694
+ };
695
+
696
+ var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetMSBuildSafelyAsync(args, projectDirectory);
697
+ if (exitCode != 0)
698
+ {
699
+ if (IsGetPropertyUnsupported(stdOut, stdErr))
700
+ {
701
+ return await GetProjectPropertyUsingCompatibilityTargetAsync(projectPath, propertyName, logger);
620
702
  }
621
703
 
622
704
  logger.Warn($"Unable to determine property '{propertyName}' for project [{projectPath}]:\nSTDOUT:\n{stdOut}\nSTDERR:\n{stdErr}\n");
@@ -629,6 +711,11 @@ internal static partial class MSBuildHelper
629
711
  internal static async Task<ImmutableArray<string>> GetProjectTargetFrameworksAsync(string projectPath, ILogger logger)
630
712
  {
631
713
  var rawValue = await GetProjectPropertyAsync(projectPath, "TargetFrameworks", logger);
714
+ return ParseProjectTargetFrameworks(rawValue);
715
+ }
716
+
717
+ internal static ImmutableArray<string> ParseProjectTargetFrameworks(string? rawValue)
718
+ {
632
719
  if (rawValue is null)
633
720
  {
634
721
  return [];
@@ -641,6 +728,49 @@ internal static partial class MSBuildHelper
641
728
  return tfms;
642
729
  }
643
730
 
731
+ private static bool IsGetPropertyUnsupported(string stdOut, string stdErr)
732
+ {
733
+ return stdOut.Contains("error MSB1001: Unknown switch.", StringComparison.Ordinal) ||
734
+ stdErr.Contains("error MSB1001: Unknown switch.", StringComparison.Ordinal);
735
+ }
736
+
737
+ private static async Task<string?> GetProjectPropertyUsingCompatibilityTargetAsync(string projectPath, string propertyName, ILogger logger)
738
+ {
739
+ // Older versions of MSBuild don't allow `-getProperty`, so import a custom target that reports the property.
740
+ var projectDirectory = Path.GetDirectoryName(projectPath)!;
741
+ var tempDir = Directory.CreateTempSubdirectory("__get_msbuild_property_");
742
+ try
743
+ {
744
+ var targetsTemplateContents = await File.ReadAllTextAsync(GetFileFromRuntimeDirectory("GetProperty.targets"));
745
+ var targetContents = targetsTemplateContents.Replace("%RequestedPropertyName%", $"$({propertyName})");
746
+ var tempTargetsPath = Path.Combine(tempDir.FullName, $"GetProperty_{propertyName}.targets");
747
+ await File.WriteAllTextAsync(tempTargetsPath, targetContents);
748
+
749
+ var args = new[]
750
+ {
751
+ projectPath,
752
+ $"/p:CustomAfterMicrosoftCommonTargets={tempTargetsPath}",
753
+ "/t:_Dependabot_GetProperty",
754
+ };
755
+ var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetMSBuildSafelyAsync(args, projectDirectory);
756
+ if (exitCode == 0)
757
+ {
758
+ var match = Regex.Match(stdOut, "__PROPERTY_VALUE:(?<PropertyValue>[^$]*)$", RegexOptions.Multiline);
759
+ if (match.Success)
760
+ {
761
+ return match.Groups["PropertyValue"].Value.Trim();
762
+ }
763
+ }
764
+
765
+ logger.Warn($"Unable to determine property '{propertyName}' for project [{projectPath}]:\nSTDOUT:\n{stdOut}\nSTDERR:\n{stdErr}\n");
766
+ return null;
767
+ }
768
+ finally
769
+ {
770
+ tempDir.Delete(recursive: true);
771
+ }
772
+ }
773
+
644
774
  internal static string? GetMissingFile(string output)
645
775
  {
646
776
  var missingFilePatterns = new[]
@@ -332,6 +332,7 @@ public class SdkProjectDiscoveryTests : DiscoveryWorkerTestBase
332
332
  [Theory]
333
333
  [InlineData("net7.0")] // under the safe limit for parallel processing
334
334
  [InlineData("net7.0", "net8.0")] // at the safe limit for parallel processing
335
+ [InlineData("net8.0", "net9.0-windows")] // platform-specific TFMs are processed individually
335
336
  [InlineData("net7.0", "net8.0", "net9.0")] // above the safe limit for parallel processing
336
337
  [InlineData("netstandard2.0", "netstandard2.1", "net6.0", "net7.0", "net8.0", "net9.0", "net10.0")] // well above the safe limit for parallel processing
337
338
  public async Task DiscoverWithMultipleTargetFrameworks(params string[] targetFrameworks)
@@ -798,4 +799,3 @@ public class SdkProjectDiscoveryTests : DiscoveryWorkerTestBase
798
799
  ValidateProjectResults(expectedProjects, projectDiscovery);
799
800
  }
800
801
  }
801
-
@@ -62,21 +62,24 @@ public class HttpApiHandlerTests
62
62
  var requestCount = 0;
63
63
  using var http = TestHttpServer.CreateTestServer((method, url) =>
64
64
  {
65
- if (requestCount < 2)
65
+ requestCount++;
66
+ if (requestCount <= 2)
66
67
  {
67
- requestCount++;
68
68
  return (500, Array.Empty<byte>());
69
69
  }
70
70
 
71
71
  return (200, Array.Empty<byte>());
72
72
  });
73
- var handler = new HttpApiHandler(http.BaseUrl, "TEST-ID");
73
+ var handler = new HttpApiHandler(http.BaseUrl, "TEST-ID", static _ => Task.CompletedTask);
74
74
 
75
75
  // act
76
76
  await handler.IncrementMetric(new()
77
77
  {
78
78
  Metric = "test",
79
79
  });
80
+
81
+ // assert
82
+ Assert.Equal(3, requestCount);
80
83
  }
81
84
 
82
85
  [Fact]
@@ -93,7 +96,29 @@ public class HttpApiHandlerTests
93
96
 
94
97
  // act
95
98
  await Assert.ThrowsAsync<HttpRequestException>(() => handler.IncrementMetric(new() { Metric = "test" }));
96
- Assert.True(requestCount == 1, $"Expected only 1 request, but received {requestCount}.");
99
+
100
+ // assert
101
+ Assert.Equal(1, requestCount);
102
+ }
103
+
104
+ [Fact]
105
+ public async Task ApiCallsStopRetryingAfterTheMaximumNumberOfAttempts()
106
+ {
107
+ // arrange
108
+ var requestCount = 0;
109
+ using var http = TestHttpServer.CreateTestStringServer((method, url) =>
110
+ {
111
+ requestCount++;
112
+ return (500, $"attempt-{requestCount}");
113
+ });
114
+ var handler = new HttpApiHandler(http.BaseUrl, "TEST-ID", static _ => Task.CompletedTask);
115
+
116
+ // act
117
+ var exception = await Assert.ThrowsAsync<HttpRequestException>(() => handler.IncrementMetric(new() { Metric = "test" }));
118
+
119
+ // assert
120
+ Assert.Equal("500 (InternalServerError): attempt-4", exception.Message);
121
+ Assert.Equal(4, requestCount);
97
122
  }
98
123
 
99
124
  [Theory]
@@ -284,6 +284,79 @@ public class MSBuildHelperTests : TestBase
284
284
  AssertEx.Equal(expectedDependencies, actualDependencies);
285
285
  }
286
286
 
287
+ [Fact]
288
+ public async Task MultipleProjectPropertiesCanBeEvaluated()
289
+ {
290
+ using var tempDir = await TemporaryDirectory.CreateWithContentsAsync(
291
+ [
292
+ ("project.csproj", """
293
+ <Project Sdk="Microsoft.NET.Sdk">
294
+ <PropertyGroup>
295
+ <TargetFrameworks>net8.0;net9.0</TargetFrameworks>
296
+ </PropertyGroup>
297
+ </Project>
298
+ """)
299
+ ]);
300
+ var projectPath = Path.Combine(tempDir.DirectoryPath, "project.csproj");
301
+ var properties = await MSBuildHelper.GetProjectPropertiesAsync(
302
+ projectPath,
303
+ ["TargetFrameworks", "NETCoreSdkVersion"],
304
+ new TestLogger()
305
+ );
306
+
307
+ Assert.NotNull(properties);
308
+ Assert.Equal("net8.0;net9.0", properties["TargetFrameworks"]);
309
+ Assert.False(string.IsNullOrEmpty(properties["NETCoreSdkVersion"]));
310
+ }
311
+
312
+ [Theory]
313
+ [InlineData("TargetFrameworks")]
314
+ [InlineData("TargetFrameworks", "targetframeworks")]
315
+ public async Task SingleDistinctProjectPropertyCanBeEvaluated(params string[] propertyNames)
316
+ {
317
+ using var tempDir = await TemporaryDirectory.CreateWithContentsAsync(
318
+ [
319
+ ("project.csproj", """
320
+ <Project Sdk="Microsoft.NET.Sdk">
321
+ <PropertyGroup>
322
+ <TargetFrameworks>net8.0;net9.0</TargetFrameworks>
323
+ </PropertyGroup>
324
+ </Project>
325
+ """)
326
+ ]);
327
+ var projectPath = Path.Combine(tempDir.DirectoryPath, "project.csproj");
328
+ var properties = await MSBuildHelper.GetProjectPropertiesAsync(
329
+ projectPath,
330
+ propertyNames,
331
+ new TestLogger()
332
+ );
333
+
334
+ Assert.NotNull(properties);
335
+ Assert.Single(properties);
336
+ Assert.Equal("net8.0;net9.0", properties["TARGETFRAMEWORKS"]);
337
+ }
338
+
339
+ [Theory]
340
+ [InlineData(".sln")]
341
+ [InlineData(".slnx")]
342
+ public async Task MultipleProjectPropertiesAreNotEvaluatedForSolutions(string extension)
343
+ {
344
+ using var tempDir = await TemporaryDirectory.CreateWithContentsAsync(
345
+ [
346
+ ($"solution{extension}", string.Empty)
347
+ ]);
348
+ var solutionPath = Path.Combine(tempDir.DirectoryPath, $"solution{extension}");
349
+ var logger = new StringLogger();
350
+ var properties = await MSBuildHelper.GetProjectPropertiesAsync(
351
+ solutionPath,
352
+ ["TargetFrameworks", "NETCoreSdkVersion"],
353
+ logger
354
+ );
355
+
356
+ Assert.Null(properties);
357
+ Assert.Empty(logger.Messages);
358
+ }
359
+
287
360
  [Theory]
288
361
  [MemberData(nameof(GetTargetFrameworkValuesFromProjectData))]
289
362
  public async Task GetTargetFrameworkValuesFromProject(string projectContents, string[] expectedTfms)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-nuget
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.394.0
4
+ version: 0.396.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.394.0
18
+ version: 0.396.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.394.0
25
+ version: 0.396.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: debug
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -178,19 +178,19 @@ dependencies:
178
178
  - !ruby/object:Gem::Version
179
179
  version: '1.1'
180
180
  - !ruby/object:Gem::Dependency
181
- name: turbo_tests
181
+ name: turbo_tests2
182
182
  requirement: !ruby/object:Gem::Requirement
183
183
  requirements:
184
184
  - - "~>"
185
185
  - !ruby/object:Gem::Version
186
- version: 2.2.5
186
+ version: 3.2.7
187
187
  type: :development
188
188
  prerelease: false
189
189
  version_requirements: !ruby/object:Gem::Requirement
190
190
  requirements:
191
191
  - - "~>"
192
192
  - !ruby/object:Gem::Version
193
- version: 2.2.5
193
+ version: 3.2.7
194
194
  - !ruby/object:Gem::Dependency
195
195
  name: vcr
196
196
  requirement: !ruby/object:Gem::Requirement
@@ -587,7 +587,7 @@ licenses:
587
587
  - MIT
588
588
  metadata:
589
589
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
590
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.394.0
590
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.396.0
591
591
  rdoc_options: []
592
592
  require_paths:
593
593
  - lib