Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public AppInsightsProvider(
string sessionId)
{
_ = bool.TryParse(configuration[PlatformConfigurationConstants.PlatformTelemetryIsDevelopmentRepository], out _isDevelopmentRepository);
_isCi = CIEnvironmentDetectorForTelemetry.IsCIEnvironment();
_isCi = new CIEnvironmentDetector(environment).IsCIEnvironment();
_environment = environment;
_currentSessionId = sessionId;
_testApplicationCancellationTokenSource = testApplicationCancellationTokenSource;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

<ItemGroup>
<!-- Embedded helpers from Microsoft.Testing.Platform -->
<Compile Include="$(RepoRoot)src\Platform\Microsoft.Testing.Platform\Helpers\CIEnvironmentDetector.cs" Link="Helpers\CIEnvironmentDetector.cs" />
<Compile Include="$(RepoRoot)src\Platform\Microsoft.Testing.Platform\Helpers\RoslynString.cs" Link="Helpers\RoslynString.cs" />
<Compile Include="$(RepoRoot)src\Platform\Microsoft.Testing.Platform\Messages\SingleConsumerUnboundedChannel.cs" Link="Helpers\SingleConsumerUnboundedChannel.cs" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.Testing.Platform;
using Microsoft.CodeAnalysis;

namespace Microsoft.Testing.Extensions.Telemetry;
namespace Microsoft.Testing.Platform.Helpers;

// Detection of CI: https://learn.microsoft.com/dotnet/core/tools/telemetry#continuous-integration-detection
// From: https://github.com/dotnet/sdk/blob/main/src/Cli/dotnet/Telemetry/CIEnvironmentDetectorForTelemetry.cs
internal sealed class CIEnvironmentDetectorForTelemetry
// Based on: https://github.com/dotnet/sdk/blob/main/src/Cli/Microsoft.DotNet.Cli.Definitions/Telemetry/CIEnvironmentDetectorForTelemetry.cs
[Embedded]
Comment thread
Evangelink marked this conversation as resolved.
[ExcludeFromCodeCoverage]
internal sealed class CIEnvironmentDetector
{
// Systems that provide boolean values only, so we can simply parse and check for true
private static readonly string[] BooleanVariables =
Expand All @@ -22,14 +24,13 @@ internal sealed class CIEnvironmentDetectorForTelemetry
"APPVEYOR",

// A general-use flag - Many of the major players support this: AzDo, GitHub, GitLab, AppVeyor, Travis CI, CircleCI.
// Given this, we could potentially remove all of these other options?
"CI",

// Travis CI - https://docs.travis-ci.com/user/environment-variables/#default-environment-variables
"TRAVIS",

// CircleCI - https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables
"CIRCLECI"
"CIRCLECI",
];

// Systems where every variable must be present and not-null before returning true
Expand All @@ -52,30 +53,53 @@ internal sealed class CIEnvironmentDetectorForTelemetry
"TEAMCITY_VERSION",

// JetBrains Space - https://www.jetbrains.com/help/space/automation-environment-variables.html#general
"JB_SPACE_API_URL"
"JB_SPACE_API_URL",
];

public static bool IsCIEnvironment()
private readonly IEnvironment _environment;

/// <summary>
/// Initializes a new instance of the <see cref="CIEnvironmentDetector"/> class.
/// </summary>
/// <param name="environment">The environment abstraction to use for reading environment variables.</param>
public CIEnvironmentDetector(IEnvironment environment)
=> _environment = environment;

/// <summary>
/// Detects if the current environment is a CI environment.
/// </summary>
/// <returns><c>true</c> if running in a CI environment; otherwise, <c>false</c>.</returns>
public bool IsCIEnvironment()
{
foreach (string booleanVariable in BooleanVariables)
{
if (bool.TryParse(Environment.GetEnvironmentVariable(booleanVariable), out bool envVar) && envVar)
if (bool.TryParse(_environment.GetEnvironmentVariable(booleanVariable), out bool envVar) && envVar)
{
return true;
}
}

foreach (string[] variables in AllNotNullVariables)
{
if (variables.All(variable => !RoslynString.IsNullOrEmpty(Environment.GetEnvironmentVariable(variable))))
bool allVariablesPresent = true;
foreach (string variable in variables)
{
if (global::Microsoft.Testing.Platform.RoslynString.IsNullOrEmpty(_environment.GetEnvironmentVariable(variable)))
{
allVariablesPresent = false;
break;
}
}
Comment on lines +85 to +92

if (allVariablesPresent)
{
return true;
}
}

foreach (string variable in IfNonNullVariables)
{
if (!RoslynString.IsNullOrEmpty(Environment.GetEnvironmentVariable(variable)))
if (!global::Microsoft.Testing.Platform.RoslynString.IsNullOrEmpty(_environment.GetEnvironmentVariable(variable)))
{
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
namespace Microsoft.Testing.Platform.OutputDevice.Terminal;

/// <summary>
/// Simple terminal that uses 4-bit ANSI for colors but does not move cursor and does not do other fancy stuff to stay compatible with CI systems like AzDO.
/// The colors are set on start of every line to properly color multiline strings in AzDO output.
/// Simple terminal that uses 4-bit ANSI for colors but does not move cursor and does not do other fancy stuff to stay compatible with CI systems.
/// The colors are set on start of every line to properly color multiline strings in CI log viewers that reset color state at each newline (e.g. Azure DevOps).
/// </summary>
internal sealed class SimpleAnsiTerminal : SimpleTerminal
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ await _policiesService.RegisterOnAbortCallbackAsync(
_isServerMode = _commandLineOptions.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey);
bool noAnsi = _commandLineOptions.IsOptionSet(TerminalTestReporterCommandLineOptionsProvider.NoAnsiOption);

// TODO: Replace this with proper CI detection that we already have in telemetry. https://github.com/microsoft/testfx/issues/5533#issuecomment-2838893327
bool inCI = string.Equals(_environment.GetEnvironmentVariable("TF_BUILD"), "true", StringComparison.OrdinalIgnoreCase) || string.Equals(_environment.GetEnvironmentVariable("GITHUB_ACTIONS"), "true", StringComparison.OrdinalIgnoreCase);

@Youssef1313 Youssef1313 May 20, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure all other CI systems have support for simple ANSI? This can easily be a regression if other systems don't support that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on AI lookup there are few uncertainties but it seemed overall easier than just picking some and waiting.

bool inCI = new CIEnvironmentDetector(_environment).IsCIEnvironment();

AnsiMode ansiMode = AnsiMode.AnsiIfPossible;
// In LLM environments, prefer simple text output so that LLM can parse it easily.
Expand Down
Loading