Skip to content

draw service sidecar for tauri app - #1126

Merged
Micheal Parks (micheal-parks) merged 11 commits into
mainfrom
draw-server-sidecar
Oct 23, 2026
Merged

Micheal Parks (micheal-parks) merged 11 commits into
mainfrom
draw-server-sidecar

Conversation

@DTCurrie

Copy link
Copy Markdown
Member

Bundles the Go draw server with the desktop app, so the draw-service panel is live in a standalone install with nothing else running. Stacks on #1116 (tauri-app-workflows), which stacks on #1114.

Draw server

  • cmd/draw-server accepts -port 0 and prints draw-server: listening on http://127.0.0.1:<port> as its first stdout line, with the port the OS actually bound. server.Start now takes the address from the listener instead of the configured port.
  • -exit-on-stdin-close shuts the server down when stdin reaches EOF. Off by default, so make up and pnpm dev are unaffected.
  • MotionPlanService registration moves into motionplan_handlers.go behind //go:build !nomotionplan. Building with -tags nomotionplan drops the nlopt link and IK inspection.
  • main_test.go builds the binary and checks the stdout line and the stdin-close exit. server_test.go checks that port 0 reports a bound address.

Desktop app

  • draw_server.rs spawns the sidecar through tauri-plugin-shell with -port 0 -tmp-dir <app local data>/draw-tmp -exit-on-stdin-close, parses the port from the first stdout line within 10 s, forwards the rest to the log plugin, and answers the draw_service_port command with the port or null.
  • lib.rs spawns only in release builds, or in a debug build with VIAM_VISUALIZER_SIDECAR=1, and kills the child on ExitRequested and Exit.
  • tauri.conf.json declares bundle.externalBin: ["binaries/draw-server"]. beforeBuildCommand is pnpm tauri:before-build. beforeDevCommand runs pnpm build:sidecar first, because tauri-build refuses a missing external binary.

Frontend

  • useDrawServicePort reads the sidecar port over IPC when running in Tauri and resolves to undefined otherwise.
  • +layout.svelte picks the draw port as drawPort query override, then sidecar port, then the compile-time default, and mounts DrawService only once the port is known so it never dials 3030 first in the desktop app.

Build and CI

  • scripts/build-sidecar.mjs, behind the wireit target build:sidecar, builds src-tauri/binaries/draw-server-<triple> with nlopt linked statically and fails if otool -L or ldd shows a non-system library.
  • tauri-release.yml installs Go and nlopt on every leg. The macOS x86_64 leg builds nlopt from source for that arch on the arm64 runner. Windows installs the MinGW toolchain and nlopt through MSYS2.
  • tauri-check.yml now triggers on the Go sources and builds the sidecar before clippy and cargo test, which need the binary.

Why?

Why a free port instead of 3030?

server.Start attaches to whatever already listens on its port, which is right for make up and wrong for an unrelated process. With port 0 the sidecar always owns its server and the frontend is told which port.

Why does the sidecar watch stdin when the app already kills it on exit?

Tauri's exit events fire for Cmd+Q and window close, not for a signal or a crash. The first runtime check left a draw-server running after kill -TERM on the app. The shell plugin pipes the child's stdin, so the pipe closes when the app dies by any cause. The explicit kill stays for the fast path.

Why a private nlopt.pc instead of CGO_LDFLAGS?

go-nlopt links through #cgo pkg-config: nlopt, and Homebrew's .pc points at a dylib. A shell-level CGO_LDFLAGS=-L/opt/homebrew/lib goes first on the link line and the dylib wins again, so the script clears it and hands pkg-config a directory holding only libnlopt.a.

Testing

Ran pnpm check, pnpm vitest run src/routes/lib/tauri, go test ./cmd/draw-server/... ./client/server/... with and without -tags nomotionplan, and cargo fmt --check, cargo clippy --all-targets -- -D warnings, cargo test in src-tauri. Ran pnpm lint through the repo's fixer and pnpm exec eslint scripts/build-sidecar.mjs.

Added useDrawServicePort.svelte.spec.ts, two tests in cmd/draw-server/main_test.go, one in client/server/server_test.go, and four parse_port_line tests in draw_server.rs.

Verified on macOS with pnpm tauri build --debug --bundles app and VIAM_VISUALIZER_SIDECAR=1: the sidecar starts under ~/Library/Application Support/com.viam.visualizer/draw-tmp, the webview holds two Connect streams to the sidecar port and none to 3030 while a make up server sits there, and pgrep -f draw-server is empty after both kill -TERM and kill -KILL on the app.

Not verified: the release legs. Windows static linking and the macOS x86_64 cross-build are checked structurally only, until the next release run.

@DTCurrie
Devin T. Currie (DTCurrie) added this pull request to stack #1115 October 19, 2026 16:36
@changeset-bot

changeset-bot Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: dc3f312

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@viamrobotics/visualization Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-10-23 15:40 UTC

Base automatically changed from tauri-app-workflows to main October 21, 2026 20:58
@micheal-parks
Micheal Parks (micheal-parks) merged commit 5198bfd into main Oct 23, 2026
8 checks passed
@micheal-parks
Micheal Parks (micheal-parks) deleted the draw-server-sidecar branch October 23, 2026 15:40
@claude claude Bot mentioned this pull request Oct 23, 2026
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.

2 participants