Repository navigation
feat!: rewrite the Node server in Rust - #355
Merged
Merged
Conversation
The Rust server in crates/diffle now serves every review; the TypeScript server, CLI and their tests are gone. Release binaries are built with cargo, the e2e harness and its Docker image build and run the release binary, and the Nix flake builds the client and the binary in two stages. BREAKING CHANGE: npm publishing is paused until the binaries ship as per-platform npm packages; install with the installers, pixi, or Nix. `--dev` is gone (a debug build reads dist/client from disk), and comment stores older than format version 3 are no longer migrated.
Contributor
|
This pull request changes committed snapshots. View the snapshot report to compare them side by side. |
pavelzw
marked this pull request as draft
October 21, 2026 00:22
…ee off the queue Diff-content search reads patches from the snapshotter's cache instead of re-running git diff per file and query; the LSP routes check snapshot membership through Session::has_side; the Linux inotify walk runs on spawn_blocking so the session queue keeps serving; the config path uses std::env::home_dir like the LSP modules.
…container The browser is the only part that must be the same everywhere for the screenshots to match, so only it stays in Docker: compose.yml serves Playwright's Linux Chromium over WebSocket, test/e2e/browser-server.ts starts and stops it, and the runner connects with the loopback exposed so each natively run diffle stays on 127.0.0.1. CI runs the same way. The zoom regression test zooms the root element instead of launching a local Chromium with a profile, which a remote browser cannot take.
…m the removed suites
A Review names what is reviewed and nothing resolved: a range between two revisions carrying the slice on screen (the whole range, one commit, the uncommitted changes, or an interdiff against an iteration or a named range, narrowed to one pair), or one commit against its parent. The Snapshot carries the hashes, what the review follows and where its comments live. Entering a review (POST /api/review) and showing a slice (PUT /api/view) replace POST /api/mode; the view resolver and the comment keys replace the three transition builders, and the liveness and comment-key fields stored on the old ModeSpec are derived. Comment keys keep their strings, so comments.json needs no migration. 💥 Breaking: the API's mode request and the snapshot's mode field are gone; clients send EnterRequest and RangeView and read Snapshot.review, follows and commentKey.
…form scripts/npm-packages.mjs assembles them from the release archives: the platform packages carry the binary with os and cpu set, the launcher in npm/ depends on all of them optionally and runs the matching one. CI assembles and smoke-tests them after the binaries; Publish uploads them with provenance, platform packages first, skipping versions already published.
npm run build builds the client alone; the dev and full build scripts that wrapped cargo are gone, and every reference names cargo run or cargo build. A base checkout from before the port still builds its client under the old script name.
The committed prompt fixture had been reformatted by the pre-commit formatter, which flattens blockquotes in Markdown; insta's .snap files are left alone, and cargo insta review replaces UPDATE_SNAPSHOTS.
diffle config prints the file path on stderr and the settings on stdout; its subcommands are gone, so language-server overrides and GitHub hosts are edited in the file. diffle completion takes a required --shell from clap's own Shell values and prints clap's script unchanged. 💥 Breaking: diffle config set-lsp, unset-lsp, set-context, set-follow-refs, add-auto-viewed and remove-auto-viewed are removed.
Both crates were already in the tree through clap; anstream strips the styles off a terminal and honours NO_COLOR and CLICOLOR_FORCE.
An interdiff answered the threads and viewed-mark routes with 400, and the client fetches both on every view switch, so entering a range-diff or comparing two iterations never took effect. Every review has a comment key again, the interdiff keys as before the review model; only creating threads stays refused there.
The binary is built unwrapped by crane with cached dependency artifacts; each package is a symlinkJoin wrapping it with git, gh and language servers on PATH, and takes more through the withLsps passthru.
clap took the name from the executable, so Windows printed diffle.exe and the help snapshot differed there.
…Script Node 24 strips the types itself, so the workflows keep running them with plain node on a bare checkout, and the hand-written declaration file goes.
A slash inside a join keeps the slash on Windows, while the code reports what it found on disk; three project tests compared the two.
A slash inside a joined component stays a slash on Windows, so tsserver was handed a mixed path. CI's Rust job now runs every test binary before failing, so one run reports all platform differences.
pavelzw
marked this pull request as ready for review
October 21, 2026 19:58
A Windows checkout carried the carriage returns into the schema's descriptions, so the committed schema mismatched there.
The reusable workflows reported their jobs as binaries / build (...) and npm-packages / assemble, which branch protection could not name. Their steps are composite actions now, run by jobs named build and npm-packages in CI and Publish alike.
On Linux a program written moments ago can still be open for writing in a process mid-fork; the window closes on its own, as the process tests showed on an arm64 runner.
pixi run e2e builds the release binary the browser suite drives instead of the suite building it in its global setup; release, smoke, package and npm-packages are tasks too, and the environment locks for Windows ARM so every runner can use it.
Each job is a checkout, the pixi environment, a cargo cache where it helps, and one task. The composite actions are gone: packaging the binary and installing it from a served release is a script behind pixi run package.
The composite actions were deleted a commit early; the jobs now spell out their few steps. The tests point git at a global config that does not exist instead of NUL, which conda-forge's git on Windows refuses.
…d package with the system tar on Windows
…es or a PATH edit
Merged
6 of 7 tasks
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.
closes #351
Rewrites diffle's server and CLI in Rust (
crates/diffle: axum on tokio). The React client is unchanged, andsrc/shared/protocol.tsstays the one client/server contract: a vitest snapshot exports it to JSON Schema (crates/diffle/protocol.generated.json), andbuild.rsgenerates the Rust types from it with typify, JSDoc included as doc comments.What changed
src/server,src/cliand their tests are replaced bycrates/diffle.gitCLI with the same argv; paths are canonicalized throughdunce, so Windows gets no\\?\prefixes.notify, GitHub requests use reqwest, and language servers use our own JSON-RPC framing. Language servers start without a shell (shlexsplits the command line, Windows resolves it on PATH, a busy executable is retried briefly).NO_COLORand a piped stderr stay plain.Reviewis a range (old/new, with a base, a pull request or a view) or a single commit. A range'sRangeViewshows the whole range, one commit, the uncommitted changes, or an interdiff between two iterations and one of its pairs.POST /api/reviewenters a review,PUT /api/viewchanges the view, and theSnapshotcarries what was resolved: the hashes,follows,commentKey, and the interdiff. Every review has a comment key, so threads and viewed marks load in interdiffs too.diffle configprints the config path on stderr and the JSON on stdout, nothing else.diffle completion --shell <shell>takes one of clap_complete's shells.test/lsp/fake-lsp.mjs, and the prompt and review payloads are pinned with insta.compose.yml), so screenshots match CI on every host. The suite drives the release binary;DIFFLE_BINoverrides it.build-client,build-release,test-node,test-rust,e2e,lefthook-run,smoke,package,npm-packages,licenses,diffle. npm scripts cover the client alone; cargo owns the server.pixi runper job, coloured logs,build (<os>)jobs that package and prove each release binary (archive, checksums, and the installers against a served release, under both PowerShells on Windows), annpm-packagesjob that assembles and smoke-tests the npm packages, and alockfilejob that checkspackage-lock.jsonandTHIRD_PARTY_NOTICES.mdare current.@moritzwilksch/diffleis a launcher with oneoptionalDependenciesentry per platform package;scripts/npm-packages.tsassembles them from the release archives. Publishing uses npm trusted publishing (OIDC), no token.THIRD_PARTY_NOTICES.mdis generated (pixi run licenses): the fonts, the packages vite bundled into the client, and the crates the binary links (cargo-about). An unaccepted licence fails the build.unwrappedis the bare binary,defaultwraps it with git and gh on PATH, andwithLspscomposes the language-server variants..rsfiles, minus their trailing#[cfg(test)]module..gitattributesmarks the lockfiles and generated files.💥 Breaking
ModeSpecand the mode endpoints are replaced byReview,RangeView,POST /api/reviewandPUT /api/view.diffle configonly prints; theset/unsetsubcommands are gone.diffle completionrequires--shell.--devis gone. A debug build readsdist/clientfrom disk, sonpm run buildplus a page reload is the dev loop.comments.jsonfiles older than format version 3 are no longer migrated.npm run dev,npm run check,npm run install-hooksand the e2e Docker scripts are replaced by the pixi tasks above.Verification
Follow-ups
darwin-x64build, if Intel Macs come up.TreeRefenum for resolved sides, and explicit re-export lists inprotocol/mod.rs.