Skip to content

Isolate AppHost launches in linked git worktrees. - #19384

Open
Shaurya Srivastava (Shaurya2k06) wants to merge 63 commits into
microsoft:mainfrom
Shaurya2k06:worktree-aware-apphost-lifecycle
Open

Isolate AppHost launches in linked git worktrees.#19384
Shaurya Srivastava (Shaurya2k06) wants to merge 63 commits into
microsoft:mainfrom
Shaurya2k06:worktree-aware-apphost-lifecycle

Conversation

@Shaurya2k06

@Shaurya2k06 Shaurya Srivastava (Shaurya2k06) commented Aug 14, 2026

Copy link
Copy Markdown

Description

Linked git worktrees used to start a second AppHost on the same ports and user secrets as the primary checkout, because isolation was opt-in and socket matching is path-based. Running aspire stop without --apphost from the primary tree could also stop an AppHost in a nested linked worktree (for example repo/.worktrees/feature), because scope was based on path containment alone.

This change makes AppHost lifecycle worktree-aware:

  • Start (aspire start, aspire run, VS Code Run/Debug, and aspire_apphost_start): linked worktrees default to isolated mode (--isolated). Primary checkout behavior is unchanged. Explicit --isolated still forces isolation on; the LM tool accepts optional isolated: true|false to override inference.
  • Stop (aspire stop without --apphost): only AppHosts in the current worktree are in scope. Nested linked worktrees are no longer stopped from the primary checkout. --apphost and --all are unchanged.
  • Argument forwarding: when aspire start or aspire run delegates a launch to VS Code, explicitly supplied CLI options and AppHost arguments after -- now reach the final AppHost process without flattening token boundaries. This preserves spaces, quotes, empty arguments, and option-shaped values while keeping forwarded values out of logs.

Worktree detection is filesystem-only (no git spawn): a linked worktree has a .git file whose gitdir: line points at .git/worktrees/<name>. Submodule .git files (.git/modules/) are not treated as linked worktrees.

Fixes #19357

Why is this PR 83 files?

The original worktree-isolation change had to cover every AppHost launch path, including launches delegated from the CLI to VS Code. End-to-end testing of that path exposed a broader existing problem: arguments could be dropped or flattened as they crossed CLI parsing, the extension backchannel, debug configuration, command shims, and the final process spawn. Since --isolated is itself one of those arguments, fixing isolation reliably required fixing the general forwarding path rather than special-casing one flag.

The diff therefore follows one launch pipeline across both the CLI and extension. It also adds the cross-platform process inspection, command-shim handling, process-tree cleanup, capability negotiation, reservation-race coverage, and unit/E2E tests needed to verify the actual final argv on Windows, macOS, and Linux.

Most of the size is coverage and test infrastructure, not 83 separate product changes: 57 extension files (including its unit and E2E suites), 12 CLI test files, 13 CLI source files, and one documentation file.

User-facing usage

# In a linked worktree: isolated by default
aspire start
aspire run

# Explicit isolation still works from any checkout
aspire start --isolated

# AppHost arguments retain their original boundaries
aspire run -- --custom-option "value with spaces"

# Stop only AppHosts in this worktree
aspire stop

# Unchanged
aspire stop --apphost ./AppHost/AppHost.csproj
aspire stop --all

For the VS Code language-model tool aspire_apphost_start:

  • Omit isolated to auto-isolate in a linked worktree.
  • Pass isolated: true or isolated: false to override.
  • Newly launched results include the effective isolated value. Idempotent or otherwise uncertain results omit it because the extension cannot verify the existing launch mode.

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
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
    • No

Linked worktrees used to start a second AppHost on the same ports and secrets, and aspire stop could target the nested checkout from the primary tree. Default to --isolated in linked worktrees and keep stop scoped to the current worktree.

Signed-off-by: shaurya2k06 <shaurya2k06@gmail.com>

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 was unable to review this pull request because the user who requested the review has reached their quota limit.

@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 -- 19384

Or

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

Comment thread extension/src/services/AppHostLaunchService.ts Outdated
Comment thread src/Aspire.Cli/Commands/AppHostLauncher.cs Outdated
Comment thread extension/src/utils/gitWorktree.ts Outdated
Comment thread src/Aspire.Cli/Git/GitWorktree.cs Outdated

@adamint Adam Ratzman (adamint) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I left four inline comments. The big two are that worktree inference is getting applied to deploy/publish/do, and the false opt-out is lost in both the extension and detached CLI path. I also reproduced the Git layout parser missing bare/separate-git-dir worktrees and treating a linked-worktree submodule as its own worktree.

Targeted CLI tests passed (66), as did the extension unit suite (1,910). I wasn't able to get a live AppHost running within the review budget, so randomized ports/user-secret isolation itself is still unverified.

