Skip to content

Fix Linux compatibility without changing macOS/Windows behavior - #9

Open
josephdadams wants to merge 1 commit into
mainfrom
linux-compatibility-fixes
Open

josephdadams wants to merge 1 commit into
mainfrom
linux-compatibility-fixes

Conversation

@josephdadams

Copy link
Copy Markdown
Owner

Summary

Several command handlers used Linux branches that no longer work on mainstream distributions, and one packaging choice made the released build hard to run. This fixes them without changing macOS or Windows behavior — every change is confined to the Linux code path, and the darwin/win32 branches are byte-identical to before.

What was broken on Linux

Issue Effect Fix
robotjs imported at module scope in inputHandlers The command table pulls this file in, so a native module that failed to build or load took down the whole app — runScript included, not just sendInput Lazy, cached load on first use; throws CommandError(501) with a platform-specific hint
lock shelled out to gnome-screensaver-command Absent from modern GNOME (removed years ago on Ubuntu, never present on RHEL 9) loginctl lock-session, falling back to xdg-screensaver lock, then the old command
shutdown / reboot / shutdown_cancel used sudo spawn gives sudo no TTY to prompt on, so these silently failed Go through systemd, which authorizes an active local session via polkit
"Run at Login" app.setLoginItemSettings is macOS/Windows only — the checkbox wrote to the store and did nothing New helpers/autoLaunch.ts writes an XDG autostart entry
focusApp required wmctrl Not installed by default on any distro Falls back to xdotool
quitApp used pkill -f Matched the entire command line, so it could kill bystander processes pkill -x, matching the process name

Packaging

  • Ship deb and rpm alongside AppImage. Ubuntu 22.04+ no longer ships libfuse2, so the AppImage fails with a libfuse.so.2 load error on a stock modern Ubuntu. Native packages sidestep FUSE entirely and give RPM distros a first-class install.
  • Pin CI to ubuntu-22.04 (was ubuntu-latest, now 24.04). The native module compiled on the runner links against the build host's glibc, so building on 24.04 (2.39) produced artifacts too new for Ubuntu 22.04 (2.35) and RHEL 9 (2.34). Building on the oldest supported distro widens compatibility.
  • Added rpm to the Linux apt step for the new target.

Incidental hardening

While making the Linux shutdown path shell-free I noticed the shutdown command's time parameter was completely unvalidated and interpolated into the macOS osascript -e string. Added assertNonNegativeInteger with test coverage. Valid input behaves exactly as before on every platform.

Not fixable in code

Two Linux limitations remain, for the record:

  • Tray icon needs a StatusNotifier host. Ubuntu ships ubuntu-appindicators enabled so it works there, but stock GNOME (RHEL 9, Fedora, Debian GNOME) shows nothing — and since Settings and Quit live only in the tray menu, that leaves no way to set the password or port short of hand-editing config.json. Worth considering a non-tray settings path separately.
  • sendInput cannot work under Wayland. robotjs is X11/XTest-only, and Ubuntu 22.04+ and RHEL 9 both default to Wayland. The new error message says so explicitly instead of failing opaquely.

Also noted, deliberately left alone

runSystemCommand returns void, so every await runSystemCommand(...) in the command table is a no-op — failures reach the socket but never the caller's return value, meaning a lock on a box without loginctl still reports "System locked." Fixing that changes error propagation on all three platforms, so it felt out of scope here.

Testing

  • yarn build clean, yarn test 17/17 passing (one new test for the validator).
  • Verified under Electron on macOS that the new dynamic import yields all six robotjs methods sendInputCommand uses, so input simulation is unaffected.
  • Booted the app on macOS: tray loads, server comes up on 8810, /commands and getFonts respond, password check still rejects bad credentials.
  • Did not execute a valid shutdown for obvious reasons.

Version bumped 1.9.0 → 1.10.0 (minor: working Linux autostart and the new package targets are user-facing additions, not just fixes).

🤖 Generated with Claude Code

Several command handlers used Linux branches that no longer work on
mainstream distributions, and one packaging choice made the released
build hard to run. Every change here is confined to the Linux code
path; the darwin and win32 branches are untouched.

- robotjs was imported at module scope in inputHandlers, which the
  command table pulls in. A native module that failed to build or load
  took down the whole app rather than just sendInput. It now loads
  lazily on first use and reports a CommandError(501).
- lock used gnome-screensaver-command, absent from modern GNOME. Try
  loginctl lock-session, then xdg-screensaver, then the old command.
- shutdown, reboot and shutdown_cancel invoked sudo, which has no TTY
  to prompt on when spawned. On Linux these go through systemd, which
  authorizes an active local session via polkit.
- Run at Login silently did nothing: setLoginItemSettings is
  macOS/Windows only. Linux now writes an XDG autostart entry,
  preferring $APPIMAGE over getPath('exe') since the latter points at
  a temporary mount inside an AppImage.
- focusApp falls back to xdotool when wmctrl is unavailable, and
  quitApp uses pkill -x so it matches the process name instead of
  anything containing that string.
- Ship deb and rpm alongside AppImage, which avoids the missing
  libfuse2 on Ubuntu 22.04+, and pin CI to ubuntu-22.04 so the
  compiled native module does not require a newer glibc than the
  target provides.

Also validate the shutdown 'time' parameter, which was unvalidated and
interpolated into the macOS osascript string.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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