Repository navigation
Add Rust hosting package and VS Code debugger wiring #18906
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Adam Ratzman (adamint)
merged 159 commits into
microsoft:main
from
afscrome:rust-hosting-worktree
Aug 14, 2026
+19,343
−4,425
Merged
Changes from 1 commit
Commits
Show all changes
159 commits
Select commit
Hold shift + click to select a range
d997246
Add Rust hosting package and VS Code debugger wiring
alexanderc-tray 7c2d313
More fixes
afscrome 8ee0844
Expose debug adapters as capabilities
afscrome ff2631e
Hosting Api tweaks
afscrome ed8fc76
Tweak rust app
afscrome f7396d9
Fix Rust publish flow and add test coverage
afscrome 137b7ba
Merge remote-tracking branch 'origin/main' into alexanderc-trayport-r…
afscrome 64ec642
Fix cargo arguments leaking into debugged Rust binary
afscrome 81461c7
Clarify debug arg ordering comment and cover late cargo args
afscrome 66e7b5a
Simplify debug arg ordering comment and link tracking issue
afscrome ba3f096
Shorten debug arg ordering comment
afscrome a1e8a35
Drop bacon and publish support; address review feedback
afscrome 35066c3
Address remaining Rust review feedback
afscrome 9f2e0d2
Restore publish/deploy support for Rust apps
afscrome 5b2fc4e
Address review feedback on Rust publish support
afscrome 1a3040e
Resolve the debug executable on the host instead of parsing cargo bui…
afscrome 454dea8
Address review feedback: trim validation, use ExecutionContext, DI me…
afscrome a974caa
Rebase the cargo manifest path into the container when publishing
afscrome 56ec8fb
Add --locked support and make the publish build defaults opt-out
afscrome 919cbd0
Drop stale blocking-wait comment
afscrome 949c0e0
Emit a default .dockerignore for published Rust images
afscrome cdb72d0
Document cargo option handling and harden the runtime user creation
afscrome 3f6e5d4
Address review feedback on cargo target resolution and image defaults
afscrome 377fbb9
Regenerate the extension localization bundle
afscrome 1b4e690
Rename the Alpine check and fix .dockerignore precedence
afscrome 44e1e05
Address Copilot review feedback on the Rust integration
afscrome 4d28f01
Fix the dated nightly image tag order
afscrome edd9046
Spell out that raw cargo arguments are not validated
afscrome 90007b1
Merge upstream/main and make the Rust debug launch configuration asyn…
afscrome f3e652f
Stop installing a C toolchain into the build stage
afscrome eb0f752
Correct the comment on stripping a channel host suffix
afscrome badbdf0
Stop inferring the runtime distro from the image name
afscrome 4bd6ca1
Say musl and glibc where that is what is meant
afscrome 75f5915
Install nothing into the runtime image either
afscrome 7fca034
Move the publish cargo defaults next to the other cargo options
afscrome 46bb345
Resolve the lock file from the resource working directory
afscrome 4228217
Trim the publishing docs down to what a consumer needs
afscrome c434e92
Reduce the libc pairing note to a single sentence
afscrome a18b16d
Simplify readme
afscrome c8c49c3
Tidy the readme samples and warn off raw target selection
afscrome 7d913e4
Let the runtime image allocate the app user ids
afscrome 5ee1d61
Let rustup pick the toolchain instead of picking an image for it
afscrome 8ebde55
Inline the base image defaults and move the runtime image to alpine 3.24
afscrome cc22367
Trim the Dockerfile generator comments back to what is not obvious
afscrome 309134c
Cut the generator comments back further
afscrome e52415b
Read the working directory from the resource when publishing
afscrome 9d56e03
Cache the cargo target directory and fix the cross target install
afscrome f6d3e43
Handle a taskkill that starts but fails
afscrome 7a69b88
Stop the readme promising a reference it does not show
afscrome 98a9743
Fail the Rust build when the artifact is ambiguous, drop the workspac…
afscrome 36a5310
Document launch configuration callback context design
fc48eb7
docs: add launch configuration callback plan
ad326ea
Add launch configuration callback context
bfb9027
Fix launch producer restart handling
9a8c99a
Reset debug execution before rebuilding arguments
e9ea853
Migrate debug launch configuration producers
9efdff8
Finalize debug callback context API
65a56a7
Migrate debug support playground
ac32975
Fix debug support documentation whitespace
b0fcfd3
Address callback context review findings
8de33ce
Remove agent planning artifacts and restore load-bearing comments
900a331
Fix MAUI launch configuration regression and rename PrepareObjects
7d4d8fe
Address launch configuration context review
0ee07ef
Install Azure Functions Core Tools from release archive
2db433e
Recompute debug argument callbacks after original snapshot
6cb79d3
Validate legacy debug producer return type
1481f78
Revert unrelated Azure Functions tool install change
dcf4b09
Preserve launch override process args
024577a
Fix debug executable configuration snapshots
adamint 24588ab
Reuse original argument resolutions across the debug rewrite
adamint 5b8d2f3
Address review findings on debug launch configuration
adamint 92901b8
Fix debug argument rewrite reuse and legacy overload validation
adamint 64dd50d
Address debug launch context review feedback
3b95b28
Merge branch 'main' into adamint/issue-18956-launch-config-context
adamint a904ac7
Prevent later arg mutations from stripping debug-injected arguments
30d272c
Narrow debug launch context to resolved environment
35bea19
Clarify process-mode launch producer behavior
fa5c96c
Fix launch-override producer wiring
c5855b8
Clarify asynchronous debug producer guidance
7cdb91a
Cover the no-fallback rule when a launch configuration producer throws
b739005
Merge branch 'main' into adamint/issue-18956-launch-config-context
629c3ea
Merge remote-tracking branch 'upstream/main' into adamint/issue-18956…
92946d6
Merge PR 19077 launch configuration changes
45bd16e
Address Rust hosting review feedback
90ef108
Harden Rust publish and debugger diagnostics
0237479
Make Rust build cache IDs deterministic
7007266
Merge latest main into Rust hosting PR
0914793
Preserve project launch configuration compatibility
557eea1
Address Rust hosting review findings
99b5b0a
Harden Rust publish validation and lifecycle
3e8ea39
Validate Rust artifact path components
b4e8cc0
Harden Rust debugger process handling
f8925c0
Complete Rust debugger target detection
1810624
Bound Rust toolchain detection
7b19fea
Safely terminate Rust toolchain probes
2ec7766
Cover Rust probe graceful shutdown
ed225d4
Update Rust debugger localization catalog
24e5a9c
Merge latest main before Rust PR validation
66b3536
Fix Windows Rust CI expectations
05cc077
Redact credential-bearing URL values
1a17c23
Reject Cargo credentials in generated Dockerfiles
ac060df
Harden Rust artifact diagnostics
387d216
Update Windows Rust publish snapshot
2b14a7a
Restore launch configuration inspection API
98f57a6
Merge main into Rust hosting branch
3564e99
Update Aspire schemas and Rust playground launch configuration
e7a88f7
Add repo-local Aspire CLI runner
ea33451
Remove retired Aspire Skills schema flag
bf2a5ec
Add Rust AppHost debugging support
b3862db
Document Rust AppHost parity design
ddde2bb
Plan Rust AppHost parity implementation
0df061a
Add Rust AppHost editor parser
684f2bb
Redact Cargo metadata diagnostics
9e76d11
Test Rust AppHost editor features
d82a8da
Align Rust AppHost scaffold and runtime
385b1ed
Classify Rust AppHosts in telemetry
3c5d0c8
Document Rust AppHost parity work
1dd3259
Harden Rust AppHost parsing
7a1ec85
Recognize Rust Cargo AppHosts in telemetry
d124f6b
Redact Cargo metadata diagnostics
81368c5
Improve resource command failure notifications
4eaabf1
Guide Rust AppHost runs through Aspire
32eecc9
Gate Rust ATS diagnostics behind ASPIRE_DEBUG
5d0f18f
Suppress generated Rust SDK warnings
26e7437
Disable incremental Rust AppHost debug builds
588e217
Accumulate Rust cargo features
6d2d977
Fix Rust SDK validation scaffold path
3b7c429
Remove Rust planning documents
08db695
Remove unused duplicate isBuildableAppHostCandidate from utils/workspace
c3133c8
Clarify Rust OTLP certificate setup
5334688
Consolidate path comparison, shortening, and filesystem identity help…
a902715
Deduplicate delay, stripResourceSuffix, and getErrorMessage helpers
5de74ad
Share tree-sitter AST helpers between the C# and Rust AppHost parsers
79f0b0f
Move AppHostDataRepository from views/ to data/ to match its layer
8ff2955
Extract CLI wire contracts and one-shot CLI runner from AppHostDataRe…
eb3750f
Give AppHostCliRunner a conventional public API and Disposable teardown
9f1ba41
Extract ps poller, describe stream manager, and status-output filteri…
ae1a73d
Move app-host path matching predicates out of the data layer
5b87a30
Split tree item classes and presentation helpers out of AspireAppHost…
a532518
Extract AppHostParentOutputFilter into its own module
3d37600
Move generic CLI process spawn/terminate helpers into utils/process
9ab057f
Make AppHostPsPoller.dispose release its timer and child processes
d748c85
Point imports at real modules and drop dead re-export shims
5e4a8c9
Split extension activation command registration into focused modules
9ff2fbb
Group app-host discovery helper functions into cohesive modules
49ce3d1
Remove unused InteractionService import from extension activation
07f7213
Support hierarchical local package feeds
10acf80
Extract app-host launch contracts, reservation bookkeeping, and telem…
037eecb
Extract dashboard and browser launch handling from AspireDebugSession
1c3c231
Separate app-host lifecycle tool contracts and language model adapter…
7d698c8
Address extension review threads: cross-platform log paths, Rust debu…
694e742
Address Rust hosting review threads: cache scoping, URL redaction, lo…
f9be1da
Merge remote-tracking branch 'microsoft/main' into rust-hosting-worktree
17fea22
Honor caller-supplied mappings for local template discovery and make …
3e50ad2
Stop leaking one-shot CLI processes and tighten describe compatibilit…
268a0ea
Fix missing Rust loc catalog entry, unlisted CodeLens command, and tu…
453da9c
Decouple Aspire.Hosting.Rust from Hosting internals and give cargo ar…
5ef1dcc
Make the Rust playground AppHost launchable and drop its unused deps
1258dda
Align package-surface and source-override E2E assertions with shipped…
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| import * as path from 'path'; | ||
| import * as vscode from 'vscode'; | ||
| import { getRustDebugAdapterType } from "../../capabilities"; | ||
| import { AspireResourceExtendedDebugConfiguration, ExecutableLaunchConfiguration, RustLaunchConfiguration, isRustLaunchConfiguration } from "../../dcp/types"; | ||
| import { invalidLaunchConfiguration, rustDisplayName, rustLabel } from "../../loc/strings"; | ||
| import { extensionLogOutputChannel } from "../../utils/logging"; | ||
| import { ResourceDebuggerExtension } from "../debuggerExtensions"; | ||
|
|
||
| const defaultRustDebugAdapter = getRustDebugAdapterType() ?? 'codelldb'; | ||
|
|
||
| function getProjectFile(launchConfig: ExecutableLaunchConfiguration): string { | ||
| if (isRustLaunchConfiguration(launchConfig) && launchConfig.working_directory) { | ||
| return launchConfig.working_directory; | ||
| } | ||
|
|
||
| throw new Error(invalidLaunchConfiguration(JSON.stringify(launchConfig))); | ||
| } | ||
|
|
||
| function createCargoConfiguration(launchConfig: RustLaunchConfiguration): { args: string[]; filter?: string } | undefined { | ||
| if (!launchConfig.cargo) { | ||
| return undefined; | ||
| } | ||
|
|
||
| return { | ||
| args: launchConfig.cargo.args ?? [], | ||
| ...(launchConfig.cargo.filter ? { filter: launchConfig.cargo.filter } : {}) | ||
| }; | ||
| } | ||
|
|
||
| function getRustBinaryPath(launchConfig: RustLaunchConfiguration): string { | ||
| const workingDirectory = launchConfig.working_directory ?? '.'; | ||
| const binaryName = launchConfig.cargo?.filter ?? path.basename(workingDirectory); | ||
| const executableName = process.platform === 'win32' | ||
| ? `${binaryName}.exe` | ||
| : binaryName; | ||
|
|
||
| return path.join(workingDirectory, 'target', 'debug', executableName); | ||
| } | ||
|
|
||
| export const rustDebuggerExtension: ResourceDebuggerExtension = { | ||
| resourceType: 'rust', | ||
| debugAdapter: defaultRustDebugAdapter, | ||
| extensionId: null, | ||
| getDisplayName: (launchConfiguration: ExecutableLaunchConfiguration) => { | ||
| if (isRustLaunchConfiguration(launchConfiguration) && launchConfiguration.working_directory) { | ||
| return rustDisplayName(vscode.workspace.asRelativePath(launchConfiguration.working_directory)); | ||
| } | ||
|
|
||
| return rustLabel; | ||
| }, | ||
| getSupportedFileTypes: () => ['.rs'], | ||
| getProjectFile: (launchConfig) => getProjectFile(launchConfig), | ||
| createDebugSessionConfigurationCallback: async (launchConfig, args, _env, launchOptions, debugConfiguration: AspireResourceExtendedDebugConfiguration): Promise<void> => { | ||
| if (!isRustLaunchConfiguration(launchConfig)) { | ||
| extensionLogOutputChannel.info(`The resource type was not rust for ${JSON.stringify(launchConfig)}`); | ||
| throw new Error(invalidLaunchConfiguration(JSON.stringify(launchConfig))); | ||
| } | ||
|
|
||
| const debugAdapter = getRustDebugAdapterType(); | ||
| if (debugAdapter === undefined) { | ||
| throw new Error(invalidLaunchConfiguration(JSON.stringify(launchConfig))); | ||
| } | ||
|
|
||
| debugConfiguration.type = debugAdapter; | ||
| debugConfiguration.request = 'launch'; | ||
| debugConfiguration.noDebug = !launchOptions.debug; | ||
| debugConfiguration.cwd = launchConfig.working_directory; | ||
| debugConfiguration.args = args ?? []; | ||
|
|
||
| if (debugAdapter === 'cppvsdbg' || debugAdapter === 'cppdbg') { | ||
| debugConfiguration.program = getRustBinaryPath(launchConfig); | ||
| delete debugConfiguration.cargo; | ||
| return; | ||
| } | ||
|
|
||
| const cargo = createCargoConfiguration(launchConfig); | ||
| if (cargo) { | ||
| debugConfiguration.cargo = cargo; | ||
| } | ||
|
|
||
| delete debugConfiguration.program; | ||
| } | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| import * as assert from 'assert'; | ||
| import * as path from 'path'; | ||
| import * as sinon from 'sinon'; | ||
| import * as vscode from 'vscode'; | ||
| import { getSupportedCapabilities } from '../capabilities'; | ||
| import { AspireDebugSession } from '../debugger/AspireDebugSession'; | ||
| import { getResourceDebuggerExtensions } from '../debugger/debuggerExtensions'; | ||
| import { rustDebuggerExtension } from '../debugger/languages/rust'; | ||
| import { AspireResourceExtendedDebugConfiguration, RustLaunchConfiguration } from '../dcp/types'; | ||
|
|
||
| suite('Rust Debugger Extension Tests', () => { | ||
| const fakeAspireDebugSession = {} as AspireDebugSession; | ||
|
|
||
| teardown(() => sinon.restore()); | ||
|
|
||
| test('advertises Rust support when a Rust debugger extension is installed', () => { | ||
| sinon.stub(vscode.extensions, 'getExtension').returns(undefined); | ||
| sinon.stub(vscode.extensions, 'all').value([{ | ||
| packageJSON: { | ||
| contributes: { | ||
| debuggers: [{ type: 'codelldb' }] | ||
| } | ||
| } | ||
| } as unknown as vscode.Extension<unknown>]); | ||
|
|
||
| const capabilities = getSupportedCapabilities(); | ||
| assert.ok(capabilities.includes('rust')); | ||
| assert.ok(getResourceDebuggerExtensions().some(extension => extension.resourceType === 'rust')); | ||
| }); | ||
|
|
||
| test('configures VS Code Rust debugger with cargo metadata', async () => { | ||
| sinon.stub(vscode.extensions, 'getExtension').returns(undefined); | ||
| sinon.stub(vscode.extensions, 'all').value([{ | ||
| packageJSON: { | ||
| contributes: { | ||
| debuggers: [{ type: 'codelldb' }] | ||
| } | ||
| } | ||
| } as unknown as vscode.Extension<unknown>]); | ||
|
|
||
| const launchConfig: RustLaunchConfiguration = { | ||
| type: 'rust', | ||
| working_directory: '/workspace/rust-app', | ||
| cargo: { | ||
| args: ['build', '--release'], | ||
| filter: 'rust-app' | ||
| } | ||
| }; | ||
| const debugConfig = createDebugConfig(); | ||
|
|
||
| await rustDebuggerExtension.createDebugSessionConfigurationCallback!( | ||
| launchConfig, | ||
| ['--port', '8080'], | ||
| [], | ||
| { debug: true, runId: '1', debugSessionId: '1', isApphost: false, debugSession: fakeAspireDebugSession }, | ||
| debugConfig); | ||
|
|
||
| assert.ok(['cppvsdbg', 'cppdbg', 'codelldb', 'lldb'].includes(debugConfig.type)); | ||
| assert.strictEqual(debugConfig.request, 'launch'); | ||
| assert.strictEqual(debugConfig.cwd, '/workspace/rust-app'); | ||
| assert.deepStrictEqual(debugConfig.args, ['--port', '8080']); | ||
| if (debugConfig.type === 'cppvsdbg' || debugConfig.type === 'cppdbg') { | ||
| assert.strictEqual(debugConfig.cargo, undefined); | ||
| const expectedExecutable = process.platform === 'win32' ? 'rust-app.exe' : 'rust-app'; | ||
| assert.strictEqual(debugConfig.program, path.join('/workspace/rust-app', 'target', 'debug', expectedExecutable)); | ||
| } | ||
| else { | ||
| assert.deepStrictEqual(debugConfig.cargo, { args: ['build', '--release'], filter: 'rust-app' }); | ||
| assert.strictEqual(debugConfig.program, undefined); | ||
| } | ||
| assert.strictEqual(debugConfig.noDebug, false); | ||
| }); | ||
|
|
||
| test('sets noDebug when launch option disables debugging', async () => { | ||
| sinon.stub(vscode.extensions, 'getExtension').returns(undefined); | ||
| sinon.stub(vscode.extensions, 'all').value([{ | ||
| packageJSON: { | ||
| contributes: { | ||
| debuggers: [{ type: 'lldb' }] | ||
| } | ||
| } | ||
| } as unknown as vscode.Extension<unknown>]); | ||
|
|
||
| const launchConfig: RustLaunchConfiguration = { | ||
| type: 'rust', | ||
| working_directory: '/workspace/rust-app', | ||
| cargo: { | ||
| args: ['build'] | ||
| } | ||
| }; | ||
| const debugConfig = createDebugConfig(); | ||
|
|
||
| await rustDebuggerExtension.createDebugSessionConfigurationCallback!( | ||
| launchConfig, | ||
| [], | ||
| [], | ||
| { debug: false, runId: '1', debugSessionId: '1', isApphost: false, debugSession: fakeAspireDebugSession }, | ||
| debugConfig); | ||
|
|
||
| assert.strictEqual(debugConfig.noDebug, true); | ||
| }); | ||
| }); | ||
|
|
||
| function createDebugConfig(): AspireResourceExtendedDebugConfiguration { | ||
| return { | ||
| runId: '1', | ||
| debugSessionId: '1', | ||
| type: 'rust', | ||
| name: 'Rust', | ||
| request: 'launch', | ||
| program: '/workspace/rust-app', | ||
| args: [] | ||
| }; | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.