More work on ssh copy
This commit is contained in:
parent
fadae42715
commit
4b6bfaffba
@ -5,7 +5,9 @@
|
||||
import glob
|
||||
import os
|
||||
import shlex
|
||||
from typing import Iterable, Iterator, List, Optional, Sequence, Tuple
|
||||
from typing import (
|
||||
Iterable, Iterator, List, NamedTuple, Optional, Sequence, Tuple
|
||||
)
|
||||
|
||||
from kitty.cli import parse_args
|
||||
from kitty.cli_stub import CopyCLIOptions
|
||||
@ -24,8 +26,15 @@ Interpret file arguments as glob patterns.
|
||||
The destination on the remote computer to copy to. Relative paths are resolved
|
||||
relative to HOME on the remote machine. When this option is not specified, the
|
||||
local file path is used as the remote destination (with the HOME directory
|
||||
getting automatically replaced by the remote HOME). Note that enviroment
|
||||
getting automatically replaced by the remote HOME). Note that environment
|
||||
variables and ~ are not expanded.
|
||||
|
||||
|
||||
--exclude
|
||||
type=list
|
||||
A glob pattern. Files whose names would match this pattern after transfer
|
||||
are excluded from being transferred. Useful when adding directories. Can
|
||||
be specified multiple times, if any of the patterns match the file will be excluded.
|
||||
'''
|
||||
|
||||
|
||||
@ -58,7 +67,23 @@ class CopyCLIError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def parse_copy_instructions(val: str) -> Iterable[Tuple[str, CopyCLIOptions]]:
|
||||
def get_arcname(loc: str, dest: Optional[str], home: str) -> str:
|
||||
if dest:
|
||||
arcname = dest
|
||||
else:
|
||||
arcname = os.path.normpath(loc)
|
||||
if arcname.startswith(home):
|
||||
arcname = os.path.relpath(arcname, home)
|
||||
arcname = os.path.normpath(arcname).replace(os.sep, '/')
|
||||
return arcname
|
||||
|
||||
|
||||
class CopyInstruction(NamedTuple):
|
||||
arcname: str
|
||||
exclude_patterns: Tuple[str, ...]
|
||||
|
||||
|
||||
def parse_copy_instructions(val: str) -> Iterable[Tuple[str, CopyInstruction]]:
|
||||
opts, args = parse_copy_args(shlex.split(val))
|
||||
locations: List[str] = []
|
||||
for a in args:
|
||||
@ -67,5 +92,7 @@ def parse_copy_instructions(val: str) -> Iterable[Tuple[str, CopyCLIOptions]]:
|
||||
raise CopyCLIError('No files to copy specified')
|
||||
if len(locations) > 1 and opts.dest:
|
||||
raise CopyCLIError('Specifying a remote location with more than one file is not supported')
|
||||
home = os.path.expanduser('~')
|
||||
for loc in locations:
|
||||
yield loc, opts
|
||||
arcname = get_arcname(loc, opts.dest, home)
|
||||
yield loc, CopyInstruction(arcname, tuple(opts.exclude))
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import atexit
|
||||
import fnmatch
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
@ -15,7 +16,7 @@ import traceback
|
||||
from base64 import standard_b64decode
|
||||
from contextlib import suppress
|
||||
from typing import (
|
||||
Any, Dict, Iterator, List, NoReturn, Optional, Set, Tuple, Union
|
||||
Any, Callable, Dict, Iterator, List, NoReturn, Optional, Set, Tuple, Union
|
||||
)
|
||||
|
||||
from kitty.constants import cache_dir, shell_integration_dir, terminfo_dir
|
||||
@ -66,10 +67,13 @@ def make_tarfile(ssh_opts: SSHOptions, base_env: Dict[str, str]) -> bytes:
|
||||
tf.addfile(ans, io.BytesIO(data))
|
||||
return ans
|
||||
|
||||
def filter_files(tarinfo: tarfile.TarInfo) -> Optional[tarfile.TarInfo]:
|
||||
if tarinfo.name.endswith('ssh/bootstrap.sh'):
|
||||
return None
|
||||
return normalize_tarinfo(tarinfo)
|
||||
def filter_from_globs(*pats: str) -> Callable[[tarfile.TarInfo], Optional[tarfile.TarInfo]]:
|
||||
def filter(tarinfo: tarfile.TarInfo) -> Optional[tarfile.TarInfo]:
|
||||
for pat in pats:
|
||||
if fnmatch.fnmatch(tarinfo.name, pat):
|
||||
return None
|
||||
return normalize_tarinfo(tarinfo)
|
||||
return filter
|
||||
|
||||
from kitty.shell_integration import get_effective_ksi_env_var
|
||||
if ssh_opts.shell_integration == 'inherit':
|
||||
@ -94,10 +98,13 @@ def make_tarfile(ssh_opts: SSHOptions, base_env: Dict[str, str]) -> bytes:
|
||||
buf = io.BytesIO()
|
||||
with tarfile.open(mode='w:bz2', fileobj=buf, encoding='utf-8') as tf:
|
||||
rd = ssh_opts.remote_dir.rstrip('/')
|
||||
for location, ci in ssh_opts.copy.items():
|
||||
tf.add(location, arcname=ci.arcname, filter=filter_from_globs(*ci.exclude_patterns))
|
||||
add_data_as_file(tf, 'kitty-ssh-kitten-data.sh', env_script)
|
||||
if ksi:
|
||||
tf.add(shell_integration_dir, arcname=rd + '/shell-integration', filter=filter_files)
|
||||
tf.add(terminfo_dir, arcname='.terminfo', filter=filter_files)
|
||||
arcname = rd + '/shell-integration'
|
||||
tf.add(shell_integration_dir, arcname=arcname, filter=filter_from_globs(f'{arcname}/ssh/bootstrap.*'))
|
||||
tf.add(terminfo_dir, arcname='.terminfo', filter=normalize_tarinfo)
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# generated by gen-config.py DO NOT edit
|
||||
|
||||
import typing
|
||||
import kitty.cli_stub
|
||||
import kittens.ssh.copy
|
||||
|
||||
|
||||
option_names = ( # {{{
|
||||
@ -12,7 +12,7 @@ class Options:
|
||||
hostname: str = '*'
|
||||
remote_dir: str = '.local/share/kitty-ssh-kitten'
|
||||
shell_integration: str = 'inherit'
|
||||
copy: typing.Dict[str, kitty.cli_stub.CLIOptions] = {}
|
||||
copy: typing.Dict[str, kittens.ssh.copy.CopyInstruction] = {}
|
||||
env: typing.Dict[str, str] = {}
|
||||
config_paths: typing.Tuple[str, ...] = ()
|
||||
config_overrides: typing.Tuple[str, ...] = ()
|
||||
|
||||
@ -4,9 +4,7 @@
|
||||
import posixpath
|
||||
from typing import Any, Dict, Iterable, Optional, Tuple
|
||||
|
||||
from kitty.cli_stub import CopyCLIOptions
|
||||
|
||||
from ..copy import parse_copy_instructions
|
||||
from ..copy import CopyInstruction, parse_copy_instructions
|
||||
|
||||
DELETE_ENV_VAR = '_delete_this_env_var_'
|
||||
|
||||
@ -33,7 +31,7 @@ def env(val: str, current_val: Dict[str, str]) -> Iterable[Tuple[str, str]]:
|
||||
yield val, DELETE_ENV_VAR
|
||||
|
||||
|
||||
def copy(val: str, current_val: Dict[str, str]) -> Iterable[Tuple[str, CopyCLIOptions]]:
|
||||
def copy(val: str, current_val: Dict[str, str]) -> Iterable[Tuple[str, CopyInstruction]]:
|
||||
yield from parse_copy_instructions(val)
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user