Merge pull request #10381 from spencerbull/hermes-system-theme
Make Hermes follow the Omarchy theme as a skin
This commit is contained in:
11 files changed
+775
-3
No files matched your search
@@ -21,6 +21,12 @@ omarchy-install-hermes-cli || true
|
||||
echo "Opening Hermes Desktop..."
|
||||
setsid uwsm-app -- /usr/bin/hermes-desktop >/dev/null 2>&1 &
|
||||
|
||||
# Only a running Hermes can be told which skin to show, and the first launch
|
||||
# takes minutes; a unit outlives this terminal and reports to the journal.
|
||||
echo "Matching Hermes to the current theme once it is set up..."
|
||||
systemctl --user stop omarchy-hermes-theme.service 2>/dev/null || true
|
||||
systemd-run --user --quiet --collect --unit=omarchy-hermes-theme omarchy-theme-set-hermes --wait
|
||||
|
||||
echo ""
|
||||
echo "Hermes Desktop has been installed."
|
||||
echo "Its first launch installs the Hermes runtime, which takes a few minutes."
|
||||
@@ -8,6 +8,9 @@ set -euo pipefail
|
||||
|
||||
omarchy-pkg-drop hermes-desktop
|
||||
|
||||
# The installer leaves a unit waiting to hand the app the Omarchy theme.
|
||||
systemctl --user stop omarchy-hermes-theme.service 2>/dev/null || true
|
||||
|
||||
# The mise CLI is the app's predecessor, not the app itself: Hermes Desktop takes
|
||||
# it over on install and runs its own runtime instead, so a copy still here is one
|
||||
# the app never superseded -- an interrupted install, or the terminal CLI from
|
||||
|
||||
@@ -326,6 +326,7 @@ post_theme_commands=(
|
||||
omarchy-theme-set-gnome
|
||||
omarchy-theme-set-pi
|
||||
omarchy-theme-set-claude
|
||||
omarchy-theme-set-hermes
|
||||
omarchy-theme-set-browser
|
||||
omarchy-theme-set-vscode
|
||||
omarchy-theme-set-obsidian
|
||||
|
||||
Executable
+233
@@ -0,0 +1,233 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Sync the generated Omarchy theme to Hermes as a skin
|
||||
# omarchy:args=[--activate] [--wait]
|
||||
# omarchy:hidden=true
|
||||
|
||||
# A skin is Hermes' one theme unit for the desktop app, the TUI and the CLI;
|
||||
# its gateway watches the active skin file and repaints every surface on change.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
HERMES_SOURCE_PATH="$HOME/.local/state/omarchy/current/theme/hermes.yaml"
|
||||
HERMES_THEME_NAME_PATH="$HOME/.local/state/omarchy/current/theme.name"
|
||||
HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}"
|
||||
HERMES_CONFIG_PATH="$HERMES_HOME/config.yaml"
|
||||
HERMES_SKIN_NAME="omarchy"
|
||||
# The command the readiness probe vets, rather than whichever hermes is on PATH.
|
||||
HERMES_COMMAND="$HOME/.local/bin/hermes"
|
||||
# Written by the desktop app once the runtime its first launch provisions is in.
|
||||
HERMES_BOOTSTRAP_MARKER="$HERMES_HOME/hermes-agent/.hermes-bootstrap-complete"
|
||||
HERMES_ACTIVATE=0
|
||||
HERMES_WAIT=0
|
||||
HERMES_WAIT_LIMIT=$((30 * 60))
|
||||
|
||||
usage() {
|
||||
echo "Usage: omarchy-theme-set-hermes [--activate] [--wait]"
|
||||
}
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--activate)
|
||||
HERMES_ACTIVATE=1
|
||||
;;
|
||||
--wait)
|
||||
HERMES_ACTIVATE=1
|
||||
HERMES_WAIT=1
|
||||
;;
|
||||
-h | --help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# A theme switch runs this beside a dozen other hooks; only --activate explains.
|
||||
note() {
|
||||
if (( HERMES_ACTIVATE == 1 )); then
|
||||
echo "$*" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# A theme applied before the template existed has no skin rendered yet.
|
||||
if [[ ! -f $HERMES_SOURCE_PATH ]]; then
|
||||
(( HERMES_ACTIVATE == 1 )) || exit 0
|
||||
|
||||
if [[ ! -s $HERMES_THEME_NAME_PATH ]]; then
|
||||
echo "Hermes skin source missing: $HERMES_SOURCE_PATH" >&2
|
||||
echo "Select an Omarchy theme first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
omarchy-theme-refresh
|
||||
|
||||
if [[ ! -f $HERMES_SOURCE_PATH ]]; then
|
||||
echo "Hermes skin source missing after refreshing the theme: $HERMES_SOURCE_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# The first launch takes minutes and may be abandoned; the readiness probe
|
||||
# would start Hermes to answer, so poll for the marker instead.
|
||||
if (( HERMES_WAIT == 1 )); then
|
||||
waited=0
|
||||
until [[ -f $HERMES_BOOTSTRAP_MARKER && -f $HERMES_CONFIG_PATH ]]; do
|
||||
if (( waited >= HERMES_WAIT_LIMIT )); then
|
||||
echo "Hermes did not finish setting up within $((HERMES_WAIT_LIMIT / 60)) minutes; run omarchy-theme-set-hermes --activate once it has." >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
sleep 10
|
||||
waited=$((waited + 10))
|
||||
done
|
||||
fi
|
||||
|
||||
# Provisioning creates ~/.hermes on every machine for the Omarchy skill; the
|
||||
# config is what Hermes writes once it has actually run.
|
||||
if [[ ! -f $HERMES_CONFIG_PATH ]]; then
|
||||
note "Hermes is not set up yet; launch it once, then run omarchy-theme-set-hermes --activate."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Hermes parses the skin as YAML and hands its strings to every surface, so only
|
||||
# the name, a plain description and #rrggbb colours may reach it, in the order
|
||||
# YAML needs them. YAML breaks lines on bytes grep does not, so the bytes are
|
||||
# counted first, NUL included.
|
||||
skin_is_well_formed() {
|
||||
local skin="$1"
|
||||
|
||||
[[ -f $skin ]] &&
|
||||
(( $(LC_ALL=C tr -d ' -~\n' <"$skin" | wc -c) == 0 )) &&
|
||||
awk -v name="name: $HERMES_SKIN_NAME" '
|
||||
bad { next }
|
||||
/^#/ || /^[[:space:]]*$/ { next }
|
||||
!seen_name { if ($0 == name) seen_name = 1; else bad = 1; next }
|
||||
!seen_colors {
|
||||
if ($0 == "colors:") seen_colors = 1
|
||||
else if (!seen_description && $0 ~ /^description: [A-Za-z0-9 ,.()-]{0,200}$/) seen_description = 1
|
||||
else bad = 1
|
||||
next
|
||||
}
|
||||
/^ [a-z_]{1,64}: "#[0-9a-fA-F]{6}"$/ { colors++; next }
|
||||
{ bad = 1 }
|
||||
END { exit (bad || !seen_colors || colors == 0) }
|
||||
' "$skin"
|
||||
}
|
||||
|
||||
# The check has to cover the bytes that get published, and the theme can change
|
||||
# underneath between the two, so a private copy is taken and that is checked.
|
||||
snapshot_dir=$(mktemp -d)
|
||||
trap 'rm -rf "$snapshot_dir"' EXIT
|
||||
HERMES_SNAPSHOT="$snapshot_dir/$HERMES_SKIN_NAME.yaml"
|
||||
|
||||
take_snapshot() {
|
||||
cp "$HERMES_SOURCE_PATH" "$HERMES_SNAPSHOT" 2>/dev/null && skin_is_well_formed "$HERMES_SNAPSHOT"
|
||||
}
|
||||
|
||||
if ! take_snapshot; then
|
||||
echo "Skipping Hermes skin: $(basename "$HERMES_SOURCE_PATH") is not a plain color palette." >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# The gateway reads the file whole on an mtime change, so the write is atomic;
|
||||
# -T so a directory at the skin's path is an error rather than a destination.
|
||||
publish_skin() {
|
||||
local skins_dir="$1"
|
||||
local tmp
|
||||
|
||||
mkdir -p "$skins_dir" 2>/dev/null || return 1
|
||||
tmp=$(mktemp "$skins_dir/$HERMES_SKIN_NAME.yaml.XXXXXX" 2>/dev/null) || return 1
|
||||
if ! cp "$HERMES_SNAPSHOT" "$tmp" 2>/dev/null || ! mv -T "$tmp" "$skins_dir/$HERMES_SKIN_NAME.yaml" 2>/dev/null; then
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# A profile is a Hermes home of its own; existing ones get the skin, none are
|
||||
# made, and one that cannot take it does not cost the others.
|
||||
publish_skin_everywhere() {
|
||||
local profile
|
||||
|
||||
publish_skin "$HERMES_HOME/skins" || {
|
||||
echo "Could not publish the Hermes skin to $HERMES_HOME/skins." >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
for profile in "$HERMES_HOME"/profiles/*/; do
|
||||
[[ -d $profile ]] || continue
|
||||
publish_skin "${profile%/}/skins" || note "Could not publish the skin to the Hermes profile $(basename "$profile")."
|
||||
done
|
||||
}
|
||||
|
||||
publish_skin_everywhere
|
||||
|
||||
# Hermes reads the config of the profile named in active_profile; the profile
|
||||
# exists once its directory does, with or without a config of its own.
|
||||
active_config_path() {
|
||||
local profile
|
||||
|
||||
profile=$(cat "$HERMES_HOME/active_profile" 2>/dev/null || true)
|
||||
profile=${profile,,}
|
||||
|
||||
if [[ -n $profile && $profile != "default" && -d $HERMES_HOME/profiles/$profile ]]; then
|
||||
echo "$HERMES_HOME/profiles/$profile/config.yaml"
|
||||
else
|
||||
echo "$HERMES_CONFIG_PATH"
|
||||
fi
|
||||
}
|
||||
|
||||
# A theme switch finishes the hand-over only for the app Omarchy installed, and
|
||||
# only Hermes' default is ever replaced, so a config plainly naming another skin
|
||||
# ends it here without starting Hermes. Anything less plain is for Hermes to read.
|
||||
if (( HERMES_ACTIVATE == 0 )); then
|
||||
omarchy-pkg-present hermes-desktop || exit 0
|
||||
|
||||
skin_line=$(grep -m1 -x ' skin: .*' "$(active_config_path)" 2>/dev/null || true)
|
||||
case "${skin_line# skin: }" in
|
||||
"" | default | null | true | false | *[!A-Za-z0-9_-]*) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Omarchy's cold stub installs Hermes when run, so ask the probe before running it.
|
||||
if ! omarchy-install-hermes-cli --check 2>/dev/null; then
|
||||
note "Hermes is not ready, so the Omarchy skin is published but not active."
|
||||
note "Once Hermes runs, activate it with: hermes config set display.skin $HERMES_SKIN_NAME"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Only Hermes' own default is replaced, so a skin chosen in Hermes stays; an
|
||||
# answer that did not come is not a default.
|
||||
if ! current_skin=$(timeout 15 "$HERMES_COMMAND" config get display.skin 2>/dev/null); then
|
||||
note "Hermes did not say which skin it is on, so the Omarchy skin is published but not active."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -n $current_skin && $current_skin != "default" && $current_skin != "$HERMES_SKIN_NAME" ]]; then
|
||||
note "Hermes is set to the '$current_skin' skin; leaving it. Switch with: hermes config set display.skin $HERMES_SKIN_NAME"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Hermes' own writer: it updates the active profile's config and touches the
|
||||
# skin file so a running gateway broadcasts the change. A refusal is cosmetic.
|
||||
if ! timeout 30 "$HERMES_COMMAND" config set display.skin "$HERMES_SKIN_NAME" >/dev/null 2>&1; then
|
||||
note "Hermes refused to switch skins, so the Omarchy skin is published but not active."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
note "Hermes is on the Omarchy skin."
|
||||
|
||||
# The desktop applies a skin only from a broadcast, and a config written before
|
||||
# the gateway seeded its watcher goes unannounced; a later write is announced.
|
||||
# The theme may have changed underneath in the meantime, so it is checked again.
|
||||
if (( HERMES_WAIT == 1 )); then
|
||||
sleep 60
|
||||
|
||||
if take_snapshot; then
|
||||
publish_skin_everywhere
|
||||
fi
|
||||
fi
|
||||
@@ -0,0 +1,47 @@
|
||||
name: omarchy
|
||||
description: Omarchy system theme
|
||||
colors:
|
||||
background: "{{ background }}"
|
||||
ui_text: "{{ foreground }}"
|
||||
ui_primary: "{{ accent }}"
|
||||
ui_accent: "{{ accent }}"
|
||||
ui_border: "{{ muted }}"
|
||||
ui_label: "{{ accent }}"
|
||||
ui_ok: "{{ green }}"
|
||||
ui_warn: "{{ yellow }}"
|
||||
ui_error: "{{ red }}"
|
||||
ui_tool: "{{ cyan }}"
|
||||
ui_thinking: "{{ dark_foreground }}"
|
||||
banner_border: "{{ muted }}"
|
||||
banner_title: "{{ accent }}"
|
||||
banner_accent: "{{ accent }}"
|
||||
banner_dim: "{{ dark_foreground }}"
|
||||
banner_text: "{{ foreground }}"
|
||||
prompt: "{{ bright_foreground }}"
|
||||
input_rule: "{{ muted }}"
|
||||
response_border: "{{ accent }}"
|
||||
shell_dollar: "{{ blue }}"
|
||||
selection_bg: "{{ selection }}"
|
||||
session_label: "{{ accent }}"
|
||||
session_border: "{{ muted }}"
|
||||
status_bar_bg: "{{ dark_background }}"
|
||||
status_bar_text: "{{ foreground }}"
|
||||
status_bar_strong: "{{ accent }}"
|
||||
status_bar_dim: "{{ dark_foreground }}"
|
||||
status_bar_good: "{{ green }}"
|
||||
status_bar_warn: "{{ yellow }}"
|
||||
status_bar_bad: "{{ red }}"
|
||||
status_bar_critical: "{{ bright_red }}"
|
||||
voice_status_bg: "{{ dark_background }}"
|
||||
completion_menu_bg: "{{ lighter_background }}"
|
||||
completion_menu_current_bg: "{{ selection }}"
|
||||
completion_menu_meta_bg: "{{ lighter_background }}"
|
||||
completion_menu_meta_current_bg: "{{ selection }}"
|
||||
diff_added: "{{ mix background green 15% }}"
|
||||
diff_removed: "{{ mix background red 15% }}"
|
||||
diff_added_word: "{{ green }}"
|
||||
diff_removed_word: "{{ red }}"
|
||||
syntax_string: "{{ green }}"
|
||||
syntax_number: "{{ yellow }}"
|
||||
syntax_keyword: "{{ magenta }}"
|
||||
syntax_comment: "{{ muted }}"
|
||||
+2
-2
@@ -26,7 +26,7 @@ Pick your default agent with `omarchy default agent <name>` or under _Setup > De
|
||||
|
||||
Once you've chosen, `Super + Shift + Ctrl + A` launches the default agent in a dedicated terminal window (or brings up the picker if you haven't chosen yet). You can also launch it straight into a task with `omarchy agent prompt "Review this project"`. Agents launched this way run unattended in their respective don't-stop-to-ask modes, so be ready for them to actually do things! And since agents refuse to remember trust for your home directory, launches from `$HOME` start in `~/Work` instead.
|
||||
|
||||
There are terminal shortcuts too: `a` runs the default agent inline in the current terminal, while `c`, `cx`, and `cy` start OpenCode, Claude Code, and Codex directly (again in their auto-approving modes). Theme changes sync to the agents as well: Claude Code, Pi, and OpenCode all follow along when you switch the Omarchy theme.
|
||||
There are terminal shortcuts too: `a` runs the default agent inline in the current terminal, while `c`, `cx`, and `cy` start OpenCode, Claude Code, and Codex directly (again in their auto-approving modes). Theme changes sync to the agents as well: Claude Code, Pi, OpenCode, and Hermes (once Hermes Desktop is installed) all follow along when you switch the Omarchy theme.
|
||||
|
||||
### The agents panel
|
||||
|
||||
@@ -46,7 +46,7 @@ Crashes can also be silenced one program at a time, which is what the diagnosis
|
||||
|
||||
The _Install > AI_ menu also carries a few graphical AI apps: the ChatGPT desktop app, Grok Bot for chatting with xAI's models, Hermes Desktop, OpenClaw, and the Perplexity desktop app.
|
||||
|
||||
Hermes Desktop is the one to know about, because there is only ever one Hermes on a machine. The app only runs against a runtime built from its own commit, so it installs one of its own under `~/.hermes` on first launch, which takes a few minutes and shows its own progress. From then on that is the Hermes the terminal `hermes` command and the default agent use too, whichever order you installed them in. Removing the app under _Remove > AI_ takes that runtime with it, and keeps your chats, memories, and the skills Hermes wrote for itself unless you tell it otherwise: it asks, defaulting to no, whether that data and your connection settings should go too.
|
||||
Hermes Desktop is the one to know about, because there is only ever one Hermes on a machine. The app only runs against a runtime built from its own commit, so it installs one of its own under `~/.hermes` on first launch, which takes a few minutes and shows its own progress. From then on that is the Hermes the terminal `hermes` command and the default agent use too, whichever order you installed them in. Installing it also hands Hermes the Omarchy theme as a skin named `omarchy`, which every Hermes surface follows as you switch themes; pick another under Hermes' Appearance settings or with `/skin` if you'd rather it didn't, and Omarchy leaves that choice alone. Removing the app under _Remove > AI_ takes that runtime with it, and keeps your chats, memories, and the skills Hermes wrote for itself unless you tell it otherwise: it asks, defaulting to no, whether that data and your connection settings should go too.
|
||||
|
||||
OpenClaw's desktop experience is its Control UI, which opens as a web app backed by its own local gateway. OpenClaw updates arrive through Omarchy's package updates, so skip the Control UI's own "Update Gateway" button: it would try to write into the package-managed install and fail. Removing OpenClaw under _Remove > AI_ takes the gateway service and the app with it and then asks whether `~/.openclaw` should go too, since that holds your chats and credentials alongside the plugin runtimes OpenClaw downloads for itself; the default keeps it.
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
echo "Hand Hermes Desktop the Omarchy theme as a skin"
|
||||
|
||||
# Only the app Omarchy installed under Install > AI follows the theme by itself.
|
||||
# A Hermes the user set up some other way keeps whatever skin they chose.
|
||||
omarchy-pkg-present hermes-desktop || exit 0
|
||||
|
||||
# The same hand-over a fresh install does. A Hermes that is not ready or refuses
|
||||
# the write is reported and done with there; only Omarchy's own failures return.
|
||||
omarchy-theme-set-hermes --activate
|
||||
@@ -34,6 +34,11 @@ cat >"$mock_bin/gum" <<'SH'
|
||||
printf '%s\0' "$@" >>"$OMARCHY_TEST_GUM_LOG"
|
||||
exit "${OMARCHY_TEST_GUM_STATUS:-1}"
|
||||
SH
|
||||
cat >"$mock_bin/systemctl" <<'SH'
|
||||
#!/bin/bash
|
||||
echo "systemctl $*" >>"$OMARCHY_TEST_SYSTEMCTL_LOG"
|
||||
SH
|
||||
|
||||
chmod +x "$mock_bin"/*
|
||||
|
||||
seed_install() {
|
||||
@@ -58,9 +63,11 @@ seed_install() {
|
||||
remove() {
|
||||
: >"$test_tmp/installer-log"
|
||||
: >"$test_tmp/gum-log"
|
||||
: >"$test_tmp/systemctl-log"
|
||||
OMARCHY_TEST_DROP_LOG="$test_tmp/drop-log" \
|
||||
OMARCHY_TEST_INSTALLER_LOG="$test_tmp/installer-log" \
|
||||
OMARCHY_TEST_INSTALLER_STATUS="${OMARCHY_TEST_INSTALLER_STATUS:-0}" \
|
||||
OMARCHY_TEST_SYSTEMCTL_LOG="$test_tmp/systemctl-log" \
|
||||
OMARCHY_TEST_GUM_LOG="$test_tmp/gum-log" \
|
||||
HOME="$test_home" PATH="$mock_bin:$PATH" \
|
||||
bash "$ROOT/bin/omarchy-remove-ai-hermes" </dev/null >/dev/null 2>&1
|
||||
@@ -71,8 +78,10 @@ remove() {
|
||||
remove_tty() {
|
||||
: >"$test_tmp/installer-log"
|
||||
: >"$test_tmp/gum-log"
|
||||
: >"$test_tmp/systemctl-log"
|
||||
OMARCHY_TEST_DROP_LOG="$test_tmp/drop-log" \
|
||||
OMARCHY_TEST_INSTALLER_LOG="$test_tmp/installer-log" \
|
||||
OMARCHY_TEST_SYSTEMCTL_LOG="$test_tmp/systemctl-log" \
|
||||
OMARCHY_TEST_GUM_LOG="$test_tmp/gum-log" \
|
||||
OMARCHY_TEST_GUM_STATUS="${OMARCHY_TEST_GUM_STATUS:-1}" \
|
||||
HOME="$test_home" PATH="$mock_bin:$PATH" \
|
||||
@@ -89,6 +98,10 @@ remove || fail "remove succeeds"
|
||||
[[ ! -d $test_home/.hermes/node ]] || fail "the node the app installed is removed"
|
||||
pass "removal takes the whole runtime the app installed"
|
||||
|
||||
grep -Fxq 'systemctl --user stop omarchy-hermes-theme.service' "$test_tmp/systemctl-log" ||
|
||||
fail "the unit the installer left waiting to hand over the theme is stopped" "$(cat "$test_tmp/systemctl-log")"
|
||||
pass "removal stops the installer's theme hand-over"
|
||||
|
||||
[[ -d $test_home/.config/Hermes ]] ||
|
||||
fail "gateway connections, tokens and settings survive removal"
|
||||
pass "removal keeps the app's connections and settings"
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# The migration hands an existing Hermes Desktop install the Omarchy skin. It
|
||||
# is exercised here with the package probe and the skin hook stubbed, so a
|
||||
# migration that reached a Hermes Omarchy did not install, or that marked a
|
||||
# failed hand-over done, shows up in what it ran.
|
||||
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
migration="$ROOT/migrations/1788619462.sh"
|
||||
[[ -f $migration ]] || fail "Hermes skin migration exists"
|
||||
[[ $(stat -c %a "$migration") == "644" ]] || fail "migration is a plain 0644 file"
|
||||
|
||||
test_tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$test_tmp"' EXIT
|
||||
|
||||
mock_bin="$test_tmp/bin"
|
||||
calls="$test_tmp/calls"
|
||||
mkdir -p "$mock_bin"
|
||||
|
||||
cat >"$mock_bin/omarchy-pkg-present" <<'SH'
|
||||
#!/bin/bash
|
||||
[[ $1 == "hermes-desktop" && ${OMARCHY_TEST_DESKTOP_INSTALLED:-0} == 1 ]]
|
||||
SH
|
||||
|
||||
cat >"$mock_bin/omarchy-theme-set-hermes" <<'SH'
|
||||
#!/bin/bash
|
||||
echo "omarchy-theme-set-hermes $*" >>"$OMARCHY_TEST_CALLS"
|
||||
[[ ${OMARCHY_TEST_HOOK_FAILS:-0} == 0 ]]
|
||||
SH
|
||||
|
||||
chmod +x "$mock_bin"/*
|
||||
|
||||
run_migration() {
|
||||
: >"$calls"
|
||||
OMARCHY_TEST_DESKTOP_INSTALLED="${OMARCHY_TEST_DESKTOP_INSTALLED:-1}" \
|
||||
OMARCHY_TEST_HOOK_FAILS="${OMARCHY_TEST_HOOK_FAILS:-0}" \
|
||||
OMARCHY_TEST_CALLS="$calls" \
|
||||
PATH="$mock_bin:$PATH" \
|
||||
HOME="$test_tmp/home" \
|
||||
OMARCHY_PATH="$ROOT" \
|
||||
bash -euo pipefail "$migration" >/dev/null
|
||||
}
|
||||
|
||||
OMARCHY_TEST_DESKTOP_INSTALLED=0 run_migration || fail "migration exits clean without Hermes Desktop"
|
||||
[[ ! -s $calls ]] || fail "a machine without Hermes Desktop is left alone" "$(cat "$calls")"
|
||||
pass "migration only applies where Omarchy installed Hermes Desktop"
|
||||
|
||||
run_migration || fail "migration exits clean with Hermes Desktop installed"
|
||||
[[ $(cat "$calls") == "omarchy-theme-set-hermes --activate" ]] ||
|
||||
fail "the skin is rendered, published and activated through the hook's deliberate form" "$(cat "$calls")"
|
||||
pass "migration hands the skin over through the hook"
|
||||
|
||||
if OMARCHY_TEST_HOOK_FAILS=1 run_migration; then
|
||||
fail "a hand-over that failed on Omarchy's side stays pending"
|
||||
fi
|
||||
pass "migration stays pending when the hand-over fails"
|
||||
@@ -0,0 +1,401 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# omarchy-theme-set-hermes writes a file another program parses and asks that
|
||||
# program to switch to it while it is still on its default. Both are exercised
|
||||
# here against a throwaway HOME with the Hermes readiness probe, the hermes
|
||||
# command and the theme refresh stubbed, so a skin that stopped being
|
||||
# validated, a write into a Hermes that was never set up, or an activation
|
||||
# that trampled a chosen skin shows up in what landed on disk and what was run.
|
||||
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
test_tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$test_tmp"' EXIT
|
||||
|
||||
mock_bin="$test_tmp/bin"
|
||||
mkdir -p "$mock_bin"
|
||||
|
||||
cat >"$mock_bin/omarchy-install-hermes-cli" <<'SH'
|
||||
#!/bin/bash
|
||||
echo "check" >>"$OMARCHY_TEST_HERMES_CALLS"
|
||||
[[ $1 == "--check" && ${OMARCHY_TEST_HERMES_READY:-0} == "1" ]]
|
||||
SH
|
||||
|
||||
# A theme switch finishes the hand-over only for the desktop app Omarchy
|
||||
# installed; --activate is asked for by name and does not look.
|
||||
cat >"$mock_bin/omarchy-pkg-present" <<'SH'
|
||||
#!/bin/bash
|
||||
[[ $1 == "hermes-desktop" && ${OMARCHY_TEST_DESKTOP_INSTALLED:-1} == "1" ]]
|
||||
SH
|
||||
|
||||
# The hook runs the hermes the probe vets, ~/.local/bin/hermes, not one on PATH;
|
||||
# reset_home installs this stub there and a decoy on PATH that must never run.
|
||||
cat >"$mock_bin/hermes" <<'SH'
|
||||
#!/bin/bash
|
||||
echo "PATH hermes ran: $*" >>"$OMARCHY_TEST_HERMES_CALLS"
|
||||
exit 1
|
||||
SH
|
||||
|
||||
cat >"$mock_bin/hermes-stub" <<'SH'
|
||||
#!/bin/bash
|
||||
printf '%s\n' "$*" >>"$OMARCHY_TEST_HERMES_CALLS"
|
||||
if [[ $1 == "config" && $2 == "get" ]]; then
|
||||
[[ ${OMARCHY_TEST_HERMES_GET_FAILS:-0} == 0 ]] || exit 1
|
||||
printf '%s\n' "${OMARCHY_TEST_HERMES_SKIN-default}"
|
||||
fi
|
||||
if [[ $1 == "config" && $2 == "set" ]]; then
|
||||
[[ ${OMARCHY_TEST_HERMES_SET_FAILS:-0} == 0 ]] || exit 1
|
||||
fi
|
||||
SH
|
||||
|
||||
# A refresh re-stages the current theme, which is where the skin gets rendered.
|
||||
cat >"$mock_bin/omarchy-theme-refresh" <<'SH'
|
||||
#!/bin/bash
|
||||
echo "refresh" >>"$OMARCHY_TEST_HERMES_CALLS"
|
||||
printf 'name: omarchy\ndescription: Omarchy system theme\ncolors:\n background: "#1a1b26"\n' \
|
||||
>"$HOME/.local/state/omarchy/current/theme/hermes.yaml"
|
||||
SH
|
||||
|
||||
# --wait sleeps between its polls and once more after activating; the stub
|
||||
# records the delays it was asked for and returns at once.
|
||||
cat >"$mock_bin/sleep" <<'SH'
|
||||
#!/bin/bash
|
||||
printf 'sleep %s\n' "$1" >>"$OMARCHY_TEST_HERMES_CALLS"
|
||||
if [[ $1 == 60 && -n ${OMARCHY_TEST_SWAP_SOURCE:-} ]]; then
|
||||
printf '%s\n' "$OMARCHY_TEST_SWAP_SOURCE" >"$HOME/.local/state/omarchy/current/theme/hermes.yaml"
|
||||
fi
|
||||
if [[ $1 == 60 && ${OMARCHY_TEST_DROP_SKINS:-0} == 1 ]]; then
|
||||
rm -f "$HOME/.hermes/skins/omarchy.yaml" "$HOME"/.hermes/profiles/*/skins/omarchy.yaml
|
||||
fi
|
||||
SH
|
||||
|
||||
chmod +x "$mock_bin"/*
|
||||
|
||||
good_skin='name: omarchy
|
||||
description: Omarchy system theme
|
||||
colors:
|
||||
background: "#1a1b26"
|
||||
ui_text: "#a9b1d6"
|
||||
ui_accent: "#7aa2f7"'
|
||||
|
||||
test_home="$test_tmp/home"
|
||||
hermes_home="$test_home/.hermes"
|
||||
skin="$hermes_home/skins/omarchy.yaml"
|
||||
hermes_calls="$test_tmp/hermes-calls"
|
||||
|
||||
# Each case gets a fresh HOME so no file survives from the one before. The
|
||||
# Hermes home is created the way provisioning does on every machine; only
|
||||
# --set-up adds the config that says Hermes itself has run, on its default
|
||||
# skin unless --on names another.
|
||||
reset_home() {
|
||||
local source="$good_skin"
|
||||
|
||||
rm -rf "$test_home"
|
||||
mkdir -p "$test_home/.local/state/omarchy/current/theme" "$test_home/.local/bin" "$hermes_home/skills"
|
||||
cp "$mock_bin/hermes-stub" "$test_home/.local/bin/hermes"
|
||||
: >"$hermes_calls"
|
||||
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
--set-up) printf 'display:\n skin: default\n' >"$hermes_home/config.yaml" ;;
|
||||
--on) printf 'display:\n skin: %s\n' "$2" >"$hermes_home/config.yaml"; shift ;;
|
||||
*) source="$1" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
printf '%s\n' "$source" >"$test_home/.local/state/omarchy/current/theme/hermes.yaml"
|
||||
}
|
||||
|
||||
run_hook() {
|
||||
OMARCHY_TEST_HERMES_READY="${OMARCHY_TEST_HERMES_READY:-0}" \
|
||||
OMARCHY_TEST_HERMES_SKIN="${OMARCHY_TEST_HERMES_SKIN-default}" \
|
||||
OMARCHY_TEST_HERMES_GET_FAILS="${OMARCHY_TEST_HERMES_GET_FAILS:-0}" \
|
||||
OMARCHY_TEST_HERMES_SET_FAILS="${OMARCHY_TEST_HERMES_SET_FAILS:-0}" \
|
||||
OMARCHY_TEST_HERMES_CALLS="$hermes_calls" \
|
||||
OMARCHY_TEST_DESKTOP_INSTALLED="${OMARCHY_TEST_DESKTOP_INSTALLED:-1}" \
|
||||
OMARCHY_TEST_SWAP_SOURCE="${OMARCHY_TEST_SWAP_SOURCE:-}" \
|
||||
OMARCHY_TEST_DROP_SKINS="${OMARCHY_TEST_DROP_SKINS:-0}" \
|
||||
PATH="$mock_bin:$PATH" \
|
||||
HOME="$test_home" \
|
||||
HERMES_HOME='' \
|
||||
"$ROOT/bin/omarchy-theme-set-hermes" "$@"
|
||||
}
|
||||
|
||||
# -- publishing ---------------------------------------------------------------
|
||||
|
||||
reset_home
|
||||
run_hook
|
||||
[[ ! -e $hermes_home/skins ]] || fail "a Hermes home that only holds the Omarchy skill gets no skin"
|
||||
[[ ! -s $hermes_calls ]] || fail "nothing is run for a Hermes that never ran" "$(cat "$hermes_calls")"
|
||||
pass "a theme switch leaves a machine that never ran Hermes alone"
|
||||
|
||||
reset_home --set-up
|
||||
mkdir -p "$hermes_home/profiles/work"
|
||||
run_hook 2>"$test_tmp/stderr"
|
||||
diff -q "$test_home/.local/state/omarchy/current/theme/hermes.yaml" "$skin" >/dev/null ||
|
||||
fail "the generated skin is published to ~/.hermes/skins/omarchy.yaml"
|
||||
diff -q "$skin" "$hermes_home/profiles/work/skins/omarchy.yaml" >/dev/null ||
|
||||
fail "an existing Hermes profile gets the skin too"
|
||||
[[ $(ls "$hermes_home/skins") == "omarchy.yaml" ]] || fail "no temporary file is left beside the skin"
|
||||
[[ $(cat "$hermes_calls") == "check" ]] || fail "a Hermes that is not ready is asked nothing more" "$(cat "$hermes_calls")"
|
||||
[[ ! -s $test_tmp/stderr ]] || fail "a theme switch says nothing about Hermes" "$(cat "$test_tmp/stderr")"
|
||||
pass "the skin is published to the Hermes home and every profile"
|
||||
|
||||
reset_home --set-up 'name: omarchy
|
||||
description: Omarchy system theme
|
||||
colors:
|
||||
background: "{{ background }}"'
|
||||
mkdir -p "$hermes_home/skins"
|
||||
printf 'name: omarchy\ncolors:\n background: "#000000"\n' >"$skin"
|
||||
run_hook 2>"$test_tmp/stderr"
|
||||
grep -q '#000000' "$skin" || fail "an unresolved placeholder keeps the previous skin in place"
|
||||
grep -q 'not a plain color palette' "$test_tmp/stderr" || fail "an unresolved placeholder is reported"
|
||||
pass "a skin with unresolved colors is not published"
|
||||
|
||||
# mv would otherwise move the temp file inside a directory at the skin's path,
|
||||
# leaving Hermes a directory to read and the temp file behind.
|
||||
reset_home --set-up
|
||||
mkdir -p "$skin"
|
||||
if run_hook 2>/dev/null; then
|
||||
fail "a directory at the skin's path is an error, not a place to put the skin"
|
||||
fi
|
||||
[[ -z $(ls -A "$skin") && $(ls "$hermes_home/skins") == "omarchy.yaml" ]] ||
|
||||
fail "nothing is left inside or beside a directory at the skin's path" "$(ls -R "$hermes_home/skins")"
|
||||
pass "a directory at the skin's path is refused cleanly"
|
||||
|
||||
for bad in \
|
||||
$'name: omarchy\ndescription: Omarchy system theme\ncolors:\n background: "#1a1b26"\nbanner_logo: "[link=file:///etc/passwd]x[/link]"' \
|
||||
$'name: omarchy\ndescription: Omarchy system theme\ncolors:\n background: "#1a1b26\\"\\n ui_text: \\"#ffffff"' \
|
||||
$'name: nord\ndescription: Nord\ncolors:\n background: "#2e3440"' \
|
||||
$'description: Omarchy system theme\ncolors:\n background: "#1a1b26"' \
|
||||
$'name: omarchy\ndescription: Nord: arctic palette\ncolors:\n background: "#2e3440"' \
|
||||
$'name: omarchy\ncolors:\n background: "#1a1b26"\n#\rbanner_logo: "[link=file:///etc/passwd]x[/link]"' \
|
||||
$'name: omarchy\ncolors:\n background: "#1a1b26"\n#\xe2\x80\xa8banner_logo: "evil"' \
|
||||
$'name: omarchy\ncolors:\n background: "#1a1b26"\n#\xc2\x85banner_logo: "evil"' \
|
||||
$'name: omarchy\n background: "#1a1b26"\ncolors:' \
|
||||
$'name: omarchy\ncolors:\n background: "#1a1b26"\ncolors:' \
|
||||
$'colors:\n background: "#1a1b26"\nname: omarchy' \
|
||||
$'name: omarchy\ncolors:'; do
|
||||
reset_home --set-up "$bad"
|
||||
run_hook 2>/dev/null
|
||||
[[ ! -e $skin ]] || fail "a skin that is not exactly a named palette of hex colors is not published" "$bad"
|
||||
done
|
||||
pass "a skin is held to the shape Hermes loads, on the lines Hermes' YAML reader sees"
|
||||
|
||||
# A NUL cannot travel through a shell string, so it is written straight to the
|
||||
# source; grep reads past one where YAML stops.
|
||||
reset_home --set-up
|
||||
printf 'name: omarchy\ncolors:\n background: "#1a1b26"\0\n' >"$test_home/.local/state/omarchy/current/theme/hermes.yaml"
|
||||
run_hook 2>/dev/null
|
||||
[[ ! -e $skin ]] || fail "a NUL byte in the skin is rejected"
|
||||
pass "a skin carrying a NUL byte is not published"
|
||||
|
||||
reset_home --set-up $'# rendered by Omarchy\nname: omarchy\n\ndescription: Omarchy system theme\ncolors:\n background: "#1a1b26"\n'
|
||||
run_hook 2>/dev/null
|
||||
[[ -f $skin ]] || fail "comments and blank lines are allowed around the palette"
|
||||
pass "a well-formed skin with comments and blank lines is published"
|
||||
|
||||
# -- a theme switch finishes a missed hand-over ---------------------------------
|
||||
|
||||
reset_home --set-up
|
||||
OMARCHY_TEST_HERMES_READY=1 run_hook 2>"$test_tmp/stderr"
|
||||
[[ $(cat "$hermes_calls") == $'check\nconfig get display.skin\nconfig set display.skin omarchy' ]] ||
|
||||
fail "a ready Hermes still on its default is switched by a theme switch" "$(cat "$hermes_calls")"
|
||||
[[ ! -s $test_tmp/stderr ]] || fail "a theme switch activates quietly" "$(cat "$test_tmp/stderr")"
|
||||
pass "a theme switch activates the skin on a Hermes still on its default"
|
||||
|
||||
reset_home --on omarchy
|
||||
OMARCHY_TEST_HERMES_READY=1 run_hook
|
||||
[[ -f $skin ]] || fail "the skin is published when it is already active"
|
||||
[[ ! -s $hermes_calls ]] || fail "a Hermes already on the skin is not started" "$(cat "$hermes_calls")"
|
||||
pass "a theme switch does not start a Hermes already on the skin"
|
||||
|
||||
reset_home --set-up
|
||||
OMARCHY_TEST_HERMES_READY=1 OMARCHY_TEST_DESKTOP_INSTALLED=0 run_hook
|
||||
[[ -f $skin ]] || fail "a Hermes installed some other way still gets the skin published"
|
||||
[[ ! -s $hermes_calls ]] || fail "a theme switch does not touch a Hermes Omarchy did not install as the app" "$(cat "$hermes_calls")"
|
||||
pass "a theme switch activates only for the desktop app Omarchy installed"
|
||||
|
||||
reset_home --set-up
|
||||
OMARCHY_TEST_HERMES_READY=1 OMARCHY_TEST_DESKTOP_INSTALLED=0 run_hook --activate 2>/dev/null
|
||||
grep -Fxq 'config set display.skin omarchy' "$hermes_calls" ||
|
||||
fail "--activate switches whichever Hermes it is asked about" "$(cat "$hermes_calls")"
|
||||
pass "--activate does not ask which Hermes it is"
|
||||
|
||||
reset_home --on ares
|
||||
OMARCHY_TEST_HERMES_READY=1 OMARCHY_TEST_HERMES_SKIN=ares run_hook 2>"$test_tmp/stderr"
|
||||
[[ -f $skin ]] || fail "a chosen skin still gets the Omarchy skin published beside it"
|
||||
[[ ! -s $hermes_calls ]] || fail "a theme switch does not start a Hermes whose config names a chosen skin" "$(cat "$hermes_calls")"
|
||||
[[ ! -s $test_tmp/stderr ]] || fail "a chosen skin is left without comment on a theme switch" "$(cat "$test_tmp/stderr")"
|
||||
pass "a theme switch leaves a skin the user chose in Hermes"
|
||||
|
||||
# Hermes reads the config of the profile named in active_profile, so that is
|
||||
# the config that says whether there is anything left to do.
|
||||
reset_home --on omarchy
|
||||
mkdir -p "$hermes_home/profiles/work"
|
||||
printf 'display:\n skin: default\n' >"$hermes_home/profiles/work/config.yaml"
|
||||
echo work >"$hermes_home/active_profile"
|
||||
OMARCHY_TEST_HERMES_READY=1 run_hook
|
||||
grep -Fxq 'config set display.skin omarchy' "$hermes_calls" ||
|
||||
fail "an active profile still on its default is switched even when the root config names the skin" "$(cat "$hermes_calls")"
|
||||
pass "a theme switch follows the active Hermes profile"
|
||||
|
||||
reset_home --set-up
|
||||
mkdir -p "$hermes_home/profiles/work"
|
||||
printf 'display:\n skin: ares\n' >"$hermes_home/profiles/work/config.yaml"
|
||||
echo work >"$hermes_home/active_profile"
|
||||
OMARCHY_TEST_HERMES_READY=1 run_hook
|
||||
[[ ! -s $hermes_calls ]] || fail "a skin chosen in the active profile is not replaced" "$(cat "$hermes_calls")"
|
||||
pass "a theme switch leaves a skin chosen in the active Hermes profile"
|
||||
|
||||
# Hermes selects a profile once its directory exists; without a config of its
|
||||
# own it is on the default skin whatever the root config says.
|
||||
reset_home --on omarchy
|
||||
mkdir -p "$hermes_home/profiles/work"
|
||||
echo Work >"$hermes_home/active_profile"
|
||||
OMARCHY_TEST_HERMES_READY=1 run_hook
|
||||
grep -Fxq 'config set display.skin omarchy' "$hermes_calls" ||
|
||||
fail "an active profile without a config of its own is on the default and gets switched" "$(cat "$hermes_calls")"
|
||||
pass "a theme switch follows an active profile that has no config yet"
|
||||
|
||||
# Only a plainly named skin ends a switch early; anything Hermes might read as
|
||||
# its default is left for Hermes to answer.
|
||||
for line in 'skin: "default"' 'skin: default # chosen long ago' 'skin: null' 'skin: false'; do
|
||||
reset_home --set-up
|
||||
printf 'display:\n %s\n' "$line" >"$hermes_home/config.yaml"
|
||||
OMARCHY_TEST_HERMES_READY=1 run_hook
|
||||
grep -Fxq 'config set display.skin omarchy' "$hermes_calls" ||
|
||||
fail "a config line Hermes reads as the default still gets the skin activated" "$line: $(cat "$hermes_calls")"
|
||||
done
|
||||
pass "a theme switch asks Hermes about any skin line that is not a plain name"
|
||||
|
||||
reset_home --set-up
|
||||
mkdir -p "$hermes_home/profiles/broken"
|
||||
: >"$hermes_home/profiles/broken/skins"
|
||||
OMARCHY_TEST_HERMES_READY=1 run_hook 2>"$test_tmp/stderr" || fail "a profile that cannot take the skin does not fail the hook"
|
||||
[[ ! -s $test_tmp/stderr ]] || fail "a theme switch stays quiet about a profile it could not reach" "$(cat "$test_tmp/stderr")"
|
||||
[[ -f $skin ]] || fail "the Hermes home still gets the skin beside a broken profile"
|
||||
grep -Fxq 'config set display.skin omarchy' "$hermes_calls" ||
|
||||
fail "activation still happens beside a broken profile" "$(cat "$hermes_calls")"
|
||||
pass "a profile that cannot take the skin costs nobody else"
|
||||
|
||||
# -- activation ---------------------------------------------------------------
|
||||
|
||||
reset_home
|
||||
run_hook --activate 2>"$test_tmp/stderr"
|
||||
[[ ! -e $hermes_home/skins && ! -e $hermes_home/config.yaml ]] ||
|
||||
fail "--activate writes nothing into a Hermes that has never run"
|
||||
grep -q 'not set up yet' "$test_tmp/stderr" || fail "--activate says why nothing happened"
|
||||
pass "--activate waits for Hermes to have been set up"
|
||||
|
||||
reset_home --set-up
|
||||
run_hook --activate 2>"$test_tmp/stderr"
|
||||
[[ -f $skin ]] || fail "--activate publishes the skin when Hermes is not ready"
|
||||
[[ $(cat "$hermes_calls") == "check" ]] || fail "a Hermes that is not ready is not run" "$(cat "$hermes_calls")"
|
||||
grep -q 'hermes config set display.skin omarchy' "$test_tmp/stderr" || fail "an unready Hermes gets the command that finishes the job"
|
||||
pass "--activate publishes but does not run a Hermes that is not ready"
|
||||
|
||||
reset_home --set-up
|
||||
OMARCHY_TEST_HERMES_READY=1 run_hook --activate 2>"$test_tmp/stderr"
|
||||
[[ $(cat "$hermes_calls") == $'check\nconfig get display.skin\nconfig set display.skin omarchy' ]] ||
|
||||
fail "a ready Hermes is asked for its skin and then to switch" "$(cat "$hermes_calls")"
|
||||
grep -q 'on the Omarchy skin' "$test_tmp/stderr" || fail "--activate reports success"
|
||||
pass "--activate goes through hermes config set when Hermes runs"
|
||||
|
||||
reset_home --on omarchy
|
||||
OMARCHY_TEST_HERMES_READY=1 OMARCHY_TEST_HERMES_SKIN=omarchy run_hook --activate 2>/dev/null
|
||||
grep -Fxq 'config set display.skin omarchy' "$hermes_calls" ||
|
||||
fail "--activate goes through Hermes even when the config already names the skin" "$(cat "$hermes_calls")"
|
||||
pass "--activate always asks Hermes to switch"
|
||||
|
||||
for chosen in omarchy ""; do
|
||||
reset_home --set-up
|
||||
OMARCHY_TEST_HERMES_READY=1 OMARCHY_TEST_HERMES_SKIN="$chosen" run_hook --activate 2>/dev/null
|
||||
grep -Fxq 'config set display.skin omarchy' "$hermes_calls" ||
|
||||
fail "the default and the omarchy skin are both replaced" "skin='$chosen': $(cat "$hermes_calls")"
|
||||
done
|
||||
pass "--activate replaces Hermes' default skin"
|
||||
|
||||
reset_home --set-up
|
||||
OMARCHY_TEST_HERMES_READY=1 OMARCHY_TEST_HERMES_SKIN=ares run_hook --activate 2>"$test_tmp/stderr"
|
||||
[[ -f $skin ]] || fail "a chosen skin still gets the Omarchy skin published beside it"
|
||||
! grep -q 'config set' "$hermes_calls" || fail "a skin the user chose is not replaced" "$(cat "$hermes_calls")"
|
||||
grep -q "'ares' skin" "$test_tmp/stderr" || fail "leaving a chosen skin is reported"
|
||||
pass "--activate leaves a skin the user chose in Hermes"
|
||||
|
||||
reset_home --set-up
|
||||
OMARCHY_TEST_HERMES_READY=1 OMARCHY_TEST_HERMES_GET_FAILS=1 run_hook --activate 2>"$test_tmp/stderr" || fail "a Hermes that does not answer is not an error"
|
||||
! grep -q 'config set' "$hermes_calls" || fail "no answer from Hermes is not taken for the default" "$(cat "$hermes_calls")"
|
||||
grep -q 'did not say' "$test_tmp/stderr" || fail "an unanswered question is reported"
|
||||
pass "--activate does not switch a Hermes that did not say which skin it is on"
|
||||
|
||||
reset_home --set-up
|
||||
mkdir -p "$hermes_home/hermes-agent"
|
||||
touch "$hermes_home/hermes-agent/.hermes-bootstrap-complete"
|
||||
OMARCHY_TEST_HERMES_READY=1 OMARCHY_TEST_HERMES_SET_FAILS=1 run_hook --wait 2>"$test_tmp/stderr" || fail "a Hermes that refuses the write is not an error"
|
||||
grep -q 'refused' "$test_tmp/stderr" || fail "a refused write is reported"
|
||||
! grep -q 'sleep 60' "$hermes_calls" || fail "nothing is announced for a write that did not happen" "$(cat "$hermes_calls")"
|
||||
pass "--activate reports a Hermes that refused the skin and moves on"
|
||||
|
||||
# -- a skin the current theme has not rendered yet -------------------------------
|
||||
|
||||
reset_home --set-up
|
||||
rm "$test_home/.local/state/omarchy/current/theme/hermes.yaml"
|
||||
run_hook
|
||||
[[ ! -e $hermes_home/skins && ! -s $hermes_calls ]] ||
|
||||
fail "a theme switch without a rendered skin publishes nothing" "$(cat "$hermes_calls")"
|
||||
pass "a theme switch has nothing to do without a rendered skin"
|
||||
|
||||
reset_home --set-up
|
||||
rm "$test_home/.local/state/omarchy/current/theme/hermes.yaml"
|
||||
if run_hook --activate 2>"$test_tmp/stderr"; then
|
||||
fail "--activate fails when no theme has been selected"
|
||||
fi
|
||||
grep -q 'Select an Omarchy theme' "$test_tmp/stderr" || fail "a missing theme is reported"
|
||||
pass "--activate fails without a current theme to render the skin from"
|
||||
|
||||
reset_home --set-up
|
||||
rm "$test_home/.local/state/omarchy/current/theme/hermes.yaml"
|
||||
echo tokyo-night >"$test_home/.local/state/omarchy/current/theme.name"
|
||||
OMARCHY_TEST_HERMES_READY=1 run_hook --activate 2>/dev/null
|
||||
[[ $(head -1 "$hermes_calls") == "refresh" ]] || fail "a theme applied before the template existed is re-staged" "$(cat "$hermes_calls")"
|
||||
[[ -f $skin ]] || fail "the freshly rendered skin is published"
|
||||
grep -Fxq 'config set display.skin omarchy' "$hermes_calls" || fail "the freshly rendered skin is activated" "$(cat "$hermes_calls")"
|
||||
pass "--activate renders the skin for a theme that predates it"
|
||||
|
||||
# -- waiting for the desktop app's first launch ----------------------------------
|
||||
|
||||
reset_home --set-up
|
||||
mkdir -p "$hermes_home/hermes-agent"
|
||||
touch "$hermes_home/hermes-agent/.hermes-bootstrap-complete"
|
||||
OMARCHY_TEST_HERMES_READY=1 run_hook --wait 2>/dev/null
|
||||
[[ $(cat "$hermes_calls") == $'check\nconfig get display.skin\nconfig set display.skin omarchy\nsleep 60' ]] ||
|
||||
fail "--wait activates as soon as the runtime marker is there, then republishes after the gateway is up" "$(cat "$hermes_calls")"
|
||||
[[ -f $skin ]] || fail "--wait publishes the skin"
|
||||
pass "--wait activates once the desktop app has built its runtime"
|
||||
|
||||
reset_home --set-up
|
||||
mkdir -p "$hermes_home/hermes-agent" "$hermes_home/profiles/work"
|
||||
touch "$hermes_home/hermes-agent/.hermes-bootstrap-complete"
|
||||
OMARCHY_TEST_HERMES_READY=1 OMARCHY_TEST_SWAP_SOURCE=$'name: omarchy\ncolors:\n background: "#1a1b26"\nbanner_logo: "evil"' run_hook --wait 2>/dev/null
|
||||
! grep -q 'banner_logo' "$skin" || fail "the republish after the gateway is up does not publish a theme that changed underneath into something rejected"
|
||||
pass "--wait checks the skin again before republishing it"
|
||||
|
||||
reset_home --set-up
|
||||
mkdir -p "$hermes_home/hermes-agent" "$hermes_home/profiles/work"
|
||||
touch "$hermes_home/hermes-agent/.hermes-bootstrap-complete"
|
||||
OMARCHY_TEST_HERMES_READY=1 OMARCHY_TEST_DROP_SKINS=1 run_hook --wait 2>/dev/null
|
||||
[[ -f $skin && -f $hermes_home/profiles/work/skins/omarchy.yaml ]] ||
|
||||
fail "the republish after the gateway is up writes the skin to the Hermes home and every profile again" "$(ls -R "$hermes_home")"
|
||||
pass "--wait republishes the skin everywhere once the gateway is up"
|
||||
|
||||
reset_home
|
||||
OMARCHY_TEST_HERMES_READY=1 run_hook --wait 2>"$test_tmp/stderr"
|
||||
[[ $(head -1 "$hermes_calls") == "sleep 10" && ! -e $hermes_home/skins ]] ||
|
||||
fail "--wait polls for the runtime instead of running Hermes" "$(head -3 "$hermes_calls")"
|
||||
[[ $(grep -c 'sleep 10' "$hermes_calls") == 180 ]] || fail "--wait gives up after 30 minutes" "$(grep -c 'sleep 10' "$hermes_calls")"
|
||||
grep -q 'did not finish setting up' "$test_tmp/stderr" || fail "giving up is reported"
|
||||
pass "--wait polls until the desktop app has built its runtime and gives up in time"
|
||||
@@ -207,7 +207,7 @@ pass "a theme name cannot climb out of the theme directories"
|
||||
# generates. Every generated theme file is either denied to an installed theme or
|
||||
# recorded here as carrying colour, so a new template fails until it is placed.
|
||||
denied=(alacritty.toml foot.ini ghostty.conf kitty.conf gum_env.lua hyprland.lua neovim.lua vscode.json)
|
||||
colour_only=(btop.theme chromium.theme claude.json helix.toml hyprland-preview-share-picker.css keyboard.rgb obsidian.css pi.json shell.toml vscode-theme.json)
|
||||
colour_only=(btop.theme chromium.theme claude.json helix.toml hermes.yaml hyprland-preview-share-picker.css keyboard.rgb obsidian.css pi.json shell.toml vscode-theme.json)
|
||||
|
||||
for tpl in "$ROOT"/default/themed/*.tpl; do
|
||||
generated=$(basename "$tpl" .tpl)
|
||||
|
||||
Reference in new issue
Block a user