Skip to content

Add sample Temporal integration for Aspire#127

Open
cecilphillip wants to merge 18 commits into
mainfrom
cp-aspire
Open

Add sample Temporal integration for Aspire#127
cecilphillip wants to merge 18 commits into
mainfrom
cp-aspire

Conversation

@cecilphillip

Copy link
Copy Markdown

What was changed

This PR adds comprehensive .NET Aspire integration for Temporal, including:

  • Custom Aspire resource definitions for Temporal server deployment with three deployment models:

    • Local test server using Temporalio.Testing.WorkflowEnvironment
    • Container-based deployment using Docker
    • CLI-based server for environments without Docker
  • Temporal.Extensions.Aspire.Hosting library with:

    • Extension methods for adding Temporal resources to Aspire applications
    • Health check integration for Temporal connectivity
    • Automatic service discovery and environment variable injection
    • Resource lifecycle management (start/stop) via Aspire dashboard
    • Configurable options for ports, namespaces, search attributes, and dynamic config
  • Sample Aspire application demonstrating the integration:

    • TemporalioSamples.SampleAppHost - Aspire app host configuring Temporal resources
    • TemporalioSamples.SampleWorker - Worker service consuming workflows/activities
    • TemporalioSamples.SampleClient - Client triggering workflow execution
    • TemporalioSamples.SampleWorkflow - Simple workflow and activity definitions

@cecilphillip cecilphillip requested a review from a team as a code owner February 21, 2026 15:30
@CLAassistant

CLAassistant commented Feb 21, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Comment thread src/AspireIntegrations/TemporalioSamples.SampleAppHost/AppHost.cs Outdated
Comment on lines +7 to +10
var connectOptions = ClientEnvConfig.LoadClientConnectOptions();
Console.WriteLine("\nAttempting to connect client to temporal server...");

var client = await TemporalClient.ConnectAsync(connectOptions);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Aren't you supposed to be able to use Aspire to get client connectivity information too? Even if it is powered by env vars under the hood, one might expect a type-safe Aspire way to get a client or client options for the given "temporal" reference from other project.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Aspire only does that through environment variables. There isn't anything type-safe configuration support specifically from aspire. Typically library authors would create their own using Microsoft.Extensions.Configuration bindings

@cretz cretz Feb 23, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I mean from a caller POV. For instance, stuff like https://aspire.dev/integrations/databases/postgres/postgres-get-started/?lang=csharp#use-the-integration-in-client-projects. I would expect that you can just put a client on the DI container w/ a single Aspire helper. Sure you can always still manually create clients.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I mean we could create an extension method that makes that a little nicer, but that would be a client specific library/package. From an aspire API perspective, they just inject configuration properties that get consumed .

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

They inject configuration properties for those not wanting to use the helper, but they also provide a helper. I figure the nice aspect of "Aspire helper to make server, Aspire helper to make client" is valuable even though they can use the env vars directly of course, same as Postgres there.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@cecilphillip I think what you have is idiomatic to Temporal and Aspire and what you'd want to do if a customer would actually deploy this from their Aspire setup.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@jmaeagle99 What's currently in this PR covers a hosting integration. We could also include a client integration at some point later on.

@cecilphillip cecilphillip requested a review from cretz February 23, 2026 16:16
@cecilphillip cecilphillip requested review from Copilot and removed request for cretz May 21, 2026 18:26

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a new Aspire integration sample set for Temporal and introduces a Temporal.Extensions.Aspire.Hosting library that defines Temporal resources (local dev server, Docker container, and Temporal CLI) with health checks and environment variable wiring for dependent projects.

Changes:

  • Added an Aspire sample AppHost + client/worker + workflow projects under src/AspireIntegrations.
  • Introduced Temporal.Extensions.Aspire.Hosting with custom Temporal resources, options, lifecycle subscriber, and health check extensions.
  • Added documentation and Aspire settings for running the new sample.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
