Rethink packaging service: per-build static NuGet feeds in blob storage
- Dominant language
- C#
- Stars
- 6.3k
- Forks
- 991
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 196
Description
## Summary
The Aspire CLI's channel / `PackagingService` layer is one of the largest sources of late-stage shipping bugs and shiproom risk we have. Today the CLI resolves NuGet packages through **five different channel-specific code paths**: nuget.org for stable; a shared rolling `dnceng/public/_packaging/dotnet9` feed for daily; a SHA-derived `darc-pub-microsoft-aspire-` feed for staging (only synthesizable on stable-quality CLIs); a GitHub-Actions-artifact-plus-local-hive scheme for PRs (via `gh run download`); and a `~/.aspire/hives/*/packages` sideload for local. Resolution depends on the CLI's baked identity, the project channel, feature flags, override config, and the CLI version's commit hash, with quality-matrix fan-out behind it. PR / daily builds are exercised through completely different acquisition logic than what ships to customers, so packaging bugs routinely surface for the first time during release.
## Proposal
Replace all five branches with **one** code path:
- **Every CI build** (PR, main, release/\*, GA) publishes a complete, immutable, static NuGet v3 feed to public Azure Blob storage at a per-build URL: `builds///`. The feed is just files in a container; no service, no auth, no database to operate.
- **Channel names become tiny mutable pointer files**: `channels/daily.json`, `channels/staging.json`, etc. Each names the current per-build feed URL for that channel. Promotion (staging → stable, recall, rollback) is a pointer flip, not a rebuild.
- **The CLI's job collapses** to "fetch the pointer file → write a `NuGet.config` pointing at the URL it names → hand off to NuGet." The polymorphic `channel` field also accepts literal URLs, so PR builds and local hives use exactly the same code path with a different URL scheme.
- **Stable still resolves to nuget.org by default** — the blob feed is opt-in for stable. Customer-visible behavior does not change. What changes is the internal pipeline that produces the bits stable eventually publishes.
- **Promote-not-rebuild for staging → stable.** Staging IS the GA candidate; promotion is a pointer flip on already-signed bytes. Eliminates the "we tested an RC and rebuilt for GA" risk class.
- **Per-build path immutability** + a separate locked-down publish workflow (the only holder of the federated identity, triggered `workflow_run` from `main`) prevents a PR from redirecting uploads or tampering with already-published feeds.
The result: almost all of today's channel-resolution machinery — the quality matrix, staging synthesis gating, SHA extraction, PR hive discovery, local hive sideload, override config keys, `gh` dependency in acquisition scripts — becomes deletable. Testing a PR build, a daily, a staging RC, and a stable GA all exercise the **same** code path with the same shape, which is precisely what we don't have today.
## Side benefits
- Version filtering and prerelease selection logic in the CLI is dramatically simplified — each channel resolves to one specific feed URL with one set of packages.
- Concurrent agentic development with multiple local hives in different worktrees falls out for free; no machine-global "active hive" selection.
- CLI identity moves to a co-located `aspire-cli.identity.json` written at install time — same binary across channels, enables local "fake stable / staging" debugging without a rebuild.
- Acquisition scripts (`get-aspire-cli*.sh` / `.ps1`) drop their `gh` dependency.
## Full design
The complete design, including security model, threat surface, open questions (provenance / signing of static feeds, lock-file mechanics for polyglot, identity-file schema, retention, etc.), goals / non-goals, and scope of impact lives in the spec PR:
- **Spec PR:** #17578
- **Spec file:** [`docs/specs/per-build-feeds.md`](https://github.com/microsoft/aspire/blob/mitchdenny/rethink-packaging-service/docs/specs/per-build-feeds.md)
The implementation sequencing will be worked out when we pick up the work; this issue tracks the overall effort.
Contributor guide
Assessment
This issue has not been assessed yet.