From 351f188d025d7e07d47bd1cb78bd11bada0ea2ac Mon Sep 17 00:00:00 2001 From: Spencer Bull Date: Sun, 6 Sep 2026 02:21:16 -0500 Subject: [PATCH] Register Hermes Desktop only after publishing its native CLI Keep build-time registration private, verify the published launcher, and then let the native command register the final desktop entry. Keep that launcher first on the desktop environment PATH for subsequent registrations. Co-Authored-By: GPT-6 Codex (xhigh) --- pkgbuilds/hermes-desktop/PKGBUILD | 2 +- pkgbuilds/hermes-desktop/hermes-desktop.sh | 7 +++- test/hermes-desktop-test.py | 40 ++++++++++++++++++---- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/pkgbuilds/hermes-desktop/PKGBUILD b/pkgbuilds/hermes-desktop/PKGBUILD index 4f2f7954..5d32d599 100644 --- a/pkgbuilds/hermes-desktop/PKGBUILD +++ b/pkgbuilds/hermes-desktop/PKGBUILD @@ -30,7 +30,7 @@ source=("install-${_commit}.sh::https://raw.githubusercontent.com/NousResearch/h 'hermes-desktop.png') sha256sums=('5854b15670b51a8daae8f59ddfa917062de9f74be261eb73b4b8d719710f8968' '821556e6336796450ab852d375117b48a4887e71d255794fd6318d99982a5ab6' - 'b4c282ead8a5d1a631f702474ea36eb7362ab1f0f87ad2e71f60a8698de07035' + '85834b56348529413bbc248282d3a1769a2276a016affe9eafb7dd8773d9e12f' 'c37d4cfa4801eccbd769fddaebb2115d54faa49a5b7bc0f305f563e3cefd9bd8' 'd60d164e24fdcf6532133b8ea43c77a201e4b9e9dbc396187b58d51d8590ef52') diff --git a/pkgbuilds/hermes-desktop/hermes-desktop.sh b/pkgbuilds/hermes-desktop/hermes-desktop.sh index 4d9b8cd6..77f87ef3 100644 --- a/pkgbuilds/hermes-desktop/hermes-desktop.sh +++ b/pkgbuilds/hermes-desktop/hermes-desktop.sh @@ -192,12 +192,15 @@ PY fi # The native builder also handles updates and supports the namespace # sandbox. The shell installer's desktop stage still requires a sudo chown. - env -u PYTHONPATH -u PYTHONHOME "${cli[@]}" desktop --build-only + # The build registers a desktop entry, too. Keep it private until the new + # CLI is published so a different hermes on PATH cannot become its target. + env -u PYTHONPATH -u PYTHONHOME XDG_DATA_HOME="$install_backup/desktop" "${cli[@]}" desktop --build-only # Build first: a failed download leaves the old terminal CLI usable. stage complete stage path runtime_ready || die "The CLI or desktop is not ready. See the installer output above." + env -u PYTHONPATH -u PYTHONHOME PATH="$HOME/.local/bin:$PATH" "${cli[@]}" desktop --skip-build --build-only if ! grep -qxF '/.omarchy-hermes-desktop' "$root/.git/info/exclude"; then printf '/.omarchy-hermes-desktop\n' >>"$root/.git/info/exclude" fi @@ -238,6 +241,8 @@ fi export HERMES_DESKTOP_HERMES_ROOT="$root" export HERMES_DESKTOP_PASSWORD_STORE="${HERMES_DESKTOP_PASSWORD_STORE:-gnome-libsecret}" +# Upstream desktop registration resolves its launcher through PATH. +export PATH="$HOME/.local/bin:$PATH" unset ELECTRON_RUN_AS_NODE PYTHONPATH PYTHONHOME flags=() diff --git a/test/hermes-desktop-test.py b/test/hermes-desktop-test.py index 78062b6a..03958958 100644 --- a/test/hermes-desktop-test.py +++ b/test/hermes-desktop-test.py @@ -62,6 +62,8 @@ class LauncherTests(unittest.TestCase): self.log = self.base / 'stages' self.output = self.base / 'launch.json' self.env = dict(os.environ, HOME=str(self.home), HERMES_HOME=str(self.home / '.hermes'), + XDG_DATA_HOME=str(self.home / '.local/share'), + XDG_CONFIG_HOME=str(self.home / '.config'), XDG_CACHE_HOME=str(self.home / '.cache'), PATH=f'{self.bin}:/usr/bin:/bin', TEST_LOG=str(self.log), TEST_GUI=str(self.base / 'gui'), TEST_PYTHON=str(self.base / 'python'), TEST_OUTPUT=str(self.output), GIT_CONFIG_NOSYSTEM='1', @@ -72,13 +74,21 @@ class LauncherTests(unittest.TestCase): self.write(self.base / 'python', '''#!/bin/bash set -eu if [[ ${2:-} == desktop ]]; then - [[ ${3:-} == --build-only ]] - printf 'desktop\\n' >> "$TEST_LOG" - [[ ${FAIL_STAGE:-} != desktop ]] || exit 42 root=$(dirname "$1") - mkdir -p "$root/apps/desktop/release/linux-unpacked" - cp "$TEST_GUI" "$root/apps/desktop/release/linux-unpacked/Hermes" - printf '/apps/\\n/.hermes-bootstrap-complete\\n' >> "$root/.git/info/exclude" + if [[ ${3:-} == --skip-build ]]; then + [[ ${4:-} == --build-only && -x $root/apps/desktop/release/linux-unpacked/Hermes ]] + [[ ${FAIL_FINAL_REGISTER:-} != 1 ]] || exit 45 + else + [[ ${3:-} == --build-only ]] + printf 'desktop\\n' >> "$TEST_LOG" + [[ ${FAIL_STAGE:-} != desktop ]] || exit 42 + mkdir -p "$root/apps/desktop/release/linux-unpacked" + cp "$TEST_GUI" "$root/apps/desktop/release/linux-unpacked/Hermes" + printf '/apps/\\n/.hermes-bootstrap-complete\\n' >> "$root/.git/info/exclude" + fi + entry_dir="${XDG_DATA_HOME:-$HOME/.local/share}/applications" + mkdir -p "$entry_dir" + printf 'Exec=%s desktop\\n' "$(command -v hermes || printf '%s/venv/bin/python -m hermes_cli.main' "$root")" > "$entry_dir/hermes.desktop" else [[ ${FAIL_READY:-} != 1 ]] fi @@ -130,6 +140,24 @@ if os.environ.get('TEST_GUI_WAIT'): time.sleep(30) self.assertFalse((self.root/'.omarchy-hermes-desktop').exists()) self.assertFalse((self.home/'.local/bin/hermes').exists()) + def test_desktop_registration_uses_published_cli(self): + self.write(self.bin/'hermes', '#!/bin/bash\necho foreign\n') + self.install() + entry=self.home/'.local/share/applications/hermes.desktop' + self.assertEqual(entry.read_text(),f'Exec={self.home}/.local/bin/hermes desktop\n') + + def test_failed_registration_preserves_previous_launcher(self): + wrapper=self.home/'.local/bin/hermes' + self.write(wrapper,'#!/bin/bash\n# Written by omarchy-install-hermes-cli.\necho old\n') + entry=self.home/'.local/share/applications/hermes.desktop' + self.write(entry,'previous desktop entry\n') + before=wrapper.read_bytes() + self.run_launcher('--install',ok=False,FAIL_FINAL_REGISTER='1') + self.assertEqual(wrapper.read_bytes(),before) + self.assertEqual(entry.read_text(),'previous desktop entry\n') + self.run_launcher('--check',ok=False) + self.install() + def test_warm_launch_and_package_reinstall_leave_runtime_untouched(self): self.install() before = self.log.read_bytes()