Ship the LSP binaries, the Rider plugin, and the Homebrew/Scoop channels - #239
Merged
Conversation
The previous commit landed the release workflow and Makefile changes but not the scripts they call: `tools/dist/` was swallowed whole by the bare `dist/` pattern in .gitignore, which matches a directory of that name at ANY depth, not just the build output at the repo root. CI would have run `make _package-archive` against a missing `tools/dist/archive.sh`. Renamed to `tools/packaging/` rather than negating the ignore rule. These are source, not build output, and nothing named `dist` in the tree can be trusted to survive; the new name also says what the scripts do. Also removes src/editors/vscode/test-fixtures/workspace/.editorconfig, which promoted CS0219 to an error across the shared TestFixtures project. Refactor.cs carries an unused local on purpose — it is the fixture the CS0219 quick-fix test refactors — so the fixture build failed, and with it every VS Code chunk at pretest plus the five sidecar tests that build the same workspace. Nothing referenced the file: it set severities no test asserts on, and the quick-fix test gets CS0219 from the compiler regardless. Roslyn honours editorconfig severity over the csproj's TreatWarningsAsErrors=false, so a fixture that needs one must carry it in an isolated nested scope, never at the workspace root.
…ures
Test Explorer work plus the lint and format fixes CI enforces:
- `.split('\n')` in test-explorer-fixtures.ts was written with a literal newline
instead of the escape, so the suite could not compile (TS1002) and every VS
Code chunk died at pretest.
- Two `as vscode.TestItem` casts in test-explorer-adapter-ids.test.ts tripped
@typescript-eslint/non-nullable-type-assertion-style; applied eslint --fix.
- Reformatted four files prettier reported as unformatted.
CLAUDE.md: no SharpLsp code is "legacy" — code that does not match the specs
gets deleted, not preserved.
Prettier gate in ci-build.yml rejected it.
Running a class group left every theory reporting "No result reported". The two halves of a test id disagreed. `parseFullyQualifiedTestList` strips an adapter's appended unique ID when it builds the tree, so the id is the bare `Ns.Class.Method`. `toTestResult` built `fullyQualifiedName` straight from `TestMethod/@className` + `@name`, and xunit.runner.visualstudio 2.2.0 stamps that attribute with the unique ID — so the report keyed on a name no tree item carries and no outcome could be attributed back (issue #232). A theory made it worse: each row carries a DIFFERENT unique ID, so the rows never collapsed onto the single id they share — which is exactly what `worse()` and OUTCOME_SEVERITY in test-execution.ts already assume when they judge a data-driven test by its worst row. Stripped with the same rule at the one boundary where a TRX name becomes an id. `displayName` keeps the decoration: it is a label, not a key. NUnit's `Adds_Case(2,2,4)` still round-trips untouched — no space before the paren, and its contents are not hex.
SHARPLSP_DAP_TRACE showed only what arrived FROM the adapter, so a response the router synthesises or re-sequences itself — the attach retrier's, for one — was invisible, and an unanswered client request could not be told from an answered one. Same flag, same shape, on the way out.
Pressing Debug on a test reported the attach settled the moment `startDebugging` resolved, which is only "the session exists" — several DAP round trips before it can stop anywhere. The gesture handed control back while the debugger was still coming up, so the whole `debug-tests` chunk failed: no `configurationDone` in the handshake, no breakpoint bound, no stop, and every later test timed out behind the wedged run. The router now settles on ARMED rather than on "configuration was requested": netcoredbg has ANSWERED `configurationDone` — it answers ~80ms later and finishes the attach as it does — and every breakpoint it accepted has bound. A VSTEST host attached under `VSTEST_HOST_DEBUG` has not loaded the test assembly yet, so every breakpoint in the user's own test starts out `verified: false` and binds later by a `breakpoint` event ([DEBUG-FEATURES-BREAKPOINTS-VERIFY]); reporting "attached" before that is issue #233's Debug press that ends in silence. Also fixes Run/Debug Test at the cursor, which invoked the workbench commands `testing.runTests`/`testing.debugTests`. Neither exists, so the lens gesture died with "command not found" and ran nothing; it now presses the extension's own registered profile, the same entry point the Testing view uses. The multi-select expectation asserted breakpoints come back in the order they were armed. VS Code's debug model sorts them by uri then line (`sortAndDeDup`) before sending, and DAP requires the response array to correspond to the request array, so the adapter answers ascending — the expected array is corrected to the order the workbench provably sends, with all three breakpoints still required to bind. Local `make _run-vsix-suite CHUNK=debug-tests`: 22 passing, 0 failing (was 0 passing, 9 failing).
`statSync(path).size === 0` followed by `readFileSync(path)` is a check the read cannot rely on: the file may change between the two (CodeQL js/file-system-race, high). Read once and judge the bytes in hand — the emptiness check is then about the same bytes that get hashed, and a release archive is no longer walked twice.
…test `writeDebugTestFixture` only WROTE the project and solution, so the restore and compile were paid by whichever test ran first, inside its 50s `DEBUG_TEST_MS` budget. C# fits in that; F# — FSharp.Core plus a cold compiler start in a fresh scratch directory — does not. On Ubuntu the first F# test timed out mid-build and every later test in the run, F# and group alike, timed out queued behind the invocation still building: 12 passing, 10 failing, all ten exactly 50.0s apart with no work in between. The fixture is now built where the cost belongs, in `suiteSetup`, which already owns `FIXTURE_BUILD_MS`.
CI's extension log names the wedge exactly. The last C# debug test armed its session at 08:28:58.075 and passed at 08:28:59.0; its `dotnet test` was still running, because a Debug gesture resolves at the ATTACH and the invocation continues until the debugged tests finish. `suiteTeardown` then deleted the fixture directory at 08:29:00 — a fraction of a second before that invocation would have written its TRX and exited. `dotnet test` was left pointed at a directory that no longer existed and never exited, and because every invocation the controller makes is serialised behind one queue, it took the rest of the run with it: all four F# tests and all six group tests timed out at exactly 50.0s intervals with no work in between, 12 passing / 10 failing. The teardown now waits for that queue to drain before removing the tree, so it is ordered rather than lucky.
Prettier fits it on one line now that removeDirRecursive is gone.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Releases published nothing but VSIXs. Anyone not using VS Code — Rider, Zed, Neovim, Helix, Emacs — had no way to install SharpLsp at all, and the docs described release jobs that had never existed.
Standalone server archives
Every release now publishes
sharplsp-<platform>.tar.gz/.zipfor all five built platforms, alongside the VSIXs and covered bySHA256SUMS.The layout is dictated by the host's own sidecar resolution (
installed_sidecar_exelayout 1), so unpacking anywhere and runningsharplspfinds Roslyn and FCS with no env vars and no configuration.package-vsix-<platform>builds the Rust host and both sidecars once and emits both artifacts, so this costs no extra CI time.tools/packaging/verify-archive.shasserts the layout and unpacks and executes all three binaries — a .NET apphost separated from its managed assembly exists but cannot start, which no listing detects. It runs on every PR fromci-build.yml, not just at tag time.Rider
The plugin was dead code. It sat in package
com.forgelsp, hunted for a binary namedforge-lsp, and sentforge/*requests to a host that only answerssharplsp/*— nothing it did could ever have worked, while CI compiled it and ran a coverage gate over it.com.sharplsp.rider/SharpLsp*before anything shipped, so no install is orphaned@JsonRequestnames repointed atsharplsp/*, checked against the host's dispatch tablepluginVersionis now stamped bymake _stamp-version; unstamped it would have shipped0.1.0from every tagbuild-riderrelease job withRIDER_REQUIRED=1, so a missing JDK fails loudly instead of publishing a release with no pluginHomebrew and Scoop
publish-homebrewandpublish-scooppushFormula/sharplsp.rbandbucket/sharplsp.jsonto the tap and bucket after the release succeeds. Both files are generated whole from the published archives — the Scoop manifest as an object serialized to JSON, never string-templated — so a checksum cannot survive a version bump. Prerelease tags are skipped; neither package manager has a prerelease channel.Homebrew installs the sidecars to
lib/sharplsp/and Scoop'sextract_dirleaves them besidesharplsp.exe; both are the layouts the host resolves.tools/packaging/verify-package-manifests.mjsasserts that on every PR.Two root-cause fixes found while running CI locally
tools/dist/was never committed. The baredist/pattern in.gitignorematches a directory of that name at any depth, so the workflows landed referencing scripts that were not in the repo. Renamed totools/packaging/— these are source, not build output.A stray
test-fixtures/workspace/.editorconfigpromoted CS0219 to an error across the sharedTestFixturesproject.Refactor.cscarries an unused local on purpose — it is the fixture the CS0219 quick-fix test refactors — so the fixture build failed, taking down every VS Code chunk at pretest and five sidecar tests that build the same workspace. Nothing referenced the file.Also on this branch
Test Explorer: multi-targeted projects collapse to one assembly root instead of rendering the whole tree twice, and the legacy VSTest adapter's decorated ids are parsed (
test-names.ts).Not automated
Publishing the two sidecar
dotnet toolpackages to NuGet — see #235.BREW_SCOOP_PATmust be granted to this repo; both publish jobs fail fast with an explicit message if it is absent.