feat(linux): install artifacts and docs#173
Conversation
Greptile SummaryThis PR extends OpenLogi with full Linux packaging support: udev rules, a systemd user-unit template, a
Confidence Score: 5/5Safe to merge; the install/uninstall scripts, Rust permission probing, and systemd reconciliation are all well-structured and the prior review concerns have been addressed. All changes are additive Linux-only infrastructure. The Rust paths code, probing logic, and shell scripts follow set -eu and handle errors gracefully. The escape_systemd_exec backslash omission and the dead inner cfg blocks are minor cleanup items that do not affect correctness in any realistic deployment. crates/openlogi-agent/src/launch_agent.rs — the escape_systemd_exec backslash fix and crates/openlogi-gui/src/windows/settings.rs dead-code cleanup are worth landing before the first Linux release. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
subgraph install["install.sh (system-wide)"]
A[cargo build --release] --> B[install binaries to PREFIX/bin]
B --> C[install udev rules to /etc/udev/rules.d/]
C --> D[udevadm reload + trigger]
D --> E[expand @BINDIR@ in service template]
E --> F[write /usr/lib/systemd/user/openlogi-agent.service]
F --> G[systemctl --user daemon-reload]
end
subgraph gui["GUI — launch_at_login toggle"]
H[reconcile_linux enabled=true] --> I[current_exe path]
I --> J[render_unit + escape_systemd_exec]
J --> K[write ~/.config/systemd/user/openlogi-agent.service]
K --> L[systemctl --user daemon-reload]
L --> M[systemctl --user enable]
end
subgraph perm["GUI — Permissions page"]
N[probe_uinput: open /dev/uinput write] --> P{classify}
O[probe_logitech_hidraw: read /sys uevent] --> P
P -->|Granted| Q[green badge]
P -->|Denied| R[amber badge + install hint]
P -->|Unknown| S[amber badge + connect hint]
end
subgraph udev["udev rules"]
T["SUBSYSTEM==hidraw + ATTRS idVendor ==046d → TAG+=uaccess"]
U["KERNELS==*:046D:* → TAG+=uaccess (BT/uhid)"]
V["KERNEL==uinput → TAG+=uaccess + static_node"]
end
Reviews (16): Last reviewed commit: "docs(linux): note replug requirement whe..." | 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. |
- 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
- 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>
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. |
- 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>
e875455 to
689fb1a
Compare
- 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>
689fb1a to
714f973
Compare
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>
… 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>
- Add StatefulInteractiveElement as _ to unconditional gpui import so on_click() resolves on macOS (fixes E0599) - Gate status_badge with any(macos, linux) since both platforms call it (fixes Windows dead_code warning) - Remove unreachable input_device_access stub for non-mac/non-linux targets; nothing calls it there (fixes Windows dead_code warning) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Upstream added Option<bool> accessibility_granted with a 3-state match. Merge that with our Linux cfg structure: - Take upstream's Some(true)/Some(false)/None match for accessibility - Restore #[cfg(target_os = "macos")] on permission_field, permission_item, Permission import, and StatefulInteractiveElement import — all are macOS-only; gating them unconditionally caused dead_code / unused-import errors on Linux 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>
714f973 to
d290341
Compare
On Windows, PermissionStatus, classify, rgb, and the permissions module are all dead — they only exist for macOS/Linux permission dialogs. Gate each with the appropriate cfg so Windows clippy -D warnings passes. 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>
- 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.
4c0970d to
23b628e
Compare
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