Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 31, 2025

  • Add default OutputPath and IntermediateOutputPath to PipelineContext.cs
  • Make OutputPath non-nullable in PipelineStepContext.cs and expose IntermediateOutputPath
  • Remove CLI default output path logic in PublishCommand.cs
  • Remove exception throws for null OutputPath in publishing contexts (Azure, Docker, Kubernetes)
  • Update PublishingContextUtils.cs to handle non-nullable OutputPath
  • Build and test changes (all tests passed: CLI 6/6, Docker 56/56, Kubernetes 10/10)
  • Refactor to use IPipelineOutputService per feedback from @captainsafia and @davidfowl
    • Created IPipelineOutputService interface with GetOutputDirectory() and GetTempDirectory() methods
    • Implemented PipelineOutputService with better naming (avoiding MSBuild-specific "IntermediateOutputPath")
  • Add resource-specific directory support per @davidfowl feedback
    • Added GetOutputDirectory(IResource) and GetTempDirectory(IResource) overloads
    • Creates resource-specific subdirectories under base paths using resource.Name
    • Enables per-resource isolation of output and temp artifacts
  • Remove OutputPath convenience property per @davidfowl feedback
    • Removed OutputPath from PipelineContext and PipelineStepContext
    • Consumers explicitly call OutputService.GetOutputDirectory()
  • Remove OutputService from PipelineContext per @davidfowl feedback
    • Registered IPipelineOutputService in DI container
    • Removed OutputService property from contexts
    • Steps explicitly resolve IPipelineOutputService from Services when needed
  • Simplify PipelineOutputService constructor and use resource-specific methods
    • PipelineOutputService injects IOptions and IConfiguration directly
    • Simplified DI registration without factory lambda
    • PublishingContextUtils uses GetOutputDirectory(environment) instead of manual path combination
  • Add comprehensive XML documentation and update diagnostic ID
    • Updated to ASPIREPIPELINES004 diagnostic ID per @captainsafia feedback
    • Documented default fallback behavior for GetOutputDirectory()
    • Added XML comments for private fields
    • Documented AppHost:PathSha256 usage for temp directory isolation

Summary

This PR implements the fix for default output path handling during aspire deploy/publish, based on PR #12414 but retaining PublishingContextUtils as requested.

