diff --git a/azure-pipelines-pr.yml b/azure-pipelines-pr.yml
index 4f2a75102bf..76ad4297fea 100644
--- a/azure-pipelines-pr.yml
+++ b/azure-pipelines-pr.yml
@@ -124,6 +124,23 @@ stages:
/p:Test=false
displayName: Unix Build / Publish
+ - job: MacOS
+ pool:
+ vmImage: macOS-latest
+ strategy:
+ matrix:
+ Build_Debug:
+ _BuildConfig: Debug
+ preSteps:
+ - checkout: self
+ clean: true
+ steps:
+ - script: eng/common/cibuild.sh
+ --configuration $(_BuildConfig)
+ --prepareMachine
+ /p:Test=false
+ displayName: MacOS Build / Publish
+
- stage: Test
dependsOn: build
jobs:
@@ -208,6 +225,36 @@ stages:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
HelixAccessToken: ''
+ - job: MacOs
+ timeoutInMinutes: 90
+ pool:
+ vmimage: macOS-latest
+ strategy:
+ matrix:
+ Build_Debug:
+ _BuildConfig: Debug
+ variables:
+ - _Testing: Helix
+ preSteps:
+ - checkout: self
+ clean: true
+ steps:
+ - script: eng/common/build.sh
+ --configuration $(_BuildConfig)
+ --prepareMachine
+ --ci
+ --restore
+ --test
+ --warnAsError false
+ --projects $(System.DefaultWorkingDirectory)/tests/UnitTests.proj
+ /v:normal
+ /bl:$(System.DefaultWorkingDirectory)/artifacts/log/$(_BuildConfig)/Helix.binlog
+ /p:RestoreUsingNuGetTargets=false
+ displayName: Run Helix Tests
+ env:
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
+ HelixAccessToken: ''
+
- stage: Test_XHarness
displayName: Test XHarness SDK
dependsOn: build
diff --git a/src/Microsoft.DotNet.Helix/Sdk/AzureDevOpsTask.cs b/src/Microsoft.DotNet.Helix/Sdk/AzureDevOpsTask.cs
index a102acb64dc..612b7a83857 100644
--- a/src/Microsoft.DotNet.Helix/Sdk/AzureDevOpsTask.cs
+++ b/src/Microsoft.DotNet.Helix/Sdk/AzureDevOpsTask.cs
@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Http;
@@ -21,6 +23,11 @@ namespace Microsoft.DotNet.Helix.AzureDevOps
{
public abstract class AzureDevOpsTask : BaseTask
{
+ ///
+ /// Timeout in seconds for HTTP requests. Default is 180 seconds.
+ ///
+ public int Timeout { get; set; } = 180;
+
private bool InAzurePipeline => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("BUILD_BUILDNUMBER"));
protected string GetEnvironmentVariable(string name)
@@ -44,6 +51,57 @@ protected string GetEnvironmentVariable(string name)
protected abstract Task ExecuteCoreAsync(HttpClient client);
+ private ActivityListener CreateHttpActivityListener()
+ {
+ var listener = new ActivityListener
+ {
+ ShouldListenTo = source => source.Name == "System.Net.Http",
+ Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllDataAndRecorded,
+ ActivityStarted = activity =>
+ {
+ Log.LogMessage(MessageImportance.Low,
+ $"[System.Net.Http] {activity.OperationName} started - {activity.DisplayName}");
+
+ // Log tags which contain request details
+ foreach (var tag in activity.Tags)
+ {
+ Log.LogMessage(MessageImportance.Low, $" {tag.Key}: {tag.Value}");
+ }
+ },
+ ActivityStopped = activity =>
+ {
+ var status = activity.Status == ActivityStatusCode.Error ? "ERROR" : "OK";
+ Log.LogMessage(MessageImportance.Low,
+ $"[System.Net.Http] {activity.OperationName} stopped - Duration: {activity.Duration.TotalMilliseconds:F2}ms, Status: {status}");
+
+ if (activity.Status == ActivityStatusCode.Error && !string.IsNullOrEmpty(activity.StatusDescription))
+ {
+ Log.LogMessage(MessageImportance.Normal,
+ $"[System.Net.Http] Error: {activity.StatusDescription}");
+ }
+
+ // Log response tags (status code, etc.)
+ foreach (var tag in activity.Tags)
+ {
+ Log.LogMessage(MessageImportance.Low, $" {tag.Key}: {tag.Value}");
+ }
+
+ // Log events (connection established, request/response headers sent/received, etc.)
+ foreach (var evt in activity.Events)
+ {
+ var tagsStr = string.Join(", ", evt.Tags);
+ Log.LogMessage(MessageImportance.Low,
+ $" Event: {evt.Name} at {evt.Timestamp:O}" +
+ (string.IsNullOrEmpty(tagsStr) ? "" : $" ({tagsStr})"));
+ }
+ }
+ };
+
+ ActivitySource.AddActivityListener(listener);
+ Log.LogMessage(MessageImportance.Low, "System.Net.Http ActivityListener registered");
+ return listener;
+ }
+
public override bool Execute()
=> ExecuteAsync().GetAwaiter().GetResult();
@@ -57,6 +115,9 @@ private async Task ExecuteAsync()
}
else
{
+ // Set up HTTP activity listener for diagnostics - only active during this task execution
+ using var httpActivityListener = CreateHttpActivityListener();
+
// Configure the cert revocation check in a fail-open state to avoid intermittent failures
// on Mac if the endpoint is not available. This is only available on .NET Core, but has only been
// observed on Mac anyway.
@@ -96,6 +157,7 @@ private async Task ExecuteAsync()
})
#endif
{
+ Timeout = Timeout > 0 ? TimeSpan.FromSeconds(Timeout) : System.Threading.Timeout.InfiniteTimeSpan,
DefaultRequestHeaders =
{
Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes("unused:" + AccessToken))),
diff --git a/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs b/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs
index 671215d884a..b26333da139 100644
--- a/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs
+++ b/src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs
@@ -59,7 +59,11 @@ public class FindDotNetCliPackage : MSBuildTaskBase
public override void ConfigureServices(IServiceCollection collection)
{
- _httpMessageHandler = new HttpClientHandler { CheckCertificateRevocationList = true };
+ bool isOSX = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX);
+ _httpMessageHandler = new HttpClientHandler
+ {
+ CheckCertificateRevocationList = !isOSX // Disable on macOS
+ };
collection.TryAddSingleton(_httpMessageHandler);
collection.TryAddSingleton(Log);
}
@@ -245,7 +249,7 @@ await _retry.RunAsync(async attempt =>
}
catch (Exception toLog)
{
- Log.LogMessage(MessageImportance.Low, $"Hit exception in GetAsync(); will retry up to 10 times ({SanitizeString(toLog.Message)})");
+ Log.LogMessage(MessageImportance.Low, $"Hit exception in GetAsync(); will retry up to 10 times ({SanitizeString(toLog.ToString())})");
return false;
}
});
diff --git a/src/Microsoft.DotNet.Helix/Sdk/tools/azure-pipelines/AzurePipelines.MultiQueue.targets b/src/Microsoft.DotNet.Helix/Sdk/tools/azure-pipelines/AzurePipelines.MultiQueue.targets
index ea35a0158f3..27f6ac3a394 100644
--- a/src/Microsoft.DotNet.Helix/Sdk/tools/azure-pipelines/AzurePipelines.MultiQueue.targets
+++ b/src/Microsoft.DotNet.Helix/Sdk/tools/azure-pipelines/AzurePipelines.MultiQueue.targets
@@ -15,7 +15,7 @@
<_CurrentTestRunName Condition="'$(_CurrentTestRunName)' == ''">$(TestRunNamePrefix)$(_CurrentTargetQueue)$(TestRunNameSuffix)
-
+
@@ -29,13 +29,13 @@
-
+
-
+
diff --git a/src/Microsoft.DotNet.Helix/Sdk/tools/azure-pipelines/AzurePipelines.props b/src/Microsoft.DotNet.Helix/Sdk/tools/azure-pipelines/AzurePipelines.props
index e25086445d4..12375f18758 100644
--- a/src/Microsoft.DotNet.Helix/Sdk/tools/azure-pipelines/AzurePipelines.props
+++ b/src/Microsoft.DotNet.Helix/Sdk/tools/azure-pipelines/AzurePipelines.props
@@ -4,6 +4,7 @@
true
false
+ 180
@@ -13,7 +14,7 @@
- false
+ false
true
diff --git a/tests/UnitTests.proj b/tests/UnitTests.proj
index baaa1b68e4f..d9994c2c5d8 100644
--- a/tests/UnitTests.proj
+++ b/tests/UnitTests.proj
@@ -60,9 +60,7 @@
-
-
-
+
@@ -72,9 +70,7 @@
-
-
-
+