Summary
Today the bundled Aspire CLI always embeds bundle.tar.gz as a manifest resource and self-extracts it at runtime (BundleService.EnsureExtractedAsync). This means the CLI needs write permissions to its installation directory regardless of how it was distributed.
For archive and installer distributions the layout is already fully extracted on disk — self-extraction is unnecessary. Only the dotnet-tool distribution (Aspire.Cli.Tool.csproj) genuinely needs self-extraction because NuGet enforces a fixed package layout that cannot include the pre-extracted bundle.
Proposal
Make self-extraction conditional by introducing an MSBuild property that gets baked into the binary at build time.
Mechanism
- New MSBuild property — e.g.
<SelfExtractingBundle>true</SelfExtractingBundle> — controls whether the CLI binary should perform self-extraction at runtime.
- Bake into the binary — surface the property value as assembly metadata (e.g.
[assembly: AssemblyMetadata("SelfExtractingBundle", "true")]) or a compile-time constant so BundleService can read it without additional files.
- Set per distribution mode:
Aspire.Cli.Tool.csproj (dotnet-tool): set SelfExtractingBundle=true — payload is embedded and extracted on first run.
eng/clipack projects (archives/installers): set SelfExtractingBundle=false (or omit, defaulting to false) — the layout is already on disk.
- Update
BundleService — check the new flag alongside the existing IsBundle (embedded resource) check. When the flag is false, skip extraction and rely on the pre-extracted layout discovered via BundleDiscovery.
Benefits
- No write permissions required for installers/archives — the CLI can run from read-only installation directories (e.g.
/usr/local/, Program Files).
- Faster first-run for archive installs — no extraction step needed.
- dotnet-tool continues to work — self-extraction is preserved where it is actually necessary due to NuGet packaging constraints.
Key files
| File |
Role |
src/Aspire.Cli/Bundles/BundleService.cs |
Runtime self-extraction logic; IsBundle / EnsureExtractedAsync |
src/Aspire.Cli/Aspire.Cli.csproj |
Embeds bundle.tar.gz when BundlePayloadPath is set |
src/Aspire.Cli/Aspire.Cli.Tool.csproj |
dotnet-tool packaging; sets IsCliToolProject=true |
eng/clipack/Common.projitems |
Shared build logic for archive/installer distributions |
src/Shared/BundleDiscovery.cs |
Layout discovery (works for both extracted and pre-extracted layouts) |
Summary
Today the bundled Aspire CLI always embeds
bundle.tar.gzas a manifest resource and self-extracts it at runtime (BundleService.EnsureExtractedAsync). This means the CLI needs write permissions to its installation directory regardless of how it was distributed.For archive and installer distributions the layout is already fully extracted on disk — self-extraction is unnecessary. Only the dotnet-tool distribution (
Aspire.Cli.Tool.csproj) genuinely needs self-extraction because NuGet enforces a fixed package layout that cannot include the pre-extracted bundle.Proposal
Make self-extraction conditional by introducing an MSBuild property that gets baked into the binary at build time.
Mechanism
<SelfExtractingBundle>true</SelfExtractingBundle>— controls whether the CLI binary should perform self-extraction at runtime.[assembly: AssemblyMetadata("SelfExtractingBundle", "true")]) or a compile-time constant soBundleServicecan read it without additional files.Aspire.Cli.Tool.csproj(dotnet-tool): setSelfExtractingBundle=true— payload is embedded and extracted on first run.eng/clipackprojects (archives/installers): setSelfExtractingBundle=false(or omit, defaulting tofalse) — the layout is already on disk.BundleService— check the new flag alongside the existingIsBundle(embedded resource) check. When the flag isfalse, skip extraction and rely on the pre-extracted layout discovered viaBundleDiscovery.Benefits
/usr/local/,Program Files).Key files
src/Aspire.Cli/Bundles/BundleService.csIsBundle/EnsureExtractedAsyncsrc/Aspire.Cli/Aspire.Cli.csprojbundle.tar.gzwhenBundlePayloadPathis setsrc/Aspire.Cli/Aspire.Cli.Tool.csprojIsCliToolProject=trueeng/clipack/Common.projitemssrc/Shared/BundleDiscovery.cs