Refuse before anything of the user's is touched, whatever state the runtime is in
The refusals that leave a self-installed Hermes alone came before the command was replaced only for a finished runtime. An unfinished one was still bootstrapped over a git status that could not be read, since a failed status read as a clean tree, and a half-built app beside it, or an edit the Linux runtime patch could not land on, was found only after the user's command had been saved aside and replaced and main switched. All three are asked first now, in both states, so a run that is going to stop leaves the launcher, the checkout and ~/.local/bin as they were. Co-Authored-By: Codex XHigh <noreply@openai.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
73a2b91cf5
commit
f3989092b2
2 files changed
+73
-20
No files matched your search
@@ -368,19 +368,24 @@ if runtime_ready; then
|
||||
runtime_present=true
|
||||
fi
|
||||
|
||||
# A seeded app has to be whole, whatever state the runtime beside it is in.
|
||||
refuse_unless_app_whole() {
|
||||
if [[ -e $native_app || -L $native_app ]] && ! native_app_complete; then
|
||||
echo "The Hermes desktop app at $native_app is incomplete. Repair it with 'hermes desktop --build-only' before trying again." >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# The packaged app can be seeded only beside a runtime at the release it was
|
||||
# built from, with its desktop sources untouched, and a seeded app has to be
|
||||
# whole. Asked before a command is replaced, so that a refusal leaves the
|
||||
# user's Hermes exactly as it was, and again before seeding.
|
||||
# built from, with its desktop sources untouched and the Linux runtime patch
|
||||
# still able to land. Asked before a command is replaced, so that a refusal
|
||||
# leaves the user's Hermes exactly as it was, and again before seeding.
|
||||
# Asked through ||, so nothing in here stops on its own: every git answer is
|
||||
# checked, and one that cannot be read refuses rather than reading as clean.
|
||||
refuse_unless_seedable() {
|
||||
local runtime_commit changes
|
||||
refuse_unless_app_whole || return 1
|
||||
if [[ -e $native_app || -L $native_app ]]; then
|
||||
if ! native_app_complete; then
|
||||
echo "The Hermes desktop app at $native_app is incomplete. Repair it with 'hermes desktop --build-only' before trying again." >&2
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
if ! runtime_commit=$(git -C "$runtime" rev-parse HEAD 2>/dev/null) || [[ -z $runtime_commit ]]; then
|
||||
@@ -399,26 +404,31 @@ refuse_unless_seedable() {
|
||||
echo "Hermes desktop sources have local changes. Run 'hermes desktop --build-only', then try again; existing files have been kept." >&2
|
||||
return 1
|
||||
fi
|
||||
if ! git -C "$runtime" apply --check /usr/share/hermes-desktop/runtime.patch >/dev/null 2>&1 &&
|
||||
! git -C "$runtime" apply --reverse --check /usr/share/hermes-desktop/runtime.patch >/dev/null 2>&1; then
|
||||
echo "The Hermes Linux runtime patch conflicts with local changes. Existing files have been kept." >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ $runtime_present == "true" ]]; then
|
||||
refuse_unless_seedable || exit 1
|
||||
elif [[ -e $runtime || -L $runtime ]]; then
|
||||
# The upstream installer can reset an existing checkout. Do not pin a newer
|
||||
# or modified runtime back to the package release while repairing setup,
|
||||
# and a git state that cannot be read is not a clean one.
|
||||
refuse_unless_app_whole || exit 1
|
||||
if ! runtime_commit=$(git -C "$runtime" rev-parse HEAD 2>/dev/null) || [[ $runtime_commit != "$release_commit" ]] ||
|
||||
! changes=$(git -C "$runtime" status --porcelain --untracked-files=all 2>/dev/null) || [[ -n $changes ]]; then
|
||||
echo "Hermes setup is incomplete at $runtime. Repair that installation before trying again; existing files have been kept." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Whether this run gave Hermes something new to show the theme to: a runtime
|
||||
# it set up, or the app it seeded. Putting a command back is neither.
|
||||
set_up=false
|
||||
|
||||
if [[ $runtime_present == "false" && ( -e $runtime || -L $runtime ) ]]; then
|
||||
# The upstream installer can reset an existing checkout. Do not pin a newer
|
||||
# or modified runtime back to the package release while repairing setup.
|
||||
if [[ $(git -C "$runtime" rev-parse HEAD 2>/dev/null) != "$release_commit" ]] ||
|
||||
[[ -n $(git -C "$runtime" status --porcelain --untracked-files=all) ]]; then
|
||||
echo "Hermes setup is incomplete at $runtime. Repair that installation before trying again; existing files have been kept." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# The command is the runtime's own and runs; anything else at the path gets
|
||||
# replaced below. The retired wrapper is never run to find out.
|
||||
command_ready=false
|
||||
|
||||
@@ -384,6 +384,50 @@ run_installer && fail "incomplete modified runtime cannot be reset by upstream i
|
||||
! grep -qx bootstrap "$test_tmp/events" || fail "modified runtime never reaches upstream installer"
|
||||
pass "patch conflicts and incomplete modified runtimes retain local changes and stop safely"
|
||||
|
||||
# Every refusal comes before anything of the user's is touched, whatever state
|
||||
# the runtime is in: a launcher of their own is neither saved aside nor
|
||||
# replaced, and no bootstrap runs, by a run that is going to stop anyway.
|
||||
own_launcher="#!/bin/bash
|
||||
exec \"$test_home/tools/hermes\" \"\$@\""
|
||||
assert_untouched() {
|
||||
[[ $(cat "$test_home/.local/bin/hermes") == "$own_launcher" ]] || fail "$1: the user's launcher is not as it was"
|
||||
[[ -z $(find "$test_home/.local/bin" -maxdepth 1 -name '.hermes-before-desktop.*' -print) ]] || fail "$1: something was saved aside"
|
||||
[[ ! -s $test_tmp/events ]] || fail "$1: setup ran" "$(cat "$test_tmp/events")"
|
||||
}
|
||||
# A finished runtime at the release whose edit the runtime patch cannot land on.
|
||||
new_home patch-conflict-own-launcher
|
||||
touch "$test_tmp/package-installed"
|
||||
HOME="$test_home" HERMES_HOME="$hermes_home" bash "$test_tmp/share/install.sh" --dir "$runtime" --hermes-home "$hermes_home"
|
||||
printf 'local edit\n' >"$runtime/runtime.txt"
|
||||
printf '%s\n' "$own_launcher" >"$test_home/.local/bin/hermes"
|
||||
: >"$test_tmp/events"
|
||||
run_cli --now && fail "a conflicting edit still stops setup"
|
||||
grep -q 'patch conflicts' "$test_tmp/output" || fail "a conflicting edit is named" "$(cat "$test_tmp/output")"
|
||||
assert_untouched "a patch conflict"
|
||||
# An unfinished runtime beside a half-built app.
|
||||
new_home incomplete-app
|
||||
touch "$test_tmp/package-installed"
|
||||
OMARCHY_TEST_NO_MARKER=1 HOME="$test_home" HERMES_HOME="$hermes_home" bash "$test_tmp/share/install.sh" --dir "$runtime" --hermes-home "$hermes_home"
|
||||
mkdir -p "$native/resources"
|
||||
printf 'half\n' >"$native/resources/app.asar"
|
||||
printf '%s\n' "$own_launcher" >"$test_home/.local/bin/hermes"
|
||||
: >"$test_tmp/events"
|
||||
run_cli --now && fail "a half-built app beside an unfinished runtime still stops setup"
|
||||
grep -q 'is incomplete' "$test_tmp/output" || fail "a half-built app is named" "$(cat "$test_tmp/output")"
|
||||
assert_untouched "a half-built app"
|
||||
# An unfinished runtime whose git state cannot be read is not a clean one.
|
||||
new_home unreadable-incomplete
|
||||
touch "$test_tmp/package-installed"
|
||||
OMARCHY_TEST_NO_MARKER=1 HOME="$test_home" HERMES_HOME="$hermes_home" bash "$test_tmp/share/install.sh" --dir "$runtime" --hermes-home "$hermes_home"
|
||||
printf '%s\n' "$own_launcher" >"$test_home/.local/bin/hermes"
|
||||
chmod 000 "$runtime/.git/index"
|
||||
: >"$test_tmp/events"
|
||||
run_cli --now && { chmod 644 "$runtime/.git/index"; fail "an unreadable git state beside an unfinished runtime is not a clean tree"; }
|
||||
chmod 644 "$runtime/.git/index"
|
||||
grep -q 'incomplete at' "$test_tmp/output" || fail "an unreadable git state is refused as incomplete" "$(cat "$test_tmp/output")"
|
||||
assert_untouched "an unreadable git state"
|
||||
pass "a refusal leaves the user's launcher and runtime as they were, whatever state the runtime is in"
|
||||
|
||||
# Once set up, a runtime is the user's to edit; a finished install is not
|
||||
# re-patched or re-verified, only opened.
|
||||
new_home finished-edit
|
||||
@@ -543,9 +587,8 @@ run_cli --now || fail "a finished install is accepted by the default agent path"
|
||||
run_cli --check || fail "--check follows the installed runtime"
|
||||
pass "choosing Hermes as the default agent installs the app's runtime without opening the app"
|
||||
|
||||
# A Hermes the user set up themselves is what the default agent runs, and
|
||||
# nothing is installed beside it. Asked for the app by name, Omarchy installs
|
||||
# the package first, and then the runtime supersedes it with the command saved.
|
||||
# A Hermes from elsewhere is not the app's: choosing Hermes installs the app
|
||||
# over it, with the previous command saved aside.
|
||||
new_home own-hermes
|
||||
mkdir -p "$test_home/.local/bin"
|
||||
cat >"$test_home/.local/bin/hermes" <<'SH'
|
||||
|
||||
Reference in new issue
Block a user