Ask the installer who owns the Hermes wrapper

Three files spelled out the line that marks ~/.local/bin/hermes as Omarchy's:
the installer that writes it, Remove Preinstalls, and the migration. Two of
them were copies, and a change to what ownership means would have left them
matching a line nobody writes any more -- Remove Preinstalls quietly sweeping
nothing, the migration mistaking Omarchy's own wrapper for a stranger's.

omarchy-install-hermes-cli --owns answers it now, and the other two ask. The
installer's own metadata was also a flag behind: --check has been there since
this landed and was never listed.

A test pins the marker to one file, so a second copy fails rather than drifts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
David Heinemeier Hansson
2026-08-27 11:45:23 +02:00
co-authored by Claude Opus 5
parent 12646eb5a1
commit cda02f0a88
5 changed files with 65 additions and 9 deletions
+8 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
# omarchy:summary=Install the Hermes CLI as a mise-backed wrapper in ~/.local/bin
# omarchy:args=[--now]
# omarchy:args=[--check|--now|--owns]
# omarchy:examples=omarchy install hermes cli | omarchy install hermes cli --now
# Hermes pins every one of its dependencies exactly and declares
@@ -88,6 +88,13 @@ hermes_runs() {
timeout 15 "$HOME/.local/bin/hermes" --version >/dev/null 2>&1
}
# --owns answers whether the wrapper on PATH is the one this command wrote, so
# the migration and Remove Preinstalls do not each carry their own copy of the
# marker and drift from it.
if [[ $mode == "--owns" ]]; then
if ours; then exit 0; else exit 1; fi
fi
# --check lets callers tell a cold stub from a working one before they commit
# to a path that assumes Hermes is ready.
if [[ $mode == "--check" ]]; then
+3 -2
View File
@@ -19,8 +19,9 @@ if gum confirm "Are you sure you want to remove all preinstalled web apps, TUI w
# Only the wrapper omarchy-install-hermes-cli wrote is a preinstall. Hermes
# Desktop's command, an official install, or anything else at that path is
# the user's, so it is the marker that decides, not which packages are around.
if [[ -f ~/.local/bin/hermes && ! -L ~/.local/bin/hermes ]] && grep -qxF '# Written by omarchy-install-hermes-cli.' ~/.local/bin/hermes; then
# the user's, so it is the installer that decides whether the wrapper is its
# own, rather than a copy of its marker kept here.
if omarchy-install-hermes-cli --owns; then
rm -f ~/.local/bin/hermes
fi
+4 -5
View File
@@ -15,12 +15,11 @@ fi
# Anything already answering to hermes that this installer did not write --
# an official install, a hand-rolled wrapper, even a dangling link -- belongs to
# the user and stays exactly as it is.
# the user and stays exactly as it is. The installer is asked rather than
# matched against here, so there is one answer to who owns that wrapper.
wrapper="$HOME/.local/bin/hermes"
if [[ -e $wrapper || -L $wrapper ]]; then
if [[ -L $wrapper || ! -f $wrapper ]] || ! grep -qxF '# Written by omarchy-install-hermes-cli.' "$wrapper"; then
exit 0
fi
if [[ -e $wrapper || -L $wrapper ]] && ! omarchy-install-hermes-cli --owns; then
exit 0
fi
omarchy-install-hermes-cli
+46
View File
@@ -283,6 +283,52 @@ HOME="$leak_home" PATH="$leak_bin:$mock_bin:$PATH" \
fail "the interpreter pin does not follow Hermes into the commands it runs"
pass "the interpreter pin does not follow Hermes into the commands it runs"
# --owns is the one answer to whether the wrapper on PATH is this installer's.
# Remove Preinstalls and the migration both ask it rather than carrying their
# own copy of the marker, so a change to what ownership means reaches them.
owns_home="$test_tmp/owns-home"
mkdir -p "$owns_home/.local/bin"
run_owns() {
OMARCHY_TEST_DESKTOP_INSTALLED=0 \
OMARCHY_TEST_MISE_LOG="$mise_log" \
HOME="$owns_home" \
PATH="$mock_bin:$PATH" \
bash "$ROOT/bin/omarchy-install-hermes-cli" --owns
}
rm -f "$owns_home/.local/bin/hermes"
run_owns && fail "--owns says no when there is no wrapper at all"
printf '%s\n' "#!/bin/bash" "$stub_marker" >"$owns_home/.local/bin/hermes"
chmod +x "$owns_home/.local/bin/hermes"
run_owns || fail "--owns recognises the stub this installer wrote"
printf '%s\n' "#!/bin/bash" "# Replaces the stub omarchy-install-hermes-cli used to write." \
>"$owns_home/.local/bin/hermes"
run_owns && fail "--owns needs the exact marker line, not a mention"
# Quoting the marker inside a longer line is not the same as carrying it: the
# match is whole-line, so a wrapper describing what it replaced stays the
# user's.
printf '%s\n' "#!/bin/bash" "# Replaced '$stub_marker' with my own." \
>"$owns_home/.local/bin/hermes"
run_owns && fail "--owns needs the marker to be the whole line, not part of one"
rm -f "$owns_home/.local/bin/hermes"
ln -s "$test_home/.local/bin/hermes" "$owns_home/.local/bin/hermes"
run_owns && fail "--owns disclaims a symlink, whatever it resolves to"
rm -f "$owns_home/.local/bin/hermes"
pass "--owns answers for the wrapper this installer wrote and nothing else"
# The marker lives in exactly one place. Every other caller asks --owns, so a
# second copy is drift waiting to happen.
marker_copies=$(grep -rl "Written by omarchy-install-hermes-cli" \
"$ROOT/bin" "$ROOT/install" "$ROOT/migrations" 2>/dev/null | wc -l)
(( marker_copies == 1 )) ||
fail "only omarchy-install-hermes-cli spells out the ownership marker"
pass "the ownership marker is written down once"
# The app's marker says its install once landed, not that it is still there. A
# wrapper whose runtime has since gone answers for nothing, so readiness runs
# the command, exactly as it does for a hermes the user installed themselves.
+4 -1
View File
@@ -36,7 +36,10 @@ SH
chmod +x "$mock_bin"/*
export PATH="$mock_bin:$PATH"
# $ROOT/bin after the mocks: Remove Preinstalls asks omarchy-install-hermes-cli
# whether the wrapper is Omarchy's rather than matching the marker itself, and
# that is the real command at runtime. The mocks still shadow what they name.
export PATH="$mock_bin:$ROOT/bin:$PATH"
export HOME="$test_home"
export OMARCHY_TEST_PKG_LOG="$pkg_log"