Skip to content

Add DNX AppHost CLI invocation opt-in - #18331

Draft
David Fowler (davidfowl) wants to merge 1 commit into
mainfrom
davidfowl/dnx-apphost-opt-in
Draft

David Fowler (davidfowl) wants to merge 1 commit into
mainfrom
davidfowl/dnx-apphost-opt-in

Conversation

@davidfowl

Copy link
Copy Markdown
Collaborator

Description

This explores a safer opt-in path for AppHosts that need the Aspire CLI bundle without requiring a global aspire command on PATH. The new AspireCliInvocationMode=Dnx mode lets a repo route AppHost launch through the repo-local Aspire.Cli .NET tool package when that tool has been restored.

User-facing usage

Projects can opt in with:

<PropertyGroup>
  <AspireCliInvocationMode>Dnx</AspireCliInvocationMode>
</PropertyGroup>

With Aspire.Cli declared in the repo's local tool manifest and restored, dotnet run computes a launch command shaped like:

dnx aspire.cli -- run --project "<AppHost>" --no-build -- ...

This intentionally does not pass dnx -y. A restored local tool manifest runs without prompting; a missing or unrestored tool fails with the .NET local-tool restore message instead of silently downloading and running an ad-hoc package.

Implementation notes

  • Keeps the existing PATH/global CLI behavior as the default.
  • Preserves AspireCliPath as the highest-precedence explicit override.
  • Allows missing build-time CLI bundle metadata only for DNX mode when no explicit AspireCliPath or AspireCliBundlePath was configured. The launching CLI remains responsible for injecting DCP/Dashboard paths at runtime.
  • Keeps explicit invalid CLI or bundle paths failing with ASPIRE009.

Security considerations

This adds an opt-in process-spawning path through dnx, which can execute a repo-declared local tool after dotnet tool restore. The implementation avoids dnx -y so AppHost launch does not silently install or execute an ad-hoc package when the manifest/tool is missing.

Validation

./restore.sh
dotnet test --project tests/Aspire.Hosting.Sdk.Tests/Aspire.Hosting.Sdk.Tests.csproj --no-launch-profile -- --filter-class "*.AppHostSdkTargetsTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
dotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj --no-launch-profile -- --filter-class "*.MSBuildTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
dotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj --no-launch-profile -- --filter-method "*.CliBundleDnxInvocationAllowsMissingBuildTimeBundle" --filter-method "*.CliBundleDnxInvocationFailsWhenExplicitBundlePathIsInvalid" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"

Fixes # (issue)

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

Copilot AI review requested due to automatic review settings June 19, 2026 06:11
@github-actions

github-actions Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor

🚀 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/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 18331

Or

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

@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds an opt-in AspireCliInvocationMode=Dnx mode to the AppHost SDK targets, allowing an AppHost's dotnet run to route its launch through the repo-local Aspire.Cli .NET tool via dnx instead of requiring a global aspire command on PATH. The default PATH-based behavior and the explicit AspireCliPath override are preserved, and the build-time CLI bundle metadata is allowed to be absent only in DNX mode when no explicit CLI/bundle path was configured (the launching CLI injects DCP/Dashboard paths at runtime).

Changes:

  • Adds _AspireCliInvocationMode/_AspireUseDnxCliRunHook/_AspireCliDnxPackageId plumbing and DNX-specific RunCommand/RunArguments (dnx aspire.cli -- run ..., deliberately without dnx -y) in Aspire.Hosting.AppHost.in.targets.
  • Makes the ASPIRE009 error and the orchestration/dashboard discovery-attribute targets conditional on _AspireCliBundleResolutionOptional so DNX mode tolerates a missing build-time bundle, while explicit invalid paths still fail.
  • Adds unit/integration coverage for the new run-command computation (including case-insensitive mode) and the bundle-resolution-optional behavior.
Show a summary per file
File Description
src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets Introduces the DNX invocation mode, its run-command/args, and the optional bundle-resolution gating of ASPIRE009 and discovery targets.
tests/Aspire.Hosting.Sdk.Tests/AppHostSdkTargetsTests.cs Adds tests asserting the computed RunCommand/RunArguments for DNX mode (case-insensitive) and the AspireCliPath override precedence.
tests/Aspire.Hosting.Tests/MSBuildTests.cs Adds tests that DNX mode allows a missing build-time bundle yet still fails ASPIRE009 when an explicit invalid AspireCliBundlePath is provided.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 0

@MattKotsenas

Matt Kotsenas (MattKotsenas) commented Jun 19, 2026 •

Copy link
Copy Markdown
Member

If you're requiring a tool, devs will increasingly use a tool manifest to keep dev environments consistent. As such, you also need the aspire upgrade motion to update the tool manifest (it might already, sorry on phone).

@github-actions

This comment has been minimized.

Introduce an opt-in AppHost CLI invocation mode that uses DNX to run the repo-local Aspire CLI package while preserving the existing PATH-based default. Add a preflight ASPIRE011 diagnostic when DNX mode is selected but the dnx command is unavailable, while keeping explicit AspireCliPath overrides working.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 23, 2026 07:43
@davidfowl
David Fowler (davidfowl) force-pushed the davidfowl/dnx-apphost-opt-in branch from 61be9b3 to cfd95f5 Compare June 23, 2026 07:43
@github-actions

Copy link
Copy Markdown
Contributor

Tests selector (audit mode)

The full test matrix and all jobs still run in audit mode. The tests and jobs below are what selective CI would run under enforcement.

40 / 97 test projects · 2 jobs, from 5 changed files.

Selected test projects (40 / 97)

Aspire.EndToEnd.Tests, Aspire.Hosting.Analyzers.Tests, Aspire.Hosting.Azure.Kubernetes.Tests, Aspire.Hosting.Azure.Kusto.Tests, Aspire.Hosting.Azure.Tests, Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Browsers.Tests, Aspire.Hosting.Containers.Tests, Aspire.Hosting.DevTunnels.Tests, Aspire.Hosting.Docker.Tests, Aspire.Hosting.DotnetTool.Tests, Aspire.Hosting.EntityFrameworkCore.Tests, Aspire.Hosting.Foundry.Tests, Aspire.Hosting.Garnet.Tests, Aspire.Hosting.GitHub.Models.Tests, Aspire.Hosting.Go.Tests, Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Kafka.Tests, Aspire.Hosting.Keycloak.Tests, Aspire.Hosting.Kubernetes.Tests, Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests, Aspire.Hosting.MongoDB.Tests, Aspire.Hosting.MySql.Tests, Aspire.Hosting.Nats.Tests, Aspire.Hosting.OpenAI.Tests, Aspire.Hosting.Oracle.Tests, Aspire.Hosting.PostgreSQL.Tests, Aspire.Hosting.Python.Tests, Aspire.Hosting.Qdrant.Tests, Aspire.Hosting.RabbitMQ.Tests, Aspire.Hosting.Redis.Tests, Aspire.Hosting.Sdk.Tests, Aspire.Hosting.Seq.Tests, Aspire.Hosting.SqlServer.Tests, Aspire.Hosting.Testing.Tests, Aspire.Hosting.Tests, Aspire.Hosting.Valkey.Tests, Aspire.Hosting.Yarp.Tests, Aspire.Playground.Tests

Selected jobs (2)

extension-e2e, typescript-api-compat


How these were chosen — grouped by what changed

⚠️ 38 of the 40 selected test projects come from a single change — src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets.

🔧 src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets (changed source)
→ 38 via the project graph

show 38

Aspire.Hosting.Analyzers.Tests, Aspire.Hosting.Azure.Kubernetes.Tests, Aspire.Hosting.Azure.Kusto.Tests, Aspire.Hosting.Azure.Tests, Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Browsers.Tests, Aspire.Hosting.Containers.Tests, Aspire.Hosting.DevTunnels.Tests, Aspire.Hosting.Docker.Tests, Aspire.Hosting.DotnetTool.Tests, Aspire.Hosting.EntityFrameworkCore.Tests, Aspire.Hosting.Foundry.Tests, Aspire.Hosting.Garnet.Tests, Aspire.Hosting.GitHub.Models.Tests, Aspire.Hosting.Go.Tests, Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Kafka.Tests, Aspire.Hosting.Keycloak.Tests, Aspire.Hosting.Kubernetes.Tests, Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests, Aspire.Hosting.MongoDB.Tests, Aspire.Hosting.MySql.Tests, Aspire.Hosting.Nats.Tests, Aspire.Hosting.OpenAI.Tests, Aspire.Hosting.Oracle.Tests, Aspire.Hosting.PostgreSQL.Tests, Aspire.Hosting.Python.Tests, Aspire.Hosting.Qdrant.Tests, Aspire.Hosting.RabbitMQ.Tests, Aspire.Hosting.Redis.Tests, Aspire.Hosting.Seq.Tests, Aspire.Hosting.SqlServer.Tests, Aspire.Hosting.Testing.Tests, Aspire.Hosting.Tests, Aspire.Hosting.Valkey.Tests, Aspire.Hosting.Yarp.Tests, Aspire.Playground.Tests

📦 affected project Aspire.Hosting.AppHost
→ 1 test: Aspire.EndToEnd.Tests

🧪 tests/Aspire.Hosting.Sdk.Tests/AppHostSdkTargetsTests.cs (changed test)
→ 1 directly: Aspire.Hosting.Sdk.Tests

🧪 tests/Aspire.Hosting.Sdk.Tests/Aspire.Hosting.Sdk.Tests.csproj (changed test)
→ 1 directly: Aspire.Hosting.Sdk.Tests

🧪 tests/Aspire.Hosting.Tests/MSBuildTests.cs (changed test)
→ 1 directly: Aspire.Hosting.Tests

Job reasons

Job Triggered by
extension-e2e • src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets, src/Aspire.Hosting.Tasks/ResolveDnxPath.cs
• affected project Aspire.Hosting.AppHost
typescript-api-compat affected project Aspire.Hosting.AppHost

Selection computed for commit cfd95f5.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 6/6 changed files
  • Comments generated: 1

Comment on lines +921 to +929
private static string GetAspireTasksAssemblyPath()
{
#if DEBUG
const string configuration = "Debug";
#else
const string configuration = "Release";
#endif
return Path.Combine(GetRepoRoot(), "artifacts", "bin", "Aspire.Hosting.Tasks", configuration, "net8.0", "Aspire.Hosting.Tasks.dll");
}

This branch has not been deployed

No deployments
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