Skip to content
Open
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
38 changes: 30 additions & 8 deletions src/Aspire.Cli/Utils/CliHostEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ internal sealed class CliHostEnvironment : ICliHostEnvironment
public bool SupportsAnsi { get; }

public CliHostEnvironment(IConfiguration configuration, bool nonInteractive)
: this(configuration, nonInteractive, Console.IsInputRedirected, Console.IsOutputRedirected)
{
}

public CliHostEnvironment(IConfiguration configuration, bool nonInteractive, bool isInputRedirected, bool isOutputRedirected)
{
// If --non-interactive is explicitly set, disable interactive input and output
if (nonInteractive)
Expand All @@ -75,14 +80,14 @@ public CliHostEnvironment(IConfiguration configuration, bool nonInteractive)
}
else
{
SupportsInteractiveInput = DetectInteractiveInput(configuration);
SupportsInteractiveOutput = DetectInteractiveOutput(configuration);
SupportsInteractiveInput = DetectInteractiveInput(configuration, isInputRedirected);
SupportsInteractiveOutput = DetectInteractiveOutput(configuration, isOutputRedirected);
}

SupportsAnsi = DetectAnsiSupport(configuration);
SupportsAnsi = DetectAnsiSupport(configuration, isOutputRedirected);
}

private static bool DetectInteractiveInput(IConfiguration configuration)
private static bool DetectInteractiveInput(IConfiguration configuration, bool isInputRedirected)
{
// Check if explicitly disabled via configuration
var nonInteractive = configuration["ASPIRE_NON_INTERACTIVE"];
Expand All @@ -99,10 +104,16 @@ private static bool DetectInteractiveInput(IConfiguration configuration)
return false;
}

// Check if console input is redirected (e.g., piped from a file or another command)
if (isInputRedirected)
{
return false;
}

return true;
}

private static bool DetectInteractiveOutput(IConfiguration configuration)
private static bool DetectInteractiveOutput(IConfiguration configuration, bool isOutputRedirected)
{
// Check if explicitly disabled via configuration
var nonInteractive = configuration["ASPIRE_NON_INTERACTIVE"];
Expand All @@ -119,19 +130,30 @@ private static bool DetectInteractiveOutput(IConfiguration configuration)
return false;
}

// Check if console output is redirected (e.g., piped to a file or another command)
if (isOutputRedirected)
{
return false;
}

return true;
}

private static bool DetectAnsiSupport(IConfiguration configuration)
private static bool DetectAnsiSupport(IConfiguration configuration, bool isOutputRedirected)
{
// ANSI codes are supported even in CI environments for colored output
// Only disable if explicitly configured
// Check if explicitly disabled via NO_COLOR environment variable
var noColor = configuration["NO_COLOR"];
if (!string.IsNullOrEmpty(noColor))
{
return false;
}

// Disable ANSI colors when console output is redirected to avoid garbled output in files
if (isOutputRedirected)
{
return false;
}

return true;
}

Expand Down
5 changes: 3 additions & 2 deletions tests/Aspire.Cli.Tests/TestHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ internal static class TestHelpers
public static ICliHostEnvironment CreateInteractiveHostEnvironment()
{
var configuration = new ConfigurationBuilder().Build();
return new CliHostEnvironment(configuration, nonInteractive: false);
// For tests, explicitly set redirection to false to simulate an interactive environment
return new CliHostEnvironment(configuration, nonInteractive: false, isInputRedirected: false, isOutputRedirected: false);
}

public static ICliHostEnvironment CreateNonInteractiveHostEnvironment()
{
var configuration = new ConfigurationBuilder().Build();
return new CliHostEnvironment(configuration, nonInteractive: true);
return new CliHostEnvironment(configuration, nonInteractive: true, isInputRedirected: false, isOutputRedirected: false);
}
}
67 changes: 53 additions & 14 deletions tests/Aspire.Cli.Tests/Utils/CliHostEnvironmentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public void SupportsInteractiveInput_ReturnsTrue_WhenNoConfigSet()
var configuration = new ConfigurationBuilder().Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: false);
var env = new CliHostEnvironment(configuration, nonInteractive: false, isInputRedirected: false, isOutputRedirected: false);

// Assert
Assert.True(env.SupportsInteractiveInput);
Expand All @@ -28,7 +28,7 @@ public void SupportsInteractiveOutput_ReturnsTrue_WhenNoConfigSet()
var configuration = new ConfigurationBuilder().Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: false);
var env = new CliHostEnvironment(configuration, nonInteractive: false, isInputRedirected: false, isOutputRedirected: false);

// Assert
Assert.True(env.SupportsInteractiveOutput);
Expand All @@ -41,7 +41,7 @@ public void SupportsAnsi_ReturnsTrue_WhenNoConfigSet()
var configuration = new ConfigurationBuilder().Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: false);
var env = new CliHostEnvironment(configuration, nonInteractive: false, isInputRedirected: false, isOutputRedirected: false);

