Skip to content

Add project reference support for polyglot apphost integrations#14821

Merged
davidfowl merged 26 commits intorelease/13.2from
davidfowl/polyglot-project-refs
Mar 4, 2026
Merged

Add project reference support for polyglot apphost integrations#14821
davidfowl merged 26 commits intorelease/13.2from
davidfowl/polyglot-project-refs

Conversation

@davidfowl
Copy link
Copy Markdown
Contributor

Description

Adds project reference support for polyglot AppHost integrations, enabling integration authors to test hosting integrations locally before publishing to a NuGet feed.

How it works

In .aspire/settings.json, a package entry whose value ends in .csproj is treated as a project reference instead of a NuGet version:

{
  "packages": {
    "Aspire.Hosting.Redis": "13.2.0",
    "MyIntegration": "../src/MyIntegration/MyIntegration.csproj"
  }
}

Two resolution paths in PrebuiltAppHostServer.PrepareAsync:

  • NuGet-only (no project refs): Uses BundleNuGetService — no .NET SDK required
  • Project refs present: Generates a synthetic .csproj with <PackageReference> and <ProjectReference> items, runs dotnet build with CopyLocalLockFileAssemblies=true to produce the full transitive DLL closure directly into the integration libs path. Requires .NET SDK (checked before build).

The synthetic project is isolated from parent MSBuild imports via Directory.Build.props, Directory.Build.targets, and Directory.Packages.props (CPM opt-out).

Key changes

  • IntegrationReference record: represents both NuGet packages and project references
  • AspireJsonConfiguration.GetIntegrationReferences(): replaces GetAllPackages(), detects .csproj paths
  • IAppHostServerProject.PrepareAsync: signature changed to IEnumerable<IntegrationReference>
  • PrebuiltAppHostServer: NuGet-only → BundleNuGetService; project refs → dotnet build synthetic project
  • DotNetBasedAppHostServerProject: handles project refs as <ProjectReference> in generated csproj
  • SdkGenerateCommand / SdkDumpCommand: use PrepareAsync through interface (no more casting)

Fixes #14760

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
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No
  • Does the change require an update in our Aspire docs?

Copilot AI review requested due to automatic review settings March 1, 2026 07:13
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 1, 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 -- 14821

Or

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

Copy link
Copy Markdown
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

Adds support for using local .csproj project references (in addition to NuGet package versions) for polyglot AppHost integrations via .aspire/settings.json, enabling local integration authoring/testing without publishing to a feed.

Changes:

  • Introduces IntegrationReference and updates configuration parsing to treat values ending in .csproj as project references.
  • Updates AppHost server preparation flow to accept integration references across CLI paths (scaffolding, SDK generate/dump, sessions).
  • Adds unit and end-to-end tests covering synthetic project generation and TypeScript AppHost project-reference discovery/codegen.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/Aspire.Cli.Tests/TestServices/TestAppHostServerSessionFactory.cs Updates test session factory signature to use IntegrationReference.
tests/Aspire.Cli.Tests/Projects/PrebuiltAppHostServerTests.cs Adds unit tests for synthetic integration restore .csproj generation.
tests/Aspire.Cli.Tests/Projects/GuestAppHostProjectTests.cs Updates configuration tests to validate integration reference behavior (packages + project refs).
tests/Aspire.Cli.Tests/Projects/AppHostServerProjectTests.cs Updates project scaffolding tests to pass IntegrationReference objects.
tests/Aspire.Cli.Tests/Configuration/IntegrationReferenceTests.cs Adds unit tests for IntegrationReference and .csproj detection/path resolution.
tests/Aspire.Cli.EndToEnd.Tests/ProjectReferenceTests.cs Adds E2E scenario validating TS AppHost can use a local .NET integration project reference.
src/Aspire.Cli/Scaffolding/ScaffoldingService.cs Switches scaffolding preparation to use integration references.
src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs Implements project-reference path: synthetic .csproj + dotnet build to materialize DLL closure; retains NuGet-only path.
src/Aspire.Cli/Projects/IAppHostServerSession.cs Updates session factory API to accept integration references.
src/Aspire.Cli/Projects/IAppHostServerProject.cs Updates server project API to accept integration references.
src/Aspire.Cli/Projects/GuestAppHostProject.cs Updates integration gathering and codegen hash inputs to use integration references.
src/Aspire.Cli/Projects/DotNetBasedAppHostServerProject.cs Updates generated server project to handle explicit project references and new integration list type.
src/Aspire.Cli/Projects/AppHostServerSession.cs Wires session preparation to pass integration references.
src/Aspire.Cli/Projects/AppHostServerProject.cs Updates prebuilt project construction to include SDK installer dependency.
src/Aspire.Cli/Configuration/IntegrationReference.cs Adds record representing either a NuGet reference or a .csproj project reference.
src/Aspire.Cli/Configuration/AspireJsonConfiguration.cs Replaces package enumeration with GetIntegrationReferences supporting .csproj detection and path resolution.
src/Aspire.Cli/Commands/Sdk/SdkGenerateCommand.cs Uses IAppHostServerProject.PrepareAsync with integration references (removes concrete casting).
src/Aspire.Cli/Commands/Sdk/SdkDumpCommand.cs Uses IAppHostServerProject.PrepareAsync with optional project-reference integration.
Comments suppressed due to low confidence (2)

