Skip to content

feat(vsix-build): add optional test-project input - #76

Merged
CalvinAllen merged 1 commit into
mainfrom
feat/vsix-build/optional-tests
Aug 5, 2026
Merged

feat(vsix-build): add optional test-project input#76
CalvinAllen merged 1 commit into
mainfrom
feat/vsix-build/optional-tests

Conversation

@CalvinAllen

Copy link
Copy Markdown
Contributor

🤔 Why?

Repos with a test project currently need a separate test.yml workflow, which builds the extension a second time on every PR. Test projects ProjectReference the extension project, so dotnet test rebuilds it — including any self-contained server publish the extension's build triggers. That is a meaningful chunk of wall-clock duplicated for no benefit.

✨ What changed

  • ➕ New optional test-project input (path to a test project or solution).
  • 🧪 New step 4 runs dotnet test in the same job, immediately after the build, so the compiled output is reused. Existing steps renumbered.
  • 🔢 SetVsixVersion is passed through to match the build step exactly. MSBuild treats differing global properties as a distinct project instance, so omitting it would rebuild the extension from scratch and defeat the purpose.
  • ⛔ Tests run before the artifact steps, so a failing test fails the job without publishing an artifact.

🔒 Compatibility

The input defaults to '' and the step is gated on if: inputs.test-project != ''. Repos that do not set it see no change in behavior.

📋 Usage

jobs:
  build:
    uses: CodingWithCalvin/.github/.github/workflows/vsix-build.yml@main
    with:
      extension-name: MCPServer
      test-project: src/CodingWithCalvin.MCPServer.Tests/CodingWithCalvin.MCPServer.Tests.csproj
    secrets: inherit

First consumer will be CodingWithCalvin/VS-MCPServer#99.

Repos with a test project had to run tests in a separate workflow, which meant
building the extension twice per PR — test projects reference the extension
project, so `dotnet test` rebuilds it along with any self-contained server
publish it triggers.

Adding the test run to this job reuses the build output. SetVsixVersion is
passed through to match the build step, since MSBuild would otherwise treat the
differing global properties as a separate project instance and rebuild.

The input defaults to empty, so repos that do not set it are unaffected.
@CalvinAllen
CalvinAllen merged commit 78e0475 into main Aug 5, 2026
@CalvinAllen
CalvinAllen deleted the feat/vsix-build/optional-tests branch August 5, 2026 21:25
CalvinAllen added a commit to CodingWithCalvin/VS-MCPServer that referenced this pull request Aug 5, 2026
The test project references the extension project, so a standalone test
workflow rebuilt the extension and its self-contained server publish a second
time on every PR. Passing test-project to the shared vsix-build workflow runs
the tests in the same job, reusing the build output.

Requires CodingWithCalvin/.github#76.
CalvinAllen added a commit to CodingWithCalvin/VS-MCPServer that referenced this pull request Aug 5, 2026
* fix(server): prevent devenv.exe from hanging on shutdown

Package disposal blocked the Visual Studio UI thread on ServerProcessManager
.StopAsync(), whose awaits captured that thread's synchronization context and
posted their continuations straight back to it. The shutdown never completed,
so devenv.exe stayed resident after the main window closed.

The deadlock only reproduced with the server running: with no child process
and no listening pipe, every await in the path completed synchronously and
nothing was ever posted back.

- Use ConfigureAwait(false) throughout the shutdown path in
  ServerProcessManager and RpcServer.
- Run shutdown via Task.Run and wait with a timeout in package disposal, so
  no continuation can need the UI thread and VS exits regardless.
- Bound the cooperative shutdown. A half-open pipe could leave the RPC
  shutdown call pending forever; the previous 5s + 2s waits were also
  unbounded in the worst case.
- Assign the server process to a kill-on-close job object so it dies with
  Visual Studio even when devenv.exe terminates abnormally, rather than
  surviving to hold its HTTP port against the next session.
- Stop suppressing VSTHRD002 project-wide. That analyzer flagged this exact
  deadlock; it is now suppressed only at the two Dispose call sites that
  genuinely require a blocking wait, each with a justification.
- Fix the pipe listener faulting its task when cancelled during the retry
  backoff, and avoid disposing the cancellation source out from under it.

Adds a test project with regression coverage that drives the shutdown path
from a thread whose synchronization context cannot run posted callbacks. Both
new tests fail against the previous code and pass against this change.

* ci: run tests from the build workflow instead of a separate one

The test project references the extension project, so a standalone test
workflow rebuilt the extension and its self-contained server publish a second
time on every PR. Passing test-project to the shared vsix-build workflow runs
the tests in the same job, reusing the build output.

Requires CodingWithCalvin/.github#76.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant