Skip to content

Add Rust hosting package and VS Code debugger wiring - #18906

Merged
Adam Ratzman (adamint) merged 159 commits into
microsoft:mainfrom
afscrome:rust-hosting-worktree
Aug 14, 2026
Merged

Add Rust hosting package and VS Code debugger wiring#18906
Adam Ratzman (adamint) merged 159 commits into
microsoft:mainfrom
afscrome:rust-hosting-worktree

Conversation

@afscrome

@afscrome Alex Crome (afscrome) commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Description

This adds first-party Rust hosting support and VS Code support for both Rust resources and Rust AppHosts.

The new Aspire.Hosting.Rust integration models Cargo applications, separates Cargo arguments from application arguments, resolves Cargo targets for debugging and publishing, and exports the APIs to C# and polyglot AppHosts. aspire publish and aspire deploy generate a multi-stage Dockerfile when the app does not provide one.

The VS Code extension can now discover, run, and debug apphost.rs files and debug Rust resources from any AppHost. It uses Tree-sitter to locate Rust AppHost resources, selects CodeLLDB on macOS/Linux or C/C++ on Windows, and warns that rust-analyzer's adjacent Run and Debug actions launch Cargo directly and bypass Aspire.

Changes vs Community Toolkit

Existing implementation: source | example

  • Added Rust resource and Rust AppHost debugging.
  • Added publish/deploy support using a generated multi-stage Dockerfile.
  • Added separate WithCargoArgs(...) and WithArgs(...) paths, following the direction in Resources with multiple args #18904.
  • Bacon support has not been migrated.

Publish support

The generated Dockerfile:

  • Uses rust:1.97-alpine3.24 for the build stage and alpine:3.24 for the runtime stage by default. A rust-toolchain.toml pin is installed with rustup inside the build image.
  • Cache mounts the Cargo registry and target directory, then copies the selected artifact to a stable path before the runtime stage.
  • Runs the application as a non-root app user.
  • Uses the typed WithCargo* options for target and binary resolution. Raw WithCargoArgs(...) values are forwarded to Cargo and are not parsed again.
  • Leaves build image, target ABI, linker, and runtime image compatibility to the caller. Invalid combinations fail during the container build.

Rust OTLP and TLS setup

Aspire provides the OTLP endpoint and development certificate environment variables. The Rust application still needs to enable the transport and TLS features required by its OpenTelemetry SDK and load native trust roots when using the development certificate. The AddRustApp API remarks document that boundary, and the Rust playground demonstrates the application-side setup.

Testing

  • Rust hosting tests: 152 passed, 1 expected skip.
  • Rust code-generation tests: 23 passed.
  • VS Code extension unit tests: 2,005 passed, 4 pending.
  • VS Code package-surface E2E tests: 7 passed.
  • Rust polyglot validation script syntax and fixture mutation verified.
  • Rust README/public API tests: 24 passed.

Remaining cross-language test gaps

These gaps already apply to the other language integrations and are tracked separately:

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

Implements Rust AppHost hosting resources, playground wiring, and VS Code extension debug adapter support for Rust resources.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings July 27, 2026 18:44
@github-actions

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 -- 18906

Or

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

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

Adds first-class Rust hosting, publishing, playground, and VS Code debugging support to Aspire.

Changes:

  • Adds Rust and Bacon resource APIs with Cargo configuration and container publishing.
  • Adds Rust playgrounds and hosting tests.
  • Integrates Rust debugger detection and launch configuration into the VS Code extension.

Reviewed changes