TemporalioSamples.sln Adds the new Aspire integration projects into the solution under a new folder.
src/AspireIntegrations/TemporalioSamples.SampleWorkflow/TemporalioSamples.SampleWorkflow.csproj New workflow project for the Aspire sample.
src/AspireIntegrations/TemporalioSamples.SampleWorkflow/SimpleWorkflow.cs Sample workflow definition.
src/AspireIntegrations/TemporalioSamples.SampleWorkflow/SimpleActivities.cs Sample activity definition.
src/AspireIntegrations/TemporalioSamples.SampleWorker/TemporalioSamples.SampleWorker.csproj New worker project for the Aspire sample.
src/AspireIntegrations/TemporalioSamples.SampleWorker/Program.cs Configures and runs a hosted Temporal worker via env-configured connectivity.
src/AspireIntegrations/TemporalioSamples.SampleClient/TemporalioSamples.SampleClient.csproj New client project for the Aspire sample.
src/AspireIntegrations/TemporalioSamples.SampleClient/Program.cs Connects to Temporal and starts the sample workflow.
src/AspireIntegrations/TemporalioSamples.SampleAppHost/TemporalioSamples.SampleAppHost.csproj New Aspire AppHost project referencing the Temporal Aspire hosting library and sample projects.
src/AspireIntegrations/TemporalioSamples.SampleAppHost/Properties/launchSettings.json Launch profiles for the AppHost.
src/AspireIntegrations/TemporalioSamples.SampleAppHost/appsettings.json Logging configuration for AppHost.
src/AspireIntegrations/TemporalioSamples.SampleAppHost/appsettings.Development.json Development logging configuration for AppHost.
src/AspireIntegrations/TemporalioSamples.SampleAppHost/AppHost.cs Wires up the Temporal local dev server resource and references it from worker/client.
src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalResourceOptions.cs Adds Temporal resource options layered on Temporal’s local environment options.
src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalResourceConstants.cs Defines endpoint names, default ports, and image constants.
src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalLocalResourceSubscriber.cs Manages local dev server lifecycle (start on init, shutdown on stop).
src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalLocalResourceExtensions.cs Adds AddTemporalLocalDevServer, endpoints, health checks, and dashboard commands.
src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalLocalResource.cs Resource model for the local dev server.
src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalHealthCheckBuilderExtensions.cs Adds health check registration helper for Temporal connectivity.
src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalHealthCheck.cs Implements the Temporal connectivity health check.
src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalContainerResource.cs Container-based Temporal resource + connection string expression.
src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalContainerOptions.cs Container-specific options (image tag) for Temporal.
src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalContainerBuilderExtensions.cs Adds Docker-based Temporal dev server resource and wiring helpers.
src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalCliServerResourceExtensions.cs Adds Temporal CLI-based server resource and wiring helpers.
src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalCliServerResource.cs Executable-based Temporal resource + connection string expression.
src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/Temporal.Extensions.Aspire.Hosting.csproj New hosting library project definition and dependencies.
src/AspireIntegrations/README.md Adds documentation for running/using the new Aspire integration and configuration options.
src/AspireIntegrations/.aspire/settings.json Points Aspire tooling at the new AppHost project.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
Comment on lines +20 to +23
public new List<string> AdditionalNamespaces
{
get => additionalNamespaces.Count > 0 ? additionalNamespaces : [Namespace];
set => additionalNamespaces = value ?? [];
Comment on lines +79 to +97
if (resource.WorkflowEnvironment != null)
{
resourceLogger.LogInformation("Shutting down Temporal test server '{ResourceName}'...", resource.Name);
await resource.WorkflowEnvironment.ShutdownAsync();
resource.WorkflowEnvironment = null;
resourceLogger.LogInformation("Temporal test server '{ResourceName}' shut down successfully.", resource.Name);
}

// Publish ResourceStoppedEvent to trigger subscriber cleanup and keep _environments dictionary in sync
var resourceEvent = new ResourceEvent(resource, resource.Name, new CustomResourceSnapshot
{
ResourceType = "temporal-local",
CreationTimeStamp = DateTime.UtcNow,
State = KnownResourceStates.Exited,
Properties = []
});
var stoppedEvent = new ResourceStoppedEvent(resource, context.ServiceProvider, resourceEvent);
await eventing.PublishAsync(stoppedEvent, context.CancellationToken);

{
try
{
await TemporalClient.ConnectAsync(clientConnectOptions);
Comment thread src/AspireIntegrations/README.md
Comment thread src/AspireIntegrations/README.md
@jmaeagle99 jmaeagle99 self-assigned this Jun 11, 2026
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's move these down to net8.0 since that is what the samples repo is currently using; I believe that is supported even with the most recent versions of Aspire.


return builder.AddResource(resource)
.WithArgs(BuildContainerArgs(resource.Options))
.ExcludeFromManifest().WithEndpoint(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: put .WithEndpoint( on its own line

Comment on lines +7 to +10
var connectOptions = ClientEnvConfig.LoadClientConnectOptions();
Console.WriteLine("\nAttempting to connect client to temporal server...");

var client = await TemporalClient.ConnectAsync(connectOptions);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@cecilphillip I think what you have is idiomatic to Temporal and Aspire and what you'd want to do if a customer would actually deploy this from their Aspire setup.

Comment thread src/AspireIntegrations/README.md
Comment thread src/AspireIntegrations/README.md Outdated

// Add a worker project that depends on Temporal
builder.AddProject<Projects.SampleWorker>("worker")
.WaitFor(temporal)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we do this automatically for them in the .WithReference calls?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

that's an Apsire pattern that I've wondered about in general. It should be fine

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 40 out of 40 changed files in this pull request and generated 10 comments.

Comment thread src/AspireIntegrations/README.md
Comment thread src/AspireIntegrations/README.md
Comment thread src/AspireIntegrations/README.md
Comment thread src/AspireIntegrations/README.md
…ssing dashboard URLs

- Switch AppHost sample from AddTemporalLocalDevServer to AddTemporalCliServer
- Add TemporalCliLocator with PATH guard so AddTemporalCliServer throws immediately
  with an actionable error when the temporal CLI is not installed
- Fix temporal-local dashboard URLs by supplying explicit port values on
  WithHttpEndpoint for the UI and metrics endpoints
- Fix temporal-cli-server FailedToStart caused by ConnectionStringAvailableEvent
  callback throwing when the CLI process hasn't bound its port yet; replace with
  non-throwing retry loop (up to 30 x 500ms attempts)
- Refactor health-check client accessor from Func<ITemporalClient?> to
  Func<CancellationToken, Task<ITemporalClient?>> so each probe can lazily
  reconnect; Unhealthy resources recover to Healthy once the server is ready
  without requiring a manual restart
- Add 28 unit tests covering TemporalHealthCheck, TemporalHealthCheckHelper,
  TemporalCliLocator, TemporalResourceOptions, TemporalEnvironmentHelper, and
  TemporalCliServerResourceExtensions
…y guard

- Add explicit port to all three WithEndpoint/WithHttpEndpoint calls in
  AddTemporalCliServer so the Aspire dashboard renders clickable URLs for the
  CLI resource (parity with local and container resources)
- Add isProxied: false to all three endpoint registrations in AddTemporalDevContainer;
  without it Aspire's DCP proxy intercepts ports including gRPC on 7233, preventing
  the Temporal client from connecting and keeping the resource permanently Unhealthy
- Add internal AddTemporalCliServer overload with injectable isTemporalCliAvailable
  seam so tests can exercise the public extension method path without depending on
  temporal being on the test machine PATH
- Replace AddTemporalCliServer_Throws_WhenTemporalCliNotOnPath test: now calls the
  public extension method via builder rather than the helper directly
- Add AddTemporalCliServer_RegistersExplicitPortsOnAllEndpoints regression test
- Fix missing trailing newline in aspire.config.json
- Switch AppHost sample to AddTemporalDevContainer
- Replace nullable 'as' casts with explicit casts for type safety
- Remove conflicting VersionOverride from csproj (use central package management)
- Fix StyleCop spacing violation in SimpleWorkflow
- Correct README examples: AddTemporalCliServer → AddTemporalLocalDevServer
- Update README project references: Projects.SampleWorker → Projects.TemporalioSamples_SampleWorker
- Fix container metrics port to use constant instead of variable
Expand CLI-based deployment documentation with advanced configuration options
for ports, namespaces, search attributes, and dynamic config values. This
brings the CLI-based section to feature parity with Local Server and
Container-based sections.
Add XML documentation comments for:
- All public types (resources, extension classes, options)
- All public and internal methods and properties
- All method parameters and return values
- All public constants with their descriptions

Includes documentation for:
- Resource classes (TemporalCliServerResource, TemporalContainerResource, TemporalLocalResource)
- Extension methods for AddTemporalCliServer, AddTemporalDevContainer, AddTemporalLocalDevServer
- Helper classes (TemporalArgsBuilder, TemporalEnvironmentHelper, TemporalHealthCheck)
- Configuration classes (TemporalResourceOptions, TemporalContainerOptions)
- Event subscribers and lifecycle management

This addresses StyleCop rules SA1611 (missing parameter docs) and SA1615 (missing return value docs).
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.

5 participants