Skip to content

Add shortcut for aspire run --detached via aspire start#14644

Merged
davidfowl merged 2 commits intorelease/13.2from
davidfowl/aspire-start
Feb 25, 2026
Merged

Add shortcut for aspire run --detached via aspire start#14644
davidfowl merged 2 commits intorelease/13.2from
davidfowl/aspire-start

Conversation

@davidfowl
Copy link
Copy Markdown
Contributor

@davidfowl davidfowl commented Feb 24, 2026

Description

This PR adds a shortcut to run detached AppHost startup via aspire start (no resource argument), equivalent to aspire run --detach, and fixes detached-launch forwarding/regression issues.

Summary of changes

  • Add detached AppHost launch path to aspire start when no resource name is provided.
  • Keep detached launch behavior shared between start and run through AppHostLauncher.
  • Restore JSON-safe detached output behavior:
    • route human-readable output to stderr for --format json
    • enforce non-interactive AppHost selection in JSON mode
    • write JSON output explicitly to stdout
  • Restore detached child diagnostics behavior:
    • generate and pass child --log-file
    • surface child log path in errors and JSON output
  • Forward --no-build from aspire run --detach to the detached child process.
  • Use RunCommand.GetDetachedFailureMessage(childExitCode) for clearer early-exit errors.
  • Include the create-pr skill update used to standardize template-filled PR creation/editing in this workflow.

Motivation and context

The detached launch refactor introduced argument/output regressions and less specific failure messaging. These changes restore expected behavior and provide a clear aspire start shortcut for the detached AppHost launch flow.

Dependencies: None.

Validation

  • dotnet test tests/Aspire.Cli.Tests/Aspire.Cli.Tests.csproj -- --filter-class "*.RunCommandTests" --filter-class "*.StartCommandTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"

Fixes # (issue)
No linked issue.

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No
  • Does the change require an update in our Aspire docs?

Copilot AI review requested due to automatic review settings February 24, 2026 08:37
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 24, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 14644

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 14644"

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request refactors the CLI's detached launch functionality to fix argument forwarding for aspire start and aspire run commands. The changes extract shared detached launch logic into a new AppHostLauncher class and enhance StartCommand to support launching an AppHost in the background when no resource name is provided.

Changes:

  • Extracted detached AppHost launch logic from RunCommand into a new shared AppHostLauncher class to eliminate code duplication
  • Enhanced StartCommand to support dual modes: starting a specific resource (existing behavior) or starting the AppHost in detached mode (new behavior) when no resource name is provided
  • Updated localization strings to reflect the new dual-purpose nature of aspire start

Reviewed changes

