feat(linux): install artifacts and docs#173
Conversation
Greptile SummaryThis PR adds the full Linux packaging layer for OpenLogi: udev rules, a systemd user-unit template, a
Confidence Score: 5/5Safe to merge; all install/uninstall logic is correct and the Rust permission and autostart code is well-tested. The install/uninstall scripts handle the tricky sudo -u + XDG_RUNTIME_DIR pattern correctly, udev trigger runs on both install and uninstall, and the Rust reconciliation logic has solid unit tests. The two findings are documentation and cleanup gaps that do not affect correctness of the installed system. packaging/linux/uninstall.sh and docs/INSTALL-linux.md Important Files Changed
Reviews (9): Last reviewed commit: "refactor(linux): use @BINDIR@ placeholde..." | Re-trigger Greptile |
- install.sh: rewrite ExecStart in the systemd unit via sed so the installed service always points to $BINDIR/openlogi-agent, not the hardcoded /usr/bin path (which mismatches the default /usr/local prefix) - uninstall.sh: add udevadm trigger after reload so hidraw and uinput nodes lose the uaccess tag immediately, not only after re-plug/reboot Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- install.sh: escape sed replacement string for BINDIR metacharacters (& \ |) so paths like /opt/my&pkg don't corrupt the unit file - install.sh: best-effort daemon-reload via sudo -u $SUDO_USER after writing the unit so reinstalls pick up the new ExecStart immediately - uninstall.sh: use SUDO_USER to target the real user's systemd session when the script is run under sudo, preventing the disable from silently targeting root's session while the user's agent keeps running Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Nice to see the packaging come together — and thanks for carrying the Bluetooth/uhid matcher forward into the comment block; generalizing it to :046D: is a good call. |
Reconcile the agent's autostart state on Linux by writing/removing a systemd user unit at $XDG_CONFIG_HOME/systemd/user/openlogi-agent.service. Mirrors the macOS LaunchAgent semantics exactly: - Restart=on-failure (crash respawns; clean exit(0) stays stopped) - WantedBy=graphical-session.target (takes effect at next login) - ExecStart escaped for systemd (% doubled, spaces quoted) - Idempotent write/remove — only touches disk when content changes - systemctl --user daemon-reload + enable/disable best-effort Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a Linux-specific permission probe and settings page row: - probe_uinput(): checks write access to /dev/uinput - probe_logitech_hidraw(): iterates /dev/hidraw*, confirms Logitech vendor (HID_ID sysfs field parsed numerically — 0000046D matches 046d) - classify(uinput_ok, hidraw_ok): pure function → Granted/Denied/Unknown - Settings → Permissions shows one "Input device access" row on Linux with description only when access is not yet granted (no noise when everything works) - macOS permission rows and helpers gated #[cfg(target_os = "macos")] Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Accessibility footer in the main window hidden on Linux - "Open" button in permission rows gated to macOS only - Launch-at-login description no longer says "log in to macOS" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove misplaced sysfs comment above the open() call in probe_logitech_hidraw (the sysfs check is in is_logitech_hidraw) - Remove dead #[cfg(not(target_os = "macos"))] suppressor inside the already-macOS-gated permission_field function - Split Denied/Unknown description text: Unknown means uinput is accessible but no Logitech device is connected, so point the user at the device rather than the udev install guide Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- escape_systemd_exec: double $ → $$ to prevent systemd variable substitution in ExecStart paths containing a literal dollar sign - paths: add pub xdg_config_home() that returns the raw XDG config base without the openlogi sub-directory; refactor config_dir() to call it - unit_path: use xdg_config_home() directly instead of relying on the fragile .parent() traversal from config_dir() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Added missing translations for the Linux permission row label introduced in this PR. Follows the same pattern as 'Input Monitoring'. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- install.sh: rewrite ExecStart in the systemd unit via sed so the installed service always points to $BINDIR/openlogi-agent, not the hardcoded /usr/bin path (which mismatches the default /usr/local prefix) - uninstall.sh: add udevadm trigger after reload so hidraw and uinput nodes lose the uaccess tag immediately, not only after re-plug/reboot Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- install.sh: escape sed replacement string for BINDIR metacharacters (& \ |) so paths like /opt/my&pkg don't corrupt the unit file - install.sh: best-effort daemon-reload via sudo -u $SUDO_USER after writing the unit so reinstalls pick up the new ExecStart immediately - uninstall.sh: use SUDO_USER to target the real user's systemd session when the script is run under sudo, preventing the disable from silently targeting root's session while the user's agent keeps running Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
b57e9e0 to
e76e794
Compare
Thank you @recchia for pointing this out, I was not aware of your work in the area being merged upstream. |
- install.sh: rewrite ExecStart in the systemd unit via sed so the installed service always points to $BINDIR/openlogi-agent, not the hardcoded /usr/bin path (which mismatches the default /usr/local prefix) - uninstall.sh: add udevadm trigger after reload so hidraw and uinput nodes lose the uaccess tag immediately, not only after re-plug/reboot Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- install.sh: escape sed replacement string for BINDIR metacharacters (& \ |) so paths like /opt/my&pkg don't corrupt the unit file - install.sh: best-effort daemon-reload via sudo -u $SUDO_USER after writing the unit so reinstalls pick up the new ExecStart immediately - uninstall.sh: use SUDO_USER to target the real user's systemd session when the script is run under sudo, preventing the disable from silently targeting root's session while the user's agent keeps running Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
e76e794 to
bafdb4a
Compare
… scope - Add 'Unifying receiver' and 'Input device access' keys to en.yml (all locale files must match en.yml for the i18n test) - Add 'Ricevitore Unifying' to it.yml for key parity - Move InteractiveElement import outside #[cfg(target_os = "macos")] so on_action() calls for CloseWindow/Minimize/Zoom work on Linux - Fix rustfmt line-wrap in launch_agent::unit_path Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- udev/70-openlogi.rules: TAG+="uaccess" for hidraw (Logitech VID 046d) and uinput; includes non-systemd GROUP="input" fallback instructions - systemd/openlogi-agent.service: packaged user-unit template for /usr/lib/systemd/user/ (complements the per-user unit written by launch_at_login) - desktop/openlogi.desktop: XDG application launcher - install.sh / uninstall.sh: POSIX sh, set -eu; copies binaries + all artifacts, reloads udevadm, best-effort systemd and icon cache updates Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
docs/INSTALL-linux.md covers: prerequisites, build from source, udev rules (uaccess + non-systemd fallback), install.sh usage, autostart via systemctl, verification steps, and known limitations table. README.md gets a Linux subsection under ## Install with the minimal quick-start (build + udev) and a link to the full guide. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- install.sh: rewrite ExecStart in the systemd unit via sed so the installed service always points to $BINDIR/openlogi-agent, not the hardcoded /usr/bin path (which mismatches the default /usr/local prefix) - uninstall.sh: add udevadm trigger after reload so hidraw and uinput nodes lose the uaccess tag immediately, not only after re-plug/reboot Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- install.sh: escape sed replacement string for BINDIR metacharacters (& \ |) so paths like /opt/my&pkg don't corrupt the unit file - install.sh: best-effort daemon-reload via sudo -u $SUDO_USER after writing the unit so reinstalls pick up the new ExecStart immediately - uninstall.sh: use SUDO_USER to target the real user's systemd session when the script is run under sudo, preventing the disable from silently targeting root's session while the user's agent keeps running Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bluetooth HID devices go through the uhid virtual bus, which has no
idVendor sysfs attribute — ATTRS{idVendor}=="046d" doesn't match them.
Add a second rule matching on the HID kernel name format
"bus:VID:PID.iface", whose VID field (046D) covers both BT and USB.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sudo -u strips the environment, so systemctl --user cannot locate the user's D-Bus socket without XDG_RUNTIME_DIR. Without it, disable --now in uninstall.sh silently fails (exit code 1, swallowed by || true), leaving the agent enabled even after the binary is removed. daemon-reload in install.sh has the same problem — it would silently skip the reload, requiring a manual reload before the updated unit takes effect. Fix both by computing REAL_UID / INSTALL_USER and passing XDG_RUNTIME_DIR=/run/user/<uid> explicitly to the sudo -u invocation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both install.sh (PREFIX/bin) and nfpm postinstall (/usr/bin) now expand the placeholder explicitly, removing the implicit assumption that the template's hardcoded path matches any particular installer.
bafdb4a to
0c40851
Compare
|
Confirmed on the new push — single source of truth restored, and the rewritten rules look good (generalizing the Bluetooth matcher to :046D: covers more transports than my original). One small thing from my hardware testing that got lost in the rewrite, in case it's worth a line in INSTALL-linux.md: on my setup (Bolt receiver and Bluetooth-direct), already-connected devices needed a replug/power-cycle after installing the rules before the uaccess ACL applied — udevadm trigger alone didn't re-grant it on the existing hidraw nodes. Since install.sh will mostly run with the device already connected, a one-line note could save some "rules don't work" reports. Resolving this thread — thanks for the quick turnaround on the relocation. |
Summary
packaging/linux/udev/70-openlogi.rules—TAG+="uaccess"for Logitechhidrawnodes anduinput; no group membership required on systemd-logind systems. Includes non-systemdGROUP="input"fallback instructions in comments.packaging/linux/systemd/openlogi-agent.service— packaged user-unit template for/usr/lib/systemd/user/; complements the per-user unit written by thelaunch_at_loginsetting.packaging/linux/desktop/openlogi.desktop— XDG application launcher entry.packaging/linux/install.sh/uninstall.sh— POSIXsh,set -eu; copies binaries and all artifacts to system paths, reloadsudevadm, and performs best-effort icon cache and desktop database updates.docs/INSTALL-linux.md— full Linux install guide: prerequisites, build from source, udev rules (uaccess + non-systemd fallback),install.shusage, autostart viasystemctl, verification steps, known limitations.README.md— Linux subsection under## Installwith the quick-start snippet and a link to the full guide.Test plan
desktop-file-validate packaging/linux/desktop/openlogi.desktop— cleanshellcheck packaging/linux/install.sh packaging/linux/uninstall.sh— clean (requires shellcheck)udevadm verify packaging/linux/udev/70-openlogi.rules— clean (requires udevadm ≥ 252)bash -n packaging/linux/install.sh && bash -n packaging/linux/uninstall.sh— no syntax errorsinstall.shrun aftercargo build --release— binaries, rules, unit, desktop, icon all land in expected pathsudevadm triggerafter install —/dev/uinputand/dev/hidraw*become accessible withoutsudouninstall.sh— all installed files removed, udevadm reloaded🤖 Generated with Claude Code