Shirubasoft.Aspire.E2E 2.0.4

dotnet add package Shirubasoft.Aspire.E2E --version 2.0.4
                    
NuGet\Install-Package Shirubasoft.Aspire.E2E -Version 2.0.4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Shirubasoft.Aspire.E2E" Version="2.0.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Shirubasoft.Aspire.E2E" Version="2.0.4" />
                    
Directory.Packages.props
<PackageReference Include="Shirubasoft.Aspire.E2E" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Shirubasoft.Aspire.E2E --version 2.0.4
                    
#r "nuget: Shirubasoft.Aspire.E2E, 2.0.4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Shirubasoft.Aspire.E2E@2.0.4
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Shirubasoft.Aspire.E2E&version=2.0.4
                    
Install as a Cake Addin
#tool nuget:?package=Shirubasoft.Aspire.E2E&version=2.0.4
                    
Install as a Cake Tool

Aspire E2E

NuGet Build & Test License: MIT

Share .NET Aspire services across multiple repositories. A service runs as a Project in its home repo and as a Container everywhere else — with a single declaration.

The Problem

In a multi-repo setup, you want payments-service to:

  • Run as a Project in the payments repo (for inner-loop development with hot reload, debugging, etc.)
  • Run as a Container in the frontend repo (consuming it as a dependency)

Aspire has no built-in way to do this. You'd need to maintain separate AppHost configurations, manually build container images, and keep them in sync.

How It Works

Aspire E2E introduces a SharedResourceReference MSBuild item and a global configuration file (~/.aspire-e2e/resources.json) that tracks how each shared service should run. A Roslyn source generator creates type-safe builders at compile time, and MSBuild targets handle container image builds automatically.

graph TD
    CLI["CLI tool<br/><code>a2a</code>"] -->|reads/writes| Config["~/.aspire-e2e/<br/>resources.json"]
    MSBuild["MSBuild targets<br/>(at build time)"] -->|reads| Config
    Config -->|feeds| Generator["Source generator<br/>(typed builders)"]

Core Flows

Register (producer)

Set up a repo's service projects as shared resources. The CLI discovers .csproj files, injects EnableSdkContainerSupport and ContainerRepository properties, and saves the resource definitions to the global config. In interactive mode, all resource fields are prompted (container image, tag, build settings, registry, GitHub repository). You can also write a full SharedResourceReference with metadata directly into an AppHost .csproj in the same git repo — the project path is stored relative to the AppHost so references work across machines. This is the producer's home repo — before any consumer exists.

See samples/register for a working example.

Init (consumer)

Bootstrap an AppHost to consume shared resources. The CLI adds the hosting package, injects AddGlobalResourceConfiguration(), and scaffolds type-safe builder.Add*() calls from a multi-select prompt. This is the consumer's repo — pulling in dependencies registered elsewhere.

See samples/init for a working example.

Import (onboarding)

Onboard a new developer or CI environment. When you clone a repo whose AppHost already has SharedResourceReference elements with <GitHubRepository> metadata, import enriches entries from already-cloned repos found at the parent of the git root. If a repo isn't already cloned, it auto-clones it (unless --no-auto-clone is set). This decouples enrichment from cloning — pre-existing clones are always used regardless of auto-clone settings. Import also reads .aspire-e2e.json manifests from cloned repos, preferring manifest data over csproj SharedResourceReference elements.

See samples/import for a working example.

Manifest (universal)

For repos without an AppHost — including non-.NET repos (Go, Node, Python), SQL projects (.sqlproj), or .NET services that don't need an AppHost — you can declare shared resources in a .aspire-e2e.json manifest file at the repo root. The manifest uses the same format as ~/.aspire-e2e/resources.json (without the Overrides section).

a2a register creates a manifest automatically when no .csproj files are found (generic mode), or when you choose "Save to repo manifest" in interactive mode. a2a import reads manifests from cloned repos and merges their entries into the global config. For .NET projects, ProjectPath in the manifest is relative to the manifest file location.

{
  "Aspire": {
    "Resources": {
      "payments-api": {
        "Id": "payments-api",
        "Name": "PaymentsApi",
        "Mode": "Container",
        "ContainerImage": "payments-api",
        "ContainerTag": "latest",
        "BuildImage": true,
        "BuildImageCommand": "docker build -t payments-api .",
        "ImageRegistry": "ghcr.io/myorg",
        "GitHubRepository": "myorg/payments"
      }
    }
  }
}

Packages

