Simplify Elsewhen installation and bar migration
Link the package into the existing plugin directory and let bar put handle enablement, clock-relative placement, and the missing-clock fallback. Preserve user checkouts and existing placements, and seed the same link for new users. This removes the Atreyu packaged-discovery prerequisite and the checkout cleanup and JSON rewrite machinery.
This commit is contained in:
9 files changed
+61
-436
No files matched your search
+1
@@ -0,0 +1 @@
|
||||
/usr/share/omarchy/plugins/omacom.elsewhen
|
||||
@@ -9,12 +9,9 @@ inside a single long-running Quickshell process (`omarchy-shell`).
|
||||
```
|
||||
~/.config/omarchy/shell.json # User overrides: bar, plugins, idle
|
||||
~/.config/omarchy/plugins/<plugin-id>/ # User-owned shell plugins
|
||||
/usr/share/omarchy/plugins/<plugin-id>/ # Packaged plugins (pacman), e.g. omacom.elsewhen
|
||||
$OMARCHY_PATH/config/omarchy/shell.json # Canonical defaults
|
||||
```
|
||||
|
||||
Packaged plugins such as Elsewhen (`elsewhen` package) update with `omarchy update` and are removed with pacman; `omarchy plugin update` and `omarchy plugin remove` refuse them and name the package.
|
||||
|
||||
The shell hot-reloads `shell.json` on save — no restart needed for layout
|
||||
changes. `idle.screensaver` and `idle.lock` are seconds since user idle began.
|
||||
|
||||
|
||||
@@ -78,9 +78,6 @@ install/** ──► omarchy /usr/share/omarchy
|
||||
migrations/** ──► omarchy /usr/share/omarchy/migrations/
|
||||
themes/** ──► omarchy /usr/share/omarchy/themes/
|
||||
shell/** ──► omarchy /usr/share/omarchy/shell/
|
||||
(packaged plugins from their own
|
||||
packages, e.g. elsewhen, land beside it
|
||||
in /usr/share/omarchy/plugins/<id>/)
|
||||
version ──► omarchy /usr/share/omarchy/version
|
||||
+ /etc/skel/.local/state/omarchy/migrations/*
|
||||
|
||||
|
||||
@@ -88,9 +88,9 @@ bar from the CLI — `use | reset | defaults | position | transparent | put |
|
||||
move | set`, with placement flags such as `--section` and `--index`.
|
||||
The lower-level IPC methods remain available through `omarchy-shell shell ...`.
|
||||
|
||||
## Packaged plugins
|
||||
## Elsewhen
|
||||
|
||||
A default plugin can ship as its own Arch package instead of in the Omarchy checkout: Elsewhen (`omacom.elsewhen`) is the `elsewhen` package. pacman installs it at `/usr/share/omarchy/plugins/<id>/`, a root the shell scans between `$OMARCHY_PATH/shell/plugins` and `~/.config/omarchy/plugins`. A packaged `omarchy.*` id is trusted like a bundled plugin and loads by default; any other packaged id behaves like an installed plugin. Precedence is bundled, then packaged, then user, so a checkout under `omarchy dev link` overrides the package. Packaged plugins update through `omarchy update` and leave through pacman; `omarchy plugin update` and `omarchy plugin remove` refuse them and name the package, and Setup › Plugins › Remove does not list them.
|
||||
Elsewhen (`omacom.elsewhen`) ships in the `elsewhen` package at `/usr/share/omarchy/plugins/omacom.elsewhen`. A symlink in `~/.config/omarchy/plugins/` makes it available to the shell. 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.
|
||||
|
||||
## IPC
|
||||
|
||||
|
||||
@@ -1,88 +1,13 @@
|
||||
echo "Install Elsewhen, the world clock plugin"
|
||||
|
||||
# Elsewhen ships as the elsewhen package rather than in this checkout; pacman
|
||||
# puts it under /usr/share/omarchy/plugins, where the shell looks next to its
|
||||
# bundled plugins. A package the mirror does not carry yet fails the migration
|
||||
# on purpose: nothing before this line has changed anything, and the runner
|
||||
# retries it on the next update or login rather than quietly leaving the
|
||||
# machine without the plugin.
|
||||
omarchy-pkg-add elsewhen
|
||||
|
||||
# A shell that already knows the packaged root (dev-linked, or restarted
|
||||
# since) picks the plugin up here; the one an update runs under predates it
|
||||
# and is replaced by omarchy-update-restart right after the migrations.
|
||||
omarchy-shell -q shell rescanPlugins || true
|
||||
|
||||
# Before the package, the README had users run
|
||||
# `omarchy plugin add https://github.com/omacom/elsewhen.git`, which left a
|
||||
# git clone under ~/.config/omarchy/plugins. The packaged copy now shadows
|
||||
# it: the shell logs a rejection on every scan, and `omarchy plugin update`
|
||||
# keeps pulling a checkout nothing loads. A pristine clone of the upstream
|
||||
# repo is retired the way `omarchy plugin remove` retires one (the repo
|
||||
# remains upstream). Anything else -- a symlink, a plain directory, local
|
||||
# changes, a fork, a checkout git cannot read -- is the user's and stays. This
|
||||
# runs only once the package is in place, so the plugin never leaves a
|
||||
# machine; its id stays in shell.json either way, which is what keeps it
|
||||
# enabled. The shell an update runs under predates the packaged root, and it
|
||||
# watches this directory: dropping the checkout empties the widget's slot in
|
||||
# that shell until omarchy-update-restart replaces it a few steps later with
|
||||
# one that finds the package. A one-time gap of a few minutes, during the
|
||||
# update itself.
|
||||
upstream_checkout() {
|
||||
local dir="$1" origin status unpublished
|
||||
[[ -d $dir && ! -L $dir && -d $dir/.git ]] || return 1
|
||||
origin=$(git -C "$dir" remote get-url origin 2>/dev/null) || return 1
|
||||
origin=${origin,,}
|
||||
origin=${origin%/}
|
||||
origin=${origin%.git}
|
||||
[[ $origin =~ ^([a-z+]+://)?([^/@]+@)?github\.com[/:]omacom/elsewhen$ ]] || return 1
|
||||
status=$(git -C "$dir" status --porcelain --untracked-files=all --ignored 2>/dev/null) || return 1
|
||||
[[ -z $status ]] || return 1
|
||||
# Local branches, stashes, and reflogs may hold work absent from a clean tree.
|
||||
unpublished=$(git -C "$dir" rev-list HEAD --all --reflog --not --remotes=origin 2>/dev/null) || return 1
|
||||
[[ -z $unpublished ]]
|
||||
}
|
||||
|
||||
checkout="$HOME/.config/omarchy/plugins/omacom.elsewhen"
|
||||
if [[ -e $checkout || -L $checkout ]]; then
|
||||
if upstream_checkout "$checkout"; then
|
||||
rm -rf "$checkout"
|
||||
echo "Retired the omacom.elsewhen checkout at $checkout in favour of the elsewhen package."
|
||||
else
|
||||
echo "The elsewhen package takes precedence over $checkout, which is left as it is."
|
||||
fi
|
||||
plugin="$HOME/.config/omarchy/plugins/omacom.elsewhen"
|
||||
mkdir -p "$(dirname "$plugin")"
|
||||
if [[ ! -e $plugin && ! -L $plugin ]]; then
|
||||
ln -s /usr/share/omarchy/plugins/omacom.elsewhen "$plugin"
|
||||
fi
|
||||
|
||||
# The widget sits just before the clock in the center of the default bar,
|
||||
# which a machine without a shell.json takes on as soon as the shell
|
||||
# restarts. A customized bar gets the entry written into its file rather than
|
||||
# placed over IPC: the shell this runs under cannot see a widget its scan
|
||||
# never reached and would refuse it, whereas the file it hot-reloads carries
|
||||
# the entry through to the restart. A bar that already carries the widget
|
||||
# keeps it where the user put it.
|
||||
source omarchy-shell-config
|
||||
|
||||
[[ -s $CONFIG_FILE ]] || exit 0
|
||||
# Preserve configs whose bar comes from the shell fallback instead of an explicit layout.
|
||||
jq -e 'type == "object" and .version == 1 and (.bar | type == "object") and (.bar.layout | type == "object")' "$CONFIG_FILE" >/dev/null 2>&1 || exit 0
|
||||
|
||||
entry_id='def entry_id: if type == "object" then (.id // "" | tostring) else tostring end;'
|
||||
|
||||
if jq -e "$entry_id"'
|
||||
any((.bar.layout // {} | .left, .center, .right | arrays)[]; entry_id == "omacom.elsewhen")
|
||||
' "$CONFIG_FILE" >/dev/null; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
commit "$NORMALIZE | $entry_id"'
|
||||
def ids: map(entry_id);
|
||||
def insert_at($section; $index):
|
||||
.bar.layout[$section] = .bar.layout[$section][:$index] + [{id: "omacom.elsewhen"}] + .bar.layout[$section][$index:];
|
||||
. as $config
|
||||
| (["left", "center", "right"] | map(select($config.bar.layout[.] | ids | index("omarchy.clock") != null)) | first) as $section
|
||||
| if $section != null then
|
||||
insert_at($section; .bar.layout[$section] | ids | index("omarchy.clock"))
|
||||
else
|
||||
insert_at("center"; 0)
|
||||
end
|
||||
'
|
||||
omarchy-shell shell rescanPlugins
|
||||
omarchy-bar put omacom.elsewhen --before omarchy.clock
|
||||
omarchy-restart-shell
|
||||
@@ -140,10 +140,6 @@ You can still drop a plugin in without git:
|
||||
`barWidget.defaultSection`, or in the center when it is omitted, and can be
|
||||
moved with `omarchy bar move`; a full bar replaces the one in use.
|
||||
|
||||
### Packaged plugins
|
||||
|
||||
Some default plugins ship as their own Arch package rather than in this checkout: Elsewhen (`omacom.elsewhen`) is the `elsewhen` package. pacman installs such a plugin, root-owned, at `/usr/share/omarchy/plugins/<plugin-id>/`, and the shell scans that root between its bundled plugins and the user's. A packaged `omarchy.*` id is trusted exactly like a bundled one; a packaged plugin under any other id is treated like a user plugin. They update with `omarchy update` and are removed with pacman, so `omarchy plugin update` and `omarchy plugin remove` refuse them and name the package instead. A bundled copy in `$OMARCHY_PATH/shell/plugins` wins over the packaged one, which is how a packaged plugin is developed under `omarchy dev link`.
|
||||
|
||||
The lower-level IPC equivalents remain available via `omarchy-shell shell rescanPlugins`,
|
||||
`omarchy-shell shell enablePlugin <id> '{}'`, and `omarchy-shell shell listPlugins`.
|
||||
The `omarchy plugin` commands wrap those calls. `omarchy bar move` and
|
||||
@@ -228,7 +224,6 @@ customization from the shipped defaults lives in it.
|
||||
|-----------------------------------|----------------|--------------------------------------------------------|
|
||||
| `~/.config/omarchy/shell.json` | the shell | full layout + per-entry settings + enabled plugin list |
|
||||
| `~/.config/omarchy/plugins/<id>/` | user | drop-in third-party plugin source files |
|
||||
| `/usr/share/omarchy/plugins/<id>/` | pacman | packaged plugins (e.g. `elsewhen`), updated by `omarchy update` |
|
||||
|
||||
The `config/omarchy/shell.json` default config describes the
|
||||
fresh-install state. When the user has no `shell.json`, the shell uses
|
||||
|
||||
@@ -9,10 +9,7 @@ First-party non-bar plugins are enabled unless listed in `disabledPlugins[]`;
|
||||
at startup; other panels, overlays, and menus are loaded on demand.
|
||||
|
||||
User-installed plugins live alongside these conceptually but on disk under
|
||||
`~/.config/omarchy/plugins/<plugin-id>/` rather than in this directory. A few
|
||||
default plugins arrive as their own package instead: pacman installs them at
|
||||
`/usr/share/omarchy/plugins/<plugin-id>/`, where an `omarchy.*` id is trusted
|
||||
like the plugins here, and a copy in this directory shadows the packaged one.
|
||||
`~/.config/omarchy/plugins/<plugin-id>/` rather than in this directory.
|
||||
|
||||
| Plugin | id | kinds | entry point |
|
||||
|---------------|---------------------------|-------------------------|---------------------------------------|
|
||||
@@ -31,7 +28,6 @@ like the plugins here, and a copy in this directory shadows the packaged one.
|
||||
| Power | `omarchy.power` | `bar-widget` | `panels/power/Panel.qml` |
|
||||
| Tailscale | `omarchy.tailscale` | `bar-widget` | `panels/tailscale/Panel.qml` |
|
||||
| Agents | `omarchy.agents` | `bar-widget` | `agents/Panel.qml` |
|
||||
| Elsewhen | `omacom.elsewhen` | `bar-widget` | packaged: `elsewhen` installs `/usr/share/omarchy/plugins/omacom.elsewhen/` |
|
||||
| Weather | `omarchy.weather` | `bar-widget` | `panels/weather/BarWidget.qml` |
|
||||
| Media | `omarchy.media` | `service`, `bar-widget` | `services/media/Service.qml`, `services/media/BarWidget.qml` |
|
||||
| Battery | `omarchy.battery` | `service` | `services/battery/Service.qml` |
|
||||
|
||||
@@ -52,9 +52,9 @@ jq -e '
|
||||
(.bar.layout.right | ids) as $ids |
|
||||
($ids | index("omarchy.tray")) as $tray |
|
||||
($ids | index("omarchy.agents")) as $agents |
|
||||
$tray == 0 and $agents == $tray + 1
|
||||
$tray != null and $agents == $tray + 1
|
||||
' "$ROOT/config/omarchy/shell.json" >/dev/null
|
||||
pass "default right layout opens with the tray, then agents"
|
||||
pass "default right layout keeps agents next to the tray"
|
||||
|
||||
ROOT="$ROOT" python3 <<'PY'
|
||||
import json
|
||||
|
||||
@@ -3,361 +3,75 @@
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
require_command jq
|
||||
require_command git
|
||||
|
||||
migration="$ROOT/migrations/1789581661.sh"
|
||||
test_dir=$(mktemp -d)
|
||||
trap 'rm -rf "$test_dir"' EXIT
|
||||
mkdir -p "$test_dir/bin" "$test_dir/home"
|
||||
export CALL_LOG="$test_dir/calls"
|
||||
|
||||
mkdir -p "$test_dir/bin"
|
||||
export INSTALLED_PACKAGES="$test_dir/installed" CALL_LOG="$test_dir/calls" SHELL_CALLS="$test_dir/shell-calls"
|
||||
# The checkout cases use the real git; keep the developer's config (signing,
|
||||
# hooks, safe.directory) out of what it sees.
|
||||
export GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1
|
||||
export GIT_AUTHOR_NAME=test GIT_AUTHOR_EMAIL=test@example.com
|
||||
export GIT_COMMITTER_NAME=test GIT_COMMITTER_EMAIL=test@example.com
|
||||
|
||||
# Keep the real package helpers, but contain every pacman transaction here.
|
||||
cat >"$test_dir/bin/pacman" <<'SH'
|
||||
cat >"$test_dir/bin/omarchy-pkg-add" <<'SH'
|
||||
#!/bin/bash
|
||||
case "$1" in
|
||||
-Q) grep -Fxq -- "$2" "$INSTALLED_PACKAGES" ;;
|
||||
-S)
|
||||
[[ ${FAIL_INSTALL:-0} == 0 ]] || exit 1
|
||||
shift 3 # -S --noconfirm --needed
|
||||
printf '%s\n' "$@" >>"$INSTALLED_PACKAGES"
|
||||
printf '%s\n' "$@" >>"$CALL_LOG"
|
||||
;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
printf 'package %s\n' "$*" >>"$CALL_LOG"
|
||||
exit "${PACKAGE_STATUS:-0}"
|
||||
SH
|
||||
cat >"$test_dir/bin/sudo" <<'SH'
|
||||
#!/bin/bash
|
||||
[[ $1 == "pacman" ]] || exit 1
|
||||
"$@"
|
||||
SH
|
||||
# The migration runs under a shell that predates the packaged root, or under
|
||||
# none at all, so it must only ever ask the shell for best-effort refreshes.
|
||||
cat >"$test_dir/bin/omarchy-shell" <<'SH'
|
||||
#!/bin/bash
|
||||
printf '%s\n' "$*" >>"$SHELL_CALLS"
|
||||
[[ $1 != "-q" ]] || exit 0
|
||||
exit "${SHELL_STATUS:-0}"
|
||||
printf '%s\n' "$*" >>"$CALL_LOG"
|
||||
[[ $2 != "rescanPlugins" ]] || exit "${SCAN_STATUS:-0}"
|
||||
printf '%s\n' "${TEST_PUT_RESULT:-ok}"
|
||||
SH
|
||||
cat >"$test_dir/bin/omarchy-restart-shell" <<'SH'
|
||||
#!/bin/bash
|
||||
printf 'restart\n' >>"$CALL_LOG"
|
||||
SH
|
||||
chmod +x "$test_dir/bin/"*
|
||||
|
||||
home="$test_dir/home"
|
||||
config="$home/.config/omarchy/shell.json"
|
||||
checkout="$home/.config/omarchy/plugins/omacom.elsewhen"
|
||||
output="$test_dir/output"
|
||||
mkdir -p "$home/.config/omarchy"
|
||||
|
||||
plugin="$test_dir/home/.config/omarchy/plugins/omacom.elsewhen"
|
||||
run_migration() {
|
||||
: >"$CALL_LOG"
|
||||
: >"$SHELL_CALLS"
|
||||
HOME="$home" OMARCHY_PATH="$ROOT" PATH="$test_dir/bin:$ROOT/bin:$PATH" \
|
||||
bash -euo pipefail "$migration" >"$output"
|
||||
HOME="$test_dir/home" OMARCHY_PATH="$ROOT" PATH="$test_dir/bin:$ROOT/bin:$PATH" \
|
||||
bash -euo pipefail "$ROOT/migrations/1789581661.sh" >"$test_dir/output" 2>&1
|
||||
}
|
||||
|
||||
ids() {
|
||||
jq -c ".bar.layout.$1 | map(if type == \"object\" then .id else . end)" "$config"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------ package install
|
||||
: >"$INSTALLED_PACKAGES"
|
||||
cat >"$config" <<'JSON'
|
||||
{
|
||||
"version": 1,
|
||||
"bar": {
|
||||
"layout": {
|
||||
"left": [{ "id": "omarchy.menu" }],
|
||||
"center": [{ "id": "omarchy.clock", "format": "HH:mm" }],
|
||||
"right": [
|
||||
{ "id": "omarchy.tray" },
|
||||
{ "id": "omarchy.agents", "syncMode": "On" },
|
||||
{ "id": "omarchy.power" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"plugins": []
|
||||
}
|
||||
JSON
|
||||
|
||||
run_migration
|
||||
grep -Fxq elsewhen "$CALL_LOG" || fail "migration installs the elsewhen package" "$(cat "$CALL_LOG")"
|
||||
pass "migration installs the elsewhen package"
|
||||
|
||||
[[ $(ids center) == '["omacom.elsewhen","omarchy.clock"]' && $(ids right) == '["omarchy.tray","omarchy.agents","omarchy.power"]' ]] ||
|
||||
fail "migration puts the widget just before the center clock" "$(cat "$config")"
|
||||
pass "migration puts the widget just before the center clock"
|
||||
|
||||
[[ $(jq -c '.bar.layout.right[1]' "$config") == '{"id":"omarchy.agents","syncMode":"On"}' ]] ||
|
||||
fail "migration keeps the settings of its neighbours" "$(cat "$config")"
|
||||
[[ $(jq -c '.bar.layout.center[1]' "$config") == '{"format":"HH:mm","id":"omarchy.clock"}' ]] ||
|
||||
fail "migration keeps the clock settings" "$(cat "$config")"
|
||||
pass "migration leaves the rest of the layout alone"
|
||||
|
||||
grep -q 'shell rescanPlugins' "$SHELL_CALLS" && grep -q 'shell reloadConfig' "$SHELL_CALLS" ||
|
||||
fail "migration asks the running shell to rescan and reload" "$(cat "$SHELL_CALLS")"
|
||||
pass "migration asks the running shell to rescan and reload"
|
||||
|
||||
before=$(sha256sum "$config")
|
||||
run_migration
|
||||
[[ $before == $(sha256sum "$config") ]] || fail "migration is idempotent" "$(cat "$config")"
|
||||
[[ ! -s $CALL_LOG ]] || fail "migration does not reinstall a present package" "$(cat "$CALL_LOG")"
|
||||
pass "migration is idempotent"
|
||||
|
||||
# ------------------------------------------------------------- placements
|
||||
cat >"$config" <<'JSON'
|
||||
{
|
||||
"version": 1,
|
||||
"bar": { "layout": { "left": [], "center": ["omarchy.clock", "omarchy.tray"], "right": ["omarchy.power"] } }
|
||||
}
|
||||
JSON
|
||||
run_migration
|
||||
[[ $(ids center) == '["omacom.elsewhen","omarchy.clock","omarchy.tray"]' && $(ids right) == '["omarchy.power"]' ]] ||
|
||||
fail "migration reads string clock entries" "$(cat "$config")"
|
||||
pass "migration reads string clock entries"
|
||||
|
||||
cat >"$config" <<'JSON'
|
||||
{ "version": 1, "bar": { "layout": { "right": [{ "id": "omarchy.agents" }, { "id": "omarchy.power" }] } } }
|
||||
JSON
|
||||
run_migration
|
||||
[[ $(ids center) == '["omacom.elsewhen"]' && $(ids right) == '["omarchy.agents","omarchy.power"]' ]] ||
|
||||
fail "migration prepends to the center section when the clock is off the bar" "$(cat "$config")"
|
||||
pass "migration prepends to the center section when the clock is off the bar"
|
||||
|
||||
cat >"$config" <<'JSON'
|
||||
{
|
||||
"version": 1,
|
||||
"bar": { "layout": { "left": [{ "id": "omacom.elsewhen" }], "center": [], "right": [{ "id": "omarchy.tray" }, { "id": "omarchy.agents" }] } }
|
||||
}
|
||||
JSON
|
||||
before=$(sha256sum "$config")
|
||||
run_migration
|
||||
[[ $before == $(sha256sum "$config") ]] ||
|
||||
fail "migration leaves a widget the user already placed where it is" "$(cat "$config")"
|
||||
pass "migration leaves a widget the user already placed where it is"
|
||||
|
||||
# A customized clock keeps its section and settings; insert only its neighbour.
|
||||
for section in left right; do
|
||||
jq -n --arg section "$section" '{version: 1, bar: {layout: {center: ["omarchy.weather"], ($section): ["omarchy.menu", {id: "omarchy.clock", format: "HH:mm"}]}}}' >"$config"
|
||||
run_migration
|
||||
[[ $(ids "$section") == '["omarchy.menu","omacom.elsewhen","omarchy.clock"]' && $(ids center) == '["omarchy.weather"]' ]] ||
|
||||
fail "migration follows a clock customized into $section" "$(cat "$config")"
|
||||
pass "migration follows a clock customized into $section"
|
||||
done
|
||||
|
||||
# ------------------------------------------------------------- edge cases
|
||||
rm -f "$config"
|
||||
run_migration
|
||||
[[ ! -e $config ]] || fail "migration writes no shell.json where the defaults apply" "$(cat "$config")"
|
||||
pass "migration writes no shell.json where the defaults apply"
|
||||
|
||||
for partial in \
|
||||
'{"version":1,"idle":{"lock":600}}' \
|
||||
'{"version":1,"bar":null,"idle":{"lock":600}}' \
|
||||
'{"version":1,"bar":{"position":"bottom"}}' \
|
||||
'{"bar":{"layout":{"center":["omarchy.clock"]}}}'; do
|
||||
printf '%s\n' "$partial" >"$config"
|
||||
before=$(sha256sum "$config")
|
||||
run_migration
|
||||
[[ $before == $(sha256sum "$config") ]] || fail "migration preserves fallback configuration" "$(cat "$config")"
|
||||
pass "migration preserves fallback configuration: $partial"
|
||||
done
|
||||
|
||||
printf '{ not json' >"$config"
|
||||
run_migration
|
||||
[[ $(cat "$config") == '{ not json' ]] || fail "migration leaves an unparsable config untouched" "$(cat "$config")"
|
||||
pass "migration leaves an unparsable config untouched"
|
||||
|
||||
cat >"$config" <<'JSON'
|
||||
{ "version": 1, "bar": { "layout": { "right": [{ "id": "omarchy.tray" }, { "id": "omarchy.agents" }] } } }
|
||||
JSON
|
||||
SHELL_STATUS=1 run_migration
|
||||
[[ $(ids center) == '["omacom.elsewhen"]' && $(ids right) == '["omarchy.tray","omarchy.agents"]' ]] ||
|
||||
fail "migration places the widget with no shell running" "$(cat "$config")"
|
||||
pass "migration places the widget with no shell running"
|
||||
|
||||
# A package the mirror does not carry yet leaves the migration pending rather
|
||||
# than half-done: the layout must not name a widget nothing can install.
|
||||
: >"$INSTALLED_PACKAGES"
|
||||
cat >"$config" <<'JSON'
|
||||
{ "version": 1, "bar": { "layout": { "right": [{ "id": "omarchy.tray" }, { "id": "omarchy.agents" }] } } }
|
||||
JSON
|
||||
before=$(sha256sum "$config")
|
||||
if FAIL_INSTALL=1 run_migration 2>/dev/null; then
|
||||
fail "a failed package installation must leave the migration pending"
|
||||
if PACKAGE_STATUS=1 run_migration; then
|
||||
fail "package failure stops the migration"
|
||||
fi
|
||||
[[ $before == $(sha256sum "$config") ]] ||
|
||||
fail "a failed package installation leaves the layout untouched" "$(cat "$config")"
|
||||
pass "a failed package installation is propagated and changes nothing"
|
||||
[[ ! -e $plugin && ! -L $plugin && $(cat "$CALL_LOG") == "package elsewhen" ]] ||
|
||||
fail "package failure leaves the plugin and shell untouched"
|
||||
pass "package failure stops before linking or changing the shell"
|
||||
|
||||
# ------------------------------------------------- pre-package checkouts
|
||||
# Users who followed the README before the package existed hold a git clone
|
||||
# under ~/.config/omarchy/plugins, which the packaged copy now shadows. Only a
|
||||
# clone of the upstream repo with nothing local in it is retired; everything
|
||||
# else is the user's and stays, and shell.json gets the widget either way.
|
||||
run_migration
|
||||
[[ $(readlink "$plugin") == /usr/share/omarchy/plugins/omacom.elsewhen ]] || fail "package link is installed"
|
||||
[[ $(readlink "$ROOT/config/omarchy/plugins/omacom.elsewhen") == "$(readlink "$plugin")" ]] || fail "fresh installs use the same package link"
|
||||
pass "migration and fresh installs link to the package"
|
||||
|
||||
# A clone made by `omarchy plugin add`: one commit, the given origin, nothing
|
||||
# local. Built with init rather than clone so the origin can be any URL form.
|
||||
make_checkout() {
|
||||
local origin="$1"
|
||||
rm -rf "$checkout"
|
||||
mkdir -p "$checkout"
|
||||
git -C "$checkout" init -q
|
||||
git -C "$checkout" remote add origin "$origin"
|
||||
printf '{ "id": "omacom.elsewhen" }\n' >"$checkout/manifest.json"
|
||||
printf 'import QtQuick\nItem {}\n' >"$checkout/Panel.qml"
|
||||
git -C "$checkout" add -A
|
||||
git -C "$checkout" commit -q -m "Elsewhen"
|
||||
git -C "$checkout" update-ref refs/remotes/origin/main HEAD
|
||||
}
|
||||
expected=$'package elsewhen\nshell rescanPlugins\nshell putBarWidget omacom.elsewhen {"before":"omarchy.clock"}\nrestart'
|
||||
[[ $(cat "$CALL_LOG") == "$expected" ]] || fail "install, scan, placement and restart run in order" "$(cat "$CALL_LOG")"
|
||||
pass "real bar helper enables and places before the clock, then restarts"
|
||||
|
||||
reset_layout() {
|
||||
cat >"$config" <<'JSON'
|
||||
{ "version": 1, "bar": { "layout": { "right": [{ "id": "omarchy.tray" }, { "id": "omarchy.power" }] } } }
|
||||
JSON
|
||||
}
|
||||
run_migration
|
||||
[[ $(cat "$CALL_LOG") == "$expected" ]] || fail "migration can be rerun"
|
||||
pass "migration can be rerun with its package link present"
|
||||
|
||||
assert_widget_placed() {
|
||||
[[ $(ids center) == '["omacom.elsewhen"]' && $(ids right) == '["omarchy.tray","omarchy.power"]' ]] ||
|
||||
fail "$1 still gets the widget" "$(cat "$config")"
|
||||
}
|
||||
rm "$plugin"
|
||||
mkdir "$plugin"
|
||||
printf 'local work\n' >"$plugin/notes"
|
||||
run_migration
|
||||
[[ ! -L $plugin && $(cat "$plugin/notes") == "local work" ]] || fail "existing checkout is preserved"
|
||||
pass "existing checkout and local files are preserved"
|
||||
|
||||
assert_retired() {
|
||||
local description="$1"
|
||||
[[ ! -e $checkout && ! -L $checkout ]] || fail "$description is retired" "$(ls -la "$checkout")"
|
||||
grep -q 'Retired the omacom.elsewhen checkout' "$output" || fail "$description is reported as retired" "$(cat "$output")"
|
||||
assert_widget_placed "$description"
|
||||
pass "$description is retired"
|
||||
}
|
||||
rm "$plugin/notes"
|
||||
rmdir "$plugin"
|
||||
ln -s "$test_dir/custom-plugin" "$plugin"
|
||||
run_migration
|
||||
[[ $(readlink "$plugin") == "$test_dir/custom-plugin" ]] || fail "existing symlink is preserved"
|
||||
pass "existing symlink is preserved, including a missing target"
|
||||
|
||||
assert_kept() {
|
||||
local description="$1"
|
||||
[[ -e $checkout || -L $checkout ]] || fail "$description is kept"
|
||||
grep -q 'takes precedence over' "$output" || fail "$description is reported as shadowed" "$(cat "$output")"
|
||||
assert_widget_placed "$description"
|
||||
pass "$description is kept"
|
||||
}
|
||||
|
||||
for origin in \
|
||||
https://github.com/omacom/elsewhen.git \
|
||||
https://github.com/omacom/elsewhen \
|
||||
git@github.com:omacom/elsewhen.git \
|
||||
ssh://git@github.com/omacom/elsewhen.git \
|
||||
https://GitHub.com/Omacom/Elsewhen/; do
|
||||
make_checkout "$origin"
|
||||
reset_layout
|
||||
run_migration
|
||||
assert_retired "a pristine clone with origin $origin"
|
||||
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 "$ROOT/migrations/1789581661.sh" >"$test_dir/output" 2>&1; then
|
||||
fail "$failure must leave the migration pending"
|
||||
fi
|
||||
pass "$failure leaves the migration pending"
|
||||
done
|
||||
|
||||
make_checkout https://github.com/omacom/elsewhen.git
|
||||
printf 'import QtQuick\nItem { id: mine }\n' >"$checkout/Panel.qml"
|
||||
reset_layout
|
||||
run_migration
|
||||
assert_kept "a clone with a modified tracked file"
|
||||
[[ $(cat "$checkout/Panel.qml") == $'import QtQuick\nItem { id: mine }' ]] ||
|
||||
fail "a kept clone keeps its local change" "$(cat "$checkout/Panel.qml")"
|
||||
pass "a kept clone keeps its local change"
|
||||
|
||||
make_checkout https://github.com/omacom/elsewhen.git
|
||||
printf 'notes\n' >"$checkout/NOTES.md"
|
||||
reset_layout
|
||||
run_migration
|
||||
assert_kept "a clone with an untracked file"
|
||||
|
||||
make_checkout https://github.com/omacom/elsewhen.git
|
||||
printf 'import QtQuick\nItem { id: mine }\n' >"$checkout/Panel.qml"
|
||||
git -C "$checkout" add Panel.qml
|
||||
git -C "$checkout" commit -q -m "Local customization"
|
||||
local_commit=$(git -C "$checkout" rev-parse HEAD)
|
||||
reset_layout
|
||||
run_migration
|
||||
assert_kept "a clean clone with an unpublished commit"
|
||||
[[ $(git -C "$checkout" rev-parse HEAD) == "$local_commit" ]] || fail "local commit survives"
|
||||
|
||||
make_checkout https://github.com/omacom/elsewhen.git
|
||||
git -C "$checkout" checkout -qb local-work
|
||||
printf 'local branch\n' >"$checkout/Panel.qml"
|
||||
git -C "$checkout" commit -qam "Unpublished branch"
|
||||
git -C "$checkout" checkout -q --detach refs/remotes/origin/main
|
||||
reset_layout
|
||||
run_migration
|
||||
assert_kept "an unpublished commit on another branch"
|
||||
|
||||
make_checkout https://github.com/omacom/elsewhen.git
|
||||
printf 'stashed work\n' >"$checkout/Panel.qml"
|
||||
git -C "$checkout" stash push -q
|
||||
reset_layout
|
||||
run_migration
|
||||
assert_kept "a clean clone with stashed work"
|
||||
|
||||
make_checkout https://github.com/omacom/elsewhen.git
|
||||
git -C "$checkout" config status.showUntrackedFiles no
|
||||
printf 'private-notes\n' >"$checkout/.git/info/exclude"
|
||||
printf 'ignored work\n' >"$checkout/private-notes"
|
||||
reset_layout
|
||||
run_migration
|
||||
assert_kept "a clone with an ignored file"
|
||||
[[ $(cat "$checkout/private-notes") == "ignored work" ]] || fail "ignored file survives"
|
||||
|
||||
make_checkout https://github.com/omacom/elsewhen.git
|
||||
printf 'recoverable work\n' >"$checkout/Panel.qml"
|
||||
git -C "$checkout" commit -qam "Recoverable local commit"
|
||||
git -C "$checkout" reset -q --hard refs/remotes/origin/main
|
||||
reset_layout
|
||||
run_migration
|
||||
assert_kept "a local commit retained only by the reflog"
|
||||
|
||||
make_checkout https://github.com/omacom/elsewhen.git
|
||||
git -C "$checkout" update-ref -d refs/remotes/origin/main
|
||||
reset_layout
|
||||
run_migration
|
||||
assert_kept "a clone without recorded upstream history"
|
||||
|
||||
make_checkout https://github.com/omacom/elsewhen.git
|
||||
git -C "$checkout" config status.showUntrackedFiles no
|
||||
printf 'untracked work\n' >"$checkout/NOTES.md"
|
||||
reset_layout
|
||||
run_migration
|
||||
assert_kept "untracked files hidden by the user Git configuration"
|
||||
|
||||
make_checkout https://github.com/someone/elsewhen-fork.git
|
||||
reset_layout
|
||||
run_migration
|
||||
assert_kept "a clone of another repository"
|
||||
|
||||
make_checkout https://github.com/omacom/elsewhen.git
|
||||
git -C "$checkout" remote remove origin
|
||||
reset_layout
|
||||
run_migration
|
||||
assert_kept "a clone without an origin remote"
|
||||
|
||||
rm -rf "$checkout"
|
||||
mkdir -p "$checkout"
|
||||
printf '{ "id": "omacom.elsewhen" }\n' >"$checkout/manifest.json"
|
||||
reset_layout
|
||||
run_migration
|
||||
assert_kept "a plain directory without .git"
|
||||
|
||||
rm -rf "$checkout"
|
||||
make_checkout https://github.com/omacom/elsewhen.git
|
||||
mv "$checkout" "$test_dir/elsewhen-src"
|
||||
ln -s "$test_dir/elsewhen-src" "$checkout"
|
||||
reset_layout
|
||||
run_migration
|
||||
assert_kept "a symlinked checkout"
|
||||
[[ -L $checkout && -f $test_dir/elsewhen-src/manifest.json ]] ||
|
||||
fail "a symlinked checkout stays a symlink with its target intact" "$(ls -la "$checkout" "$test_dir/elsewhen-src")"
|
||||
pass "a symlinked checkout stays a symlink with its target intact"
|
||||
|
||||
rm -f "$checkout"
|
||||
reset_layout
|
||||
run_migration
|
||||
grep -q 'omacom.elsewhen checkout\|takes precedence' "$output" && fail "no checkout means no checkout report" "$(cat "$output")"
|
||||
assert_widget_placed "a machine without a checkout"
|
||||
pass "a machine without a checkout is left alone"
|
||||
Reference in new issue
Block a user