Adam Ratzman and others added 14 commits August 14, 2026 14:28
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5647184b-a0e8-42a7-b6a7-654c5b956b8d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5647184b-a0e8-42a7-b6a7-654c5b956b8d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5647184b-a0e8-42a7-b6a7-654c5b956b8d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5647184b-a0e8-42a7-b6a7-654c5b956b8d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5647184b-a0e8-42a7-b6a7-654c5b956b8d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5647184b-a0e8-42a7-b6a7-654c5b956b8d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5647184b-a0e8-42a7-b6a7-654c5b956b8d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5647184b-a0e8-42a7-b6a7-654c5b956b8d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5647184b-a0e8-42a7-b6a7-654c5b956b8d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5647184b-a0e8-42a7-b6a7-654c5b956b8d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5647184b-a0e8-42a7-b6a7-654c5b956b8d

# Conflicts:
#	extension/src/lm/appHostLifecycleTools.ts
#	extension/src/services/AppHostLaunchService.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5647184b-a0e8-42a7-b6a7-654c5b956b8d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5647184b-a0e8-42a7-b6a7-654c5b956b8d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5647184b-a0e8-42a7-b6a7-654c5b956b8d
Copilot AI review requested due to automatic review settings August 15, 2026 01:20
Adam Ratzman and others added 4 commits August 17, 2026 08:27
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 466da694-ac7b-4ade-b31d-87ab0b2f01d5
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 466da694-ac7b-4ade-b31d-87ab0b2f01d5
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 466da694-ac7b-4ade-b31d-87ab0b2f01d5
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 466da694-ac7b-4ade-b31d-87ab0b2f01d5
Copilot AI review requested due to automatic review settings August 17, 2026 16:03

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 encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Note

This error may be related to your runner configuration. You can now configure runners for Copilot code review separately from Copilot cloud agent by creating a copilot-code-review.yml file with your setup steps. Read the docs for details.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 466da694-ac7b-4ade-b31d-87ab0b2f01d5
Copilot AI review requested due to automatic review settings August 17, 2026 16:24

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.

Review details

  • Files reviewed: 84/84 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread extension/src/utils/cliPath.ts Outdated
Comment thread extension/src/lm/appHostLifecycleToolService.ts
Comment thread extension/src/utils/cmdShim.ts
@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.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 466da694-ac7b-4ade-b31d-87ab0b2f01d5
Copilot AI review requested due to automatic review settings August 17, 2026 17:31

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.

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

extension/src/utils/cliPath.ts:517

  • A bare configured command name is still treated as an exact CLI pin, but it is not stable across the probe and launch working directories. For example, with a relative PATH entry, probing aspire from the extension host can inspect one binary while AspireDebugSession later spawns aspire from the AppHost directory and runs another; on Windows, a bare name that resolves to aspire.cmd also bypasses the .cmd wrapper because the returned value has no shim extension. Resolve bare names through findCliOnPath and retain the resulting concrete path before capability negotiation and launch.
    extension/src/lm/appHostLifecycleToolContracts.ts:189
  • This contradicts the PR's stated tool contract that “every start result includes the effective isolated value.” The implementation only adds it when the service knows a value (currently a newly started launch), while idempotent/other start outcomes omit it. Either make the result contract supply the value for every start-tool outcome or update the PR description to document the narrower contract.
    if (tool === aspireAppHostStartToolName && isolated !== undefined) {
        result.isolated = isolated;
    }
  • Files reviewed: 84/84 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 466da694-ac7b-4ade-b31d-87ab0b2f01d5
Copilot AI review requested due to automatic review settings August 17, 2026 18:16

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.

Review details

  • Files reviewed: 84/84 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 466da694-ac7b-4ade-b31d-87ab0b2f01d5
Copilot AI review requested due to automatic review settings August 17, 2026 18:26

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.

Review details

  • Files reviewed: 84/84 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread extension/src/utils/cliPath.ts Outdated
@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.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 466da694-ac7b-4ade-b31d-87ab0b2f01d5
Copilot AI review requested due to automatic review settings August 18, 2026 00:25

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.

Review details

  • Files reviewed: 83/83 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Assert.Equal(CliExitCodes.Success, exitCode);
var expectedPath = Path.Combine("AppHost", "AppHost.csproj");
Assert.Contains(statusMessages, message => message == string.Format(CultureInfo.CurrentCulture, StopCommandStrings.StoppingAppHost, expectedPath));
Assert.DoesNotContain(GetDisplayedText(interactionService, statusMessages), message => message.Contains(".worktrees", StringComparison.Ordinal));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make AppHost start and stop operations worktree-aware

3 participants