From bae9b095b4740ae14baf32f0c6a8811036711571 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 3 Apr 2022 15:10:20 +0530 Subject: [PATCH] Start work on kitty launcher for remote servers This will automatically download kitty and run it. The ssh kitten can add it to PATH thereby making kitty available on the remote machine at low cost. --- kittens/ssh/main.py | 5 +- kitty_tests/ssh.py | 3 +- shell-integration/ssh/kitty | 92 +++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100755 shell-integration/ssh/kitty diff --git a/kittens/ssh/main.py b/kittens/ssh/main.py index c9a630094..015db4198 100644 --- a/kittens/ssh/main.py +++ b/kittens/ssh/main.py @@ -30,7 +30,7 @@ from typing import ( from kitty.constants import ( cache_dir, runtime_dir, shell_integration_dir, ssh_control_master_template, - terminfo_dir + str_version, terminfo_dir ) from kitty.options.types import Options from kitty.shm import SharedMemory @@ -171,6 +171,9 @@ def make_tarfile(ssh_opts: SSHOptions, base_env: Dict[str, str], compression: st f'{arcname}/ssh/*', # bootstrap files are sent as command line args f'{arcname}/zsh/kitty.zsh', # present for legacy compat not needed by ssh kitten )) + arcname = 'home/' + rd + '/kitty' + add_data_as_file(tf, arcname + '/version', str_version.encode('ascii')) + tf.add(shell_integration_dir + '/ssh/kitty', arcname=arcname + '/bin/kitty', filter=normalize_tarinfo) tf.add(f'{terminfo_dir}/kitty.terminfo', arcname='home/.terminfo/kitty.terminfo', filter=normalize_tarinfo) tf.add(glob.glob(f'{terminfo_dir}/*/xterm-kitty')[0], arcname='home/.terminfo/x/xterm-kitty', filter=normalize_tarinfo) return buf.getvalue() diff --git a/kitty_tests/ssh.py b/kitty_tests/ssh.py index 919a9d362..1f4241466 100644 --- a/kitty_tests/ssh.py +++ b/kitty_tests/ssh.py @@ -136,7 +136,8 @@ copy --exclude */w.* d1 contents.discard(f'{tname}/x/xterm-kitty') contents.discard(f'{tname}/78/xterm-kitty') self.ae(contents, { - 'g.1', 'g.2', f'{tname}/kitty.terminfo', 'simple-file', 'd1/d2/x', 'd1/y', 'a/sfa' + 'g.1', 'g.2', f'{tname}/kitty.terminfo', 'simple-file', 'd1/d2/x', 'd1/y', 'a/sfa', + '.local/share/kitty-ssh-kitten/kitty/version', '.local/share/kitty-ssh-kitten/kitty/bin/kitty' }) self.ae(len(glob.glob(f'{remote_home}/{tname}/*/xterm-kitty')), 2) diff --git a/shell-integration/ssh/kitty b/shell-integration/ssh/kitty new file mode 100755 index 000000000..78ecdde7b --- /dev/null +++ b/shell-integration/ssh/kitty @@ -0,0 +1,92 @@ +#!/bin/sh + +{ \unalias command; \unset -f command; } >/dev/null 2>&1 + + +die() { printf "\033[31m%s\033[m\n\r" "$*" > /dev/stderr; exit 1; } + +exec_kitty() { + exec "$kitty_exe" "$@" + die "Failed to execute kitty" +} + +script_path=$(command readlink -f "$0" 2> /dev/null) +[ $? == 0 ] || script_path="$0" +script_dir=$(command dirname "$script_path") +install_dir="$(command dirname "$script_dir")/install" +kitty_exe="$install_dir/bin/kitty" +if [ -e "$kitty_exe" ]; then + local_kitty_version=$("$kitty_exe" +runpy "from kitty.constants import str_version; print(str_version)") + if [ $? = 0 ]; then + version_sort_key() { + printf "%05d%05d%05d" $(echo "$1" | command tr '.' '\n' | command head -n 3) + } + remote_kitty_version=$(command cat "$script_dir"/../version) + [ $? = 0 ] || die "Failed to read remote kitty version" + [ $(version_sort_key "$remote_kitty_version") -le $(version_sort_key "$local_kitty_version") ] && exec_kitty "$@" + fi +fi + +if command -v curl 2> /dev/null > /dev/null; then + fetch() { + command curl -fL "$1" + } + fetch_quiet() { + command curl -fsSL "$1" + } +elif command -v wget 2> /dev/null > /dev/null; then + fetch() { + command wget -O- "$1" + } + fetch_quiet() { + command wget --quiet -O- "$1" + } +else + die "Neither curl nor wget available, cannot download kitty" +fi + +case "$(command uname)" in + 'Linux') OS="linux";; + 'Darwin') OS="macos";; + *) die "kitty pre-built binaries are not available for the $(command uname) operating system";; +esac + +release_version=$(fetch_quiet "https://api.github.com/repos/kovidgoyal/kitty/releases/latest" | command grep tag_name | command cut -dv -f2 | command cut -d '"' -f1) +[ -z "$release_version" ] && die "Could not get kitty latest release version" +command rm -rf "$install_dir" + +if [ "$OS" = "linux" ]; then + case "$(command uname -m)" in + x86_64) arch="x86_64";; + aarch64*) arch="arm64";; + armv8*) arch="arm64";; + i386) arch="i686";; + i686) arch="i686";; + *) die "Unknown CPU architecture $(command uname -m)";; + esac + url="https://github.com/kovidgoyal/kitty/releases/download/v$release_version/kitty-$release_version-$arch.txz" +else + url="https://github.com/kovidgoyal/kitty/releases/download/v$release_version/kitty-$release_version.dmg" +fi + +printf "Downloading kitty from: \033[32m%s\033[m\n\n" "$url" +command mkdir -p "$install_dir" + +if [ "$OS" = "linux" ]; then + fetch "$url" | command tar -C "$install_dir" -xJof - + [ $? = 0 ] || die "Failed to download and install kitty" +else + tdir=$(command mktemp -d "$install_dir/tmp-for-dmg-XXXXXXXXXXXX") + [ $? = 0 ] || die "Creating temp directory failed" + fetch "$url" > "$tdir/kitty.dmg" + command mkdir "$tdir/mp" + command hdiutil attach "$tdir/kitty.dmg" "-mountpoint" "$tdir/mp" || die "Failed to mount kitty.dmg" + command ditto -v "$tdir/mp/kitty.app" "$install_dir/kitty.app" + rc="$?" + command hdiutil detach "$tdir/mp" + command rm -rf "$tdir" + [ "$rc" != "0" ] && die "Failed to copy kitty.app from mounted dmg" + command mkdir "$install_dir/bin" + command ln -sf "$install_dir/kitty.app/Contents/MacOS/kitty" "$install_dir/bin/kitty" +fi +exec_kitty "$@"