src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs:214

  • When copying nuget.config, appHostDirectory is computed as Path.GetDirectoryName(_appPath)!, which points to the parent of the AppHost directory. This can cause restores to miss the user’s nuget.config. Use _appPath as the app host directory.
        // Copy nuget.config from the user's apphost directory if present
        var appHostDirectory = Path.GetDirectoryName(_appPath)!;
        var userNugetConfig = Path.Combine(appHostDirectory, "nuget.config");
        if (File.Exists(userNugetConfig))
        {
            File.Copy(userNugetConfig, Path.Combine(restoreDir, "nuget.config"), overwrite: true);
        }

src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs:105

  • ResolveChannelNameAsync is called to read the local .aspire/settings.json channel, but inside it currently uses Path.GetDirectoryName(_appPath). Since _appPath is the AppHost directory, this will look in the parent directory and can ignore the app’s configured channel. Update ResolveChannelNameAsync to load from _appPath directly.
            // Resolve the configured channel (local settings.json → global config fallback)
            var channelName = await ResolveChannelNameAsync(cancellationToken);

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 1, 2026

🎬 CLI E2E Test Recordings

The following terminal recordings are available for commit b682686:

Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View Recording
AddPackageWhileAppHostRunningDetached ▶️ View Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View Recording
AgentInitCommand_WithMalformedMcpJson_ShowsErrorAndExitsNonZero ▶️ 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
CreateStartWaitAndStopAspireProject ▶️ 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
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 ❌ Upload failed

📹 Recordings uploaded automatically from CI run #22659199017

davidfowl and others added 18 commits March 1, 2026 10:21
Allow .aspire/settings.json packages entries to reference local .csproj files instead of NuGet versions. When a value ends in '.csproj', it's treated as a project reference: PrebuiltAppHostServer publishes it via dotnet publish and copies the full transitive DLL closure into the integration libs path. DotNetBasedAppHostServerProject adds ProjectReference elements to the generated csproj.

Introduces IntegrationReference record type to represent both NuGet packages and project references, replacing the (Name, Version) tuple throughout IAppHostServerProject.PrepareAsync and related APIs.

Fixes #14760

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the now-unused GetAllPackages methods from AspireJsonConfiguration. Update tests to use GetIntegrationReferences instead. Add test for project reference detection (.csproj paths).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ution

