From 3208fe09ae41e11ddd43e3b38cde3d3e42fce83d Mon Sep 17 00:00:00 2001 From: Spencer Bull Date: Mon, 21 Sep 2026 19:33:42 -0500 Subject: [PATCH 1/2] Let the Elsewhen migration carry on without a shell to ask A bare `omarchy-shell shell rescanPlugins` exits 1 when no shell answers on the caller's tree, and omarchy-migrate runs migrations under `set -e`, so an update run from a TTY or over ssh, or one whose shell had gone down while the package replaced its QML, died at "omarchy-shell is not running" before its post-update hooks, shell restart and reboot prompt, and re-failed the same way every time until a shell could be asked. The rescan is now best-effort, like `omarchy-bar put` already is and like #11117 makes the plugin commands' closing rescan: the update restarts the shell once the migrations are through, and a session without one has no bar to place on. An unknown widget on a live shell still leaves the migration pending. --- docs/omarchy-shell.md | 2 +- migrations/1789581661.sh | 4 +- .../elsewhen-default-migration-test.sh | 41 +++++++++++++++---- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/docs/omarchy-shell.md b/docs/omarchy-shell.md index 0b9601ad..e90282d5 100644 --- a/docs/omarchy-shell.md +++ b/docs/omarchy-shell.md @@ -100,7 +100,7 @@ The lower-level IPC methods remain available through `omarchy-shell shell ...`. ## Elsewhen -Elsewhen (`omacom.elsewhen`) ships in the `elsewhen` package at `/usr/share/omarchy/shell/plugins/omacom.elsewhen`, where the shell discovers it automatically. New installs place it immediately before the clock; the migration uses `omarchy bar put omacom.elsewhen --before omarchy.clock`, which preserves an existing placement and uses Elsewhen's normal right-side placement if the clock is absent. Existing plugin directories and symlinks are left intact. The normal update flow restarts the shell after migrations; the migration does not interrupt plugin loading with an immediate restart. +Elsewhen (`omacom.elsewhen`) ships in the `elsewhen` package at `/usr/share/omarchy/shell/plugins/omacom.elsewhen`, where the shell discovers it automatically. New installs place it immediately before the clock; the migration uses `omarchy bar put omacom.elsewhen --before omarchy.clock`, which preserves an existing placement and uses Elsewhen's normal right-side placement if the clock is absent. Existing plugin directories and symlinks are left intact. The normal update flow restarts the shell after migrations; the migration does not interrupt plugin loading with an immediate restart. With no shell to ask, as in an update from a TTY, the migration installs the package and skips the placement rather than failing the update; `omarchy bar put omacom.elsewhen --before omarchy.clock` places the widget later. ## IPC diff --git a/migrations/1789581661.sh b/migrations/1789581661.sh index ad0dfd5e..cc881c26 100644 --- a/migrations/1789581661.sh +++ b/migrations/1789581661.sh @@ -10,5 +10,7 @@ if [[ ! $OMARCHY_PATH -ef /usr/share/omarchy && -d $packaged_plugin && ! -e $use ln -s "$packaged_plugin" "$user_plugin" fi -omarchy-shell shell rescanPlugins +# Best-effort, like the put below: an update whose shell cannot be asked still +# finishes, and restarts the shell once the migrations are through. +omarchy-shell -q shell rescanPlugins omarchy-bar put omacom.elsewhen --before omarchy.clock diff --git a/test/shell.d/elsewhen-default-migration-test.sh b/test/shell.d/elsewhen-default-migration-test.sh index 0c2565f4..2775e1bb 100644 --- a/test/shell.d/elsewhen-default-migration-test.sh +++ b/test/shell.d/elsewhen-default-migration-test.sh @@ -18,7 +18,17 @@ SH cat >"$test_dir/bin/omarchy-shell" <<'SH' #!/bin/bash printf '%s\n' "$*" >>"$CALL_LOG" -[[ $2 != "rescanPlugins" ]] || exit "${SCAN_STATUS:-0}" +quiet=0 +if [[ $1 == "-q" ]]; then + quiet=1 + shift +fi +if [[ ${SHELL_ABSENT:-0} == 1 ]]; then + (( quiet )) && exit 0 + echo "omarchy-shell is not running" >&2 + exit 1 +fi +[[ $2 != "rescanPlugins" ]] || exit 0 printf '%s\n' "${TEST_PUT_RESULT:-ok}" SH cat >"$test_dir/bin/omarchy-restart-shell" <<'SH' @@ -50,7 +60,7 @@ run_migration [[ ! -e $ROOT/config/omarchy/plugins/omacom.elsewhen && ! -L $ROOT/config/omarchy/plugins/omacom.elsewhen ]] || fail "fresh installs do not ship a user plugin link" pass "migration and fresh installs rely on the packaged plugin directory" -expected=$'package elsewhen\nshell rescanPlugins\nshell putBarWidget omacom.elsewhen {"before":"omarchy.clock"}' +expected=$'package elsewhen\n-q shell rescanPlugins\nshell putBarWidget omacom.elsewhen {"before":"omarchy.clock"}' [[ $(cat "$CALL_LOG") == "$expected" ]] || fail "install, scan and placement run in order" "$(cat "$CALL_LOG")" pass "real bar helper enables and places before the clock without restarting during reload" @@ -80,10 +90,23 @@ run_migration "$ROOT" [[ $(readlink "$plugin") == "$test_dir/custom-plugin" ]] || fail "existing symlink is preserved" pass "existing symlink is preserved, including a missing target" -for failure in 'SCAN_STATUS=1' 'TEST_PUT_RESULT=unknown'; do - if env "$failure" HOME="$test_dir/home" OMARCHY_PATH="$ROOT" PATH="$test_dir/bin:$ROOT/bin:$PATH" \ - bash -euo pipefail "$test_dir/migration.sh" >"$test_dir/output" 2>&1; then - fail "$failure must leave the migration pending" - fi - pass "$failure leaves the migration pending" -done + +if env TEST_PUT_RESULT=unknown HOME="$test_dir/home" OMARCHY_PATH="$ROOT" PATH="$test_dir/bin:$ROOT/bin:$PATH" \ + bash -euo pipefail "$test_dir/migration.sh" >"$test_dir/output" 2>&1; then + fail "an unknown widget must leave the migration pending" +fi +pass "an unknown widget leaves the migration pending" + +# An update with no shell to ask, from a TTY or with the shell down, still +# finishes: the package and link land, the placement is skipped, and the update +# restarts the shell afterwards. +rm "$plugin" +: >"$CALL_LOG" +if ! env SHELL_ABSENT=1 OMARCHY_SHELL_ABSENT_ATTEMPTS=1 HOME="$test_dir/home" OMARCHY_PATH="$ROOT" PATH="$test_dir/bin:$ROOT/bin:$PATH" \ + bash -euo pipefail "$test_dir/migration.sh" >"$test_dir/output" 2>&1; then + fail "an absent shell must not fail the migration" "$(cat "$test_dir/output")" +fi +grep -q "omacom.elsewhen was not put on the bar" "$test_dir/output" || fail "an absent shell is reported" "$(cat "$test_dir/output")" +[[ $(readlink "$plugin") == "$test_dir/packaged/shell/plugins/omacom.elsewhen" ]] || fail "the package and plugin link land without a shell" +[[ $(cat "$CALL_LOG") == "$expected" ]] || fail "the rescan is best-effort and the put is still asked" "$(cat "$CALL_LOG")" +pass "an absent shell leaves the update running with the package and link in place" From aa5c4beb50af71e387890f3169dbcdebe5ca4129 Mon Sep 17 00:00:00 2001 From: Spencer Bull Date: Mon, 21 Sep 2026 19:33:42 -0500 Subject: [PATCH 2/2] Re-point the Elsewhen link stranded by the package's move The 2026-09-19 revision of this migration, and a symlink then shipped under config/, pointed every install's ~/.config/omarchy/plugins/omacom.elsewhen at /usr/share/omarchy/plugins/omacom.elsewhen. elsewhen 1.0.0-2 moved to shell/plugins, so the link dangles, the shell lists the plugin as enabled but never loads it, and the bar carries an empty slot. The migration kept any existing link, resolving or not, so a rerun could not repair it. A link into /usr/share/omarchy that no longer resolves is Omarchy's own and is re-pointed; a link the user made is still left alone. --- migrations/1789581661.sh | 9 ++++++++- test/shell.d/elsewhen-default-migration-test.sh | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/migrations/1789581661.sh b/migrations/1789581661.sh index cc881c26..8dce1c3e 100644 --- a/migrations/1789581661.sh +++ b/migrations/1789581661.sh @@ -2,9 +2,16 @@ echo "Install Elsewhen, the world clock plugin" omarchy-pkg-add elsewhen -# Dev checkouts do not contain plugins installed by system packages. packaged_plugin="/usr/share/omarchy/shell/plugins/omacom.elsewhen" user_plugin="$HOME/.config/omarchy/plugins/omacom.elsewhen" + +# The package moved from plugins/ to shell/plugins/ once, stranding the link an +# earlier run made to the old path. A link the user made is left alone. +if [[ -L $user_plugin && ! -e $user_plugin && $(readlink "$user_plugin") == /usr/share/omarchy/* && -d $packaged_plugin ]]; then + ln -sfn "$packaged_plugin" "$user_plugin" +fi + +# Dev checkouts do not contain plugins installed by system packages. if [[ ! $OMARCHY_PATH -ef /usr/share/omarchy && -d $packaged_plugin && ! -e $user_plugin && ! -L $user_plugin ]]; then mkdir -p "${user_plugin%/*}" ln -s "$packaged_plugin" "$user_plugin" diff --git a/test/shell.d/elsewhen-default-migration-test.sh b/test/shell.d/elsewhen-default-migration-test.sh index 2775e1bb..78dc69a9 100644 --- a/test/shell.d/elsewhen-default-migration-test.sh +++ b/test/shell.d/elsewhen-default-migration-test.sh @@ -90,6 +90,16 @@ run_migration "$ROOT" [[ $(readlink "$plugin") == "$test_dir/custom-plugin" ]] || fail "existing symlink is preserved" pass "existing symlink is preserved, including a missing target" +# An earlier revision, and a symlink once shipped under config/, pointed every +# install at the package's old path. +for tree in "$ROOT" "$test_dir/packaged"; do + ln -sfn "$test_dir/packaged/plugins/omacom.elsewhen" "$plugin" + run_migration "$tree" + [[ $(readlink "$plugin") == "$test_dir/packaged/shell/plugins/omacom.elsewhen" ]] || + fail "a stranded link to the package's old path is re-pointed (OMARCHY_PATH=$tree)" "$(readlink "$plugin")" +done +pass "a stranded link to the package's old path is re-pointed on every install" +rm "$plugin" if env TEST_PUT_RESULT=unknown HOME="$test_dir/home" OMARCHY_PATH="$ROOT" PATH="$test_dir/bin:$ROOT/bin:$PATH" \ bash -euo pipefail "$test_dir/migration.sh" >"$test_dir/output" 2>&1; then