Key Changes

  1. Created IPipelineOutputService - Service pattern for managing pipeline output directories
    • GetOutputDirectory() / GetOutputDirectory(IResource) - Returns output directory for deployment artifacts (defaults to aspire-output)
    • GetTempDirectory() / GetTempDirectory(IResource) - Returns temporary directory for build artifacts
    • Resource-specific overloads create subdirectories using resource name for per-resource isolation
    • Implemented by PipelineOutputService injecting IOptions<PipelineOptions> and IConfiguration
    • Uses ASPIREPIPELINES004 diagnostic ID
  2. Registered in DI container - Available via context.Services.GetRequiredService<IPipelineOutputService>()
  3. Not exposed via PipelineContext - Steps explicitly resolve service from DI when needed
  4. Kept PublishingContextUtils (unlike PR Set default OutputPath and add IntermediateOutputPath at PipelineContext level #12414) - uses resource-specific service methods
  5. Removed CLI default logic - now handled centrally via the service
  6. Comprehensive documentation - XML docs explain defaults, field purposes, and temp directory isolation via AppHost SHA

Testing Results

✅ CLI tests (DeployCommand): 6/6 passed
✅ Docker tests: 56/56 passed
✅ Kubernetes tests: 10/10 passed

Addresses feedback from @captainsafia and @davidfowl for service-based design with explicit DI resolution, resource-specific directory support, and comprehensive documentation.

Fixes #12412

Original prompt

Open a new PR based on #12414 but leave publishingcontextutils alone


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI changed the title [WIP] Open new PR based on existing changes in #12414 Set default OutputPath and IntermediateOutputPath at PipelineContext level Oct 31, 2025
Copilot AI requested a review from davidfowl October 31, 2025 16:00
@captainsafia
Copy link
Member

I have two disagreements with this change:

  • IntermediateOutputPath is not a great name for what is effectively a temporary directory for the pipeline. I think we're biasing too much MSBuild here and this term is extremely difficult to understand for people who don't already know what that term means.
  • Long-term, I think we want to move away from having state-specific things in the PipelineContext. I'd anticipate that these properties eventually become a part of the IDeploymentStateManager (or whatever we end up renaming it too) since the output path you use will likely differ based on the context in which the pipeline is being executed.

@davidfowl
Copy link
Member

@copilot Take safia's feedback and expose:

Yeah, something like IPipelineOutputService that exposes methods for getting each directory type

- Create IPipelineOutputService interface with GetOutputDirectory() and GetTempDirectory() methods
- Implement PipelineOutputService with better naming than IntermediateOutputPath
- Expose service via PipelineContext.OutputService and PipelineStepContext.OutputService
- Keep backward compatible OutputPath property for existing consumers
- Remove direct IntermediateOutputPath property per feedback

Co-authored-by: davidfowl <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 31, 2025

@copilot Take safia's feedback and expose:

Yeah, something like IPipelineOutputService that exposes methods for getting each directory type

Implemented in 106cfb6. Created IPipelineOutputService interface with GetOutputDirectory() and GetTempDirectory() methods. The service is exposed via PipelineContext.OutputService and handles all output path logic, including the default and temporary directory creation. Removed IntermediateOutputPath property entirely.

Copilot AI changed the title Set default OutputPath and IntermediateOutputPath at PipelineContext level Set default OutputPath and expose IPipelineOutputService for pipeline directories Oct 31, 2025
@davidfowl
Copy link
Member

@copilot On IPipelineOutputService make it possible to get the output directory and temp directory for a specific resource

- Add GetOutputDirectory(IResource) overload for resource-specific output directories
- Add GetTempDirectory(IResource) overload for resource-specific temp directories
- Implementation creates subdirectories using resource.Name under base directories
- Enables per-resource isolation of output and temp artifacts

Co-authored-by: davidfowl <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 31, 2025

@copilot On IPipelineOutputService make it possible to get the output directory and temp directory for a specific resource

Implemented in e0d5ffd. Added GetOutputDirectory(IResource) and GetTempDirectory(IResource) overloads to IPipelineOutputService. The implementation creates resource-specific subdirectories using the resource name under the base output and temp directories, enabling per-resource isolation of artifacts.

@davidfowl
Copy link
Member

@copilot Remove the OutputPath from PipelineContext and OutputService. Any step that needs it can resolve it from IPipelineOutputService explicitly.

- Remove OutputPath convenience property per @davidfowl feedback
- Consumers now explicitly call OutputService.GetOutputDirectory()
- Update AzureEnvironmentResource to use OutputService.GetOutputDirectory()
- Update PublishingContextUtils to use OutputService.GetOutputDirectory()
- Update test helper to use OutputService.GetOutputDirectory()
- Keep OutputService property for explicit access to output management

Co-authored-by: davidfowl <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 31, 2025

@copilot Remove the OutputPath from PipelineContext and OutputService. Any step that needs it can resolve it from IPipelineOutputService explicitly.

Implemented in a650bd9. Removed OutputPath property from both PipelineContext and PipelineStepContext. Consumers now explicitly call OutputService.GetOutputDirectory() to get the output path. Updated AzureEnvironmentResource, PublishingContextUtils, and test helpers to use the explicit service calls.

/// Gets the service for managing pipeline output directories.
/// </summary>
public string? OutputPath { get; } = outputPath;
public IPipelineOutputService OutputService { get; } = new PipelineOutputService(outputPath, serviceProvider.GetRequiredService<IConfiguration>());
Copy link
Member

Choose a reason for hiding this comment

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

@copilot remove this from PipelineContext

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Implemented in a481f66. Removed OutputService from PipelineContext and registered IPipelineOutputService in the DI container instead. Steps now explicitly resolve the service via context.Services.GetRequiredService<IPipelineOutputService>() when needed. Also removed the outputPath parameter from PipelineContext constructor since it's now handled by the DI-registered service.

- Register IPipelineOutputService in DI container with PipelineOptions
- Remove OutputService property from PipelineContext and PipelineStepContext
- Remove outputPath parameter from PipelineContext constructor
- Update all consumers to resolve IPipelineOutputService from Services
  - AzureEnvironmentResource
  - PublishingContextUtils
  - JsonDocumentManifestPublisher test helper
- Remove IOptions<PipelineOptions> dependency from PipelineExecutor
- Steps now explicitly resolve IPipelineOutputService when needed

Co-authored-by: davidfowl <[email protected]>
Copilot AI changed the title Set default OutputPath and expose IPipelineOutputService for pipeline directories Register IPipelineOutputService in DI for pipeline output directory management Oct 31, 2025
Copilot AI requested a review from davidfowl October 31, 2025 20:13
@davidfowl
Copy link
Member

@copilot PublishingContextUtils should use GetOutputDirectory(environment) when there's a compute environment.

…specific overload

- PipelineOutputService now injects IOptions<PipelineOptions> and IConfiguration directly in constructor
- Simplified DI registration to use standard AddSingleton without factory lambda
- PublishingContextUtils uses GetOutputDirectory(environment) for resource-specific paths
- Removes manual path combination logic in favor of service method

Co-authored-by: davidfowl <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 31, 2025

@copilot PublishingContextUtils should use GetOutputDirectory(environment) when there's a compute environment.

Implemented in da9a619. PublishingContextUtils now uses GetOutputDirectory(environment) when there are multiple compute environments, leveraging the resource-specific overload instead of manually combining paths. Also updated PipelineOutputService to inject IOptions<PipelineOptions> and IConfiguration directly in the constructor, simplifying the DI registration.

@github-actions
Copy link
Contributor

github-actions bot commented Oct 31, 2025

🚀 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 -- 12563

Or

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

@davidfowl davidfowl marked this pull request as ready for review October 31, 2025 20:31
@davidfowl davidfowl requested a review from mitchdenny as a code owner October 31, 2025 20:31
Copilot AI review requested due to automatic review settings October 31, 2025 20:31
Copy link
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 PR refactors the pipeline output path handling by introducing a new IPipelineOutputService to centralize output directory management. Previously, the output path was passed through the PipelineContext constructor and stored as a property. Now, a dedicated service manages output paths, temporary directories, and resource-specific paths, providing better encapsulation and defaulting behavior.

Key Changes

  • Introduced IPipelineOutputService interface and PipelineOutputService implementation for centralized output directory management
  • Removed OutputPath property from PipelineContext and PipelineStepContext
  • Updated all consumers to retrieve output paths via IPipelineOutputService.GetOutputDirectory()
  • Modified CLI to only pass output path when explicitly provided (previously defaulted to "aspire-output" in CLI)

Reviewed Changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/Aspire.Hosting/Pipelines/IPipelineOutputService.cs New interface defining output directory and temp directory methods
src/Aspire.Hosting/Pipelines/PipelineOutputService.cs Implementation providing default output directory logic and temp directory management
src/Aspire.Hosting/Pipelines/PipelineContext.cs Removed outputPath parameter and OutputPath property
src/Aspire.Hosting/Pipelines/PipelineStepContext.cs Removed OutputPath property that delegated to PipelineContext
src/Aspire.Hosting/Publishing/PipelineExecutor.cs Removed dependency on IOptions<PipelineOptions> and outputPath construction
src/Aspire.Hosting/DistributedApplicationBuilder.cs Registered IPipelineOutputService as singleton
src/Shared/PublishingContextUtils.cs Updated to use IPipelineOutputService for environment-specific output paths
src/Aspire.Hosting.Azure/AzureEnvironmentResource.cs Changed to use output service instead of context property, removed error validation
src/Aspire.Hosting.Docker/DockerComposePublishingContext.cs Removed null check that threw exception for OutputPath
src/Aspire.Hosting.Kubernetes/KubernetesPublishingContext.cs Removed null check that threw exception for OutputPath
src/Aspire.Cli/Commands/PublishCommand.cs Only passes output path when explicitly provided instead of defaulting
tests/Aspire.Hosting.Tests/Pipelines/DistributedApplicationPipelineTests.cs Updated test helper to use new PipelineContext constructor
tests/Aspire.Hosting.Tests/Helpers/JsonDocumentManifestPublisher.cs Updated to retrieve manifest path via output service

_tempDirectory = new Lazy<string>(() => CreateTempDirectory(configuration));
}

