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 @@ - - - +