Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 1, 2025

  • Investigate the issue: pnpm is not installed in the Docker image when generating Dockerfile for JavaScript apps
  • Understand the root cause: Node Docker images include npm and yarn (via corepack), but pnpm needs to be explicitly enabled via corepack enable pnpm
  • Initial fix: Add RUN corepack enable pnpm before pnpm commands in Dockerfile generation
  • Update test snapshots to reflect the new Dockerfile content
  • Add end-to-end test that builds the generated pnpm Dockerfile to verify it works
  • Refactor to use extension mechanism instead of special-casing on executable name
  • Build and run tests to verify the refactored code (75 tests pass)
  • Run code review and address feedback
  • Simplify XML documentation per code review feedback
  • Run security checks
Original prompt

This section details on the original issue you should resolve

<issue_title>WithPnpm breaks PublishAsDockerFile</issue_title>
<issue_description>### Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

.WithPnpm() plus .PublishAsDockerFile() causes aspire deploy to fail.

Other package managers, including .WithNpm() and .WithYarn() succeed.

There is an existing test that checks for a generated Dockerfile when using .WithPnpm(), but I believe the test does not check if the Dockerfile runs.

Expected Behavior

var builder = DistributedApplication.CreateBuilder(args);

builder.AddDockerComposeEnvironment("foo");

var frontend = builder.AddViteApp("frontend", "./frontend")
    // .WithNpm() // works with aspire deploy
    // .WithYarn() // works with aspire deploy
    .WithPnpm() // fails with aspire deploy
    .PublishAsDockerFile();

builder.Build().Run();

aspire deploy

Should deploy successfully, including successfully building the generated docker files.

Steps To Reproduce

Please see the README.md for https://github.com/yoDon/aspire-bug-report-pnpm

Steps:

  1. git clone https://github.com/yoDon/aspire-bug-report-pnpm.git
  2. cd aspire-bug-report-pnpm
  3. aspire deploy

Expected Behavior:
✓ PIPELINE SUCCEEDED

Actual Behavior:
✗ PIPELINE FAILED

Exceptions (if any)

dotnet/aspire#8 [stage-0 4/6] RUN --mount=type=cache,target=/pnpm/store pnpm install --prefer-frozen-lockfile
dotnet/aspire#8 0.110 /bin/sh: 1: pnpm: not found
dotnet/aspire#8 ERROR: process "/bin/sh -c pnpm install --prefer-frozen-lockfile" did not complete successfully: exit code: 127
------
 > [stage-0 4/6] RUN --mount=type=cache,target=/pnpm/store pnpm install --prefer-frozen-lockfile:
0.110 /bin/sh: 1: pnpm: not found
------
Dockerfile.frontend.46b84912aa404bd5938735a536f3c019:4
--------------------
   2 |     WORKDIR /app
   3 |     COPY package.json ./
   4 | >>> RUN --mount=type=cache,target=/pnpm/store pnpm install --prefer-frozen-lockfile
   5 |     COPY . .
   6 |     RUN pnpm run mybuild
--------------------
ERROR: failed to solve: process "/bin/sh -c pnpm install--prefer-frozen-lockfile" did not complete successfully: exit code: 127
✗ [ERR] docker buildx for frontend:ab49c32a8fc924bcd914c582c071cc65c309a8d4 failed with exit code 1.
✗ [ERR] Failed to build container image from Dockerfile for frontend
✗ [ERR] Step 'build-frontend' failed.
✗ Step 'build-frontend' failed: Docker build failed with exit code 1.

.NET Version info

.NET SDK:
Version: 10.0.100
Commit: b0f34d51fc
Workload version: 10.0.100-manifests.4c0ca8ba
MSBuild version: 18.0.2+b0f34d51f

Runtime Environment:
OS Name: Mac OS X
OS Version: 15.3
OS Platform: Darwin
RID: osx-arm64
Base Path: /usr/local/share/dotnet/sdk/10.0.100/

.NET workloads installed:
There are no installed workloads to display.
Configured to use workload sets when installing new manifests.
No workload sets are installed. Run "dotnet workload restore" to install a workload set.

Host:
Version: 10.0.0
Architecture: arm64
Commit: b0f34d51fc

.NET SDKs installed:
6.0.400 [/usr/local/share/dotnet/sdk]
6.0.404 [/usr/local/share/dotnet/sdk]
6.0.405 [/usr/local/share/dotnet/sdk]
7.0.101 [/usr/local/share/dotnet/sdk]
7.0.102 [/usr/local/share/dotnet/sdk]
8.0.100 [/usr/local/share/dotnet/sdk]
8.0.201 [/usr/local/share/dotnet/sdk]
9.0.308 [/usr/local/share/dotnet/sdk]
10.0.100 [/usr/local/share/dotnet/sdk]

.NET runtimes installed:
Microsoft.AspNetCore.App 6.0.8 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.12 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.13 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 7.0.1 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 7.0.2 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 8.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 8.0.2 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 9.0.11 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 10.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.8 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.12 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.13 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.1 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.2 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 8.0.0 [/usr/local/share/dotnet/sh...


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI changed the title [WIP] Fix issue with PublishAsDockerFile when using WithPnpm Fix pnpm Dockerfile generation by enabling pnpm via corepack Dec 1, 2025
Copilot AI requested a review from davidfowl December 1, 2025 09:16
Copilot finished work on behalf of davidfowl December 1, 2025 09:16
@github-actions
Copy link
Contributor

github-actions bot commented Dec 1, 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 -- 13267

Or

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

@davidfowl
Copy link
Member

@copilot As part of this test, attempt to add an end to end test in this vein #12898:

Proposal
Extend current test coverage to:
Build the Dockerfile into an image (e.g., using Docker.Build or equivalent CLI invocations from the test).
Run docker inspect and assert image metadata is correct.
Optionally, run the container and verify behavioral aspects (correctly launches expected entrypoint, serves HTTP endpoint if applicable, etc.).
Integrate this approach for Python app tests, but enable for any tests where Dockerfiles are generated (e.g. Aspire.Hosting.Containers.Tests, Aspire.Hosting.Python.Tests, etc.).
Should fail CI if the image does not build or fails verification.

We want to verify the end to end. These tests will have [RequireDocker]

Copy link
Contributor Author

Copilot AI commented Dec 1, 2025

@copilot As part of this test, attempt to add an end to end test in this vein #12898:

Proposal...

Added an end-to-end test VerifyPnpmDockerfileBuildSucceeds (5909a0b) that:

  1. Generates a pnpm Dockerfile using the Aspire infrastructure
  2. Verifies the Dockerfile contains corepack enable pnpm
  3. Builds the Dockerfile into a Docker image using docker build
  4. Asserts the build succeeds

The test is marked with [RequiresDocker] and [OuterloopTest] so it runs in the outerloop CI with Docker available.

Copilot finished work on behalf of davidfowl December 1, 2025 10:13
Comment on lines 275 to 277
if (packageManager.ExecutableName == "pnpm")
{
builderStage.Run("corepack enable pnpm");
Copy link
Member

Choose a reason for hiding this comment

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

@copilot - we shouldn't be special casing on the Executable name. Instead we should add an extension mechanism to the JavaScriptPackageManagerAnnotation that allows any package manager to insert "preinstall" commands to the dockerfile.

On the JavaScriptPackageManagerAnnotation class, add a new callback named InitializeDockerBuildStage that takes a DockerfileStage. This should be invoked in both places we create dockerfiles, even if there is no JavaScriptInstallCommandAnnotation.

The WithPnpm method then customizes the annotations InitializeDockerBuildStage to add corepack enable pnpm.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Refactored in commit d48f459:

  1. Added InitializeDockerBuildStage callback property to JavaScriptPackageManagerAnnotation
  2. Invoked the callback in both Dockerfile creation paths (AddNodeApp and CreateDefaultJavaScriptAppBuilder) after WORKDIR is set
  3. Removed the special-case check for "pnpm" from AddInstallCommand
  4. Updated WithPnpm to set the InitializeDockerBuildStage callback: stage => stage.Run("corepack enable pnpm")

The callback is now invoked right after WORKDIR /app is set, before any file operations or package installation.

…er setup

- Add InitializeDockerBuildStage callback to JavaScriptPackageManagerAnnotation
- Invoke callback in both Dockerfile creation paths after WORKDIR
- Move pnpm corepack logic from AddInstallCommand to WithPnpm method
- Update test snapshots to reflect new command order

Co-authored-by: eerhardt <[email protected]>
Copilot AI requested a review from eerhardt December 3, 2025 16:09
Copilot finished work on behalf of eerhardt December 3, 2025 16:09
@eerhardt eerhardt marked this pull request as ready for review December 3, 2025 17:20
Copilot AI review requested due to automatic review settings December 3, 2025 17:20
Copilot finished reviewing on behalf of eerhardt December 3, 2025 17:22
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 fixes pnpm-based JavaScript app Dockerfile generation by adding support for package manager-specific initialization commands. The issue was that pnpm is not installed by default in Node.js Docker images and must be explicitly enabled via corepack enable pnpm.

Key Changes:

  • Added an extensible callback mechanism to JavaScriptPackageManagerAnnotation for package managers to inject Docker build stage setup commands
  • Implemented the callback for pnpm to run corepack enable pnpm during Dockerfile generation
  • Added comprehensive end-to-end test that builds a Docker image to verify the fix works

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Aspire.Hosting.JavaScript/JavaScriptPackageManagerAnnotation.cs Added InitializeDockerBuildStage callback property (experimental API) to allow package managers to inject setup commands
src/Aspire.Hosting.JavaScript/JavaScriptHostingExtensions.cs Invoked the initialization callback in both Dockerfile generation code paths (AddNodeApp and CreateDefaultJavaScriptAppBuilder); set the callback for pnpm to enable corepack
tests/Aspire.Hosting.JavaScript.Tests/AddJavaScriptAppTests.cs Added VerifyPnpmDockerfileBuildSucceeds outerloop test that builds a Docker image to verify the generated Dockerfile works
tests/Aspire.Hosting.JavaScript.Tests/Snapshots/AddJavaScriptAppTests.VerifyPnpmDockerfile_hasLockFile=True.verified.txt Updated snapshot to include the RUN corepack enable pnpm command
tests/Aspire.Hosting.JavaScript.Tests/Snapshots/AddJavaScriptAppTests.VerifyPnpmDockerfile_hasLockFile=False.verified.txt Updated snapshot to include the RUN corepack enable pnpm command

Comment on lines 44 to 47
/// <summary>
/// Gets or sets a callback to initialize the Docker build stage before installing packages.
/// This can be used to add package manager-specific setup commands to the Dockerfile.
/// </summary>
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The XML documentation for InitializeDockerBuildStage should follow the internal API documentation guidelines. Since this property is marked with [Experimental] and is intended for internal use within the Dockerfile generation mechanism, the documentation should be more concise without the verbose "This can be used..." phrase.

Consider simplifying to:

/// <summary>
/// Gets or sets a callback to initialize the Docker build stage before installing packages.
/// </summary>

The detailed explanation about package manager-specific setup commands is better suited as inline comments where the callback is invoked (which is already done well in the implementation).

Copilot generated this review using guidance from repository custom instructions.
Copy link
Member

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Simplified in commit 0967907. Removed the verbose "This can be used..." phrase to align with internal API documentation guidelines.

Copilot AI requested a review from eerhardt December 3, 2025 17:43
Copilot finished work on behalf of eerhardt December 3, 2025 17:43
@davidfowl davidfowl merged commit b8947d8 into main Dec 4, 2025
290 of 292 checks passed
@davidfowl davidfowl deleted the copilot/fix-pnpm-publish-dockerfile branch December 4, 2025 06:15
@davidfowl
Copy link
Member

@radical we added an outer loop test here.

@dotnet-policy-service dotnet-policy-service bot added this to the 13.1 milestone Dec 4, 2025
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.

WithPnpm breaks PublishAsDockerFile

3 participants