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 HanssonandClaude Opus 5 committed 2026-08-27 11:45:23 +02:00
1 parent 12646eb5a1
commit cda02f0a88
5 files changed
+65 -9

No files matched your search

+8 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# omarchy:summary=Install the Hermes CLI as a mise-backed wrapper in ~/.local/bin # 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 # omarchy:examples=omarchy install hermes cli | omarchy install hermes cli --now
# Hermes pins every one of its dependencies exactly and declares # 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 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 # --check lets callers tell a cold stub from a working one before they commit
# to a path that assumes Hermes is ready. # to a path that assumes Hermes is ready.
if [[ $mode == "--check" ]]; then 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 # 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 # 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. # the user's, so it is the installer that decides whether the wrapper is its
if [[ -f ~/.local/bin/hermes && ! -L ~/.local/bin/hermes ]] && grep -qxF '# Written by omarchy-install-hermes-cli.' ~/.local/bin/hermes; then # own, rather than a copy of its marker kept here.
if omarchy-install-hermes-cli --owns; then
rm -f ~/.local/bin/hermes rm -f ~/.local/bin/hermes
fi fi
+4 -5
View File
@@ -15,12 +15,11 @@ fi
# Anything already answering to hermes that this installer did not write -- # Anything already answering to hermes that this installer did not write --
# an official install, a hand-rolled wrapper, even a dangling link -- belongs to # 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" wrapper="$HOME/.local/bin/hermes"
if [[ -e $wrapper || -L $wrapper ]]; then if [[ -e $wrapper || -L $wrapper ]] && ! omarchy-install-hermes-cli --owns; then
if [[ -L $wrapper || ! -f $wrapper ]] || ! grep -qxF '# Written by omarchy-install-hermes-cli.' "$wrapper"; then exit 0
exit 0
fi
fi fi
omarchy-install-hermes-cli 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" 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" 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 # 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 # 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. # 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"/* 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 HOME="$test_home"
export OMARCHY_TEST_PKG_LOG="$pkg_log" export OMARCHY_TEST_PKG_LOG="$pkg_log"