Skip to content

MODERNIZE: enable uv/pipx install with runtime sudo elevation - #36

Open
ventusff wants to merge 2 commits into
x2es:masterfrom
ventusff:dev/tmp-sudo
Open

MODERNIZE: enable uv/pipx install with runtime sudo elevation#36
ventusff wants to merge 2 commits into
x2es:masterfrom
ventusff:dev/tmp-sudo

Conversation

@ventusff

@ventusff ventusff commented May 8, 2026

Copy link
Copy Markdown

Summary

Make a user-level install (e.g. uv tool install bt-dualboot, pipx install bt-dualboot) the recommended install path. The tool now self-elevates via sudo at runtime when a root-needing action is requested, and sudo prompts for the password on the TTY. Pass --no-elevate to opt out and run as root yourself.

Also includes a drive-by fix for case-insensitive Windows registry path resolution, surfaced while testing on a real dual-boot machine.

Usage

$ uv tool install bt-dualboot          # or: pipx install bt-dualboot — no sudo
$ bt-dualboot --sync-all --no-backup          # no sudo required
[bt-dualboot] root privileges required; re-executing under sudo (use --no-elevate to disable)
[sudo] password for user: ********

Syncing...
==========
 [C2:9E:1D:E2:3D:A5] Keyboard K380
...done

What changed

Runtime privilege elevation (6975ad1)

  • New bt_dualboot/cli/privilege.py with action_requires_root(opts) and elevate_via_sudo().
  • cli/app.py calls elevate_via_sudo() from main() for -l, --sync, --sync-all. New --no-elevate flag opts out.
  • Re-exec prefers the entry script when it's executable (e.g. ~/.local/bin/bt-dualboot); falls back to python -m bt_dualboot otherwise. The DEBUG env var is preserved across the sudo boundary via sudo --preserve-env=DEBUG.
  • README install/usage rewritten — sudo pip install is no longer recommended; uv tool install and pipx install are documented.

Case-insensitive registry path resolution (b990db1)

  • New resolve_path_ci(base, relative) walks each segment under base, preferring exact match and falling back to a case-insensitive listdir lookup.
  • Used by WindowsRegistry._registry_file() and by locate_windows_mount_points() (replacing glob.glob, which is case-sensitive on Linux).
  • Fixes openHive(...) failed: No such file or directory on Windows installs whose hive is on disk as system (lower-case) rather than SYSTEM. NTFS is case-insensitive in Windows itself, but Linux NTFS drivers (ntfs-3g, ntfs3) are case-sensitive by default.

Test plan

  • pytest tests/ — 48 passed (was 42; +6 new tests covering the case-insensitive resolver)
  • As a regular user: bt-dualboot --version, --list-win-mounts, -h — no sudo prompt
  • As a regular user: bt-dualboot -l — re-execs under sudo, prompts for password, lists devices
  • As a regular user: bt-dualboot --sync-all --dry-run --no-backup — DRY RUN output after sudo prompt
  • As root: sudo bt-dualboot -l — runs without re-elevation (early return when euid == 0)
  • --no-elevate exits with a clear error when the action would need root
  • Verified end-to-end on a Windows 10 install whose hive is on disk as system (lower-case) — failed with openHive(...) failed: No such file or directory before this PR; resolves correctly now
  • Maintainer to verify on a Windows install with upper-case SYSTEM — exact match is tried first, so this path should be unchanged

🤖 Generated with Claude Code

ventusff and others added 2 commits May 8, 2026 07:28
Re-exec the command under sudo when a root-needing action (--list,
--sync, --sync-all) is invoked by a non-root user, so the package can be
installed at user level (uv tool / pipx / pip --user) without sudo and
still get access to /var/lib/bluetooth and the Windows registry hive.

Adds --no-elevate to opt out of the auto sudo re-exec.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NTFS is case-insensitive when accessed by Windows itself, but Linux NTFS
drivers (ntfs-3g, ntfs3) are case-sensitive by default — and not every
Windows install stores the SYSTEM hive on disk in upper-case; some have
`system` (and lower-case `windows`/`system32`/`config`). The hard-coded
`Windows/System32/config/SYSTEM` path then fails both auto-detection in
locate_windows_mount_points (glob.glob) and reged invocation
(`openHive(...) failed: No such file or directory`).

Add a resolve_path_ci helper that walks each segment under a base,
preferring exact match and falling back to a case-insensitive listdir
lookup. Use it in WindowsRegistry._registry_file() and in
locate_windows_mount_points (replacing glob.glob).

Co-Authored-By: Claude Opus 4.7 (1M context) <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