Skip to content

New dotnet watch support for aspire#14327

Closed
tmat wants to merge 13 commits intodotnet:feature/watch-prototypefrom
tmat:NewModel
Closed

New dotnet watch support for aspire#14327
tmat wants to merge 13 commits intodotnet:feature/watch-prototypefrom
tmat:NewModel

Conversation

@tmat
Copy link
Member

@tmat tmat commented Feb 4, 2026

Adds more functionality to Aspire.Watch CLI tool. Aspire can invoke it via CLI or you can also run some of it in-proc, as needed.

Defines 3 CLI commands: host, server and resource.

host runs AppHost:

Watch.Aspire host
 --sdk E:\aspire\.dotnet\sdk\10.0.102

server runs Hot Reload server and you pass it a set of C# resource projects or .cs files for file-based apps. It also takes a unique named pipe name to listen on:

Watch.Aspire server
 --server NamedPipe_6CE8A258-A439-4459-A348-1546EA0E202C
 --resource AspireApp\AspireApp.ApiService\AspireApp.ApiService.csproj 
 --resource AspireApp\AspireApp.Web.cs

resource launches resource project:

Watch.Aspire resource 
  --server NamedPipe_6CE8A258-A439-4459-A348-1546EA0E202C 
  --entrypoint AspireApp\AspireApp.ApiService\AspireApp.ApiService.csproj

Watch.Aspire resource 
  --server NamedPipe_6CE8A258-A439-4459-A348-1546EA0E202C 
  --entrypoint AspireApp\AspireApp.Web.cs

This command also takes parameters like --environment, --launch-profile, etc.
It sends request to launch an app to the server.


Bidirectional control protocol (AppHost ↔ Watch Server)

This update adds a status/control pipe protocol between the AppHost and the Watch.Aspire server:

Status events (Watch Server → AppHost):
The watch server reports lifecycle events back to the AppHost over a named pipe (--status-pipe). The AppHost uses these to update resource states in the Dashboard:

  • building / build_complete (with success/failure)
  • hot_reload_applied
  • restarting
  • process_started / process_exited (with exit code)

Control commands (AppHost → Watch Server):
The AppHost sends commands to the watch server over a named pipe (--control-pipe):

  • rebuild — triggers a forced rebuild and restart for specific projects

Dashboard integration:

  • Each watched project resource gets a Rebuild button in the Dashboard that sends a rebuild command to the watch server
  • Resource states update in real-time (Building → Starting → Running, or Build failed)

Crash recovery:

  • When a watched process crashes (non-zero exit), the watch server signals the iteration to restart and waits for a file change before rebuilding
  • The DCP resource command pipe stays alive across crash-restart cycles so the Dashboard connection is preserved

Other changes:

  • DotnetSdkUtils expanded with TryGetSdkDirectoryAsync using dotnet --list-sdks for accurate SDK resolution (respects global.json)
  • Watch server resource creation moved to WatchAspireEventHandlers (eventing subscriber) for cleaner separation from DcpExecutor
  • Fixed .in.targets to use $(OS) != 'Windows_NT' instead of IsOsPlatform('OSX') for non-Windows DLL path resolution

@github-actions
Copy link
Contributor

github-actions bot commented Feb 4, 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 -- 14327

Or

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

@tmat tmat changed the title New model Watch.Aspire CLI for polyglot Aspire apps Feb 4, 2026
@tmat
Copy link
Member Author

tmat commented Feb 4, 2026

@davidfowl @karolz-ms

@davidfowl
Copy link
Member

How is the host command special?

@tmat
Copy link
Member Author

tmat commented Feb 4, 2026

How is the host command special?

That one is equivalent to regular dotnet watch.

@tmat tmat changed the base branch from main to feature/watch-prototype February 5, 2026 19:08
@tmat tmat marked this pull request as ready for review February 6, 2026 16:55
tmat and others added 7 commits February 6, 2026 09:03
Add centralized hot reload support via Watch.Aspire server:
- DcpExecutor creates a hidden watch server ExecutableResource when
  DOTNET_WATCH=true and WatchAspirePath is configured
- Project resources use the Watch.Aspire `resource` command instead of
  `dotnet run`, with env vars passed as -e args
- Resource command stays alive proxying stdout/stderr from the server
  back to DCP via named pipe output protocol
- Server-side ProcessLauncherFactory forwards process output/exit over
  the pipe instead of disposing it after ACK

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Shorten named pipe name to fit macOS 104-char Unix socket limit
- Fix SDK path resolution to derive from runtime directory instead of process path
- Add Microsoft.CodeAnalysis.* to dotnet-public package source mapping
- Add VersionOverride for Microsoft.Build.Framework in Watch.Aspire

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add WatchControlCommand and WatchStatusEvent types for bidirectional communication
- Implement WatchControlReader/WatchStatusWriter for stdio-based protocol
- Update DcpExecutor to send watch commands and receive status events
- Add WatchAspireAnnotation and WatchAspireEventHandlers for resource integration
- Expand DotnetSdkUtils with SDK resolution and project launch info helpers
- Update ProcessLauncherFactory to support Aspire-hosted watch scenarios
- Wire up watch options and context for hot reload coordination
@davidfowl davidfowl requested a review from mitchdenny as a code owner February 8, 2026 17:38
@davidfowl davidfowl changed the title Watch.Aspire CLI for polyglot Aspire apps New dotnet watch support for aspire Feb 9, 2026
return 0;
}

static async Task ListenForControlCommandsAsync(
Copy link
Member Author

@tmat tmat Feb 17, 2026

Choose a reason for hiding this comment

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

This allows restarting all resource projects, but wouldn't we rather want to restart individual resource projects?

buildArguments: _context.BuildArguments,
onLaunchedProcessCrashed: () =>
{
// Mirror the root process onExit behavior (line 154-159):
Copy link
Member Author

@tmat tmat Feb 17, 2026

Choose a reason for hiding this comment

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

This doesn't look right. Do we actually want to restart everything if one of the resource process crashes? I guess not. We want to restart just that one process, right?

@tmat
Copy link
Member Author

tmat commented Mar 6, 2026

Superseded by #14947

@tmat tmat closed this Mar 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants