Skip to content

Fix polyglot codegen: include base types in capability target expansion#15099

Merged
IEvangelist merged 6 commits intorelease/13.2from
copilot/fix-withnpm-constraint
Mar 12, 2026
Merged

Fix polyglot codegen: include base types in capability target expansion#15099
IEvangelist merged 6 commits intorelease/13.2from
copilot/fix-withnpm-constraint

Conversation

Copy link
Contributor

Copilot AI commented Mar 10, 2026

Description

BuildTypeCompatibilityMap registered each concrete type under its interfaces and base types, but not under its own type ID. When expanding capabilities constrained to a base type (e.g., WithNpm<T>() where T : JavaScriptAppResource), the base type itself was missing from expanded targets — only derived types like NodeAppResource and ViteAppResource received the method.

Fix: Add self-registration in BuildTypeCompatibilityMap so the expansion of JavaScriptAppResource yields [JavaScriptAppResource, NodeAppResource, ViteAppResource] instead of just [NodeAppResource, ViteAppResource].

// Before: only registered under interfaces and base types
// After: also registered under own type ID
AddToCompatibilityMap(typeToCompatibleTypes, typeInfo.AtsTypeId, concreteTypeRef);

This also fixes the same class of bug for ContainerResource, ExecutableResource, and ProjectResource — their type-specific capabilities now correctly appear on the base type builders too.

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
    • No
  • Does the change require an update in our Aspire docs?
    • Yes
    • No
Original prompt

This section details on the original issue you should resolve

<issue_title>Polyglot - withNpm method generated with wrong constraint</issue_title>
<issue_description>### Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

What calling aspire add javascript the generated API for withNpm is constrained to NodeAppResourcePromise - when it should instead be the JavaScriptAppResourcePromise type:

    /** @internal */
    private async _withNpmInternal(install?: boolean, installCommand?: string, installArgs?: string[]): Promise<NodeAppResource> {
        const rpcArgs: Record<string, unknown> = { resource: this._handle };
        if (install !== undefined) rpcArgs.install = install;
        if (installCommand !== undefined) rpcArgs.installCommand = installCommand;
        if (installArgs !== undefined) rpcArgs.installArgs = installArgs;
        const result = await this._client.invokeCapability<NodeAppResourceHandle>(
            'Aspire.Hosting.JavaScript/withNpm',
            rpcArgs
        );
        return new NodeAppResource(result, this._client);
    }

    /** Configures npm as the package manager */
    withNpm(options?: WithNpmOptions): NodeAppResourcePromise {
        const install = options?.install;
        const installCommand = options?.installCommand;
        const installArgs = options?.installArgs;
        return new NodeAppResourcePromise(this._withNpmInternal(install, installCommand, installArgs));
    }

Expected Behavior

No response

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version info

No response

Anything else?

No response</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 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.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 10, 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 -- 15099

Or

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

Copilot AI changed the title [WIP] Fix withNpm method generated with wrong constraint Fix polyglot codegen: include base types in capability target expansion Mar 10, 2026
Copilot AI and others added 3 commits March 12, 2026 07:04
…atibility map

In BuildTypeCompatibilityMap, each concrete type was only registered under
its interfaces and base types, but not under its own type ID. This meant that
when expanding capabilities constrained to a base type like JavaScriptAppResource,
the base type itself was missing from the expanded targets.

For example, WithNpm<TResource>() where TResource : JavaScriptAppResource would
only generate withNpm on NodeAppResource and ViteAppResource, but not on
JavaScriptAppResource itself.

The fix adds self-registration so base types with derived types are always
included when expanding capabilities that target them directly.

Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com>
@IEvangelist IEvangelist force-pushed the copilot/fix-withnpm-constraint branch from f05ab53 to c52777b Compare March 12, 2026 13:11
@IEvangelist IEvangelist marked this pull request as ready for review March 12, 2026 13:20
Copilot AI review requested due to automatic review settings March 12, 2026 13:20
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

Fixes ATS/polyglot capability target expansion so capabilities constrained to a base type include the base type itself (not only derived types), ensuring TypeScript codegen emits methods on base resource builders like JavaScriptAppResource, ContainerResource, ExecutableResource, and ProjectResource.

Changes:

  • Register each concrete type under its own ATS type ID in BuildTypeCompatibilityMap to ensure base types are included in expanded targets.
  • Update TypeScript snapshot output to reflect newly generated methods appearing on base resource builder types.
  • Add regression tests covering JavaScript package-manager capability expansion; add JS project reference for test compilation.

Reviewed changes

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

File Description
src/Aspire.Hosting/Ats/AtsCapabilityScanner.cs Fixes capability expansion by self-registering concrete types under their own type ID.
tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts Updates expected generated TS output now that base builder types receive their capabilities.
tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/AtsTypeScriptCodeGeneratorTests.cs Adds regression tests for JavaScript capability expansion behavior.
tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests.csproj Adds project reference to Aspire.Hosting.JavaScript for the new scanner tests.

You can also share your feedback on Copilot code review. Take the survey.

@github-actions
Copy link
Contributor

The transient CI rerun workflow requested reruns for the following jobs after analyzing the failed attempt.
GitHub's job rerun API also reruns dependent jobs, so the retry is being tracked in the rerun attempt.
The job links below point to the failed attempt that matched the retry-safe transient failure rules.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 12, 2026

🎬 CLI E2E Test Recordings

The following terminal recordings are available for commit 91b8b59:

Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View Recording
AddPackageWhileAppHostRunningDetached ▶️ View Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_DefaultSelection_InstallsSkillOnly ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View Recording
AspireAddPackageVersionToDirectoryPackagesProps ▶️ View Recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View Recording
Banner_DisplayedOnFirstRun ▶️ View Recording
Banner_DisplayedWithExplicitFlag ▶️ View Recording
CreateAndDeployToDockerCompose ▶️ View Recording
CreateAndDeployToDockerComposeInteractive ▶️ View Recording
CreateAndPublishToKubernetes ▶️ View Recording
CreateAndRunAspireStarterProject ▶️ View Recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View Recording
CreateAndRunJsReactProject ▶️ View Recording
CreateAndRunPythonReactProject ▶️ View Recording
CreateAndRunTypeScriptStarterProject ▶️ View Recording
CreateEmptyAppHostProject ▶️ View Recording
CreateStartAndStopAspireProject ▶️ View Recording
CreateTypeScriptAppHostWithViteApp ▶️ View Recording
DescribeCommandResolvesReplicaNames ▶️ View Recording
DescribeCommandShowsRunningResources ▶️ View Recording
DetachFormatJsonProducesValidJson ▶️ View Recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
LogsCommandShowsResourceLogs ▶️ View Recording
PsCommandListsRunningAppHost ▶️ View Recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View Recording
RestoreGeneratesSdkFiles ▶️ View Recording
RunWithMissingAwaitShowsHelpfulError ▶️ View Recording
SecretCrudOnDotNetAppHost ▶️ View Recording
SecretCrudOnTypeScriptAppHost ▶️ View Recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View Recording
StopAllAppHostsFromAppHostDirectory ▶️ View Recording
StopAllAppHostsFromUnrelatedDirectory ▶️ View Recording
StopNonInteractiveMultipleAppHostsShowsError ▶️ View Recording
StopNonInteractiveSingleAppHost ▶️ View Recording
StopWithNoRunningAppHostExitsSuccessfully ▶️ View Recording
TypeScriptAppHostWithProjectReferenceIntegration ▶️ View Recording

📹 Recordings uploaded automatically from CI run #23006253745

Copy link
Contributor

@sebastienros sebastienros left a comment

Choose a reason for hiding this comment

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

Apply the copilot suggestions

@sebastienros
Copy link
Contributor

Update all the other polyglot snapshots (go, java, ...)

IEvangelist and others added 3 commits March 12, 2026 09:05
…riptCodeGeneratorTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Implemented methods for configuring container resources, including:
  - `withBindMount`: Adds a bind mount.
  - `withEntrypoint`: Sets the container entrypoint.
  - `withImageTag`: Sets the container image tag.
  - `withImageRegistry`: Sets the container image registry.
  - `withImage`: Sets the container image with optional tag.
  - `withImageSHA256`: Sets the image SHA256 digest.
  - `withContainerRuntimeArgs`: Adds runtime arguments for the container.
  - `withLifetime`: Sets the lifetime behavior of the container resource.
  - `withImagePullPolicy`: Sets the container image pull policy.
  - `publishAsContainer`: Configures the resource to be published as a container.
  - `withDockerfile`: Configures the resource to use a Dockerfile.
  - `withContainerName`: Sets the container name.
  - `withBuildArg`: Adds a build argument from a parameter resource.
  - `withBuildSecret`: Adds a build secret from a parameter resource.
  - `withEndpointProxySupport`: Configures endpoint proxy support.
  - `withContainerNetworkAlias`: Adds a network alias for the container.
  - `publishAsConnectionString`: Publishes the resource as a connection string.
  - `withVolume`: Adds a volume.

- Added similar methods for executable resources and project resources, including:
  - `publishAsDockerFile`: Publishes the executable as a Docker container.
  - `publishAsDockerFileWithConfigure`: Publishes an executable as a Docker file with optional container configuration.
  - `withExecutableCommand`: Sets the executable command.
  - `withWorkingDirectory`: Sets the executable working directory.
  - `withReplicas`: Sets the number of replicas for a project.
  - `disableForwardedHeaders`: Disables forwarded headers for the project.
  - `publishAsDockerFile`: Publishes a project as a Docker file with optional container configuration.
@IEvangelist IEvangelist merged commit ce6defb into release/13.2 Mar 12, 2026
254 checks passed
@IEvangelist IEvangelist deleted the copilot/fix-withnpm-constraint branch March 12, 2026 15:01
@dotnet-policy-service dotnet-policy-service bot added this to the 13.2 milestone Mar 12, 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.

4 participants