This commit is contained in:
2025-03-21 20:06:27 +01:00
parent 8bef723284
commit 3368c8149f

View File

@@ -1,109 +1,150 @@
zeditor-remote() {
ensure() {
local error
for name in "$@"; do
if ! command -v "${name}" >/dev/null; then
printf >&2 '%s\n' "ERROR: '$name' not found in PATH"
error=1
fi
done
if [[ -n "$error" ]]; then
return 1
fi
}
ensure xe fd rg fzf gum || return 1
local prefix="${1:-repos}"
local statedir="${XDG_STATE_HOME:-"$HOME/.local/state/"}/zeditor-remote-sh"
local spin=""
# doesn't forward stdin
if command -v gum >/dev/null; then
if printf '%s\n' "$(gum --version)" "gum version 0.15.1" | sort -C -V; then
: # older version don't forward stdin
else
spin="gum spin --show-output --show-error --"
fi
local -a missing=()
command -v xe >/dev/null || missing+=('xe')
command -v fd >/dev/null || missing+=('fd')
command -v rg >/dev/null || missing+=('rg')
command -v fzf >/dev/null || missing+=('fzf')
command -v gum >/dev/null || missing+=('gum')
if [[ "${#missing[@]}" -gt 0 ]]; then
printf >&2 "%s\n" "ERROR: Not found in PATH:$(printf " %s" "${missing[@]}")"
return 1
fi
local host=$(
{
printf '%s\n' localhost
{
# ask tailscale
if command -v tailscale >/dev/null; then
tailscale status --json | jq .Peer[].HostName -r;
fi
# search .ssh/known_hosts, strip DNS search domains to deduplicate
if [[ -r /etc/resolv.conf && -r "$HOME"/.ssh/known_hosts ]]; then
local -a hosts
readarray -d $'\n' -t hosts < <(
cut <"$HOME"/.ssh/known_hosts -d' ' -f1 | sort -u \
| grep -vE '^\[' \
| grep -v '[,@]' \
| grep -Ev '^([0-9]{0,3}\.){3}[0-9]{0,3}$' \
| grep -Ev '^([0-9a-fA-F]{0,4}:){0,7}:?([0-9a-fA-F]{0,4}:){0,6}[0-9a-fA-F]{0,4}$'
)
local -a domains
readarray -d ' ' -t domains < <(
grep </etc/resolv.conf '^search ' | cut -d' ' -f2-
)
printf '%s\n' "${hosts[@]}" \
| sed -E "s/\\.($(IFS='|'; printf "%s" "${domains[*]}"))\$//g" \
| grep -v '[.:]'
fi
} | grep -v '^localhost$' | sort -u
} | fzf --multi --reverse --bind 'ctrl-a:toggle-all' --height=25 --cycle \
| sed -e 's/%/%%/g'
)
if [[ -z "$host" ]]; then
if [[ ! -v HOME ]]; then
printf >&2 "%s\n" "ERROR: $HOME is not set"
return 1
fi
# make $prefix relative to $HOME
if [[ "$prefix" =~ ^/ ]]; then
prefix=${prefix//"$HOME/"/}
fi
if [[ "$prefix" =~ ^/ ]]; then
printf >&2 "%s\n" "ERROR: prefix not in \$HOME ($prefix)"
return 1
fi
uniq-stable() (
command cat -n | sort -b --key=2.1 -u | sort -n | cut -f2-
)
local hosts=$(
{
printf '%s\n' localhost
{
if [[ -s "$statedir"/ssh-host-history ]]; then
cat "$statedir"/ssh-host-history
fi
# ask tailscale
if command -v tailscale >/dev/null; then
tailscale status --json | jq .Peer[].HostName -r
fi
# search .ssh/known_hosts, strip DNS search domains to deduplicate
if [[ -r /etc/resolv.conf && -r "$HOME"/.ssh/known_hosts ]]; then
local -a hosts
readarray -d $'\n' -t hosts < <(
cut <"$HOME"/.ssh/known_hosts -d' ' -f1 | sort -u \
| grep -vE '^\[' \
| grep -v '[,@]' \
| grep -Ev '^([0-9]{0,3}\.){3}[0-9]{0,3}$' \
| grep -Ev '^([0-9a-fA-F]{0,4}:){0,7}:?([0-9a-fA-F]{0,4}:){0,6}[0-9a-fA-F]{0,4}$'
)
local -a domains
readarray -d ' ' -t domains < <(
grep </etc/resolv.conf '^search ' | cut -d' ' -f2-
)
printf '%s\n' "${hosts[@]}" \
| sed -E "s/\\.($(IFS='|'; printf "%s" "${domains[*]}"))\$//g" \
| grep -v '[.:]'
fi
} | grep -v '^localhost$' | uniq-stable
} | fzf --sync --multi --reverse --bind 'ctrl-a:toggle-all' --height=25 --cycle \
| sed -e 's/%/%%/g'
)
if [[ -z "$hosts" ]]; then
return 1
fi
# local spin=(gum spin --show-output --show-error --)
local spin=(gum spin --show-output --)
local repos=$(
export prefix statedir
worker() {
local host="$1"
# TODO: this assumes remote hosts only code in 'repos/'
# TODO: this assumes remote hosts has 'fd', use 'find' instead?
fdargs=(--hidden --max-depth 5)
# accept all, persist nothing, to scan hosts we've yet to trust,
# zed will still complain however
# accept all, persist nothing, to scan hosts we've yet to trust, zed will still complain however
sshargs=(-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null)
if [[ "$host" = localhost ]]; then
cd "$HOME"
fd "^\.git$" repos/ "${fdargs[@]}" -L -x printf '~/%q\n' "{//}"
fd "^\.git$" "$prefix/" "${fdargs[@]}" -L -x printf '~/%q\n' "{//}"
else
# TODO: report timeouts?
# TODO: auto escape this
timeout 5 ssh "${sshargs[@]}" 2>/dev/null "$host" \
fd "^\.git$" repos/ "${fdargs[@]}" -L -x printf "'ssh://%s/~/%q\n'" "'$host'" "'{//}'"
fd "^\.git$" "'$prefix/'" "${fdargs[@]}" -L -x printf "'ssh://%s/~/%q\n'" "'$host'" "'{//}'"
fi
}
$spin xe <<<"$host" -j"$(wc -l <<<"$host")" -s "$(declare -f worker); worker \"\$@\"" 2>/dev/null ||:
"${spin[@]}" xe -f <(printf "%s\n" "$hosts") -j"$(wc -l <<<"$hosts")" -s "$(declare -f worker); worker \"\$@\"" ||:
)
if [[ -z "${repos}" ]]; then
printf >&2 '%s\n' "ERROR: no repos found!"
return 1
fi
local choice=$(sort <<<"$repos" | fzf --reverse --cycle --height=25)
local choice=$(
{
if [[ -s "$statedir"/ssh-repo-history ]]; then
grep <"$statedir"/ssh-repo-history "$(printf "^ssh://%s/\n" "${hosts[@]}")"
fi
sort <<<"$repos"
} | uniq-stable | fzf -sync --reverse --cycle --height=25)
if [[ -z "${choice}" ]]; then
return
fi
local zed_cmd="zeditor ${choice}"
local host=$(
rg <<<"$choice" '^ssh://([^/]*)/(~/.*)$' -r '$1' || printf "%s\n" localhost
)
local ssh_cd_cmd=$(
rg <<<"$choice" '^ssh://([^/]*)/(~/.*)$' -r 'ssh -t "$1" "cd $2; \\$$SHELL -l"' ||:
)
# TODO: evict
lru_push() {
local line="$1"
local fname="$2"
mkdir -p "$(dirname "$fname")"
local new_lru=$(
printf "%s\n" "$line"
[[ -s "$fname" ]] && cat "$fname"
)
uniq-stable <<<"$new_lru" >"$fname"
}
if [[ -n "$ssh_cd_cmd" ]]; then
local do_ssh=""
local do_ssh=false
if gum confirm "SSH in there now?" --default=yes; then
do_ssh=1
do_ssh=true
elif [[ "$?" -eq 130 ]]; then
return 1
fi
# history -s "${zed_cmd}"
lru_push "$host" "$statedir"/ssh-host-history
lru_push "$choice" "$statedir"/ssh-repo-history
# history -s "${zed_cmd}" # TODO: this too?
eval ${zed_cmd}
# TODO: find a way to not push the rest of the unpushed history
history -s "${ssh_cd_cmd}"
history -a
if [[ -n "$do_ssh" ]]; then
if $do_ssh; then
printf >&2 "%s\n" "+ ${ssh_cd_cmd}"
eval ${ssh_cd_cmd}
fi
@@ -116,8 +157,13 @@ zeditor-remote() {
# https://github.com/zed-industries/zed/issues/4977#issuecomment-2536680602
zed_cmd="direnv exec ${choice} $zed_cmd"
fi
# lru_push "$host" "$statedir"/ssh-host-history
lru_push "$choice" "$statedir"/ssh-repo-history
# history -s "${zed_cmd}"
eval ${zed_cmd}
printf >&2 "%s\n" "+$(printf " %q" cd "${choice}")"
eval cd "${choice}" # 'eval' to expand the tilde
fi