Files
Justin M. Keyes 893d90fced feat(remote-ssh): supervise server in Lua, "Update Nvim" for nightly #42706
Problem:
- remote_ssh.sh detects ssh disconnect by writing a heartbeat every 1s,
  which the local side buffers for the whole session.
- The server's stdout (e.g. `io.write()` from a random plugin) writes to
  the tunnel, which in theory may contain a bogus "NVIM_READY".
- A nightly install is never updated on the remote.

Solution:
- Supervise the server in `vim.net._ssh.serve()` (Lua). It detects
  disconnect via stdin EOF and discards server stdout. The script only
  finds or installs `nvim`.
  - Also a step toward Windows remotes.
- User may choose "Update Nvim" to reinstall the nightly.
2026-11-05 09:48:04 -05:00

70 lines
2.3 KiB
Bash

#!/bin/sh
# Bootstraps the headless Nvim server of a |remote-ssh| session: finds or installs `nvim`, which then
# runs and supervises the server (vim.net._ssh.serve()).
#
# Usage: remote_ssh.sh [--update] {sock} {dir} {url} [{version}]
# Runs `nvim` from $PATH if it is {version} or newer, else the release {url}, which it installs
# to {dir} (relative to $HOME) unless present, or always with --update. E.g.
# remote_ssh.sh /tmp/nvim-remote.0123abcd/nvim.sock .local/share/nvim-remote/releases/v0.12.0-linux-x86_64 https://…/nvim-linux-x86_64.tar.gz v0.12.0
set -eu
# dash (Debian's sh) has no pipefail; tar still fails on an empty or truncated stream.
# shellcheck disable=SC3040
if (set -o pipefail) 2>/dev/null; then
set -o pipefail
fi
# Prints the path of `nvim` on $PATH, if it is $want or newer. It checks its own version, so an old
# or broken one fails.
# TODO: $PATH is that of a non-login shell, so it may lack e.g. `~/.local/bin`. Could take it from
# a throwaway login shell: `"$SHELL" -lc 'printf %s "$PATH"'`.
system_nvim() {
[ -n "$want" ] || return 1
nvim=$(command -v nvim) || return 1
want=$want "$nvim" --clean -es \
+'lua os.exit(vim.version.ge(vim.version(), os.getenv("want")) and 0 or 1)' \
</dev/null >/dev/null 2>&1 || return 1
echo "$nvim"
}
# Installs the release to $dest, unless present (and not $update).
install_release() (
if [ -x "$dest/bin/nvim" ] && [ -z "$update" ]; then
exit 0
fi
echo "Installing $dest..." >&2
mkdir -p "${dest%/*}"
tmp=$(mktemp -d "$dest.XXXXXX")
trap 'rm -rf "$tmp"' EXIT
mkdir "$tmp/new"
curl -fsSL "$url" | tar -xzf - -C "$tmp/new"
if [ -n "$update" ]; then
# Removed with $tmp. Gone already if a concurrent update moved it.
mv "$dest" "$tmp/old" 2>/dev/null || :
fi
# The archive holds one top-level dir, e.g. `nvim-linux-x86_64/`. Atomic rename; keeps a
# concurrent install that finished first.
[ -e "$dest" ] || mv "$tmp"/new/* "$dest"
)
main() {
update=
if [ "$1" = --update ]; then
update=1
shift
fi
sock=$1
dest="$HOME/$2"
url=$3
want=${4-}
if ! nvim=$(system_nvim); then
install_release
nvim="$dest/bin/nvim"
fi
# Nvim inherits stdin from `sh -s` (i.e. from ssh), so EOF tells serve() that ssh disconnected.
exec "$nvim" --clean -n --headless +"lua require('vim.net._ssh').serve('$sock')" +qa!
}
main "$@"