Copilot reviewed 30 out of 32 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/Aspire.Hosting.Rust/Aspire.Hosting.Rust.csproj Defines the Rust hosting package.
src/Aspire.Hosting.Rust/BaconAppResource.cs Models Bacon-managed Rust applications.
src/Aspire.Hosting.Rust/PackageInfo.cs Supplies package metadata.
src/Aspire.Hosting.Rust/README.md Introduces package documentation.
src/Aspire.Hosting.Rust/RustAnnotations.cs Defines Cargo configuration annotations.
src/Aspire.Hosting.Rust/RustAppResource.cs Models Cargo-based Rust applications.
src/Aspire.Hosting.Rust/RustCargoArgsCallbackContext.cs Exposes deferred Cargo argument configuration.
src/Aspire.Hosting.Rust/RustHostingExtensions.cs Implements hosting, publishing, and debugging APIs.
src/Aspire.Hosting.Rust/RustLaunchConfiguration.cs Defines DCP Rust launch metadata.
src/Aspire.Hosting.Rust/RustVersionDetector.cs Reads pinned Rust toolchain versions.
src/Aspire.Hosting/Aspire.Hosting.csproj Grants Rust projects internal hosting access.
tests/Aspire.Hosting.Rust.Tests/Aspire.Hosting.Rust.Tests.csproj Defines the Rust hosting test project.
tests/Aspire.Hosting.Rust.Tests/RustPublicApiTests.cs Tests resource APIs and Cargo arguments.
playground/rust/Cargo.toml Defines the Rust AppHost crate.
playground/rust/app/Cargo.toml Defines the sample Rust service.
playground/rust/app/src/main.rs Implements the sample HTTP service.
playground/rust/apphost.rs Provides the Rust AppHost entrypoint.
playground/rust/aspire.config.json Configures the polyglot playground.
playground/rust/src/main.rs Defines the Rust application model.
playground/rust-apphost/AppHost.cs Adds a C# Rust-hosting playground.
playground/rust-apphost/rust-apphost.csproj Defines its AppHost project.
Aspire.slnx Registers the new package, tests, and playground.
extension/src/debugger/languages/rust.ts Generates Rust debug configurations.
extension/src/debugger/languages/index.ts Registers Rust debugger handling.
extension/src/debugger/debuggerExtensions.ts Adds Rust debugger extension identifiers.
extension/src/capabilities.ts Advertises available Rust debug adapters.
extension/src/test/rustDebugger.test.ts Tests Rust configuration translation.
extension/src/test/capabilities.test.ts Tests Rust capability detection.
extension/src/loc/strings.ts Adds localized Rust capability text.
extension/package.nls.json Adds Rust localization resources.
extension/loc/xlf/aspire-vscode.xlf Updates generated translation data.
extension/package.json Declares Rust debugger extension dependencies.
Comments suppressed due to low confidence (1)