Replace the two-step approach (BundleNuGetService restore + per-project publish) with a single synthetic .csproj that includes all PackageReferences and ProjectReferences, then dotnet publish once to get the full transitive DLL closure. Remove BundleNuGetService dependency from PrebuiltAppHostServer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore BundleNuGetService for the NuGet-only path (no SDK required). Only use dotnet publish with a synthetic project when project references are present in settings.json (requires .NET SDK).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Build the synthetic integration project with OutDir pointing directly to the libs path, eliminating the need for dotnet publish and the extra copy step. Remove PublishProjectAsync from IDotNetCliRunner.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Check SDK availability before building project references and provide a clear error message if missing. Write Directory.Packages.props (CPM opt-out), Directory.Build.props, and Directory.Build.targets to the synthetic project directory to prevent parent MSBuild imports from interfering.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
SdkGenerateCommand and SdkDumpCommand now use IAppHostServerProject.PrepareAsync instead of casting to DotNetBasedAppHostServerProject and calling CreateProjectFilesAsync/BuildAsync directly. This means they work in both dev mode and bundle mode with project references.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
SDK commands no longer reference DotNetBasedAppHostServerProject.DefaultSdkVersion. The codegen package uses VersionHelper.GetDefaultTemplateVersion() (CLI version), and Aspire.Hosting comes transitively from the integration project reference.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tests cover IntegrationReference type properties, AspireJsonConfiguration.GetIntegrationReferences detection of .csproj paths, and PrebuiltAppHostServer.GenerateIntegrationProjectFile output (PackageReference, ProjectReference, OutDir, CopyLocalLockFileAssemblies, analyzer suppression).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tests the full flow: create a .NET hosting integration with [AspireExport], create a TypeScript AppHost, add the integration as a project reference in settings.json, start the AppHost, and verify the custom addMyService method appears in the generated TypeScript SDK.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace multiline cat heredocs with C# ExecuteCallback to write the integration project files and update settings.json. Reads sdkVersion from the settings.json that aspire init creates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Null-safe value handling in GetIntegrationReferences (trim, null check before .csproj detection)
- Clean stale libs directory before project ref build to prevent leftover DLLs
- Use XDocument for synthetic project generation instead of string interpolation (XML-safe)
- Filter SDK-only packages from ATS assembly list in DotNetBasedAppHostServerProject
- Restore xmldoc param comments on PrebuiltAppHostServer constructor

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update apphost.ts to use the custom integration (addMyService), then verify with aspire wait and aspire describe that the resource actually runs. Reduce timeouts to be more reasonable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ration

The MyIntegration directory is inside the workspace (./MyIntegration not ../MyIntegration). Also delete .modules/ before aspire start to force re-codegen with the new integration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
NuGetConfigMerger.CreateOrUpdateAsync is a no-op for implicit/hive channels. Instead, use GetNuGetSourcesAsync to get the actual feed URLs and write a nuget.config directly into the synthetic project directory. This ensures dotnet build can resolve Aspire packages from hive feeds in CI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PR #14811 removed ForceInstall from IDotNetSdkInstaller.CheckAsync, changing from 4-element to 3-element tuple.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@davidfowl davidfowl force-pushed the davidfowl/polyglot-project-refs branch from 659ee4c to 431f132 Compare March 1, 2026 18:22
davidfowl and others added 2 commits March 1, 2026 23:13
Capture and log stdout/stderr from dotnet build when the synthetic integration project fails, to help diagnose CI failures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
davidfowl and others added 2 commits March 2, 2026 07:50
…channel feeds

When the synthetic project has ProjectReferences, MSBuild restores the referenced project's packages from nuget.config nearest to that project's directory — not the synthetic project's directory. Add RestoreConfigFile to the synthetic .csproj so all projects in the build graph use our channel-aware nuget.config.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…esolution

MyIntegration.csproj needs to resolve Aspire.Hosting from the hive during dotnet build. MSBuild walks up from the project directory looking for nuget.config — without one in the workspace, it defaults to nuget.org which doesn't have prerelease versions. Write a nuget.config with hive sources in the workspace root.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@davidfowl
Copy link
Copy Markdown
Contributor Author

PR Testing Report

CLI Version: 13.2.0-pr.14821.g77549853 ✅ (matches head commit 77549853)

Scenarios Tested

Scenario Status Details
Basic CLI smoke test (aspire new) ✅ Passed C# and TypeScript empty AppHosts created successfully
Project reference (.csproj in settings.json) ✅ Passed Custom Aspire.Hosting.MyCustom integration built and linked via .csproj path. Dashboard launched.
NuGet package regression ✅ Passed Aspire.Hosting.Redis via NuGet version — 8 ATS capabilities discovered, dashboard launched

Key Observations

  • Project reference detection works correctly: PrebuiltAppHostServer logs Building integration project with 2 packages and 1 project references when a .csproj path is specified in settings.json.
  • Graceful error without .NET SDK: When .NET SDK is not installed, the CLI reports a clear error: "Project references in settings.json require .NET SDK 10.0.100 or later."
  • No regression for NuGet packages: Standard version-based package references continue to work as before.

Overall: ✅ Verified — Project reference support for polyglot AppHost integrations works as expected.

@davidfowl davidfowl requested review from JamesNK and mitchdenny March 3, 2026 08:13
Copy link
Copy Markdown
Member

@JamesNK JamesNK left a comment

Choose a reason for hiding this comment

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

Nice PR — the IntegrationReference abstraction is clean and the two resolution paths in PrebuiltAppHostServer.PrepareAsync (NuGet-only vs project refs) are well separated. The SDK commands (SdkDumpCommand / SdkGenerateCommand) are nicely simplified by routing through the interface instead of type-checking. Tests are thorough.

Main concern is around the NuGet configuration handling in BuildIntegrationProjectAsync:

  1. User nuget.config overwrite: If the user has a nuget.config and a channel is configured, the user's config is first copied then completely overwritten by the generated one. Custom sources are silently lost.
  2. RestoreConfigFile is graph-wide: This MSBuild property applies to the entire restore graph, including the referenced project's transitive dependencies. If the referenced project needs sources that aren't in the generated config, restore will fail.
  3. Unconditional nuget.org: Always adding nuget.org may be problematic in restricted environments.

These are related issues — recommend reconsidering the NuGet source strategy for the synthetic project. Options include: using RestoreAdditionalProjectSources instead of RestoreConfigFile, or merging channel sources into the user's existing config rather than overwriting it.

Smaller items: hardcoded net10.0 TFM (should share constant with DotNetBasedAppHostServerProject), method naming (GetAllPackagesAsyncGetAllIntegrationsAsync), and nullable safety around IntegrationReference.Version!.

@JamesNK
Copy link
Copy Markdown
Member

JamesNK commented Mar 3, 2026

@davidfowl Trying out AI code review 😄

davidfowl and others added 2 commits March 3, 2026 06:52
- Add IntegrationReference validation via factory methods (FromPackage/FromProject)
  ensuring exactly one of Version or ProjectPath is set
- Rename GetAllPackagesAsync to GetIntegrationReferencesAsync
- Add explicit null check for Version instead of null-forgiving operator
- Use DotNetBasedAppHostServerProject.TargetFramework constant instead of
  hardcoded "net10.0" in PrebuiltAppHostServer
- Replace RestoreConfigFile with RestoreAdditionalProjectSources MSBuild property
  to add channel sources without replacing the user's NuGet config
- Remove nuget.config generation with <clear/> and unconditional nuget.org addition
- Stop copying/overwriting user nuget.config (RestoreAdditionalProjectSources
  makes this unnecessary)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Align with PrebuiltAppHostServer: replace NuGetConfigMerger with RestoreAdditionalProjectSources for channel source resolution. This is additive and propagates to the entire restore graph including project references. Update test to verify csproj contains the correct sources instead of checking nuget.config.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- IntegrationReference: change from record to class (JamesNK)
- Rename 'packages' locals to 'integrations' where assigned from GetIntegrationReferencesAsync (JamesNK)
- Call GetNuGetSourcesAsync unconditionally in BuildIntegrationProjectAsync (sebastienros)
- Always regenerate codegen when project references are present by including timestamp in hash (sebastienros)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sebastienros
Copy link
Copy Markdown
Contributor

Can "MyIntegration": "../src/MyIntegration/MyIntegration.csproj" be using a name that doesn't match the project name? I think there is an issue in this case, need to find it again

@sebastienros
Copy link
Copy Markdown
Contributor

IntegrationReference.Name comes from the settings.json dictionary key and is used as the assembly name for Assembly.Load. If the key doesn't match the project's actual assembly name (e.g., "my-redis": "../MyRedis.csproj" => Assembly.Load("my-redis") fails), the integration silently won't be discovered.

Derive assembly name from Path.GetFileNameWithoutExtension(projectPath) for project references, or validate key matches csproj filename.