Copilot reviewed 34 out of 35 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Aspire.Cli/Commands/AppHostLauncher.cs New internal class encapsulating detached AppHost launch logic, extracted from RunCommand to enable code reuse
src/Aspire.Cli/Commands/RunCommand.cs Refactored to delegate detached launch logic to AppHostLauncher, removing ~250 lines of duplicated code
src/Aspire.Cli/Commands/StartCommand.cs Changed from ResourceCommandBase to BaseCommand; now supports optional resource argument and detached AppHost launch
src/Aspire.Cli/Program.cs Registered AppHostLauncher in DI container
tests/Aspire.Cli.Tests/Utils/CliTestHelper.cs Registered AppHostLauncher in test DI setup
src/Aspire.Cli/Resources/ResourceCommandStrings.resx Added OptionNotValidWithResource string; updated StartDescription and StartResourceArgumentDescription
src/Aspire.Cli/Resources/RunCommandStrings.resx Updated Description and JsonArgumentDescription for clarity
src/Aspire.Cli/Resources/*.xlf Updated localization files with new/modified strings marked for translation
.github/skills/create-pr/SKILL.md Added GitHub Copilot skill definition for creating PRs (unrelated to main PR purpose)
Files not reviewed (1)
  • src/Aspire.Cli/Resources/ResourceCommandStrings.Designer.cs: Language not supported

@davidfowl davidfowl changed the title Fix detached launch argument forwarding for aspire start/run Add shortcut for aspire run --detached Feb 24, 2026
@davidfowl davidfowl changed the title Add shortcut for aspire run --detached Add shortcut for aspire run --detached via aspire start Feb 24, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 24, 2026

🎬 CLI E2E Test Recordings

The following terminal recordings are available for commit 45db3d5:

Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View Recording
AddPackageWhileAppHostRunningDetached ▶️ View Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View Recording
AgentInitCommand_WithMalformedMcpJson_ShowsErrorAndExitsNonZero ▶️ View Recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View Recording
Banner_DisplayedOnFirstRun ▶️ View Recording
Banner_DisplayedWithExplicitFlag ▶️ View Recording
CreateAndDeployToDockerCompose ▶️ View Recording
CreateAndDeployToDockerComposeInteractive ▶️ View Recording
CreateAndPublishToKubernetes ▶️ View Recording
CreateAndRunAspireStarterProject ▶️ View Recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View Recording
CreateAndRunJsReactProject ▶️ View Recording
CreateAndRunPythonReactProject ▶️ View Recording
CreateEmptyAppHostProject ▶️ View Recording
CreateStartAndStopAspireProject ▶️ View Recording
CreateStartWaitAndStopAspireProject ▶️ View Recording
CreateTypeScriptAppHostWithViteApp ▶️ View Recording
DescribeCommandShowsRunningResources ▶️ View Recording
DetachFormatJsonProducesValidJson ▶️ View Recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
LogsCommandShowsResourceLogs ❌ Upload failed
PsCommandListsRunningAppHost ▶️ View Recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ❌ Upload failed
StopAllAppHostsFromAppHostDirectory ▶️ View Recording
StopAllAppHostsFromUnrelatedDirectory ▶️ View Recording
StopNonInteractiveMultipleAppHostsShowsError ▶️ View Recording
StopNonInteractiveSingleAppHost ▶️ View Recording
StopWithNoRunningAppHostExitsSuccessfully ▶️ View Recording

📹 Recordings uploaded automatically from CI run #22380578937

@davidfowl davidfowl force-pushed the davidfowl/aspire-start branch 2 times, most recently from 80a0071 to 6983e1b Compare February 25, 2026 01:22
IAnsiConsole ansiConsole,
IAuxiliaryBackchannelMonitor backchannelMonitor,
ILogger<AppHostLauncher> logger,
TimeProvider? timeProvider = null)
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.

Is there a reason this is optional?

/// </summary>
internal static readonly Option<OutputFormat?> s_formatOption = new("--format")
{
Description = RunCommandStrings.JsonArgumentDescription
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.

I'm adding a CommonCommandStrings in a PR that should merge soon. These resource strings should move there when available rather than stay in Run.

Comment on lines +89 to +92
// Avoid interactive project prompts in JSON mode to keep stdout parseable.
var multipleAppHostBehavior = format == OutputFormat.Json
? MultipleAppHostProjectsFoundBehavior.Throw
: MultipleAppHostProjectsFoundBehavior.Prompt;
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.

Is this comment still true? Now all output should go to stderr with ConsoleOutput.Error. JSON should still be parsable. However, I don't know whether it makes sense to allow prompts or throw for other reasons.


if (effectiveAppHostFile is null)
{
return ExitCodeConstants.FailedToFindProject;
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.

Does the calling code write the error message to console?

Edit: I see in later code that this method writes error messages. Double check something is displayed in this scenario.


if (runningInstanceDetectionEnabled && existingSockets.Length > 0)
{
logger.LogDebug("Found {Count} running instance(s) for this AppHost, stopping them first", existingSockets.Length);
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.

nit

Suggested change
logger.LogDebug("Found {Count} running instance(s) for this AppHost, stopping them first", existingSockets.Length);
logger.LogDebug("Found {Count} running instance(s) for this AppHost, stopping them first.", existingSockets.Length);

Comment on lines +143 to +146
"--non-interactive",
"--project",
effectiveAppHostFile.FullName,
"--log-file",
Copy link
Copy Markdown
Member

@JamesNK JamesNK Feb 25, 2026

Choose a reason for hiding this comment

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

Can these option names be changed to use Name value from options. That way if they change, e.g. --project to --apphost, then this stays in sync.

// Pass through run-specific options
if (isolated)
{
args.Add("--isolated");
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.

See above comment

var childExitedEarly = false;
var childExitCode = 0;

async Task<IAppHostAuxiliaryBackchannel?> StartAndWaitForBackchannelAsync()
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.

LaunchDetachedAsync is big. Could this local function be promoted to a normal method to reduce its size?

}

// Build the arguments for the child CLI process
var childLogFile = GenerateChildLogFilePath(executionContext.LogsDirectory.FullName, _timeProvider);
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.

Could logic for building the path and arguments for child CLI process be moved to a separate method to reduce this ones size?

Comment on lines +117 to +120
ResourceCommandStrings.ScanningForRunningAppHosts,
ResourceCommandStrings.SelectAppHost,
ResourceCommandStrings.NoInScopeAppHostsShowingAll,
ResourceCommandStrings.NoRunningAppHostsFound,
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.

Will need to update these to common strings once my PR is in.

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.

In: #14661. See examples of other commands resolving connection.

@dotnet-policy-service dotnet-policy-service bot added the needs-author-action An issue or pull request that requires more info or actions from the author. label Feb 25, 2026
@davidfowl davidfowl force-pushed the davidfowl/aspire-start branch from 9d3da02 to 6930f6c Compare February 25, 2026 01:42
- Add detached AppHost launch to 'aspire start' when no resource specified
- Share detached launch behavior between start and run via AppHostLauncher
- Register --no-build option on StartCommand and forward to child process
- Restore JSON-safe detached output (human-readable to stderr for --format json)
- Restore detached child diagnostics (--log-file generation and error surfacing)
- Forward --no-build from 'aspire run --detach' to the detached child process

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@davidfowl davidfowl force-pushed the davidfowl/aspire-start branch from 6930f6c to 88693a3 Compare February 25, 2026 01:50
ResourceCommandStrings.NoInScopeAppHostsShowingAll,
ResourceCommandStrings.NoRunningAppHostsFound,
SharedCommandStrings.ScanningForRunningAppHosts,
SharedCommandStrings.SelectAppHost,
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.

Needs to be formated with a value, e.g.

string.Format(CultureInfo.CurrentCulture, SharedCommandStrings.SelectAppHost, ResourceCommandStrings.SelectAppHostAction)

@davidfowl davidfowl force-pushed the davidfowl/aspire-start branch 3 times, most recently from e4642a4 to 63b23d8 Compare February 25, 2026 02:05

if (!result.Success)
{
_interactionService.DisplayError(result.ErrorMessage ?? SharedCommandStrings.AppHostNotRunning);
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.

There is always a value when !Success

Suggested change
_interactionService.DisplayError(result.ErrorMessage ?? SharedCommandStrings.AppHostNotRunning);
_interactionService.DisplayError(result.ErrorMessage);

@davidfowl davidfowl requested review from JamesNK February 25, 2026 02:10
@davidfowl davidfowl force-pushed the davidfowl/aspire-start branch from 63b23d8 to 2262427 Compare February 25, 2026 02:12
Copy link
Copy Markdown
Member

@JamesNK JamesNK left a comment

Choose a reason for hiding this comment

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

Need StartCommand tests

internal static void AddLaunchOptions(Command command)
{
command.Options.Add(s_formatOption);
command.Options.Add(s_isolatedOption);
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.

Should s_projectOption also be here and not declared/added in the two commands?

- Make TimeProvider required (registered in DI as TimeProvider.System)
- Add period to log message (nit)
- Use option Name properties instead of hardcoded strings for --project/--isolated
- Extract BuildChildProcessArgs, StopExistingInstancesAsync, HandleLaunchFailure,
  DisplayLaunchResultAsync, and LaunchAndWaitForBackchannelAsync as separate methods
- Add s_projectOption to AppHostLauncher for shared use
- Display error when project not found (item #4)
- Fix string references: use SharedCommandStrings for relocated strings
- Fix async void to async Task on DisplayLaunchResultAsync

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@davidfowl davidfowl force-pushed the davidfowl/aspire-start branch from 2262427 to 45db3d5 Compare February 25, 2026 03:15
@JamesNK
Copy link
Copy Markdown
Member

JamesNK commented Feb 25, 2026

Related: The skill should probably be updated to use aspire start instead of aspire run --detach

@davidfowl
Copy link
Copy Markdown
Contributor Author

#14619 (comment)

@dotnet-policy-service dotnet-policy-service bot removed the needs-author-action An issue or pull request that requires more info or actions from the author. label Feb 25, 2026
@davidfowl davidfowl merged commit 03bd202 into release/13.2 Feb 25, 2026
343 checks passed
@davidfowl davidfowl deleted the davidfowl/aspire-start branch February 25, 2026 04:15
@dotnet-policy-service dotnet-policy-service bot added this to the 13.2 milestone Feb 25, 2026
Copilot AI pushed a commit that referenced this pull request Mar 10, 2026
* Add aspire start shortcut for detached AppHost launch

- Add detached AppHost launch to 'aspire start' when no resource specified
- Share detached launch behavior between start and run via AppHostLauncher
- Register --no-build option on StartCommand and forward to child process
- Restore JSON-safe detached output (human-readable to stderr for --format json)
- Restore detached child diagnostics (--log-file generation and error surfacing)
- Forward --no-build from 'aspire run --detach' to the detached child process

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address review feedback: refactor AppHostLauncher, fix string references

- Make TimeProvider required (registered in DI as TimeProvider.System)
- Add period to log message (nit)
- Use option Name properties instead of hardcoded strings for --project/--isolated
- Extract BuildChildProcessArgs, StopExistingInstancesAsync, HandleLaunchFailure,
  DisplayLaunchResultAsync, and LaunchAndWaitForBackchannelAsync as separate methods
- Add s_projectOption to AppHostLauncher for shared use
- Display error when project not found (item #4)
- Fix string references: use SharedCommandStrings for relocated strings
- Fix async void to async Task on DisplayLaunchResultAsync

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions bot locked and limited conversation to collaborators Mar 27, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants