fix(desktop): resolve Exec to installed launcher, not raw checkout wrapper - #88709
nosliwhtes wants to merge 2 commits into
Conversation
…apper The .desktop entry generator used resolve_hermes_bin() directly, which returns $HERMES_HOME/hermes when run via `hermes desktop` (argv[0] is the raw Python wrapper). That wrapper is `#!/usr/bin/env python3` and fails outside a terminal with `ModuleNotFoundError: dotenv` because system Python has no venv deps. Every `hermes desktop` launch rewrote the entry with the broken path, so menu/icon clicks silently crashed and required a terminal to stay open. Add _is_raw_checkout_wrapper() and prefer the installed bash wrapper ~/.local/bin/hermes (which execs the venv Python) for the Exec line. Falls back to `python -m hermes_cli.main desktop` only when no good launcher is on PATH. Verified that the generated entry now reads `Exec=/home/nosliwhtes/.local/bin/hermes desktop` and desktop-file-validate passes. Fixes: icon won't open the app without keeping terminal open; breaks again on every update because the generator overwrote the entry.
|
Curated verdict (needs-decision): mergeable as-is. Verified against real code on the PR head
Fixes silent crash on Linux app-menu launches (icon/menu clicks). Recommend approve/merge. Minor (non-blocking): a small regression test for the raw-wrapper detection would harden it. |
|
Thanks for verifying against the actual head instead of just reading the diff. That's the part I cared about most, since the failure mode here only shows up when the desktop file resolves through PATH. Fair point on the missing test. I'll push a small regression case for _is_raw_checkout_wrapper() to this branch: one input that looks like a real installed launcher, one raw |
Seven cases for _is_raw_checkout_wrapper(): the raw python3 wrapper itself, the installed bash launcher (must not match), a shebang below a leading blank line (not at offset zero, must not match), the bare shebang with no body, missing file, and directory path. Plus an end to end check that resolve_exec_command() falls back to the interpreter module instead of writing the broken wrapper into Exec=. Also pin shutil.which to None in test_exec_falls_back_to_interpreter_module so it no longer depends on the host having ~/.local/bin/hermes on PATH.
|
Test is up: bd9c2ec, seven cases covering the raw wrapper, the installed bash launcher, offset-zero shebang detection, bare shebang, missing file, directory path, and the full fallback path end to end. Suite is 19 passed, 2 skipped locally. One find while running this on real hardware: test_exec_falls_back_to_interpreter_module only stubs resolve_hermes_bin, not shutil.which. On any machine where ~/.local/bin/hermes is actually on PATH, the resolver correctly picks the launcher and that test fails. That's presumably why CI stayed green while the assumption never held off-sandbox. Pinned which() to None in the same commit. |
|
Closing this because #90492 merged the same root fix into main on August 21. The upstream implementation is broader: it detects any Python launcher whose shebang escapes the running environment, then prefixes a capable interpreter. Keeping _is_raw_checkout_wrapper() alongside it would duplicate the decision path rather than improve it. Fresh verification against main 48f69e5: tests/hermes_cli/test_linux_desktop_entry.py is 15 passed, 2 skipped. The remaining uv/venv symlink edge case is being consolidated in #92122. I reproduced that on the original Zorin machine and posted the exact interpreter and Exec evidence there. The tests added here did their job and helped pin the original failure, but they target a helper that no longer exists on main. Closing as implemented upstream, not abandoned. |
Summary
The Linux desktop entry generator (
resolve_exec_command()) usedresolve_hermes_bin()directly, which returns$HERMES_HOME/hermeswhen invoked viahermes desktop(argv[0] is the raw Python wrapper). That wrapper is#!/usr/bin/env python3and fails outside a terminal withModuleNotFoundError: dotenvbecause system Python has no venv deps.Every
hermes desktoplaunch rewrites~/.local/share/applications/hermes.desktop, so the brokenExecwas regenerated on every run — icon/menu clicks silently crashed while terminal launches kept working (and had to stay open becausesubprocess.runblocks).Symptom
hermes desktopfrom a terminal works but requires the terminal to stay open.hermes update/ desktop rebuild because the generator re-creates the bad entry.Fix
Add
_is_raw_checkout_wrapper()(shebang probe) and prefer the installed bash wrapper~/.local/bin/hermes(which execs the venv Python) for theExecline. Falls back topython -m hermes_cli.main desktoponly when no good launcher is on PATH.Before:
Exec=/home/nosliwhtes/.hermes/hermes-agent/hermes desktopAfter:
Exec=/home/nosliwhtes/.local/bin/hermes desktopVerified:
desktop-file-validatepasses,resolve_exec_command()returns the installed launcher in both theargv[0]=hermesandargv[0]=~/.local/bin/hermescases, and the regenerated entry survives manual edits.Test plan
python -m hermes_cli.linux_desktop_entryshebang probe: raw wrapper → True, installed → Falseresolve_exec_command()→/home/nosliwhtes/.local/bin/hermes desktop(both launch paths)install_desktop_entry()writes correctExecdesktop-file-validatepassespython -m py_compilepassesFixes the "icon won't open, have to keep terminal open" issue plus the "breaks every update" regression.