Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
36a5310
Document launch configuration callback context design
Aug 5, 2026
fc48eb7
docs: add launch configuration callback plan
Aug 6, 2026
ad326ea
Add launch configuration callback context
Aug 6, 2026
bfb9027
Fix launch producer restart handling
Aug 6, 2026
9a8c99a
Reset debug execution before rebuilding arguments
Aug 6, 2026
e9ea853
Migrate debug launch configuration producers
Aug 6, 2026
9efdff8
Finalize debug callback context API
Aug 6, 2026
65a56a7
Migrate debug support playground
Aug 6, 2026
ac32975
Fix debug support documentation whitespace
Aug 6, 2026
b0fcfd3
Address callback context review findings
Aug 6, 2026
8de33ce
Remove agent planning artifacts and restore load-bearing comments
Aug 7, 2026
900a331
Fix MAUI launch configuration regression and rename PrepareObjects
Aug 7, 2026
7d4d8fe
Address launch configuration context review
Aug 8, 2026
0ee07ef
Install Azure Functions Core Tools from release archive
Aug 8, 2026
2db433e
Recompute debug argument callbacks after original snapshot
Aug 8, 2026
6cb79d3
Validate legacy debug producer return type
Aug 8, 2026
1481f78
Revert unrelated Azure Functions tool install change
Aug 8, 2026
dcf4b09
Preserve launch override process args
Aug 8, 2026
024577a
Fix debug executable configuration snapshots
adamint Aug 9, 2026
24588ab
Reuse original argument resolutions across the debug rewrite
adamint Aug 9, 2026
5b8d2f3
Address review findings on debug launch configuration
adamint Aug 9, 2026
92901b8
Fix debug argument rewrite reuse and legacy overload validation
adamint Aug 10, 2026
64dd50d
Address debug launch context review feedback
Aug 10, 2026
3b95b28
Merge branch 'main' into adamint/issue-18956-launch-config-context
adamint Aug 10, 2026
a904ac7
Prevent later arg mutations from stripping debug-injected arguments
Aug 10, 2026
30d272c
Narrow debug launch context to resolved environment
Aug 10, 2026
35bea19
Clarify process-mode launch producer behavior
Aug 11, 2026
fa5c96c
Fix launch-override producer wiring
Aug 11, 2026
c5855b8
Clarify asynchronous debug producer guidance
Aug 11, 2026
7cdb91a
Cover the no-fallback rule when a launch configuration producer throws
Aug 11, 2026
b739005
Merge branch 'main' into adamint/issue-18956-launch-config-context
Aug 12, 2026
629c3ea
Merge remote-tracking branch 'upstream/main' into adamint/issue-18956…
Aug 13, 2026
a506a7a
Restore launch configuration inspection API
Aug 13, 2026
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
72 changes: 63 additions & 9 deletions src/Aspire.Hosting/ApplicationModel/DebugSupportExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,23 +107,77 @@ public static bool HasLaunchToolArgsOwnedBy(this IResource resource, SupportsDeb
/// <para>
/// Launch configuration is created by invoking the producer callback passed to
/// <see cref="ResourceBuilderExtensions.WithDebugSupport{T, TLaunchConfiguration}(IResourceBuilder{T}, Func{string, TLaunchConfiguration}, string)"/>
/// (or its asynchronous overload),
/// which owns the complete configuration; Aspire serializes the result as-is.
/// The configuration is produced fresh on each call; it is not a singleton.
/// Aspire may call the producer several times for the same resource.
/// (or one of its asynchronous overloads), which owns the complete configuration; Aspire serializes the result as-is.
/// The configuration is produced fresh on each call.
/// </para>
/// <para>
/// This describes the launch configuration itself, not whether one is going to be used.
/// Depending on how the application is started, or how a resource is configured,
/// Aspire may or may not run the resource under a debugger. Use <see cref="SupportsDebugging"/> to test for that.
/// This inspection API does not resolve the resource's environment variables. A producer that accepts a
/// <see cref="LaunchConfigurationCallbackContext"/> receives an empty
/// <see cref="LaunchConfigurationCallbackContext.EnvironmentVariables"/> collection. Aspire invokes that producer
/// separately with resolved values when it creates the executable.
/// </para>
/// <para>
/// This describes the launch configuration itself, not whether one is going to be used. Depending on how the
/// application is started or how a resource is configured, Aspire may or may not run the resource under a debugger.
/// Use <see cref="SupportsDebugging"/> to test for that.
/// </para>
/// </remarks>
[AspireExportIgnore(Reason = "Debug support inspection is a local .NET helper and is not part of the ATS surface.")]
public static Task<object> CreateLaunchConfigurationAsync(this IResource resource, string mode, CancellationToken cancellationToken = default)
public static Task<object> CreateLaunchConfigurationAsync(
this IResource resource,
string mode,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(resource);
ArgumentNullException.ThrowIfNull(mode);

var context = new LaunchConfigurationCallbackContext(
mode,
resource,
new Dictionary<string, string>(),
cancellationToken);

return resource.CreateLaunchConfigurationAsync(context);
}

/// <summary>
/// Creates the launch configuration that this resource sends to the IDE using a callback context.
/// </summary>
/// <param name="resource">The resource to inspect. It must carry a <see cref="SupportsDebuggingAnnotation"/>.</param>
/// <param name="context">The callback context containing the resolved environment and launch data.</param>
/// <returns>The launch configuration, typically an <see cref="ExecutableLaunchConfiguration"/>.</returns>
/// <exception cref="ArgumentException"><paramref name="context"/> belongs to a different resource.</exception>
/// <exception cref="InvalidOperationException">The resource does not declare debug launch support.</exception>
/// <remarks>
/// <para>
/// Launch configuration is created by invoking the producer callback passed to
/// <see cref="ResourceBuilderExtensions.WithDebugSupport{T, TLaunchConfiguration}(IResourceBuilder{T}, Func{LaunchConfigurationCallbackContext, Task{TLaunchConfiguration}}, string)"/>,
/// which owns the complete configuration; Aspire serializes the result as-is.
/// </para>
/// <para>
/// This method never resolves environment variables. Aspire creates <paramref name="context"/>
/// when the active debug-support annotation is producing a launch configuration for an executable creation.
/// </para>
/// <para>
/// This overload is internal because only Aspire constructs callback contexts containing resolved environment
/// variables. Use the public overload when inspecting a launch configuration outside executable creation.
/// </para>
/// </remarks>
internal static Task<object> CreateLaunchConfigurationAsync(
Comment thread
adamint marked this conversation as resolved.
this IResource resource,
LaunchConfigurationCallbackContext context)
{
ArgumentNullException.ThrowIfNull(resource);
ArgumentNullException.ThrowIfNull(context);

if (!ReferenceEquals(resource, context.Resource))
Comment thread
adamint marked this conversation as resolved.
{
throw new ArgumentException(
$"The launch configuration callback context belongs to resource '{context.Resource.Name}', " +
$"but launch configuration was requested for resource '{resource.Name}'.",
nameof(context));
}

if (!resource.TryGetLastAnnotation<SupportsDebuggingAnnotation>(out var supportsDebuggingAnnotation))
{
throw new InvalidOperationException(
Expand All @@ -132,7 +186,7 @@ public static Task<object> CreateLaunchConfigurationAsync(this IResource resourc
$"Note that it only adds the annotation in run mode.");
}

return supportsDebuggingAnnotation.LaunchConfigurationProducer(mode, cancellationToken);
return supportsDebuggingAnnotation.LaunchConfigurationProducer(context);
}

private static string[]? GetSupportedLaunchConfigurations(IConfiguration configuration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ public static class KnownLaunchConfigurationTypes
/// </para>
/// <para>
/// Integrations create a derived type and supply it through
/// <see cref="ResourceBuilderExtensions.WithDebugSupport{T, TLaunchConfiguration}(IResourceBuilder{T}, Func{string, TLaunchConfiguration}, string)"/>
/// or its asynchronous overload.
/// one of the <c>WithDebugSupport</c> overloads on <see cref="ResourceBuilderExtensions"/>.
/// </para>
/// </remarks>
/// <param name="type">The launch configuration type identifier, for example <see cref="KnownLaunchConfigurationTypes.Project"/>.</param>
Expand Down Expand Up @@ -90,8 +89,8 @@ public class ExecutableLaunchConfiguration(string type)
/// <remarks>
/// Defaults to <see cref="ExecutableLaunchMode.Debug"/> when a debugger is attached to the app host
/// and <see cref="ExecutableLaunchMode.NoDebug"/> otherwise. The mode requested by the IDE for the
/// current debug session is passed to the producer callback of
/// <see cref="ResourceBuilderExtensions.WithDebugSupport{T, TLaunchConfiguration}(IResourceBuilder{T}, Func{string, TLaunchConfiguration}, string)"/>.
/// current debug session is passed directly to mode-based producers and is available to context-based
/// producers through <see cref="LaunchConfigurationCallbackContext.Mode"/>.
/// </remarks>
[JsonPropertyName("mode")]
public string Mode { get; set; } = System.Diagnostics.Debugger.IsAttached ? ExecutableLaunchMode.Debug : ExecutableLaunchMode.NoDebug;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;

namespace Aspire.Hosting.ApplicationModel;

/// <summary>
/// Provides the runtime data used to create a launch configuration for a resource.
/// </summary>
/// <remarks>
/// Aspire creates this context after resolving the execution configuration for a specific executable
/// creation. Environment variable values may contain secrets; only copy values into the launch
/// configuration when the IDE requires them.
/// </remarks>
[Experimental("ASPIREEXTENSION001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")]
public sealed class LaunchConfigurationCallbackContext
{
internal LaunchConfigurationCallbackContext(
string mode,
IResource resource,
IReadOnlyDictionary<string, string> environmentVariables,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(mode);
ArgumentNullException.ThrowIfNull(resource);
ArgumentNullException.ThrowIfNull(environmentVariables);

Mode = mode;
Resource = resource;
EnvironmentVariables = environmentVariables;
CancellationToken = cancellationToken;
}

/// <summary>
/// Gets the requested launch mode, one of the values on <see cref="ExecutableLaunchMode"/>.
/// </summary>
public string Mode { get; }

/// <summary>
/// Gets the resource being launched.
/// </summary>
public IResource Resource { get; }

/// <summary>
/// Gets the resolved environment variables used for this executable creation.
/// </summary>
/// <remarks>
/// Values can contain secrets. Aspire serializes only the launch configuration returned by the
/// producer; integrations should copy only values required by the IDE.
/// </remarks>
public IReadOnlyDictionary<string, string> EnvironmentVariables { get; }

/// <summary>
/// Gets the cancellation token for this executable creation.
/// </summary>
public CancellationToken CancellationToken { get; }
}
2 changes: 1 addition & 1 deletion src/Aspire.Hosting/Dcp/DcpExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ public async Task RunApplicationAsync(CancellationToken ct = default)
{
containers = _containerCreator.PrepareObjects().ToArray();
_containerCreator.PrepareContainerExecutables();
executables = (await _executableCreator.PrepareObjectsAsync(ct).ConfigureAwait(false)).ToArray();
executables = _executableCreator.PrepareObjects(ct).ToArray();

prepareResourcesActivity.SetDcpPreparedResourceCounts(containers.Length, executables.Length);
}
Expand Down
Loading
Loading