fix(update): verify uv binary can execute before using it on Termux#35292
Conversation
tonydwb
left a comment
There was a problem hiding this comment.
Review: PR #35292 — fix(update): verify uv binary can execute before using it on Termux
Verdict: APPROVED ✅
Summary
This PR addresses a real bug on Android/Termux where shutil.which("uv") returns a path for a binary that exists on disk but cannot execute (wrong ELF dynamic linker). The fix probes each uv_bin with uv --version before using it, falling back to pip if the binary fails.
Checklist
| Criterion | Verdict | Notes |
|---|---|---|
| Correctness | ✅ | Covers all three call sites (_update_via_zip, _ensure_uv_for_termux, _cmd_update_impl). The three exception types (OSError, CalledProcessError, TimeoutExpired) cover the failure modes of a non-executable ELF. |
| Security | ✅ | capture_output=True, no shell injection surface, 10-second timeout prevents hangs. |
| Code Quality | ✅ | Clear error messages ("→ uv found at {uv_bin} but cannot execute — falling back to pip"). Minor duplication (identical 9-line block × 3), but acceptable for a focused bugfix — a follow-up DRY refactor to a shared helper would be welcome. |
| Testing | ✅ | Manually verified on Termux/Android. A programmatic test for a non-executable binary would be fragile (needs permission trickery or LD loader manipulation). |
| Performance | ✅ | One extra subprocess call per update path (~50ms). Negligible. |
| Edge Cases | ✅ | Handles: uv_bin=None (early guard), existing but broken ELF (OSError), wrong-architecture binary (CalledProcessError), hung binary (TimeoutExpired). |
One Suggestion (non-blocking)
Consider extracting the probe into a helper function:
def _probe_uv(uv_bin: str | None) -> str | None:
if uv_bin:
try:
subprocess.run([uv_bin, "--version"], capture_output=True, timeout=10, check=True)
except (OSError, subprocess.CalledProcessError, subprocess.TimeoutExpired):
uv_bin = None
return uv_binThis reduces the three identical blocks to uv_bin = _probe_uv(uv_bin) each. Not required for merge — the current fix is functionally correct.
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Approve
👍 Looks Good
- Fix correctly addresses the Termux/Android issue where
uvbinary exists but can't execute (wrong ELF linker) - Probe pattern (
uv --versionwithsubprocess.run) is applied consistently at all three UV call sites - Clean fallback to pip with clear user-facing message on each path
- Exception coverage is thorough:
OSError,CalledProcessError, andTimeoutExpiredall handled - Uses
capture_output=Trueandtimeout=10— no side effects, no hanging - The same pattern is applied in
_update_via_zip,_ensure_uv_for_termux, and_cmd_update_impl— proper DRY within the fix - No new dependencies or config changes needed
💡 Minor Suggestion (not blocking)
- The three probe blocks are identical — consider extracting a helper like
_can_execute_uv(uv_bin: str) -> boolto keep the code DRY across all three call sites. This is minor and can be done in a follow-up.
✅ Verified
- Single-commit PR (+32 lines in
hermes_cli/main.py) - Only the uv executability check commit (
f38bdf469) is the PR's unique contribution - Logic does not break the existing
uv_binflow on normal platforms
Reviewed by Hermes Agent
|
🤖 Merge Request This PR has been:
Could a maintainer please merge? 🙏 |
f38bdf4 to
b625bef
Compare
|
🔁 Rebased onto latest origin/main — was 298 commits behind, now clean.\n\n✅ 2 approvals, CI should re-trigger shortly. Ready for merge when green. |
Problem
On Android/Termux, the
uvbinary exists at the expected path and is found byshutil.which("uv"), but it cannot execute — it's an ELF built for Linux with dynamic linker/lib/ld-linux-aarch64.so.1which doesn't exist on Android.hermes updatethen fails with:Fix
Probe
uv --versionbefore using it at all three call sites:_update_via_zip()— standalone ZIP installer path_cmd_update_impl()— mainhermes updatepath_ensure_uv_for_termux()— uv bootstrap helperOn
OSError,CalledProcessError, orTimeoutExpired, fall back topipwith a clear message.Testing
Verified on Termux/Android (aarch64, Python 3.13):
hermes updatecompletes cleanly with an incompatible uv binary present.