Add a custom MSBuild target to the synthetic project that writes resolved project reference assembly names to a file after build. Use these actual assembly names (which may differ from settings.json key or csproj filename if <AssemblyName> is overridden) for the AtsAssemblies list in appsettings.json. Move GenerateAppSettingsAsync to after build/restore in all cases for a simpler linear flow.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@davidfowl davidfowl merged commit 84ba0fc into release/13.2 Mar 4, 2026
760 of 763 checks passed
@davidfowl davidfowl deleted the davidfowl/polyglot-project-refs branch March 4, 2026 08:55
@dotnet-policy-service dotnet-policy-service bot added this to the 13.2 milestone Mar 4, 2026
Copilot AI pushed a commit that referenced this pull request Mar 10, 2026
* Add project reference support for polyglot apphost integrations

Allow .aspire/settings.json packages entries to reference local .csproj files instead of NuGet versions. When a value ends in '.csproj', it's treated as a project reference: PrebuiltAppHostServer publishes it via dotnet publish and copies the full transitive DLL closure into the integration libs path. DotNetBasedAppHostServerProject adds ProjectReference elements to the generated csproj.

Introduces IntegrationReference record type to represent both NuGet packages and project references, replacing the (Name, Version) tuple throughout IAppHostServerProject.PrepareAsync and related APIs.

Fixes #14760

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove GetAllPackages, migrate tests to GetIntegrationReferences

Remove the now-unused GetAllPackages methods from AspireJsonConfiguration. Update tests to use GetIntegrationReferences instead. Add test for project reference detection (.csproj paths).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use single dotnet publish for PrebuiltAppHostServer integration resolution

Replace the two-step approach (BundleNuGetService restore + per-project publish) with a single synthetic .csproj that includes all PackageReferences and ProjectReferences, then dotnet publish once to get the full transitive DLL closure. Remove BundleNuGetService dependency from PrebuiltAppHostServer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Only use dotnet publish when project references are present

Restore BundleNuGetService for the NuGet-only path (no SDK required). Only use dotnet publish with a synthetic project when project references are present in settings.json (requires .NET SDK).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use dotnet build with CopyLocalLockFileAssemblies instead of publish

Build the synthetic integration project with OutDir pointing directly to the libs path, eliminating the need for dotnet publish and the extra copy step. Remove PublishProjectAsync from IDotNetCliRunner.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add SDK check for project refs and isolate from parent MSBuild imports

Check SDK availability before building project references and provide a clear error message if missing. Write Directory.Packages.props (CPM opt-out), Directory.Build.props, and Directory.Build.targets to the synthetic project directory to prevent parent MSBuild imports from interfering.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove DotNetBasedAppHostServerProject casts from SDK commands

SdkGenerateCommand and SdkDumpCommand now use IAppHostServerProject.PrepareAsync instead of casting to DotNetBasedAppHostServerProject and calling CreateProjectFilesAsync/BuildAsync directly. This means they work in both dev mode and bundle mode with project references.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use CLI version for SDK commands, let project deps resolve transitively

SDK commands no longer reference DotNetBasedAppHostServerProject.DefaultSdkVersion. The codegen package uses VersionHelper.GetDefaultTemplateVersion() (CLI version), and Aspire.Hosting comes transitively from the integration project reference.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add unit tests for IntegrationReference and synthetic project generation

Tests cover IntegrationReference type properties, AspireJsonConfiguration.GetIntegrationReferences detection of .csproj paths, and PrebuiltAppHostServer.GenerateIntegrationProjectFile output (PackageReference, ProjectReference, OutDir, CopyLocalLockFileAssemblies, analyzer suppression).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add E2E test for project reference integration support

Tests the full flow: create a .NET hosting integration with [AspireExport], create a TypeScript AppHost, add the integration as a project reference in settings.json, start the AppHost, and verify the custom addMyService method appears in the generated TypeScript SDK.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify E2E test: use ExecuteCallback for file creation

Replace multiline cat heredocs with C# ExecuteCallback to write the integration project files and update settings.json. Reads sdkVersion from the settings.json that aspire init creates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review feedback

- Null-safe value handling in GetIntegrationReferences (trim, null check before .csproj detection)
- Clean stale libs directory before project ref build to prevent leftover DLLs
- Use XDocument for synthetic project generation instead of string interpolation (XML-safe)
- Filter SDK-only packages from ATS assembly list in DotNetBasedAppHostServerProject
- Restore xmldoc param comments on PrebuiltAppHostServer constructor

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use using directive for System.Xml.Linq

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Improve project reference E2E test with describe/wait verification

Update apphost.ts to use the custom integration (addMyService), then verify with aspire wait and aspire describe that the resource actually runs. Reduce timeouts to be more reasonable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix E2E test: use redis image instead of nonexistent myservice

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix E2E test: correct project reference path and force codegen regeneration

The MyIntegration directory is inside the workspace (./MyIntegration not ../MyIntegration). Also delete .modules/ before aspire start to force re-codegen with the new integration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix: write nuget.config with channel sources for project ref builds

NuGetConfigMerger.CreateOrUpdateAsync is a no-op for implicit/hive channels. Instead, use GetNuGetSourcesAsync to get the actual feed URLs and write a nuget.config directly into the synthetic project directory. This ensures dotnet build can resolve Aspire packages from hive feeds in CI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix CheckAsync tuple deconstruction after release/13.2 rebase

PR #14811 removed ForceInstall from IDotNetSdkInstaller.CheckAsync, changing from 4-element to 3-element tuple.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add build output logging for integration project failures

Capture and log stdout/stderr from dotnet build when the synthetic integration project fails, to help diagnose CI failures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Dump child log on E2E test failure for CI debugging

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix: use RestoreConfigFile to ensure project references resolve from channel feeds

When the synthetic project has ProjectReferences, MSBuild restores the referenced project's packages from nuget.config nearest to that project's directory — not the synthetic project's directory. Add RestoreConfigFile to the synthetic .csproj so all projects in the build graph use our channel-aware nuget.config.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix E2E test: write nuget.config in workspace for project reference resolution

MyIntegration.csproj needs to resolve Aspire.Hosting from the hive during dotnet build. MSBuild walks up from the project directory looking for nuget.config — without one in the workspace, it defaults to nuget.org which doesn't have prerelease versions. Write a nuget.config with hive sources in the workspace root.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address JamesNK review feedback

- Add IntegrationReference validation via factory methods (FromPackage/FromProject)
  ensuring exactly one of Version or ProjectPath is set
- Rename GetAllPackagesAsync to GetIntegrationReferencesAsync
- Add explicit null check for Version instead of null-forgiving operator
- Use DotNetBasedAppHostServerProject.TargetFramework constant instead of
  hardcoded "net10.0" in PrebuiltAppHostServer
- Replace RestoreConfigFile with RestoreAdditionalProjectSources MSBuild property
  to add channel sources without replacing the user's NuGet config
- Remove nuget.config generation with <clear/> and unconditional nuget.org addition
- Stop copying/overwriting user nuget.config (RestoreAdditionalProjectSources
  makes this unnecessary)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use RestoreAdditionalProjectSources in DotNetBasedAppHostServerProject

Align with PrebuiltAppHostServer: replace NuGetConfigMerger with RestoreAdditionalProjectSources for channel source resolution. This is additive and propagates to the entire restore graph including project references. Update test to verify csproj contains the correct sources instead of checking nuget.config.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address JamesNK and sebastienros review feedback

- IntegrationReference: change from record to class (JamesNK)
- Rename 'packages' locals to 'integrations' where assigned from GetIntegrationReferencesAsync (JamesNK)
- Call GetNuGetSourcesAsync unconditionally in BuildIntegrationProjectAsync (sebastienros)
- Always regenerate codegen when project references are present by including timestamp in hash (sebastienros)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Resolve project ref assembly names from MSBuild build output

Add a custom MSBuild target to the synthetic project that writes resolved project reference assembly names to a file after build. Use these actual assembly names (which may differ from settings.json key or csproj filename if <AssemblyName> is overridden) for the AtsAssemblies list in appsettings.json. Move GenerateAppSettingsAsync to after build/restore in all cases for a simpler linear flow.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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