Package Description
Shirubasoft.Aspire.E2E MSBuild targets to enable container support on service projects
Shirubasoft.Aspire.E2E.Hosting Runtime types, MSBuild targets, and source generator for AppHost projects
Shirubasoft.Aspire.E2E.Cli Global CLI tool for managing shared resources

Getting Started

The CLI has three core flows: register, init, and import.

1. Install the CLI tool

dotnet tool install -g Shirubasoft.Aspire.E2E.Cli

2. Register (producer): discover and register projects

Use register to find projects and register them as shared resources. It discovers all .csproj files, lets you pick which ones to share, and automatically injects MSBuild container support properties. If no .csproj files are found, it enters generic mode for non-.NET repos and creates a .aspire-e2e.json manifest:

a2a register /path/to/payments-repo

In interactive mode, all resource fields are prompted: resource ID, name, mode, container image, container tag, build image, build image command, image registry, and GitHub repository. After registration, you can optionally write a full SharedResourceReference element with metadata into an AppHost .csproj in the same git repo. The project path is stored relative to the AppHost directory so that references remain valid across different machines and checkout locations.

Use --defaults to skip interactive prompts and auto-generate resource configuration:

a2a register /path/to/payments-repo --defaults

This saves discovered resources to ~/.aspire-e2e/resources.json and injects EnableSdkContainerSupport and ContainerRepository properties into each selected .csproj.

3. Init (consumer): configure an AppHost

Bootstrap an AppHost project with the hosting package, global configuration, and shared resource references:

a2a init ./src/MyAppHost/MyAppHost.csproj

If the AppHost path is omitted, it's auto-detected from the current directory (or from the --search path). This:

  • Adds the Shirubasoft.Aspire.E2E.Hosting package reference
  • Injects AddGlobalResourceConfiguration() into the entry point file (Program.cs or AppHost.cs)
  • Shows a multi-select prompt to pick which registered resources to wire up
  • Adds <SharedResourceReference> elements to the .csproj
  • Scaffolds builder.Add*() calls in the entry point file

To combine discovery and initialization in one step, use --search:

a2a init --search /path/to/repo

4. Import (onboarding): import from an existing AppHost

When you clone a repo that already has SharedResourceReference elements in its AppHost .csproj, use import to populate your global config:

a2a import ./src/MyAppHost/MyAppHost.csproj

This:

  • Reads SharedResourceReference elements from the .csproj
  • Reads .aspire-e2e.json manifests from the repo root and from cloned repos (preferred over csproj)
  • Enriches entries from already-cloned repos at the parent of the git root
  • Auto-clones GitHub repositories when <GitHubRepository> is present and the repo isn't already cloned
  • Populates global config from discovered data
  • Adds the hosting package and scaffolds builder calls

Use --no-auto-clone to skip automatic cloning:

a2a import ./src/MyAppHost/MyAppHost.csproj --no-auto-clone

5. Use in your AppHost code

var builder = DistributedApplication.CreateBuilder(args);

builder.Configuration.AddGlobalResourceConfiguration();

var payments = builder.AddPaymentsService()
    .ConfigureProject(project => project
        .WithHttpHealthCheck("/health"))
    .ConfigureContainer(container => container
        .WithHttpEndpoint(targetPort: 8080)
        .WithHttpHealthCheck("/health"));

builder.Build().Run();

The AddPaymentsService() method is generated at compile time by the source generator. ConfigureProject and ConfigureContainer only execute their callbacks when the resource is in the matching mode.

Use Cases

Multi-repo microservices

A team owns orders-service in its own repo. Other teams reference it in their AppHosts as a container. The orders team runs it as a project for development:

# In orders repo — set mode to Project
a2a update orders-service --mode Project

# In frontend repo — set mode to Container
a2a update orders-service --mode Container

CI/CD container builds

The CLI can build container images with git-aware tagging:

a2a build payments-service

This runs the configured build command, tags the image with the current git branch and commit hash, and updates the global config with the new tag.

Switching modes during development

Temporarily switch a dependency to project mode for debugging, then switch back:

a2a update payments-service --mode Project --project-path /path/to/payments.csproj
# ... debug the issue ...
a2a update payments-service --mode Container

Skip image builds for faster iteration

When iterating on your own service, skip rebuilding container images for dependencies:

a2a update orders-service --build-image false

CLI Commands