extension/src/debugger/languages/rust.ts:73

  • The C++ adapter path never invokes Cargo: this branch removes cargo, points program at a presumed output file, and the caller immediately starts the debug session. On a clean checkout target/... does not exist, so cppvsdbg/cppdbg launch fails even though these adapters are advertised as supported. Add a build/task step before launching, or do not expose these adapters until that flow exists.
        if (debugAdapter === 'cppvsdbg' || debugAdapter === 'cppdbg') {
            debugConfiguration.program = getRustBinaryPath(launchConfig);
            delete debugConfiguration.cargo;
            return;

Comment thread extension/src/debugger/languages/rust.ts Outdated
Comment thread src/Aspire.Hosting.Rust/RustHostingExtensions.cs Outdated
Comment thread src/Aspire.Hosting.Rust/RustHostingExtensions.cs Outdated
Comment thread src/Aspire.Hosting.Rust/RustHostingExtensions.cs Outdated
Comment thread src/Aspire.Hosting.Rust/RustVersionDetector.cs Outdated
Comment thread playground/rust/aspire.config.json
Comment thread playground/rust/aspire.config.json Outdated
Comment thread src/Aspire.Hosting.Rust/README.md Outdated
Comment thread src/Aspire.Hosting.Rust/RustLaunchConfiguration.cs Outdated
- Move AppHost to project rather than file based app
- Get Debugging working

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

Copilot reviewed 30 out of 32 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (13)

playground/rust/aspire.config.json:3

  • This configuration declares a Rust AppHost but points at the C# project. The Rust entry point at apphost.rs is therefore never selected, and the guest-host pipeline will try to treat the .csproj as Rust input. Point this at apphost.rs (or change the language to C# if the C# AppHost is intended).
    "path": "Rust.AppHost/Rust.AppHost.csproj",

src/Aspire.Hosting.Rust/RustHostingExtensions.cs:105

  • The generated Dockerfile assumes the Cargo binary name equals the Aspire resource name. That is not a Cargo invariant, and the included sample already demonstrates the failure: the resource is named app, while Cargo builds aspire-sample-rust-app, so this COPY cannot find the executable. Resolve the default binary target from Cargo metadata/the manifest, while preserving WithCargoBinTarget as the explicit override.
                        .CopyFrom("build", $"/app/target/release/{binaryName}", $"/app/{binaryName}")

src/Aspire.Hosting.Rust/RustHostingExtensions.cs:98

  • Publish mode discards the Cargo configuration applied by WithCargoFeatures and other Cargo-argument APIs: local run/debug uses those annotations, but the generated image always executes only cargo build --release. Feature-gated binaries can therefore run locally and then fail or produce different output when published. Build the publish command from the supported Cargo options (at minimum features and binary target) and add a generated-Dockerfile regression test.
                        .Run("cargo build --release");

src/Aspire.Hosting.Rust/RustVersionDetector.cs:16

  • This regex is not a TOML parser and can select a commented-out channel before the active value. In addition, the legacy rust-toolchain filename may contain the same [toolchain] TOML form, but lines 26-29 return that whole file as an image tag. Parse both supported formats while honoring TOML comments/strings so generated FROM references are valid.
            var content = File.ReadAllText(toolchainTomlPath);
            var match = ChannelRegex().Match(content);

src/Aspire.Hosting.Rust/README.md:5

  • This scaffold README is being packaged as the integration's user documentation, but it does not explain how to install or use the new resource. Replace it with the standard hosting-integration README: Rust title/description, prerequisites, exact aspire add Aspire.Hosting.Rust command, minimal C# and generated polyglot usage, additional documentation, and the feedback section.
# Aspire.Hosting.Rust

Rust support for Aspire hosting integrations.

## Status

src/Aspire.Hosting.Rust/RustHostingExtensions.cs:82

  • The new generated-Dockerfile path has no publish-mode coverage; the added Rust tests only evaluate run arguments. Add a publish test that snapshots the Dockerfile and covers toolchain selection, differing resource/Cargo binary names, Cargo feature/bin options, and the existing-Dockerfile path. Comparable coverage exists in tests/Aspire.Hosting.Go.Tests/AddGoAppTests.cs:725-845.
            .PublishAsDockerFile(containerBuilder =>

src/Aspire.Hosting.Rust/RustHostingExtensions.cs:53

  • These new exports are not exercised by the repository's polyglot validation. There is no tests/PolyglotAppHosts/Aspire.Hosting.Rust fixture, and eng/github-ci/test-trigger-map.yml:340-350 does not include the new project in the polyglot job. Add a fixture that compiles the generated calls and update the trigger so changes to this exported surface run it; the playground is not CI validation.
    [AspireExport]

extension/src/debugger/languages/rust.ts:149

  • The tests stub IRustService, so none of the user-visible debugger flow is exercised through DCP metadata, a real Cargo build, executable discovery, or either platform adapter. Add VS Code extension E2E coverage for a Rust resource, including the platform-specific adapter selection and a build failure; the PR itself notes macOS/Linux has not been validated.
        createDebugSessionConfigurationCallback: async (launchConfig, args, _env, launchOptions, debugConfiguration: AspireResourceExtendedDebugConfiguration): Promise<void> => {

playground/rust/app/src/main.rs:23

  • The hosting integration automatically publishes this executable as a container, but the sample binds only to container loopback. Its declared HTTP endpoint and health check will be unreachable from outside the container after publish/deploy. Bind the sample to all container interfaces.
    let address = SocketAddr::from(([127, 0, 0, 1], port));

extension/src/debugger/languages/rust.ts:98

  • rustBuildFailedWithExitCode expects an exit code, but this passes the entire stderr stream whenever stderr is nonempty. Users then receive an error claiming the compiler output is the exit code, while the actual code is omitted. Pass code here; stderr is already streamed to the debug console.

This issue also appears on line 149 of the same file.

                    reject(new Error(rustBuildFailedWithExitCode(workingDirectory, stderrOutput || `${code}`)));

extension/src/loc/strings.ts:155

  • This user-facing error names the C# API, but Rust and TypeScript AppHosts receive different generated method names. A Rust AppHost user cannot call WithCargoBinTarget. Keep the message language-neutral so it is actionable for every supported AppHost language.
export const rustBuildProducedNoExecutable = (workingDirectory: string) => vscode.l10n.t('cargo build in {0} completed but did not produce a runnable binary. Ensure the crate defines a binary target, or select one with WithCargoBinTarget.', workingDirectory);

src/Aspire.Hosting.Rust/Aspire.Hosting.Rust.csproj:8

  • This package exports ATS APIs, but integration analyzers are disabled by default in Aspire.Hosting.targets. Without opting in, invalid generated-SDK shapes are not validated and the package also misses the automatic polyglot package tag used for non-C# discovery. Enable the integration analyzers for this project.
    <SuppressFinalPackageVersion>true</SuppressFinalPackageVersion>

src/Aspire.Hosting.Rust/RustHostingExtensions.cs:49

  • The primary public extension method lacks the required behavioral documentation for a new language integration. Add <remarks> covering local Cargo execution versus generated-Dockerfile publish behavior, an <example> with a working AppHost call, and documentation for the validation exceptions so IntelliSense and published API docs describe this API accurately.

This issue also appears in the following locations of the same file:

  • line 53
  • line 82
    /// <summary>
    /// Adds a Rust application to the application model.
    /// </summary>
    /// <param name="builder">The <see cref="IDistributedApplicationBuilder"/> to add the resource to.</param>
    /// <param name="name">The name of the resource.</param>

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

The generated publish Dockerfile could not build a working container:

- The binary copied out of the build stage was named after the Aspire resource
  rather than the cargo target, so `AddRustApp("app", ...)` emitted
  `COPY target/release/app` while cargo produced `aspire-sample-rust-app`. The
  target is now resolved from Cargo.toml, preserving hyphens (only library
  targets translate `-` to `_`). Crates with several `[[bin]]` targets must
  select one with WithCargoBinTarget.
- Rustup channel names were used directly as image tags, producing tags that do
  not exist (`rust:stable-alpine`, `rust:nightly-alpine`). `stable` now maps to
  `rust:alpine`; `beta` and `nightly` have no official image and throw with a
  message pointing at WithDockerfileBaseImage. Patch-level pins such as
  `1.89.0` are preserved verbatim.
- The toolchain regex matched commented TOML, so `# channel = "nightly"` could
  win over the real value. Parsing is now line-oriented and comment-aware.
- Cargo arguments configured via WithCargoArgs/WithCargoFeatures were discarded
  at publish time, so the container was built from a different command line than
  local execution. All arguments are now forwarded and shell-quoted, and the
  `COPY --from` path follows the selected profile.
- Alpine build images ship no C toolchain, so crates with native dependencies
  (ring/aws-lc via rustls, anything using cc) failed to link. musl-dev and gcc
  are installed when the build image is Alpine.
- Honour the shared DockerfileBaseImageAnnotation, matching Aspire.Hosting.Go,
  so callers can supply their own build and runtime images. The Alpine-only
  hardening steps (apk, BusyBox adduser, USER) are emitted only for Alpine
  runtime images; previously they were unconditional and produced a Dockerfile
  that could never build on, for example, debian:bookworm-slim.

Verified end to end: the playground publishes, the image builds, and the
container serves requests as a non-root user.

The playground sample bound to 127.0.0.1, which is unreachable once
containerised, so it now binds 0.0.0.0 as the Go samples do.

Also replaces the README scaffold with the standard hosting integration
structure covering the public API surface.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 28, 2026 19:08

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

Copilot reviewed 44 out of 46 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (11)

src/Aspire.Hosting.Rust/RustHostingExtensions.cs:145

  • WithCargoArgs("--target", "x86_64-unknown-linux-musl") writes the binary under target/<triple>/<profile>/, but this COPY always reads target/<profile>/. The generated image therefore fails to build for a valid cross-compilation configuration. Parse the selected target (and target-directory overrides) when resolving the artifact path, or obtain the artifact path from Cargo metadata.
                        .CopyFrom("build", $"/app/target/{profileDirectory}/{binaryName}", $"/app/{binaryName}");

src/Aspire.Hosting.Rust/RustHostingExtensions.cs:124

  • RustAppResource advertises IContainerFilesDestinationResource, so PublishWithContainerFiles can attach file sources, but this generated Dockerfile never emits their build stages or runtime copies. Those files are silently absent from the published image. Mirror the AddContainerFilesStages/AddContainerFiles handling used by Aspire.Hosting.Go (GoHostingExtensions.cs:276-305).
                    buildStage
                        .Copy(".", ".")
                        .Run(BuildCargoCommand(cargoArgs));

src/Aspire.Hosting.Rust/RustHostingExtensions.cs:487

  • A rustup channel may be a version plus host triple, for example 1.89.0-x86_64-unknown-linux-gnu. This branch treats it as a plain version and generates the nonexistent tag rust:1.89.0-x86_64-unknown-linux-gnu-alpine. Parse versioned toolchain names or reject host-qualified channels with the existing base-image override guidance.
        return $"rust:{channel}-alpine";

extension/src/debugger/languages/rust.ts:98

  • When Cargo writes anything to stderr—which it normally does on failure—stderrOutput is passed as the {exitCode} placeholder. Users receive a multiline diagnostic labeled as an exit code instead of the actual code. The stderr has already been streamed to the debug console, so pass code here.
                    reject(new Error(rustBuildFailedWithExitCode(workingDirectory, stderrOutput || `${code}`)));

src/Aspire.Hosting.Rust/README.md:1

  • The required hosting README title is # {Technology} hosting integration; adding “app” makes this inconsistent with the integration README contract.
# Rust app hosting integration

src/Aspire.Hosting.Rust/README.md:36

  • The hosting README usage example must demonstrate the resource-reference flow, but both samples stop after creating the Rust app. Add a dependent resource and call WithReference(api)/withReference(api) so users can see how Rust endpoints participate in the AppHost model.
var api = builder.AddRustApp("api", "../rust-api")
    .WithHttpEndpoint(env: "PORT")
    .WithExternalHttpEndpoints()
    .WithOtlpExporter();

extension/src/debugger/languages/rust.ts:80

  • An unfiltered workspace or crate with multiple binaries reaches this assignment multiple times, so the debugger silently launches whichever compiler artifact Cargo reports last. That order is not a target-selection contract and differs from cargo run, which requires a binary selection. Track matching artifacts and reject an ambiguous build with guidance to use WithCargoBinTarget.

This issue also appears on line 98 of the same file.

                    if (artifact.executable && artifact.target?.kind?.includes('bin') && (!filter || artifact.target?.name === filter)) {
                        executablePath = artifact.executable;

src/Aspire.Hosting.Rust/RustCargoArgsCallbackAnnotation.cs:44

  • This public list accepts arbitrary argument objects, but only run mode resolves IValueProvider values. Debugging silently drops non-strings via OfType<string>(), while publishing calls ToString(), so the same callback has different arguments in each mode. Either make Cargo arguments explicitly IList<string> or use a shared value-resolution path across run, debug, and publish.
    /// <summary>
    /// Gets the list of command-line arguments.
    /// </summary>
    public IList<object> Args { get; } = args ?? throw new ArgumentNullException(nameof(args));

src/Aspire.Hosting.Rust/RustHostingExtensions.cs:42

  • AddBaconApp remains an executable.v0 running the development watcher during publish, unlike AddRustApp, which becomes a container. This leaks dev-only watch behavior into publish output and leaves the workload undeployable on container targets. Either make Bacon run-only and exclude it from manifests, or reuse the Rust container publish path while keeping Bacon only for local run mode.

This issue also appears in the following locations of the same file:

  • line 122
  • line 145
        return builder.AddResource(resource)
            .WithArgs("run")
            .WithRequiredCommand("bacon", "https://dystroy.org/bacon/")
            .WithRustDefaults();

extension/src/debugger/languages/rust.ts:155

  • The new debugger flow has only unit tests that replace RustService with a stub, so they never exercise Cargo JSON parsing, process failures, executable selection, environment transfer, or either native adapter. Add VS Code extension E2E coverage that builds a real crate and starts the supported adapter; the PR description currently confirms only Windows was manually tested.
            const rustService = rustServiceProducer(launchOptions.debugSession);
            const executablePath = await rustService.buildAndGetExecutablePath(workingDirectory, cargoArgs, config.cargo?.filter);

src/Aspire.Hosting.Rust/RustHostingExtensions.cs:82

  • The publish tests only snapshot Dockerfile text; none builds and runs the generated image. For a new language publish path, this misses linker/runtime compatibility, artifact-path correctness, entrypoint behavior, and endpoint binding. Add a functional publish/deployment test that builds the Rust image, starts it, and probes the sample endpoint.
            .PublishAsDockerFile(containerBuilder =>

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

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 wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

# Conflicts:
#	src/Aspire.Cli/Packaging/PackageChannel.cs
#	src/Aspire.Cli/Packaging/PackageSourceOverrideMappings.cs

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 wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

Adam Ratzman and others added 5 commits August 14, 2026 16:02
…the packages-override guard recursive

`PackageChannel.GetTemplatePackagesAsync(workingDirectory, mappings, ct)` selected the
local Aspire directory from the channel's own mappings, so a caller-supplied override
(`aspire new --source <dir>`) was silently ignored for unpinned explicit channels and
templates were listed from the channel's directory instead. Thread the supplied mappings
through local-source selection. This also routes a local `--source` directory through
direct enumeration, which — unlike `dotnet package search` — sees hierarchical local feeds.

`PackagingService.EnsurePackagesOverrideDirectoryIsUnambiguous` enumerated only top-level
`Aspire*.nupkg` while local package discovery walks `SearchOption.AllDirectories`, so
duplicate versions in nested directories bypassed the fail-fast guardrail. Enumerate
recursively so the guard matches discovery.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b93d5546-f35d-4ce0-832b-afe34ed14486
…y detection

Two fixes in the extension's CLI runner:

- `runCliCommand` added the spawned handle to `_oneShotProcesses` after
  `spawnCliProcess` returned. When the spawn reported completion synchronously,
  `settle` ran before the local was assigned, so it removed nothing and the
  already-exited process stayed tracked for the lifetime of the runner. Guard the
  tracking the same way `AppHostPsPoller` does, and terminate the handle if a
  cancellation or timeout raced the spawn.

- `isDescribeUnsupportedOutput` matched any "unrecognized command/option" wording
  anywhere in the output, so a current CLI reporting a user-supplied option (for
  example an AppHost that rejects `--publisher`) was misreported as a CLI too old
  to `describe`, replacing the real error with an upgrade banner and marking it a
  CLI-wide compatibility failure. Detection is now per line and scoped to the
  tokens the extension itself passes, and a line quoting an option we never sent
  no longer counts. Old-CLI shapes (quoted or bare rejected token, localized
  rejection text, top-level help) still trigger the fallback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b93d5546-f35d-4ce0-832b-afe34ed14486
…rbofish parsing

Three small extension fixes:

- `rustDebuggerExtensionNotInstalled` existed in `loc/strings.ts` but had no
  `package.nls.json` entry, so it never reached the XLF catalog and shipped
  untranslated. Added the entry and regenerated `loc/xlf/aspire-vscode.xlf`
  with `yarn run localize`.
- `aspire-vscode.codeLensRevealAppHost` is registered and used by the CodeLens
  provider, but unlike every peer CodeLens command it was missing from
  `contributes.commands` and from the hidden `commandPalette` list.
- The Rust parser only recognized a bare `field_expression`, so valid calls with
  a turbofish (`builder.add_project::<Frontend>("web")`) parse as
  `generic_function` and were silently skipped. Unwrap that node before reading
  the `add_*` field name.

Each fix has a regression test: an nls/XLF parity check for the rust strings, a
manifest check that every registered CodeLens command is contributed and hidden
from the palette, and parser tests for turbofished `add_*` calls plus the
receiverless negative shape.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b93d5546-f35d-4ce0-832b-afe34ed14486
…gs callbacks their resource

Two independent fixes to the Rust hosting integration.

Aspire.Hosting.Rust reached PathNormalizer through a product-to-product
InternalsVisibleTo entry in Aspire.Hosting.csproj. No sibling integration does
that: JavaScript, Dotnet, and Maui all source-share src/Shared/PathNormalizer.cs
and build against the public Aspire.Hosting surface only. Drop the IVT and add
the same $(SharedDir) Compile Link item to the Rust project.

That leaves Aspire.Hosting.Rust.Tests seeing two equally accessible
Aspire.Hosting.Utils.PathNormalizer types (it still needs Aspire.Hosting's
internals for Dcp.Model), which is CS0433 at every use site. Add a
TestPathNormalizer forwarder to Aspire.Hosting.TestUtilities so tests can name
the one shared implementation, rather than duplicating it or suppressing the
conflict.

RustCargoArgsCallbackContext also carried no reference to the resource being
configured, so a callback could not read annotations or other resource state
without capturing the resource from the enclosing scope. Add a strongly typed
Resource property and make it a required constructor parameter — it has no
sensible default — then pass it from both production construction sites, the
run-mode WithArgs pipeline and the publish-mode Dockerfile generator.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b93d5546-f35d-4ce0-832b-afe34ed14486
The Rust runtime spec launches an AppHost with `cargo run --bin apphost --`
(AppHostBinaryName in RustLanguageSupport), and the scaffolded Cargo.toml
declares that `[[bin]]`. The playground named its binary
`rust-apphost-playground`, so the real launch path failed before it started:

    $ cargo run --bin apphost -- --help
    error: no bin target named `apphost` in default-run packages

Rename the bin target to `apphost` to match what the runtime and every
generated AppHost assume. The package name is left alone; only the target
name is part of the launch contract.

The manifest also carried direct `opentelemetry-otlp` and `tonic`
dependencies. Nothing in the AppHost uses them: apphost.rs only calls the
generated SDK, and the generated SDK (.aspire/modules) imports std, serde,
serde_json and lazy_static only. Telemetry belongs to the sample app under
./app, which declares those crates itself. Removing them prunes 146
transitive packages from the lockfile with no version changes to what
remains.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b93d5546-f35d-4ce0-832b-afe34ed14486

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 wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

… behavior

The extension package-surface E2E hard-codes the contributed Aspire command
list, so adding aspire-vscode.codeLensRevealAppHost to package.json broke the
deepStrictEqual on Linux and Windows. Add it to the expected list and to the
hidden command-palette check, since CodeLens commands are invoked from lenses
and must not leak into the palette.

The source-override CLI E2E asserted `dotnet package search` diagnostics that
no longer exist: a local `--source` directory is now enumerated directly, and
`dotnet new install` is pointed at the .nupkg inside source-feed instead of a
feed plus `--nuget-source`. Assert the install argument list (temp-directory
suffixes are random, so match the args rather than the generated path).

Keep the core guarantee by rejecting the feed-backed template search the CLI
under test would actually run. CI installs a bundle, so that path is
BundleNuGetPackageCache -> `aspire-managed nuget search`, whose only trace is
its two Debug lines ("Running NuGet search via aspire-managed: ..." and
"NuGet search args: nuget search --query ..."); both the helper process and
the SDK-backed `dotnet package search` fallback are spawned with
SuppressLogging, so a guard on a logged `package search` command line could
never fail. Reject those lines alongside `--nuget-source`, api.nuget.org, and
the TCP tripwire.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b93d5546-f35d-4ce0-832b-afe34ed14486

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 wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-vscode-extension NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants