Summary
When the CLI resolves the local channel (or any PR/local hive channel created in PackagingService.cs:42-61), the channel correctly maps Aspire* packages to the hive's packages/ directory in code, but BundleNuGetService invokes nuget restore without that path as a --source. Restore then falls through to the public feeds and picks up whichever public preview happens to satisfy the version constraint, completely bypassing the locally built nupkgs.
Repro
- Build the repo:
./build.sh --pack /p:SkipNativeBuild=true (produces Aspire.Hosting.JavaScript.13.4.0-dev.nupkg etc. in artifacts/packages).
- Stage them into a hive directory:
<repo>/.aspire/hives/local/packages/*.nupkg.
- Use the
aspire-managed from the same build, with aspire.config.json pinning channel: "local" and Aspire.Hosting.JavaScript: 13.4.0-dev.
- Run
aspire restore --debug from a TypeScript apphost.
Observed
[dbug] PrebuiltAppHostServer: Resolved channel: local
[dbug] BundleNuGetService: NuGet restore args: nuget restore ... \
--package Aspire.Hosting.JavaScript,13.4.0-dev ... \
--source https://api.nuget.org/v3/index.json \
--source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json
The local hive directory is never passed as a --source. NuGet then reaches out to the public feeds and resolves 13.4.0-preview.1.26229.5 (because 13.4.0-dev < 13.4.0-preview.* and --package X,Y means "min version Y"). The extracted DLLs come from the wrong build, so any new APIs in the local hive (e.g. WithWorkspaceRoot) are silently absent from the generated TypeScript SDK / loaded assemblies.
Expected
The resolved channel's PackageMapping set should be reflected verbatim in the nuget restore --source arguments. For local/PR hives, that means including the on-disk <hive>/packages path. With it, restore correctly resolves 13.4.0-dev from the hive (verified by adding a NuGet.config containing <add key="local-hive" value="/workspace/.aspire/hives/local/packages" /> as a workaround — restore then pulls Aspire.Hosting.JavaScript/13.4.0-dev and codegen produces the expected withWorkspaceRoot binding).
Workaround
Drop a NuGet.config in the apphost directory (or any ancestor) that adds the hive packages path as a feed:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="local-hive" value="/path/to/.aspire/hives/local/packages" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>
Impact
- Inner-loop dogfooding of CLI changes is broken: even with a freshly packed local hive, restore silently pulls the public preview, masking any newly added APIs.
- PR-build channels likely have the same issue: the
prHive.Name channel is created with a packages mapping, but if BundleNuGetService doesn't propagate that mapping into the restore command, PR validation runs against whatever is on the public feeds.
Pointers
src/Aspire.Cli/Packaging/PackagingService.cs:42-61 — channel mapping construction
src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs — channel resolution & restore wiring
BundleNuGetService — emits the actual nuget restore command with --source args (this is where the mappings need to land)
Summary
When the CLI resolves the
localchannel (or any PR/local hive channel created inPackagingService.cs:42-61), the channel correctly mapsAspire*packages to the hive'spackages/directory in code, butBundleNuGetServiceinvokesnuget restorewithout that path as a--source. Restore then falls through to the public feeds and picks up whichever public preview happens to satisfy the version constraint, completely bypassing the locally built nupkgs.Repro
./build.sh --pack /p:SkipNativeBuild=true(producesAspire.Hosting.JavaScript.13.4.0-dev.nupkgetc. in artifacts/packages).<repo>/.aspire/hives/local/packages/*.nupkg.aspire-managedfrom the same build, withaspire.config.jsonpinningchannel: "local"andAspire.Hosting.JavaScript: 13.4.0-dev.aspire restore --debugfrom a TypeScript apphost.Observed
The local hive directory is never passed as a
--source. NuGet then reaches out to the public feeds and resolves13.4.0-preview.1.26229.5(because13.4.0-dev<13.4.0-preview.*and--package X,Ymeans "min version Y"). The extracted DLLs come from the wrong build, so any new APIs in the local hive (e.g.WithWorkspaceRoot) are silently absent from the generated TypeScript SDK / loaded assemblies.Expected
The resolved channel's
PackageMappingset should be reflected verbatim in thenuget restore --sourcearguments. Forlocal/PR hives, that means including the on-disk<hive>/packagespath. With it, restore correctly resolves13.4.0-devfrom the hive (verified by adding aNuGet.configcontaining<add key="local-hive" value="/workspace/.aspire/hives/local/packages" />as a workaround — restore then pullsAspire.Hosting.JavaScript/13.4.0-devand codegen produces the expectedwithWorkspaceRootbinding).Workaround
Drop a
NuGet.configin the apphost directory (or any ancestor) that adds the hive packages path as a feed:Impact
prHive.Namechannel is created with a packages mapping, but ifBundleNuGetServicedoesn't propagate that mapping into the restore command, PR validation runs against whatever is on the public feeds.Pointers
src/Aspire.Cli/Packaging/PackagingService.cs:42-61— channel mapping constructionsrc/Aspire.Cli/Projects/PrebuiltAppHostServer.cs— channel resolution & restore wiringBundleNuGetService— emits the actualnuget restorecommand with--sourceargs (this is where the mappings need to land)