// Assert
Assert.True(env.SupportsAnsi);
Expand All @@ -61,7 +61,7 @@ public void SupportsInteractiveInput_ReturnsFalse_WhenNonInteractiveSet(string k
.Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: false);
var env = new CliHostEnvironment(configuration, nonInteractive: false, isInputRedirected: false, isOutputRedirected: false);

// Assert
Assert.False(env.SupportsInteractiveInput);
Expand All @@ -81,7 +81,7 @@ public void SupportsInteractiveOutput_ReturnsFalse_WhenNonInteractiveSet(string
.Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: false);
var env = new CliHostEnvironment(configuration, nonInteractive: false, isInputRedirected: false, isOutputRedirected: false);

// Assert
Assert.False(env.SupportsInteractiveOutput);
Expand All @@ -104,7 +104,7 @@ public void SupportsInteractiveInput_ReturnsFalse_InCIEnvironment(string envVar,
.Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: false);
var env = new CliHostEnvironment(configuration, nonInteractive: false, isInputRedirected: false, isOutputRedirected: false);

// Assert
Assert.False(env.SupportsInteractiveInput);
Expand All @@ -126,7 +126,7 @@ public void SupportsInteractiveOutput_ReturnsFalse_InCIEnvironment(string envVar
.Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: false);
var env = new CliHostEnvironment(configuration, nonInteractive: false, isInputRedirected: false, isOutputRedirected: false);

// Assert
Assert.False(env.SupportsInteractiveOutput);
Expand All @@ -138,7 +138,7 @@ public void SupportsInteractiveOutput_ReturnsFalse_InCIEnvironment(string envVar
[InlineData("GITHUB_ACTIONS", "true")]
public void SupportsAnsi_ReturnsTrue_InCIEnvironment(string envVar, string value)
{
// Arrange - ANSI should still be supported in CI for colored output
// Arrange - ANSI can still be supported in CI if output is not redirected
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
Expand All @@ -147,7 +147,7 @@ public void SupportsAnsi_ReturnsTrue_InCIEnvironment(string envVar, string value
.Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: false);
var env = new CliHostEnvironment(configuration, nonInteractive: false, isInputRedirected: false, isOutputRedirected: false);

// Assert
Assert.True(env.SupportsAnsi);
Expand All @@ -165,7 +165,7 @@ public void SupportsAnsi_ReturnsFalse_WhenNO_COLORSet()
.Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: false);
var env = new CliHostEnvironment(configuration, nonInteractive: false, isInputRedirected: false, isOutputRedirected: false);

// Assert
Assert.False(env.SupportsAnsi);
Expand All @@ -178,7 +178,7 @@ public void SupportsInteractiveInput_ReturnsFalse_WhenNonInteractiveTrue()
var configuration = new ConfigurationBuilder().Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: true);
var env = new CliHostEnvironment(configuration, nonInteractive: true, isInputRedirected: false, isOutputRedirected: false);

// Assert
Assert.False(env.SupportsInteractiveInput);
Expand All @@ -191,7 +191,7 @@ public void SupportsInteractiveOutput_ReturnsFalse_WhenNonInteractiveTrue()
var configuration = new ConfigurationBuilder().Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: true);
var env = new CliHostEnvironment(configuration, nonInteractive: true, isInputRedirected: false, isOutputRedirected: false);

// Assert
Assert.False(env.SupportsInteractiveOutput);
Expand All @@ -200,13 +200,52 @@ public void SupportsInteractiveOutput_ReturnsFalse_WhenNonInteractiveTrue()
[Fact]
public void SupportsAnsi_ReturnsTrue_WhenNonInteractiveTrue()
{
// Arrange - ANSI should still be supported even in non-interactive mode
// Arrange - ANSI can still be supported even in non-interactive mode
var configuration = new ConfigurationBuilder().Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: true);
var env = new CliHostEnvironment(configuration, nonInteractive: true, isInputRedirected: false, isOutputRedirected: false);

// Assert
Assert.True(env.SupportsAnsi);
}

[Fact]
public void SupportsInteractiveInput_ReturnsFalse_WhenInputRedirected()
{
// Arrange
var configuration = new ConfigurationBuilder().Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: false, isInputRedirected: true, isOutputRedirected: false);

// Assert
Assert.False(env.SupportsInteractiveInput);
}

[Fact]
public void SupportsInteractiveOutput_ReturnsFalse_WhenOutputRedirected()
{
// Arrange
var configuration = new ConfigurationBuilder().Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: false, isInputRedirected: false, isOutputRedirected: true);

// Assert
Assert.False(env.SupportsInteractiveOutput);
}

[Fact]
public void SupportsAnsi_ReturnsFalse_WhenOutputRedirected()
{
// Arrange
var configuration = new ConfigurationBuilder().Build();

// Act
var env = new CliHostEnvironment(configuration, nonInteractive: false, isInputRedirected: false, isOutputRedirected: true);

// Assert
Assert.False(env.SupportsAnsi);
}
}