Skip to content

feat: unify Windows and Linux Coder Desktop in a single repo#173

Open
ibetitsmike wants to merge 10 commits into
mainfrom
mike/unify-linux-windows
Open

feat: unify Windows and Linux Coder Desktop in a single repo#173
ibetitsmike wants to merge 10 commits into
mainfrom
mike/unify-linux-windows

Conversation

@ibetitsmike

@ibetitsmike ibetitsmike commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Merges the coder-desktop-linux Avalonia port back into this repo so one codebase builds and ships Coder Desktop for both Windows and Linux, sharing the VPN service, RPC protocol, and SDKs.

What changed

  • Imported from coder-desktop-linux: App.Avalonia (Avalonia UI), App.Shared (UI-agnostic ViewModels/services), App.Linux (Linux service impls), Vpn.Linux (Unix socket RPC transports), Packaging.Linux (deb/rpm/tar/AUR scripts), scripts/run-linux-dev.sh, and the release-linux.yaml workflow.
  • Adopted platform-aware versions of shared sources (Vpn.Service/*, MutagenSdk/MutagenClient.cs, Vpn.DebugClient): runtime OS checks plus WINDOWS conditional compilation preserve existing Windows behavior.
  • Multi-targeted Vpn, Vpn.Service, Tests.Vpn, Tests.Vpn.Service as net8.0;net8.0-windows. ManagerRpc now uses an IRpcServerTransport abstraction: named pipes (extracted to NamedPipeServerTransport) on Windows, Unix domain sockets on Linux.
  • Added Coder.Desktop.Linux.slnf (excludes WinUI App, Tests.App, Installer), Directory.Build.props (EnableWindowsTargeting on non-Windows hosts only), and NuGet lock files for the new projects.
  • CI: Windows fmt/test/build jobs preserved (fmt now passes the solution explicitly since the repo has both a .sln and a .slnf); added test-linux and build-linux jobs.
  • Normalized formatting: the current dotnet 8.0.4xx formatter enforces the .editorconfig CRLF rule that older SDKs ignored, which was failing fmt even for unmodified files.
  • Deflaked SpeakerTest.SendMessageWriteError (shutdown race, ~50% failure rate on Linux).
  • The WinUI App and Avalonia App.Avalonia/App.Shared remain separate; converging the Windows app onto App.Shared is follow-up work.

Validation

  • All 5 CI checks green: fmt, test-windows, build-windows, test-linux, build-linux.
  • Linux (local + fresh dev.coder.com aws-linux workspace): locked-mode restore of Coder.Desktop.Linux.slnf, Release build, all test projects green, format verification clean.
  • Linux end-to-end smoke on the workspace: ran CoderVpnService under sudo, drove it via Vpn.DebugClient over the Unix socket. VPN started against https://dev.coder.com, downloaded the tunnel binary, and streamed live workspace/agent peer updates with successful P2P handshakes.
  • Windows (dev.coder.com aws-windows-desktop workspace): full-solution restore, dotnet test -p:Platform=x64 on the shared test projects, Release publish of Vpn.Service (net8.0-windows). The WinUI App publish and full fmt/locked-mode+NetFrameworkHostedCompiler restore fail identically on unmodified main in that environment (SDK drift), so CI is authoritative there, and it passes.

This PR was prepared by Mux (AI agent) on behalf of Mike.

- Adopt platform-aware shared sources from coder-desktop-linux (runtime OS
  checks and WINDOWS conditional compilation preserve Windows behavior)
- Multi-target Vpn, Vpn.Service, Tests.Vpn, Tests.Vpn.Service as
  net8.0;net8.0-windows; Vpn.DebugClient becomes net8.0
- Add NamedPipeServerTransport (Windows) implementing IRpcServerTransport,
  extracted from the previous ManagerRpc named pipe server logic
- Add Directory.Build.props with EnableWindowsTargeting for non-Windows hosts
- Add Linux projects to the solution plus Coder.Desktop.Linux.slnf filter
- Enable NuGet lock files for new projects and regenerate affected locks
- CI: keep Windows fmt/test/build jobs, add Linux test and publish jobs
- UnixSocketServerTransport: refuse to delete non-socket files at the
  configured socket path, and only assign the listener after successful
  bind/listen so a failed setup is retried
- Centralize the default Unix socket path in a single constant
- DebugClient: reuse UnixSocketClientTransport (honors
  CODER_DESKTOP_RPC_SOCKET_PATH)
- Manager: fail fast when a signature signer is configured in a build
  without Authenticode support instead of silently skipping validation
On Linux the peer speaker's receive loop can observe the pipe closing
during disposal and raise an error event before it is disposed itself,
failing the test spuriously (~50% on some machines). Stop asserting on
error events once shutdown begins.
Combining EnableWindowsTargeting with BuildWithNetFrameworkHostedCompiler
(used by the Windows CI jobs) injects Microsoft.Net.Compilers.Toolset.Framework
into every project, which breaks locked-mode restore against lock files
generated without it. Windows hosts do not need EnableWindowsTargeting.
dotnet format fails to auto-discover the workspace now that the repo
contains both Coder.Desktop.sln and Coder.Desktop.Linux.slnf.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d8796feb52

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Vpn.Service/Manager.cs
Comment on lines +467 to +468
_logger.LogDebug(
"Skipping tunnel binary version validation: ProductVersion validation is only supported on Windows binaries");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Validate the Linux tunnel binary before executing it

On the Linux path this skips the only version validator, and TunnelBinarySignatureSigner defaults to empty on Linux, so the validator set is empty before the service chmods and executes the downloaded /bin/coder-linux-* as root. Because the RPC socket is made writable by all local users and StartRequest.CoderUrl is client-controlled, an unprivileged user can point the root service at a fake HTTP Coder endpoint and supply an arbitrary tunnel binary; Linux needs a trusted signature/checksum or equivalent client restriction before execution.

Useful? React with 👍 / 👎.

Comment thread App.Avalonia/App.axaml.cs
Comment on lines +106 to +109
foreach (var arg in args)
{
if (arg.Equals("--minimized", StringComparison.OrdinalIgnoreCase) ||
arg.Equals("--start-hidden", StringComparison.OrdinalIgnoreCase))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Dispatch coder:// launch arguments to the URI handler

The Linux desktop entry launches /usr/bin/coder-desktop %u, so opening a workspace app passes the coder://... URL in desktop.Args; this loop only recognizes minimized flags, and a repo-wide search shows IUriHandler is never registered or invoked by App.Avalonia. As a result, protocol activations on Linux just open the tray window and silently drop the RDP/open-workspace request.

Useful? React with 👍 / 👎.

Comment thread Packaging.Linux/PKGBUILD
rid='linux-arm64'
fi

local repo_root="${startdir}/.."

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include the repo source in the AUR build inputs

When the generated AUR archive is built with makepkg, only the files declared in source are available, and this PKGBUILD declares just the service and desktop assets. ${startdir}/.. will not contain App.Avalonia or Vpn.Service in a normal AUR checkout, so both dotnet publish commands fail unless the full repository happens to be checked out next to the PKGBUILD; add a source tarball/git source or package the full source tree.

Useful? React with 👍 / 👎.

The dotnet 8.0.4xx SDK formatter now enforces the .editorconfig CRLF
line-ending rule that older SDK versions ignored, which made the fmt CI
job fail even on unmodified files. Apply dotnet format output across the
solution.
- Guard the DebugClient Unix socket path with OperatingSystem.IsLinux()
  to satisfy CA1416
- Suppress AVP1002 in App.Avalonia (DependencyObjectSelector registers
  generic AvaloniaProperty owners by design, ported from WinUI)
- Set the SendMessageWriteError shutdown flag before injecting the write
  error, since the failed write can also tear down the receive loop
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant