* Share the git URL check between theme-install and plugin-add Both commands clone a URL a stranger can choose, and each carried its own copy of the rule that refuses a git option or a `<helper>::<address>` transport helper before cloning. Two copies of a security check drift: the second one arrived four months after the first, and only because someone went looking for it. The rule now lives in omarchy-git-url-check and the callers ask it. Its absence refuses the URL rather than waving it through, since the callers read a non-zero status as a refusal and a missing command exits 127. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Codex XHigh <noreply@openai.com> * Refuse a git URL naming a transport Omarchy does not clone from `<helper>::<address>` is only one of the two ways a URL reaches a remote helper. git also resolves git-remote-<scheme> for `<scheme>://<address>` whenever the scheme is not one it connects itself, so `ext::sh -c id` and `ext://sh -c id` arrive at the same helper while only the first was refused. That shape cannot be refused outright, because it is also how every legitimate URL arrives, so the scheme is checked against the transports git still connects itself. `git+ssh` and `ssh+git` are on that list: they are spelled like a helper and read as plain ssh, and leaving them off would refuse a URL that clones today. `ext` and `fd` are off it deliberately -- git ships a helper for each, and `ext` runs whatever command the URL carries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Codex XHigh <noreply@openai.com> --------- Co-authored-by: David Heinemeier Hansson <david@hey.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Codex XHigh <noreply@openai.com>
176 lines
4.2 KiB
Bash
Executable File
176 lines
4.2 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# omarchy:summary=Add a shell plugin from git
|
|
# omarchy:group=plugin
|
|
# omarchy:args=[git-url] [--enable] [--yes]
|
|
# omarchy:examples=omarchy plugin add https://github.com/acme/omarchy-weather.git --enable
|
|
# omarchy:alias=omarchy plugin install
|
|
|
|
set -euo pipefail
|
|
|
|
export GIT_TERMINAL_PROMPT=0
|
|
export GIT_SSH_COMMAND="${GIT_SSH_COMMAND:-ssh -oBatchMode=yes}"
|
|
|
|
PLUGINS_DIR="$HOME/.config/omarchy/plugins"
|
|
ASSUME_YES=0
|
|
|
|
fail() {
|
|
echo "omarchy-plugin-add: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
interactive() {
|
|
[[ -t 0 && -t 1 ]]
|
|
}
|
|
|
|
confirm() {
|
|
local prompt="$1"
|
|
(( ASSUME_YES )) && return 0
|
|
if interactive; then
|
|
gum confirm "$prompt"
|
|
else
|
|
fail "refusing to continue without confirmation; pass --yes"
|
|
fi
|
|
}
|
|
|
|
ENABLE_PLACEMENT=()
|
|
|
|
select_bar_widget_placement() {
|
|
local id="$1"
|
|
local section
|
|
local default_section
|
|
interactive || return 0
|
|
(( ASSUME_YES )) && return 0
|
|
|
|
jq -e '(.kinds // []) | (index("bar") | not) and (index("bar-widget") != null)' \
|
|
"$PLUGINS_DIR/$id/manifest.json" >/dev/null 2>&1 || return 0
|
|
|
|
default_section=$(jq -r '.barWidget.defaultSection // "center"' "$PLUGINS_DIR/$id/manifest.json")
|
|
section=$(printf '%s\n' left center right |
|
|
gum choose --header="Place $id in which bar section?" --selected "$default_section") || return 0
|
|
[[ -n $section ]] || return 0
|
|
ENABLE_PLACEMENT=(--section "$section")
|
|
}
|
|
|
|
plugin_id_manifest() {
|
|
omarchy-plugin-catalog | jq -r --arg id "$1" '
|
|
map(select(.id == $id))[0].manifestPath // empty
|
|
'
|
|
}
|
|
|
|
url=""
|
|
enable_after=""
|
|
|
|
while (( $# > 0 )); do
|
|
case "$1" in
|
|
--enable)
|
|
enable_after=true
|
|
shift
|
|
;;
|
|
--yes | -y)
|
|
ASSUME_YES=1
|
|
shift
|
|
;;
|
|
-h | --help)
|
|
echo "Usage: omarchy plugin add [git-url] [--enable] [--yes]"
|
|
exit 0
|
|
;;
|
|
-*)
|
|
fail "unknown add option: $1"
|
|
;;
|
|
*)
|
|
[[ -z $url ]] || fail "unexpected argument: $1"
|
|
url="$1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z $url ]]; then
|
|
interactive ||
|
|
fail "a git URL is required (e.g. omarchy plugin add https://github.com/acme/omarchy-weather.git)"
|
|
url=$(gum input --prompt "Git URL of the plugin repo: ") || fail "cancelled"
|
|
[[ -n $url ]] || fail "a git URL is required"
|
|
fi
|
|
|
|
# Refuse a URL that names a git option or a transport helper before cloning, so
|
|
# an untrusted URL cannot run a command before the plugin is validated or
|
|
# enabled. The check is shared with omarchy-theme-install and explains itself; a
|
|
# missing checker leaves this non-zero, which refuses the URL rather than
|
|
# cloning it.
|
|
omarchy-git-url-check "$url" || exit 1
|
|
|
|
if (( ! ASSUME_YES )); then
|
|
cat >&2 <<WARN
|
|
|
|
⚠️ Plugins run as arbitrary, unsandboxed code inside your long-lived
|
|
omarchy-shell process. Only add repos you trust, and review the code
|
|
before you enable it.
|
|
|
|
URL: $url
|
|
|
|
WARN
|
|
confirm "Clone and add this plugin?" || fail "aborted"
|
|
fi
|
|
|
|
mkdir -p "$PLUGINS_DIR"
|
|
|
|
stage="$PLUGINS_DIR/.add.tmp.$$"
|
|
rm -rf "$stage"
|
|
if ! git clone -- "$url" "$stage"; then
|
|
rm -rf "$stage"
|
|
fail "failed to clone $url"
|
|
fi
|
|
|
|
if ! omarchy-plugin-validate "$stage"; then
|
|
rm -rf "$stage"
|
|
fail "refusing to add: validation failed"
|
|
fi
|
|
|
|
id=$(jq -r '.id' "$stage/manifest.json")
|
|
existing_manifest=$(plugin_id_manifest "$id") || {
|
|
rm -rf "$stage"
|
|
fail "could not inspect installed plugin ids"
|
|
}
|
|
if [[ -n $existing_manifest ]]; then
|
|
rm -rf "$stage"
|
|
fail "plugin id '$id' is already used by $existing_manifest"
|
|
fi
|
|
|
|
target="$PLUGINS_DIR/$id"
|
|
if [[ -e $target || -L $target ]]; then
|
|
rm -rf "$stage"
|
|
fail "plugin '$id' is already installed; update it with: omarchy plugin update $id"
|
|
fi
|
|
|
|
mv "$stage" "$target"
|
|
echo "Added $id into $target"
|
|
|
|
omarchy-shell shell rescanPlugins >/dev/null
|
|
|
|
if [[ -z $enable_after ]]; then
|
|
if (( ASSUME_YES )) || ! interactive; then
|
|
enable_after=false
|
|
elif confirm "Enable '$id' now?"; then
|
|
enable_after=true
|
|
else
|
|
enable_after=false
|
|
fi
|
|
fi
|
|
|
|
if [[ $enable_after == true ]]; then
|
|
select_bar_widget_placement "$id"
|
|
discovered=0
|
|
for (( attempt = 0; attempt < 40; attempt++ )); do
|
|
if omarchy-plugin-list --json | jq -e --arg id "$id" 'any(.[]; .id == $id)' >/dev/null; then
|
|
discovered=1
|
|
break
|
|
fi
|
|
sleep 0.05
|
|
done
|
|
(( discovered )) || fail "plugin '$id' is not known"
|
|
omarchy-plugin-enable "$id" "${ENABLE_PLACEMENT[@]}"
|
|
else
|
|
echo "Enable it later with: omarchy plugin enable $id"
|
|
fi
|