/// <inheritdoc/>
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

The default fallback path 'aspire-output' should be documented in the XML documentation for the GetOutputDirectory() method. Currently, the documentation only states 'Gets the output directory for deployment artifacts' without mentioning that it defaults to {CurrentDirectory}/aspire-output when no output path is configured.

Suggested change
/// <inheritdoc/>
/// <summary>
/// Gets the output directory for deployment artifacts.
/// If no output path is configured, defaults to <c>{CurrentDirectory}/aspire-output</c>.
/// </summary>

Copilot uses AI. Check for mistakes.
Comment on lines 16 to 17
{
private readonly string? _outputPath;
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

These private fields lack XML documentation comments. Per the XML documentation standards, internal implementation fields should have brief summary tags explaining their purpose, especially for a service class like this.

Suggested change
{
private readonly string? _outputPath;
{
/// <summary>
/// Stores the resolved output directory path, or <c>null</c> if not specified.
/// </summary>
private readonly string? _outputPath;
/// <summary>
/// Lazily creates and stores the path to the temporary directory for pipeline output.
/// </summary>

Copilot uses AI. Check for mistakes.
Comment on lines +56 to +67
private static string CreateTempDirectory(IConfiguration configuration)
{
var appHostSha = configuration["AppHost:PathSha256"];

if (!string.IsNullOrEmpty(appHostSha))
{
return Directory.CreateTempSubdirectory($"aspire-{appHostSha}").FullName;
}

// Fallback if AppHost:PathSha256 is not available
return Directory.CreateTempSubdirectory("aspire").FullName;
}
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

The purpose of using AppHost:PathSha256 for temp directory naming is not documented. Add an XML comment explaining why this SHA is used (likely for isolation between different app hosts) and what happens when it's not available.

Copilot uses AI. Check for mistakes.
/// <summary>
/// Service for managing pipeline output directories.
/// </summary>
[Experimental("ASPIREPIPELINES001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")]
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
[Experimental("ASPIREPIPELINES001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")]
[Experimental("ASPIREPIPELINES004", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")]

Let's create a new code for this to stop overloading on the code for the core APIs.

Copy link
Member

Choose a reason for hiding this comment

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

@copilot Update the diagnostic ID introduced in this Experimental attribute.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Implemented in c01acb7. Updated the diagnostic ID to ASPIREPIPELINES004 for both IPipelineOutputService and PipelineOutputService. Also added comprehensive XML documentation including default behavior for GetOutputDirectory(), field purposes, and the AppHost:PathSha256 isolation mechanism for temp directories.

- Update Experimental attribute from ASPIREPIPELINES001 to ASPIREPIPELINES004
- Add XML documentation for GetOutputDirectory() default behavior
- Add XML documentation for private fields in PipelineOutputService
- Document purpose of AppHost:PathSha256 usage in CreateTempDirectory
- Suppress ASPIREPIPELINES004 diagnostic in all consuming files

Co-authored-by: captainsafia <[email protected]>
@davidfowl davidfowl merged commit 5886086 into main Nov 1, 2025
296 checks passed
@davidfowl davidfowl deleted the copilot/open-new-pr-based-on-12414 branch November 1, 2025 00:15
@dotnet-policy-service dotnet-policy-service bot added this to the 13.1 milestone Nov 1, 2025
@davidfowl
Copy link
Member

/backport to release/13.0

@github-actions
Copy link
Contributor

github-actions bot commented Nov 1, 2025

Started backporting to release/13.0: https://github.com/dotnet/aspire/actions/runs/18988279905


if (!string.IsNullOrEmpty(appHostSha))
{
return Directory.CreateTempSubdirectory($"aspire-{appHostSha}").FullName;
Copy link
Member

Choose a reason for hiding this comment

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

How does this directory get cleaned up?

Copy link
Member

Choose a reason for hiding this comment

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

It doesnt' yet (same as all of the other temp directories today in aspire), We don't even us this yet unfortunately. Just flowing it through the system.

Copy link
Member

Choose a reason for hiding this comment

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

Should we make it implement IDisposable and delete the directory when it gets disposed?

@github-actions github-actions bot locked and limited conversation to collaborators Dec 4, 2025
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.

Default output path set from CLI not respected during aspire deploy

4 participants