Prefix plugin clones with the username so shared clones don't collide
Clones of omarchy.clock become dhh.clock instead of local.clock, so a published clone carries its author's namespace. The clone command owns the id derivation and gains --edit to open the result in $EDITOR, and the shell exposes clonedFrom in listPlugins so the plugin menu no longer reconstructs clone ids. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
2050f28d55
commit
1bc89105d2
@@ -70,15 +70,15 @@ pick() {
|
||||
cat >"$TMPDIR/plugins.json" <<'JSON'
|
||||
[
|
||||
{"id": "omarchy.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": true},
|
||||
{"id": "local.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
|
||||
{"id": "tester.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
|
||||
]
|
||||
JSON
|
||||
|
||||
pick enable "Clock (local.clock)"
|
||||
[[ $ROWS == *"Clock (omarchy.clock)"* && $ROWS == *"Clock (local.clock)"* ]] \
|
||||
pick enable "Clock (tester.clock)"
|
||||
[[ $ROWS == *"Clock (omarchy.clock)"* && $ROWS == *"Clock (tester.clock)"* ]] \
|
||||
|| fail "picker tells two plugins of the same name apart" "$ROWS"
|
||||
pass "picker tells two plugins of the same name apart"
|
||||
[[ $CALLS == *"omarchy-plugin-enable local.clock"* ]] \
|
||||
[[ $CALLS == *"omarchy-plugin-enable tester.clock"* ]] \
|
||||
|| fail "picker acts on the row that was picked, not the one that shares its name" "$CALLS"
|
||||
pass "picker acts on the row that was picked, not the one that shares its name"
|
||||
|
||||
@@ -87,19 +87,19 @@ pass "picker acts on the row that was picked, not the one that shares its name"
|
||||
cat >"$TMPDIR/plugins.json" <<'JSON'
|
||||
[
|
||||
{"id": "omarchy.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": true, "active": false, "canDisable": true, "firstParty": true},
|
||||
{"id": "local.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
|
||||
{"id": "tester.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
|
||||
]
|
||||
JSON
|
||||
|
||||
pick enable "Clock"
|
||||
[[ $ROWS != *"("* ]] || fail "picker adorns a row only when its name is taken twice over" "$ROWS"
|
||||
pass "picker adorns a row only when its name is taken twice over"
|
||||
[[ $CALLS == *"omarchy-plugin-enable local.clock"* ]] \
|
||||
[[ $CALLS == *"omarchy-plugin-enable tester.clock"* ]] \
|
||||
|| fail "picker resolves a lone row to the plugin the verb offered, not a namesake it filtered out" "$CALLS"
|
||||
pass "picker resolves a lone row to the plugin the verb offered, not a namesake it filtered out"
|
||||
|
||||
pick remove "Clock"
|
||||
[[ $CALLS == *"omarchy-plugin-remove local.clock"* ]] \
|
||||
[[ $CALLS == *"omarchy-plugin-remove tester.clock"* ]] \
|
||||
|| fail "picker removes the plugin whose row was picked" "$CALLS"
|
||||
pass "picker removes the plugin whose row was picked"
|
||||
|
||||
@@ -118,8 +118,8 @@ pass "picker leaves an unambiguous name unadorned"
|
||||
|| fail "picker delegates plugin enablement to the plugin command" "$CALLS"
|
||||
pass "picker delegates plugin enablement to the plugin command"
|
||||
|
||||
# Clone offers only first-party plugins without an existing local counterpart,
|
||||
# then performs the clone and opens its deterministic path in $EDITOR.
|
||||
# Clone offers only first-party plugins that no installed clone points back at,
|
||||
# then hands the pick to the clone command, which opens the result in $EDITOR.
|
||||
cat >"$TMPDIR/plugins.json" <<'JSON'
|
||||
[
|
||||
{"id": "omarchy.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": true, "active": false, "canDisable": true, "firstParty": true},
|
||||
@@ -131,15 +131,16 @@ pick clone "Clock"
|
||||
[[ $ROWS == *"Clock"* && $ROWS != *"Weather"* ]] ||
|
||||
fail "clone picker offers only built-in plugins" "$ROWS"
|
||||
pass "clone picker offers built-in plugins"
|
||||
[[ $CALLS == *'terminal: omarchy-plugin-clone omarchy.clock && exec $EDITOR '*"/.config/omarchy/plugins/local.clock" ]] ||
|
||||
fail "clone picker opens the cloned path in EDITOR" "$CALLS"
|
||||
pass "clone picker clones and opens the local plugin"
|
||||
[[ $CALLS == *"terminal: omarchy-plugin-clone --edit omarchy.clock"* ]] ||
|
||||
fail "clone picker delegates cloning and editing to the clone command" "$CALLS"
|
||||
pass "clone picker clones and opens the personal plugin"
|
||||
|
||||
# Once local.<id> is discovered, the source no longer belongs in Clone.
|
||||
# Once a clone pointing back at the source is discovered, whatever it is named,
|
||||
# the source no longer belongs in Clone.
|
||||
cat >"$TMPDIR/plugins.json" <<'JSON'
|
||||
[
|
||||
{"id": "omarchy.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": true, "active": false, "canDisable": true, "firstParty": true},
|
||||
{"id": "local.clock", "name": "My Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
|
||||
{"id": "tester.clock", "name": "My Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false, "clonedFrom": "omarchy.clock"}
|
||||
]
|
||||
JSON
|
||||
|
||||
@@ -179,7 +180,7 @@ pass "picker keeps a bar out of disable"
|
||||
cat >"$TMPDIR/plugins.json" <<'JSON'
|
||||
[
|
||||
{"id": "omarchy.bar", "name": "Bar", "kinds": ["bar"], "enabled": false, "active": false, "canDisable": false, "firstParty": true},
|
||||
{"id": "local.neon-bar", "name": "Neon Bar", "kinds": ["bar"], "enabled": true, "active": true, "canDisable": false, "firstParty": false}
|
||||
{"id": "tester.neon-bar", "name": "Neon Bar", "kinds": ["bar"], "enabled": true, "active": true, "canDisable": false, "firstParty": false}
|
||||
]
|
||||
JSON
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ fi
|
||||
exit 0
|
||||
SH
|
||||
|
||||
for command in omarchy-plugin-enable omarchy-notification-send; do
|
||||
for command in omarchy-plugin-enable omarchy-notification-send fake-editor; do
|
||||
cat >"$TMPDIR/bin/$command" <<'SH'
|
||||
#!/bin/bash
|
||||
printf '%s %s\n' "${0##*/}" "$*" >>"$FAKE_CALLS"
|
||||
@@ -49,14 +49,14 @@ clone_plugin() {
|
||||
}
|
||||
}
|
||||
}'
|
||||
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" PATH="$TMPDIR/bin:$ROOT/bin:$PATH" \
|
||||
HOME="$TMPDIR/home" USER=tester OMARCHY_PATH="$ROOT" PATH="$TMPDIR/bin:$ROOT/bin:$PATH" \
|
||||
FAKE_CALLS="$CALLS" OMARCHY_TEST_ROOT="$ROOT" \
|
||||
FAKE_SHELL_CONFIG="${FAKE_CLONE_CONFIG:-$default_config}" \
|
||||
omarchy-plugin-clone "$@"
|
||||
}
|
||||
|
||||
clone_plugin omarchy.clock >/dev/null
|
||||
clock="$TMPDIR/home/.config/omarchy/plugins/local.clock"
|
||||
clock="$TMPDIR/home/.config/omarchy/plugins/tester.clock"
|
||||
|
||||
for file in manifest.json BarWidget.qml Panel.qml Model.js; do
|
||||
[[ -f $clock/$file ]] || fail "clock clone is missing $file"
|
||||
@@ -73,7 +73,7 @@ rg -qF "omarchy.clock" "$clock" -g '*.qml' -g '*.js' ||
|
||||
pass "clone preserves the built-in runtime IPC id"
|
||||
|
||||
jq -e '
|
||||
.id == "local.clock" and
|
||||
.id == "tester.clock" and
|
||||
.name == "My Clock" and
|
||||
.barWidget.displayName == "My Clock" and
|
||||
.omarchy.clonedFrom == "omarchy.clock" and
|
||||
@@ -82,53 +82,53 @@ jq -e '
|
||||
' "$clock/manifest.json" >/dev/null || fail "clock clone manifest is incorrect"
|
||||
pass "clone updates identity without replacing the manifest"
|
||||
|
||||
grep -qx 'omarchy-plugin-enable local.clock' "$CALLS" ||
|
||||
grep -qx 'omarchy-plugin-enable tester.clock' "$CALLS" ||
|
||||
fail "clone does not enable the editable copy"
|
||||
grep -qx 'omarchy-notification-send -g Editing Cloned Plugin Original plugin has been replace by clone.' "$CALLS" ||
|
||||
fail "clone does not notify that the editable clone is active"
|
||||
pass "clone enables bar widgets and confirms the editable clone"
|
||||
|
||||
clone_plugin omarchy.keyboard-layout >/dev/null
|
||||
grep -qx 'omarchy-plugin-enable local.keyboard-layout' "$CALLS" ||
|
||||
grep -qx 'omarchy-plugin-enable tester.keyboard-layout' "$CALLS" ||
|
||||
fail "clone does not enable a clone of a legacy string-form bar entry"
|
||||
pass "clone enables clones of legacy string-form bar entries"
|
||||
|
||||
clone_plugin omarchy.menu >/dev/null
|
||||
menu="$TMPDIR/home/.config/omarchy/plugins/local.menu"
|
||||
menu="$TMPDIR/home/.config/omarchy/plugins/tester.menu"
|
||||
|
||||
for file in manifest.json Menu.qml MenuModel.js BarWidget.qml; do
|
||||
[[ -f $menu/$file ]] || fail "menu clone is missing $file"
|
||||
done
|
||||
jq -e '
|
||||
.id == "local.menu" and
|
||||
.id == "tester.menu" and
|
||||
.kinds == ["menu", "bar-widget"] and
|
||||
.entryPoints.menu == "Menu.qml" and
|
||||
.entryPoints.barWidget == "BarWidget.qml"
|
||||
' "$menu/manifest.json" >/dev/null || fail "menu clone loses plugin kinds"
|
||||
grep -qx 'omarchy-plugin-enable local.menu' "$CALLS" ||
|
||||
grep -qx 'omarchy-plugin-enable tester.menu' "$CALLS" ||
|
||||
fail "clone does not enable a multi-kind plugin"
|
||||
pass "clone preserves and enables multi-kind plugins"
|
||||
|
||||
remove_output=$(HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" PATH="$TMPDIR/bin:$ROOT/bin:$PATH" \
|
||||
FAKE_CALLS="$CALLS" OMARCHY_TEST_ROOT="$ROOT" \
|
||||
omarchy-plugin-remove local.menu --yes)
|
||||
grep -qx 'omarchy-shell shell setPluginEnabled local.menu false' "$CALLS" ||
|
||||
omarchy-plugin-remove tester.menu --yes)
|
||||
grep -qx 'omarchy-shell shell setPluginEnabled tester.menu false' "$CALLS" ||
|
||||
fail "removing an enabled clone does not disable it first"
|
||||
grep -q 'Restored omarchy.menu.' <<<"$remove_output" ||
|
||||
fail "removing a clone does not report its restored source"
|
||||
pass "removing an enabled clone goes through plugin disable and reports its source"
|
||||
|
||||
clone_plugin omarchy.active-window >/dev/null
|
||||
[[ -f $TMPDIR/home/.config/omarchy/plugins/local.active-window/ActiveWindow.qml ]] ||
|
||||
[[ -f $TMPDIR/home/.config/omarchy/plugins/tester.active-window/ActiveWindow.qml ]] ||
|
||||
fail "flat bar plugin clone is incomplete"
|
||||
pass "flat bar plugins clone from adjacent manifests"
|
||||
|
||||
grep -qx 'omarchy-plugin-enable local.active-window' "$CALLS" ||
|
||||
grep -qx 'omarchy-plugin-enable tester.active-window' "$CALLS" ||
|
||||
fail "clone does not activate a bar widget whose source is absent"
|
||||
pass "clone activates an absent bar widget"
|
||||
|
||||
clone_plugin omarchy.indicators >/dev/null
|
||||
indicators="$TMPDIR/home/.config/omarchy/plugins/local.indicators"
|
||||
indicators="$TMPDIR/home/.config/omarchy/plugins/tester.indicators"
|
||||
for file in Indicators.qml indicators/Dnd.qml indicators/Reminder.qml; do
|
||||
[[ -f $indicators/$file ]] || fail "indicators clone is missing $file"
|
||||
done
|
||||
@@ -137,20 +137,26 @@ grep -q 'Qt.resolvedUrl("indicators/"' "$indicators/Indicators.qml" ||
|
||||
pass "flat bar plugins declare extra clone dependencies"
|
||||
|
||||
clone_plugin omarchy.tray >/dev/null
|
||||
[[ -f $TMPDIR/home/.config/omarchy/plugins/local.tray/TrayModel.js ]] ||
|
||||
[[ -f $TMPDIR/home/.config/omarchy/plugins/tester.tray/TrayModel.js ]] ||
|
||||
fail "tray clone is missing its model"
|
||||
pass "flat bar plugins keep local script dependencies"
|
||||
|
||||
clone_plugin omarchy.bar >/dev/null
|
||||
grep -qx 'omarchy-plugin-enable local.bar' "$CALLS" ||
|
||||
grep -qx 'omarchy-plugin-enable tester.bar' "$CALLS" ||
|
||||
fail "clone does not select a cloned bar"
|
||||
pass "clone switches full bars"
|
||||
|
||||
clone_plugin omarchy.background >/dev/null
|
||||
grep -qx 'omarchy-plugin-enable local.background' "$CALLS" ||
|
||||
grep -qx 'omarchy-plugin-enable tester.background' "$CALLS" ||
|
||||
fail "clone does not enable an ordinary cloned plugin"
|
||||
pass "clone switches ordinary plugins"
|
||||
|
||||
EDITOR=fake-editor clone_plugin omarchy.weather --edit >/dev/null
|
||||
grep -qx "fake-editor $TMPDIR/home/.config/omarchy/plugins/tester.weather" "$CALLS" ||
|
||||
fail "clone --edit does not open the clone in EDITOR"
|
||||
pass "clone --edit opens the clone in EDITOR"
|
||||
rm -rf "$TMPDIR/home/.config/omarchy/plugins/tester.weather"
|
||||
|
||||
mkdir -p "$TMPDIR/home/.config/omarchy/plugins/acme.example"
|
||||
cat >"$TMPDIR/home/.config/omarchy/plugins/acme.example/manifest.json" <<'JSON'
|
||||
{"id":"acme.example","name":"Example","kinds":["bar-widget"],"entryPoints":{"barWidget":"Widget.qml"}}
|
||||
@@ -163,14 +169,14 @@ pass "clone is limited to built-in plugins"
|
||||
if clone_plugin omarchy.weather custom.weather >/dev/null 2>&1; then
|
||||
fail "clone accepts a custom id"
|
||||
fi
|
||||
[[ ! -e $TMPDIR/home/.config/omarchy/plugins/local.weather ]] ||
|
||||
[[ ! -e $TMPDIR/home/.config/omarchy/plugins/tester.weather ]] ||
|
||||
fail "rejected custom id leaves a clone behind"
|
||||
pass "clone derives the local id"
|
||||
pass "clone derives the personal id from the username"
|
||||
|
||||
if clone_plugin omarchy.weather --replace >/dev/null 2>&1; then
|
||||
fail "clone still accepts bar layout actions"
|
||||
fi
|
||||
[[ ! -e $TMPDIR/home/.config/omarchy/plugins/local.weather ]] ||
|
||||
[[ ! -e $TMPDIR/home/.config/omarchy/plugins/tester.weather ]] ||
|
||||
fail "rejected bar action leaves a clone behind"
|
||||
pass "clone does not accept manual switch options"
|
||||
|
||||
@@ -182,6 +188,6 @@ pass "clone requires an explicit source id"
|
||||
if FAKE_NO_DISCOVERY=1 clone_plugin omarchy.osd >/dev/null 2>&1; then
|
||||
fail "clone succeeds before the shell discovers it"
|
||||
fi
|
||||
[[ ! -e $TMPDIR/home/.config/omarchy/plugins/local.osd ]] ||
|
||||
[[ ! -e $TMPDIR/home/.config/omarchy/plugins/tester.osd ]] ||
|
||||
fail "failed clone discovery leaves a partial clone behind"
|
||||
pass "clone removes a partial clone when switching fails"
|
||||
|
||||
@@ -132,7 +132,7 @@ done
|
||||
jq -e '
|
||||
map(.id) as $ids |
|
||||
all(["omarchy.menu", "omarchy.notifications", "omarchy.clock", "omarchy.osd"][]; $ids | index(.)) and
|
||||
all(.[]; (.kinds | type == "array") and (.enabled | type == "boolean") and (.canDisable | type == "boolean") and (.firstParty | type == "boolean")) and
|
||||
all(.[]; (.kinds | type == "array") and (.enabled | type == "boolean") and (.canDisable | type == "boolean") and (.firstParty | type == "boolean") and (.clonedFrom | type == "string")) and
|
||||
([.[].name] == ([.[].name] | sort))
|
||||
' <<<"$plugins" >/dev/null || {
|
||||
printf 'Plugins:\n%s\n' "$plugins" | jq . >&2
|
||||
|
||||
Reference in New Issue
Block a user