From 3059f735da073504dd88c23a551b41e42bfe3634 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 28 Sep 2026 15:34:59 +1000 Subject: [PATCH] Remove terminal commands feature flag (#20525) Backport to release/13.6. Preserve the absence of completion test fixtures, since completion functionality is not present on this release branch. (cherry picked from commit dcea2907eef56f0bb9d35fa462ab2ad115951d09) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d7aebca1-b78b-4169-9f93-4f74aecdd02c --- docs/specs/with-terminal.md | 5 ++--- extension/schemas/aspire-config.schema.json | 16 ---------------- .../schemas/aspire-global-settings.schema.json | 16 ---------------- extension/schemas/aspire-settings.schema.json | 16 ---------------- src/Aspire.Cli/Commands/RootCommand.cs | 9 +-------- src/Aspire.Cli/KnownFeatures.cs | 6 ------ .../TerminalCommandTests.cs | 1 - .../Commands/TerminalCommandTests.cs | 10 +++++----- .../Commands/TerminalCommandViewerOptionTests.cs | 6 +++--- .../Commands/TerminalTapePlayCommandTests.cs | 10 ++++------ .../TestServices/TerminalCommandTestServices.cs | 1 - 11 files changed, 15 insertions(+), 81 deletions(-) diff --git a/docs/specs/with-terminal.md b/docs/specs/with-terminal.md index 1395e2ff681..bb0a295abc5 100644 --- a/docs/specs/with-terminal.md +++ b/docs/specs/with-terminal.md @@ -518,15 +518,14 @@ backchannel response has required `resourceTerminals` and `appHostTerminals` arrays, each empty when there are no terminals of that kind. Resource entries group their replicas; AppHost entries carry a terminal ID, title, and placement. The CLI's `--format json` output combines both into one array with an `owner` -discriminator. Terminal commands are gated by `features.terminalCommandsEnabled`; +discriminator. Terminal commands are available without a feature flag; their experimental backchannel contract can change between builds. ### Tape playback `aspire terminal tape play --tape-file ` uses Hex1b's `TapeParser` and `TapePlayer` to execute a VHS `.tape` script against an -existing resource terminal. It requires the same `features.terminalCommandsEnabled` feature -flag as `terminal attach` and `terminal ps`. +existing resource terminal. ```sh aspire terminal tape play shell --tape-file ./probe.tape diff --git a/extension/schemas/aspire-config.schema.json b/extension/schemas/aspire-config.schema.json index bfea28738e9..5724b828fd7 100644 --- a/extension/schemas/aspire-config.schema.json +++ b/extension/schemas/aspire-config.schema.json @@ -221,22 +221,6 @@ "description": "Enable or disable access to the staging channel for early access to preview features and packages", "default": false }, - "terminalCommandsEnabled": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "string", - "enum": [ - "true", - "false" - ] - } - ], - "description": "(Experimental) Enable the 'aspire terminal' command group ('aspire terminal ps', 'aspire terminal attach'). Used in conjunction with the experimental WithTerminal() API (ASPIRETERMINAL001). Hidden by default while the API surface is in preview.", - "default": false - }, "updateNotificationsEnabled": { "anyOf": [ { diff --git a/extension/schemas/aspire-global-settings.schema.json b/extension/schemas/aspire-global-settings.schema.json index 4a43bee251a..8d24dc3bfda 100644 --- a/extension/schemas/aspire-global-settings.schema.json +++ b/extension/schemas/aspire-global-settings.schema.json @@ -173,22 +173,6 @@ "description": "Enable or disable access to the staging channel for early access to preview features and packages", "default": false }, - "terminalCommandsEnabled": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "string", - "enum": [ - "true", - "false" - ] - } - ], - "description": "(Experimental) Enable the 'aspire terminal' command group ('aspire terminal ps', 'aspire terminal attach'). Used in conjunction with the experimental WithTerminal() API (ASPIRETERMINAL001). Hidden by default while the API surface is in preview.", - "default": false - }, "updateNotificationsEnabled": { "anyOf": [ { diff --git a/extension/schemas/aspire-settings.schema.json b/extension/schemas/aspire-settings.schema.json index c8246c4cfd8..e00220309aa 100644 --- a/extension/schemas/aspire-settings.schema.json +++ b/extension/schemas/aspire-settings.schema.json @@ -173,22 +173,6 @@ "description": "Enable or disable access to the staging channel for early access to preview features and packages", "default": false }, - "terminalCommandsEnabled": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "string", - "enum": [ - "true", - "false" - ] - } - ], - "description": "(Experimental) Enable the 'aspire terminal' command group ('aspire terminal ps', 'aspire terminal attach'). Used in conjunction with the experimental WithTerminal() API (ASPIRETERMINAL001). Hidden by default while the API surface is in preview.", - "default": false - }, "updateNotificationsEnabled": { "anyOf": [ { diff --git a/src/Aspire.Cli/Commands/RootCommand.cs b/src/Aspire.Cli/Commands/RootCommand.cs index 53ebd2f4c9f..983048ba951 100644 --- a/src/Aspire.Cli/Commands/RootCommand.cs +++ b/src/Aspire.Cli/Commands/RootCommand.cs @@ -8,7 +8,6 @@ using Aspire.Cli.Bundles; using Aspire.Cli.Commands.Sdk; -using Aspire.Cli.Configuration; using Aspire.Cli.Interaction; using Aspire.Cli.Resources; using Aspire.Cli.Utils; @@ -176,7 +175,6 @@ public RootCommand( ExtensionInternalCommand extensionInternalCommand, IBundleService bundleService, IInteractionService interactionService, - IFeatures features, IAnsiConsole ansiConsole, CliExecutionContext executionContext) : base(RootCommandStrings.Description) @@ -228,12 +226,7 @@ public RootCommand( Subcommands.Add(describeCommand); Subcommands.Add(logsCommand); Subcommands.Add(integrationCommand); - // 'aspire terminal' is hidden behind a feature flag while WithTerminal() is experimental. - // Toggle with `aspire config set features.terminalCommandsEnabled true`. - if (features.IsFeatureEnabled(KnownFeatures.TerminalCommandsEnabled, defaultValue: false)) - { - Subcommands.Add(terminalCommand); - } + Subcommands.Add(terminalCommand); Subcommands.Add(addCommand); Subcommands.Add(publishCommand); Subcommands.Add(configCommand); diff --git a/src/Aspire.Cli/KnownFeatures.cs b/src/Aspire.Cli/KnownFeatures.cs index f6bb404000a..f85ddfee617 100644 --- a/src/Aspire.Cli/KnownFeatures.cs +++ b/src/Aspire.Cli/KnownFeatures.cs @@ -35,7 +35,6 @@ internal static class KnownFeatures public static string ExperimentalPolyglotPython => "experimentalPolyglot:python"; public static string NuGetSignatureVerificationEnabled => "nugetSignatureVerificationEnabled"; public static string AspireSkillsRemoteFetchEnabled => "aspireSkillsRemoteFetchEnabled"; - public static string TerminalCommandsEnabled => "terminalCommandsEnabled"; public static string PolyglotIntegrationFilterEnabled => "polyglotIntegrationFilterEnabled"; private static readonly Dictionary s_featureMetadata = new() @@ -99,11 +98,6 @@ internal static class KnownFeatures // still honored if set directly in config. Hidden: true), - [TerminalCommandsEnabled] = new( - TerminalCommandsEnabled, - "(Experimental) Enable the 'aspire terminal' command group ('aspire terminal ps', 'aspire terminal attach', 'aspire terminal tape play'). Used in conjunction with the experimental WithTerminal() API (ASPIRETERMINAL001). Hidden by default while the API surface is in preview.", - DefaultValue: false), - [PolyglotIntegrationFilterEnabled] = new( PolyglotIntegrationFilterEnabled, "(Experimental) Restrict 'aspire add', 'aspire integration list', and 'aspire integration search' in non-C# AppHosts to integrations carrying the 'polyglot' NuGet tag. Disabled by default because no remote feed resolves the tag usefully today: Azure DevOps Artifacts feeds ignore 'tags:' query scoping, and nuget.org returns no first-party integrations for it. The filter fails closed, so enabling it against a remote feed hides every integration. Enable it only against a local package source or hive, where the tag is read from the nuspec.", diff --git a/tests/Aspire.Cli.EndToEnd.Tests/TerminalCommandTests.cs b/tests/Aspire.Cli.EndToEnd.Tests/TerminalCommandTests.cs index 7cfd1d65a5d..1dacd808636 100644 --- a/tests/Aspire.Cli.EndToEnd.Tests/TerminalCommandTests.cs +++ b/tests/Aspire.Cli.EndToEnd.Tests/TerminalCommandTests.cs @@ -29,7 +29,6 @@ public async Task TerminalAttachFrontend_ShowsViteHelpAndDetaches() await auto.PrepareDockerEnvironmentAsync(counter, workspace); await auto.InstallAspireCliAsync(strategy, counter); - await auto.RunCommandAsync("aspire config set features.terminalCommandsEnabled true -g", counter); await auto.AspireNewAsync("TerminalSupportApp", counter, template: AspireTemplate.ExpressReact); var appHostPath = Path.Combine(workspace.WorkspaceRoot.FullName, "TerminalSupportApp", "apphost.mts"); diff --git a/tests/Aspire.Cli.Tests/Commands/TerminalCommandTests.cs b/tests/Aspire.Cli.Tests/Commands/TerminalCommandTests.cs index 6fce82d8a23..8b1d412f8e0 100644 --- a/tests/Aspire.Cli.Tests/Commands/TerminalCommandTests.cs +++ b/tests/Aspire.Cli.Tests/Commands/TerminalCommandTests.cs @@ -18,7 +18,7 @@ public class TerminalCommandTests(ITestOutputHelper outputHelper) public async Task TerminalCommand_Help_Works() { using var workspace = TemporaryWorkspace.CreateForCli(outputHelper); - var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, o => o.EnabledFeatures = [KnownFeatures.TerminalCommandsEnabled]); + var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper); using var provider = services.BuildServiceProvider(); var command = provider.GetRequiredService(); @@ -34,7 +34,7 @@ public async Task TerminalCommand_WhenNoSubcommand_PrintsHelpAndFails() // The 'terminal' parent command is non-runnable; it prints help when invoked // alone and returns InvalidCommand to mirror the DashboardCommand pattern. using var workspace = TemporaryWorkspace.CreateForCli(outputHelper); - var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, o => o.EnabledFeatures = [KnownFeatures.TerminalCommandsEnabled]); + var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper); using var provider = services.BuildServiceProvider(); var command = provider.GetRequiredService(); @@ -48,7 +48,7 @@ public async Task TerminalCommand_WhenNoSubcommand_PrintsHelpAndFails() public async Task TerminalAttachCommand_WhenNoResourceArgument_FailsParsing() { using var workspace = TemporaryWorkspace.CreateForCli(outputHelper); - var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, o => o.EnabledFeatures = [KnownFeatures.TerminalCommandsEnabled]); + var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper); using var provider = services.BuildServiceProvider(); var command = provider.GetRequiredService(); @@ -62,7 +62,7 @@ public async Task TerminalAttachCommand_WhenNoResourceArgument_FailsParsing() public async Task TerminalCommand_WhenNoAppHostRunning_ReturnsSuccess() { using var workspace = TemporaryWorkspace.CreateForCli(outputHelper); - var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, o => o.EnabledFeatures = [KnownFeatures.TerminalCommandsEnabled]); + var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper); using var provider = services.BuildServiceProvider(); var command = provider.GetRequiredService(); @@ -295,7 +295,7 @@ public async Task TerminalCommand_NonInteractiveMultiReplicaWithoutFlag_ReturnsI public async Task TerminalPsCommand_WhenNoAppHostRunning_ReturnsSuccess() { using var workspace = TemporaryWorkspace.CreateForCli(outputHelper); - var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, o => o.EnabledFeatures = [KnownFeatures.TerminalCommandsEnabled]); + var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper); using var provider = services.BuildServiceProvider(); var command = provider.GetRequiredService(); diff --git a/tests/Aspire.Cli.Tests/Commands/TerminalCommandViewerOptionTests.cs b/tests/Aspire.Cli.Tests/Commands/TerminalCommandViewerOptionTests.cs index 7d0888a0a4b..727e2779150 100644 --- a/tests/Aspire.Cli.Tests/Commands/TerminalCommandViewerOptionTests.cs +++ b/tests/Aspire.Cli.Tests/Commands/TerminalCommandViewerOptionTests.cs @@ -19,7 +19,7 @@ public class TerminalCommandViewerOptionTests(ITestOutputHelper outputHelper) public void ViewerOption_Help_DescribesPrimarySecondaryBehaviour() { using var workspace = TemporaryWorkspace.CreateForCli(outputHelper); - var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, o => o.EnabledFeatures = [KnownFeatures.TerminalCommandsEnabled]); + var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper); using var provider = services.BuildServiceProvider(); var command = provider.GetRequiredService(); @@ -47,7 +47,7 @@ public void ViewerOption_Help_DescribesPrimarySecondaryBehaviour() public void ViewerOption_DefaultIsFalse_WhenNotSpecified() { using var workspace = TemporaryWorkspace.CreateForCli(outputHelper); - var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, o => o.EnabledFeatures = [KnownFeatures.TerminalCommandsEnabled]); + var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper); using var provider = services.BuildServiceProvider(); var command = provider.GetRequiredService(); @@ -62,7 +62,7 @@ public void ViewerOption_DefaultIsFalse_WhenNotSpecified() public void ViewerOption_ParsesToTrue_WhenSpecified() { using var workspace = TemporaryWorkspace.CreateForCli(outputHelper); - var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, o => o.EnabledFeatures = [KnownFeatures.TerminalCommandsEnabled]); + var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper); using var provider = services.BuildServiceProvider(); var command = provider.GetRequiredService(); diff --git a/tests/Aspire.Cli.Tests/Commands/TerminalTapePlayCommandTests.cs b/tests/Aspire.Cli.Tests/Commands/TerminalTapePlayCommandTests.cs index 2fda7a19629..bea9729a726 100644 --- a/tests/Aspire.Cli.Tests/Commands/TerminalTapePlayCommandTests.cs +++ b/tests/Aspire.Cli.Tests/Commands/TerminalTapePlayCommandTests.cs @@ -23,8 +23,7 @@ public class TerminalTapePlayCommandTests(ITestOutputHelper outputHelper) public async Task CommandHierarchy(string arguments, int expectedExitCode) { using var workspace = TemporaryWorkspace.CreateForCli(outputHelper); - var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, - options => options.EnabledFeatures = [KnownFeatures.TerminalCommandsEnabled]); + var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper); using var provider = services.BuildServiceProvider(); var result = provider.GetRequiredService().Parse(arguments); @@ -33,7 +32,7 @@ public async Task CommandHierarchy(string arguments, int expectedExitCode) } [Fact] - public void RequiresTerminalFeature() + public void AvailableWithoutFeatureFlag() { using var workspace = TemporaryWorkspace.CreateForCli(outputHelper); var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper); @@ -41,7 +40,7 @@ public void RequiresTerminalFeature() var result = provider.GetRequiredService().Parse("terminal tape play shell --tape-file probe.tape"); - Assert.NotEmpty(result.Errors); + Assert.Empty(result.Errors); } [Theory] @@ -96,8 +95,7 @@ public async Task NoRunningAppHostFails() { using var workspace = TemporaryWorkspace.CreateForCli(outputHelper); await File.WriteAllTextAsync(Path.Combine(workspace.WorkspaceRoot.FullName, "probe.tape"), ""); - var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, - options => options.EnabledFeatures = [KnownFeatures.TerminalCommandsEnabled]); + var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper); using var provider = services.BuildServiceProvider(); var result = provider.GetRequiredService().Parse("terminal tape play shell --tape-file probe.tape"); diff --git a/tests/Aspire.Cli.Tests/TestServices/TerminalCommandTestServices.cs b/tests/Aspire.Cli.Tests/TestServices/TerminalCommandTestServices.cs index f3942671896..dde22ec5fb2 100644 --- a/tests/Aspire.Cli.Tests/TestServices/TerminalCommandTestServices.cs +++ b/tests/Aspire.Cli.Tests/TestServices/TerminalCommandTestServices.cs @@ -31,7 +31,6 @@ public static (ServiceProvider Provider, TestAppHostAuxiliaryBackchannel Backcha var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, options => { - options.EnabledFeatures = [KnownFeatures.TerminalCommandsEnabled]; options.AuxiliaryBackchannelMonitorFactory = _ => monitor; configureOptions?.Invoke(options); });