Command Description
a2a register [path] Discover projects and register them as shared resources
a2a init [apphost-path] Configure an AppHost to consume shared resources
a2a import <csproj-path> Import shared resources from a .csproj into global config
a2a list Show all registered resources (--json for JSON output)
a2a info <id> Show detailed info for a resource (--json for JSON output)
a2a update <id> Update a resource configuration
a2a remove <id> Remove a resource
a2a build <id> Build a container image with git tagging
a2a get-mode <id> Get the current mode (machine-readable)
a2a get-project-path <id> Get the project path (machine-readable)
a2a get-config <id> <key> Get a config value (machine-readable)
a2a modes Interactively toggle Project/Container mode for resources
a2a clear Delete all resources from the global configuration
a2a override set <key> <value> Set a global override (Mode, BuildImage, or AutoClone, case-insensitive)
a2a override set-registry <from> <to> Add an image registry rewrite rule
a2a override set-image <from> <to> Add an image rewrite rule (e.g. rabbitmq:4-managementrabbitmq:4)
a2a override remove <key> Remove a global override (case-insensitive key)
a2a override remove-registry <from> Remove a registry rewrite rule
a2a override remove-image <from> Remove an image rewrite rule
a2a override list Show current overrides
a2a override clear Remove all overrides

Configuration

Resources are stored in ~/.aspire-e2e/resources.json:

{
  "Aspire": {
    "Overrides": {
      "Mode": "Container",
      "BuildImage": false,
      "AutoClone": true,
      "ImageRegistryRewrites": {
        "docker.io": "ghcr.io/myorg"
      },
      "ImageRewrites": {
        "rabbitmq:4-management": "rabbitmq:4"
      }
    },
    "Resources": {
      "payments-service": {
        "Id": "payments-service",
        "Name": "PaymentsService",
        "Mode": "Container",
        "ProjectPath": "/path/to/payments-service.csproj",
        "ContainerImage": "payments-service",
        "ContainerTag": "main-abc1234",
        "BuildImage": true,
        "BuildImageCommand": "dotnet publish --os linux --arch x64 /t:PublishContainer",
        "ImageRegistry": "ghcr.io/myorg",
      "GitHubRepository": "myorg/payments-service"
      }
    }
  }
}

Overrides

The Overrides section applies values to all resources at load time without modifying the saved configuration. This is useful for switching every resource to container mode in CI, or rewriting image registries across environments.

  • Mode — overrides the Mode on every resource
  • BuildImage — overrides BuildImage on every resource
  • AutoClone — controls whether import automatically clones GitHub repositories when a GitHubRepository tag is present (default: true)
  • ImageRegistryRewrites — dictionary of from → to rewrites applied to each resource's ImageRegistry
  • ImageRewrites — dictionary of from → to rewrites applied to each resource's full image (ContainerImage:ContainerTag). For example, "rabbitmq:4-management": "rabbitmq:4" rewrites any resource with that exact image and tag.

Override keys in CLI commands (set, remove) are case-insensitive.

Overrides can also be set in a local e2e-resources.json file. Local overrides merge on top of global overrides.

Auto-clone and enrichment

When a SharedResourceReference includes a <GitHubRepository> tag (e.g. myorg/myrepo), import looks for the repository one level above the current git repo root. If the repo directory already exists, the entry is enriched from it regardless of auto-clone settings. If the directory doesn't exist and auto-clone is enabled, import clones the repository first, then enriches.

The clone prefers gh repo clone (GitHub CLI) and falls back to git clone https://github.com/{org/repo}.git.

To disable auto-clone for a single invocation:

a2a import ./MyAppHost.csproj --no-auto-clone

To disable globally:

a2a override set AutoClone false

Requirements

  • .NET 10.0 or later
  • .NET Aspire 13.1 or later
  • Docker or Podman (for container mode)

Contributing

Contributions are welcome. Please open an issue first to discuss what you'd like to change.

License

MIT

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.4 350 2/1/2026
2.0.2 338 2/1/2026
1.0.53 334 2/1/2026
1.0.49 338 2/1/2026
1.0.47 339 2/1/2026
1.0.44 337 2/1/2026
1.0.41 339 2/1/2026
1.0.39 335 2/1/2026
1.0.36 348 2/1/2026
1.0.33 350 1/31/2026
1.0.31 344 1/31/2026
1.0.20 335 1/31/2026
1.0.18 333 1/31/2026
1.0.6 334 1/31/2026
1.0.5 337 1/31/2026
1.0.3 341 1/31/2026
0.1.54 338 1/31/2026
0.1.53 338 1/31/2026
0.1.43 340 1/31/2026
0.1.39 344 1/31/2026
Loading failed