Merge pull request #12 from xonssh/0.2.0

0.2.0
This commit is contained in:
anki-code 2020-02-13 23:21:18 +03:00 committed by GitHub
commit d5d7954444
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 266 additions and 128 deletions

View file

@ -21,41 +21,57 @@ pip install xonssh-xxh
```
Then try:
```
xxh <hostname or servername from ~/.ssh/config>
xxh <[user@]host[:port] or servername from ~/.ssh/config>
```
## Usage
```
$ ./xxh --help
usage: xxh [-h] [-i] [-if] [-lxh LOCAL_XXH_HOME] [-rxh REMOTE_XXH_HOME]
[-m METHOD] [-V]
$ xxh --help
usage: xxh [user@]host[:port]
usage: xxh [ssh arguments] destination [xxh arguments]
usage: xxh [-h] [-V] [-p SSH_PORT] [-l SSH_LOGIN_NAME] [-i SSH_IDENTITY_FILE]
[-o SSH_OPTIONS] [+i] [+if] [+lxh LOCAL_XXH_HOME]
[+hxh HOST_XXH_HOME] [+m METHOD] [+v] [+vv]
[destination]
The xxh is for using the xonsh shell wherever you go through the ssh.
____ _________ @ @
______ / \ \__/
____ / ____ \ / \ contribution
_____ / / \ \ / _/ https://github.com/xonssh/xxh
___ ( \ \_/ ) /
\ \_____/ / / plugins
___\___________/ / https://github.com/search?q=xxh-plugin
/__________________/
___ __________ @ @
_____ / \ \__/
___ / ______ \ / \ contribution
____ / / __ \ \ / _/ https://github.com/xonssh/xxh
__ ( / / / \ \ /
\ \___/ / / / plugins
___\ /__/ / https://github.com/search?q=xxh-plugin
/ \________/ /
/___________________/
positional arguments:
destination Destination may be specified as hostname or server name from ~/.ssh/config
required arguments:
destination Destination may be specified as [user@]hostname[:port] or server name from ~/.ssh/config
optional arguments:
common arguments:
-h, --help show this help message and exit
-i, --install Install xxh to distanation host
-if, --install-force Delete remote xxh home and install xonsh to distanation host
-lxh LOCAL_XXH_HOME, --local-xxh-home LOCAL_XXH_HOME
Local xxh home path. Default: ~/.xxh
-rxh REMOTE_XXH_HOME, --remote-xxh-home REMOTE_XXH_HOME
Remote xxh home path. Default: ~/.xxh
-m METHOD, --method METHOD
Installation method. Currently supported only 'appimage' method
-V, --version Show xxh version
ssh arguments:
-p SSH_PORT Port to connect to on the remote host.
-l SSH_LOGIN_NAME Specifies the user to log in as on the remote machine.
-i SSH_IDENTITY_FILE Selects a file from which the identity (private key) for public key authentication is read.
-o SSH_OPTIONS Options in the ssh configuration format. See ssh man page. Example: xxh -o Port=22 -o User=snail host
xxh arguments:
+i, ++install Install xxh to destination host.
+if, ++install-force Delete remote xxh home and install xonsh to destination host.
+lxh LOCAL_XXH_HOME, ++local-xxh-home LOCAL_XXH_HOME
Local xxh home path. Default: ~/.xxh
+hxh HOST_XXH_HOME, ++host-xxh-home HOST_XXH_HOME
Host xxh home path. Default: ~/.xxh
+m METHOD, ++method METHOD
Portable method: appimage
+v, ++verbose Verbose mode.
+vv, ++vverbose Super verbose mode.
```
## Plugins
@ -84,6 +100,13 @@ When you run `xxh <server>` command:
3. Finally xxh makes ssh connection to server and running remote portable xonsh shell without any affection on the target system.
## Development
Use [xxh-tests](https://github.com/xonssh/xxh-tests) environment for development and contribution.
What will make xxh more universal and stable in the future:
* [AppImages run on Alpine](https://github.com/AppImage/AppImageKit/issues/1015)
* [Fix xonsh for WSL1](https://github.com/xonsh/xonsh/issues/3367)
## Known Issues
### GLIBs versions

View file

@ -10,7 +10,7 @@ setuptools.setup(
install_requires=['xonsh'],
platforms='Unix-like',
scripts=['xxh'],
package_data={'xonssh_xxh':['*.xsh']},
package_data={'xonssh_xxh':['*.xsh', '*.sh']},
packages=setuptools.find_packages(),
classifiers=["Programming Language :: Python :: 3"],
license="BSD",

17
xonssh_xxh/host_info.sh Normal file
View file

@ -0,0 +1,17 @@
#!/bin/bash
xxh_home_realpath=`realpath _xxh_home_`
mkdir -p $xxh_home_realpath
settings_path=$xxh_home_realpath/settings.py
xxh_version=`[ "$(ls -A $xxh_home_realpath)" ] && echo "0" || echo "-1"`
if [[ -f $settings_path ]]; then
xxh_version=`cat $settings_path | grep XXH_VERSION | sed -e "s/.*: '\(.*\)'/\\1/g"`
fi
echo xxh_home_realpath=$xxh_home_realpath
echo xxh_version=$xxh_version
echo bash=`command -v bash`
echo rsync=`command -v rsync`
echo scp=`command -v scp`
echo xxh_home_freespace=`df -k --output=avail $xxh_home_realpath | tail -n1`

View file

@ -1,10 +1,15 @@
import sys
import sys, os
global_settings = {
'XXH_VERSION': '0.1'
'XXH_VERSION': '0.2.0'
}
if __name__ == "__main__":
for e in ['XXH_HOME', 'PIP_TARGET', 'PYTHONPATH']:
if e in os.environ:
global_settings[e] = os.environ[e]
if len(sys.argv) > 1:
setting_name = sys.argv[1]
if setting_name in global_settings:

297
xxh
View file

@ -1,9 +1,11 @@
#!/usr/bin/env xonsh
import os, sys, glob, argparse
import os, sys, glob, argparse, datetime, re
from shutil import which
from sys import exit
from argparse import RawTextHelpFormatter
from urllib.parse import urlparse
from random import randint
import datetime
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
import xonssh_xxh
@ -12,13 +14,16 @@ from xonssh_xxh.settings import global_settings
url_xxh_github = 'https://github.com/xonssh/xxh'
url_xxh_plugins_search = 'https://github.com/search?q=xxh-plugin'
url_appimage = 'https://github.com/niess/linuxdeploy-plugin-python/releases/download/continuous/xonsh-x86_64.AppImage'
local_xxh_home_path = '~/.xxh'
local_xxh_version = global_settings['XXH_VERSION']
remote_xxh_home_path = '~/.xxh'
local_xxh_home_path = '~/.xxh'
host_xxh_home_path = '~/.xxh'
portable_methods = ['appimage']
portable_methods_str = ', '.join(portable_methods)
xonsh_bin_name = 'xonsh'
if os.name == 'nt':
print(f"Windows is not supported. WSL1 not recommended also. WSL2 is not tested yet.\nTry to contribution: {url_xxh_github}")
sys.exit(1)
exit(1)
def xonssh():
try:
@ -30,42 +35,97 @@ def xonssh():
if terminal_cols < 70:
return f"\n\nContribution: {url_xxh_github}\n\nPlugins: {url_xxh_plugins_search}"
# Try: watch -n.2 xxh --help
l,r,s,t = (['@','-','_'][randint(0,2)], ['@','-','_'][randint(0,2)], ['_',' '][randint(0,1)], ['_',''][randint(0,1)])
return f"""
{s}___ _________ {l} {r}
{s}_____ / \\ \\__/
{s}___ / ____ \\ / \\ contribution
{s}____ / / \\ \\ / _/ {url_xxh_github}
{s}__ ( \\ \\_/ ) /
\\ \\_____/ / / plugins
{' ' if not t else ''} _{t}_\\___________/ / {url_xxh_plugins_search}
{' ' if not t else ''} /_{t}________________/
{s}___ __________ {l} {r}
{s}_____ / \\ \\__/
{s}___ / ______ \\ / \\ contribution
{s}____ / / __ \\ \\ / _/ {url_xxh_github}
{s}__ ( / / / \\ \\ /
\\ \\___/ / / / plugins
{' ' if not t else ''} _{t}__\\ /__/ / {url_xxh_plugins_search}
{' ' if not t else ''} / {'' if not t else ' '} \\________/ /
{' ' if not t else ''} /_{t}__________________/
"""
""" # watch -n.2 xxh --help
argp = argparse.ArgumentParser(description=f"The xxh is for using the xonsh shell wherever you go through the ssh. {xonssh()}", formatter_class=RawTextHelpFormatter)
argp.add_argument('destination', nargs='?', help="Destination may be specified as hostname or server name from ~/.ssh/config")
argp.add_argument('-i','--install', default=False, action='store_true', help="Install xxh to distanation host")
argp.add_argument('-if','--install-force', default=False, action='store_true', help="Delete remote xxh home and install xonsh to distanation host")
argp.add_argument('-lxh','--local-xxh-home', default=local_xxh_home_path, help=f"Local xxh home path. Default: {local_xxh_home_path}")
argp.add_argument('-rxh','--remote-xxh-home', default=remote_xxh_home_path, help=f"Remote xxh home path. Default: {remote_xxh_home_path}")
argp.add_argument('-m','--method', default='appimage', help="Installation method. Currently supported only 'appimage' method")
argp = argparse.ArgumentParser(description=f"The xxh is for using the xonsh shell wherever you go through the ssh. {xonssh()}", formatter_class=RawTextHelpFormatter, prefix_chars='-+')
argp.add_argument('-V','--version', default=False, action='store_true', help="Show xxh version")
argp.add_argument('-p', dest='ssh_port', help="Port to connect to on the remote host.")
argp.add_argument('-l', dest='ssh_login_name', help="Specifies the user to log in as on the remote machine.")
argp.add_argument('-i', dest='ssh_identity_file', help="Selects a file from which the identity (private key) for public key authentication is read.")
argp.add_argument('-o', dest='ssh_options', action='append', help="Options in the ssh configuration format. See ssh man page. Example: xxh -o Port=22 -o User=snail host")
argp.add_argument('destination', nargs='?', help="Destination may be specified as [user@]hostname[:port] or server name from ~/.ssh/config")
argp.add_argument('+i','++install', default=False, action='store_true', help="Install xxh to destination host.")
argp.add_argument('+if','++install-force', default=False, action='store_true', help="Delete remote xxh home and install xonsh to destination host.")
argp.add_argument('+lxh','++local-xxh-home', default=local_xxh_home_path, help=f"Local xxh home path. Default: {local_xxh_home_path}")
argp.add_argument('+hxh','++host-xxh-home', default=host_xxh_home_path, help=f"Host xxh home path. Default: {host_xxh_home_path}")
argp.add_argument('+m','++method', default='appimage', help=f"Portable method: {portable_methods_str}")
argp.add_argument('+v','++verbose', default=False, action='store_true', help="Verbose mode.")
argp.add_argument('+vv','++vverbose', default=False, action='store_true', help="Super verbose mode.")
argp.usage = f'xxh [user@]host[:port]\n\nusage: xxh [ssh arguments] destination [xxh arguments]\n\n{argp.format_usage()}'
help = argp.format_help().replace('\n +','\n\nxxh arguments:\n +',1).replace('optional ', 'common ')\
.replace('xxh version', 'xxh version\n\nssh arguments:').replace('positional ', 'required ')
argp.format_help = lambda: help
opt = argp.parse_args()
if opt.vverbose:
opt.verbose = True
if opt.version:
print(f"xonssh-xxh/{local_xxh_version}")
sys.exit(0)
exit(0)
if not opt.destination:
print('Destination required. Try --help')
sys.exit(1)
exit(1)
if opt.method not in portable_methods:
print(f'Currently supported methods: {portable_methods_str}')
exit(1)
if 'ssh://' not in opt.destination:
opt.destination = f'ssh://{opt.destination}'
url = urlparse(opt.destination)
host = url.hostname
if not host:
print(f"Wrong distination '{host}'")
exit(1)
if url.port:
opt.ssh_port = url.port
if url.username:
opt.ssh_login_name = url.username
ssh_arguments = []
if not opt.verbose:
ssh_arguments = ['-o', 'LogLevel=QUIET']
if opt.ssh_port:
ssh_arguments += ['-o', f'Port={opt.ssh_port}']
if opt.ssh_identity_file:
ssh_arguments += ['-o', f'IdentityFile={opt.ssh_identity_file}']
if opt.ssh_login_name:
ssh_arguments += ['-o', f'User={opt.ssh_login_name}']
if opt.ssh_options:
for ssh_option in opt.ssh_options:
ssh_arguments += ['-o', ssh_option]
if opt.verbose:
print(f'ssh arguments: {ssh_arguments}')
if not which('ssh'):
print('Install OpenSSH client before using xxh: https://duckduckgo.com/?q=how+to+install+openssh+client+in+linux')
exit(1)
dst = opt.destination
opt.install = True if opt.install_force else opt.install
ssh_v = ['-v'] if opt.vverbose else []
local_xxh_home_path = os.path.expanduser(opt.local_xxh_home)
local_xxh_home_parent = os.path.dirname(os.path.expanduser(local_xxh_home_path))
package_dir_path = os.path.dirname(os.path.realpath(xonssh_xxh.__file__))
@ -73,85 +133,80 @@ package_dir_path = os.path.dirname(os.path.realpath(xonssh_xxh.__file__))
if os.path.exists(local_xxh_home_path):
if not os.access(local_xxh_home_path, os.W_OK):
print(f"The local xxh home path isn't writable: {local_xxh_home_path}" )
sys.exit(1)
exit(1)
elif os.path.exists(local_xxh_home_parent):
if os.access(local_xxh_home_parent, os.W_OK):
print(f'Create xxh home path: {local_xxh_home_path}')
mkdir -p @(local_xxh_home_path) @(local_xxh_home_path)/plugins
print(f'Create local xxh home path: {local_xxh_home_path}')
mkdir @(ssh_v) -p @(local_xxh_home_path) @(local_xxh_home_path)/plugins
else:
print(f"Parent for local xxh home path isn't writable: {local_xxh_home_parent}")
sys.exit(1)
exit(1)
else:
print(f"Paths aren't writable:\n {local_xxh_home_parent}\n {local_xxh_home_path}")
sys.exit(1)
exit(1)
# Fix env to avoid ssh warnings
for lc in ['LC_TIME','LC_MONETARY','LC_ADDRESS','LC_IDENTIFICATION','LC_MEASUREMENT','LC_NAME','LC_NUMERIC','LC_PAPER','LC_TELEPHONE']:
${...}[lc] = "en_US.UTF-8"
${...}[lc] = "POSIX"
if opt.remote_xxh_home == '~/.xxh':
dst_user_home = $(ssh @(dst) -T "bash -c 'cd ~ && pwd'").strip()
if dst_user_home == '':
# https://github.com/xonsh/xonsh/issues/3367
print('Unknown answer from host when checking user home path')
sys.exit(1)
dst_xxh_home = os.path.join(dst_user_home, '.xxh')
else:
dst_xxh_home = opt.remote_xxh_home_path
dst_xxh_home_abspath = os.path.abspath(dst_xxh_home)
if dst_xxh_home_abspath == '/':
print("Remote xxh home path {dst_xxh_home} looks like / and is not supported ;)")
if os.path.abspath(opt.host_xxh_home) == '/':
print("Host xxh home path {host_xxh_home} looks like /. Please check twice!")
exit(1)
xonsh_bin = 'xonsh'
dst_xonsh_bin = os.path.join(dst_xxh_home, xonsh_bin)
dst_xonshrc = os.path.join( dst_xxh_home, 'xonshrc.xsh')
dst_xonsh_plugins_rc = os.path.join( dst_xxh_home, 'xxh_plugins_rc.xsh')
host_info = $(cat @(f"{package_dir_path}/host_info.sh") | sed @(f's|_xxh_home_|{opt.host_xxh_home}|') | ssh @(ssh_v) @(ssh_arguments) @(host) -T "bash -s" ).strip()
dst_has_xxh = $(echo @(f"[[ -d {dst_xxh_home} ]] && echo -n 1 || echo -n 0") | ssh @(dst) -T "bash -s")
if host_info == '':
print(f'Unknown answer from host when checking user home path. Check your connection parameters using ordinary ssh')
exit(1)
if dst_has_xxh not in ['0','1']:
# https://github.com/xonsh/xonsh/issues/3367
print(f'Unknown answer from host when checking direcotry {dst_xxh_home}: {dst_has_xxh}')
sys.exit(1)
host_info = dict([l.split('=') for l in host_info.split('\n')])
if dst_has_xxh == '1' and opt.install_force == False:
if opt.verbose:
print(f'host_info: {host_info}')
if host_info['xxh_home_realpath'] == '':
print(f'Unknown answer from host when getting realpath for directory {host_xxh_home}')
exit(1)
if host_info['xxh_version'] == '':
print(f'Unknown answer from host when getting version for directory {host_xxh_home}')
exit(1)
if host_info['scp'] == '' and host_info['rsync'] == '':
print(f"There are no rsync or scp on target host. Sad but files can't be uploaded.")
exit(1)
host_xxh_home = host_info['xxh_home_realpath']
host_xonsh_bin = os.path.join(host_xxh_home, xonsh_bin_name)
host_xonshrc = os.path.join(host_xxh_home, 'xonshrc.xsh')
host_xonsh_plugins_rc = os.path.join(host_xxh_home, 'xxh_plugins_rc.xsh')
host_xxh_version = host_info['xxh_version']
if opt.install_force == False:
# Check version
try:
dst_xxh_settings = os.path.join(dst_xxh_home, 'settings.py')
dst_xxh_version = $(ssh -o LogLevel=QUIET @(dst) -t @(dst_xonsh_bin) --no-script-cache @(dst_xxh_settings) XXH_VERSION).strip()
except:
dst_xxh_version = None
ask_type = None
ask = False
if dst_xxh_version is None:
ask_type = 1
ask = f'Something went wrong while getting the remote xxh version.'
elif dst_xxh_version != local_xxh_version:
ask_type = 2
ask = f"Local xxh version '{local_xxh_version}' is not equal remote xxh version '{dst_xxh_version}'."
if host_xxh_version is '0':
ask = f'Host xxh home is not empty but something went wrong while getting host xxh version.'
elif host_xxh_version != '-1' and host_xxh_version != local_xxh_version:
ask = f"Local xxh version '{local_xxh_version}' is not equal host xxh version '{host_xxh_version}'."
if ask:
choice = input(f"{ask} What's next? \n"
+ f"s - [default] Stop here. You'll try to connect via ordinary ssh for backup current xxh home.\n"
+ f"u - Safe update. Remote xxh dir will be renamed and local xxh version will be installed.\n"
+ f"f - Force install local xxh version on remote host. Remote xxh installation will be lost.\n"
+ f"s - [default] Stop here. You'll try to connect using ordinary ssh for backup current xxh home.\n"
+ f"u - Safe update. Host xxh home will be renamed and local xxh version will be installed.\n"
+ f"f - Force install local xxh version on host. Host xxh installation will be lost.\n"
+ f"i - Ignore, cross fingers and continue the connection.\n"
+ f"S/u/f/i? ").lower()
if choice == 's' or choice.strip() == '':
print('Stopped')
sys.exit(0)
exit(0)
elif choice == 'u':
local_time = datetime.datetime.now().isoformat()[:19]
print(f"Move {dst}:{dst_xxh_home} to {dst}:{dst_xxh_home}-{local_time}")
echo @(f"mv {dst_xxh_home} {dst_xxh_home}-{local_time}" ) | ssh -o LogLevel=QUIET @(dst) -T "bash -s"
print(f"Move {host}:{host_xxh_home} to {host}:{host_xxh_home}-{local_time}")
echo @(f"mv {host_xxh_home} {host_xxh_home}-{local_time}") | ssh @(ssh_v) @(ssh_arguments) @(host) -T "bash -s"
opt.install = True
dst_has_xxh = '0'
elif choice == 'f':
opt.install = True
opt.install_force = True
@ -159,52 +214,90 @@ if dst_has_xxh == '1' and opt.install_force == False:
pass
else:
print('Unknown answer')
sys.exit(1)
exit(1)
if not opt.install and dst_has_xxh == '0':
yn = input(f"{dst}:{dst_xxh_home} not found. Install xxh? [y/n] ").lower()
if yn == 'y':
if host_xxh_version == '-1':
yn = input(f"{host}:{host_xxh_home} not found. Install xxh? [Y/n] ").strip().lower()
if yn == 'y' or yn == '':
opt.install = True
else:
print('Unknown answer')
sys.exit(1)
exit(1)
if opt.install:
print("\033[0;33m", end='')
if opt.method == 'appimage':
appimage_fullpath = os.path.join(local_xxh_home_path, xonsh_bin)
if not os.path.isfile(appimage_fullpath):
local_xonsh_appimage_fullpath = os.path.join(local_xxh_home_path, xonsh_bin_name)
if not os.path.isfile(local_xonsh_appimage_fullpath):
print(f'First time download and save xonsh AppImage from {url_appimage}')
wget -q --show-progress @(url_appimage) -O @(appimage_fullpath)
chmod +x @(appimage_fullpath)
if which('wget'):
r=![wget -q --show-progress @(url_appimage) -O @(local_xonsh_appimage_fullpath)]
if r.returncode != 0:
print(f'Error while download appimage using wget: {r}')
exit(0)
elif which('curl'):
r=![curl @(url_appimage) -o @(local_xonsh_appimage_fullpath)]
if r.returncode != 0:
print(f'Error while download appimage using curl: {r}')
exit(0)
else:
print('Please install wget or curl and try again. Howto: https://duckduckgo.com/?q=how+to+install+wget+in+linux')
exit(1)
chmod +x @(local_xonsh_appimage_fullpath)
else:
print(f'Method "{opt.method}" is not supported now')
if dst_has_xxh == "1":
if opt.install_force:
print(f'Before install xxh remove remote directory {dst}:{dst_xxh_home}')
echo @(f"rm -rf {dst_xxh_home}" ) | ssh -o LogLevel=QUIET @(dst) -T "bash -s"
else:
print(f'Remote directory exists: {dst_xxh_home}')
sys.exit(1)
if opt.install_force:
print(f'Before upload xxh remove host directory {host}:{host_xxh_home}')
echo @(f"rm -rf {host_xxh_home}/*") | ssh @(ssh_v) @(ssh_arguments) @(host) -T "bash -s"
print(f"Install xxh to {dst}:{dst_xxh_home}" )
rsync -az --info=progress2 --include ".*" @(local_xxh_home_path)/ @(dst):@(dst_xxh_home)/
rsync -az --info=progress2 --include ".*" @(package_dir_path)/ @(dst):@(dst_xxh_home)/
print(f"Install xxh to {host}:{host_xxh_home}" )
if which('rsync') and host_info['rsync']:
print('Upload using rsync')
rsync @(ssh_v) -e @(f"ssh {'' if ssh_v == [] else '-v'} {' '.join(ssh_arguments)}") -az --info=progress2 --include ".*" --exclude='*.pyc' @(local_xxh_home_path)/ @(host):@(host_xxh_home)/
rsync @(ssh_v) -e @(f"ssh {'' if ssh_v == [] else '-v'} {' '.join(ssh_arguments)}") -az --info=progress2 --include ".*" --exclude='*.pyc' @(package_dir_path)/ @(host):@(host_xxh_home)/
elif which('scp') and host_info['scp']:
print("Upload using scp. Note: install rsync on local and remote host to increase speed.")
scp_host = f"{host}:{host_xxh_home}/"
scp @(ssh_v) @(ssh_arguments) -r -C @([] if opt.verbose else ['-q']) @(local_xxh_home_path)/* @(scp_host)
scp @(ssh_v) @(ssh_arguments) -r -C @([] if opt.verbose else ['-q']) @(package_dir_path)/* @(scp_host)
else:
print('scp or rsync not found!')
plugins_fullpath = os.path.join(local_xxh_home_path, 'plugins')
if os.path.exists(plugins_fullpath):
print(f'Run plugins post install on {dst}')
print(f'Run plugins post install on {host}')
scripts=''
for script in sorted(glob.glob(os.path.join(plugins_fullpath, os.path.join('*','install.xsh')), recursive=True)):
scripts += " && %s -i --rc %s -- %s" % (dst_xonsh_bin, dst_xonshrc, script.replace(local_xxh_home_path + os.sep, ''))
scripts += " && %s -i --rc %s -- %s" % (host_xonsh_bin, host_xonshrc, script.replace(local_xxh_home_path + os.sep, ''))
print(f' * {script}')
if scripts:
echo @(f"cd {dst_xxh_home} {scripts}" ) | ssh -o LogLevel=QUIET @(dst) -T "bash -s"
echo @(f"cd {host_xxh_home} {scripts}" ) | ssh @(ssh_v) @(ssh_arguments) @(host) -T "bash -s"
print(f'First run xonsh on {dst}\033[0m')
print('Check xonsh')
host_settings_file = os.path.join(host_xxh_home, 'settings.py')
check = $(ssh @(ssh_v) @(ssh_arguments) @(host) -t @(host_xonsh_bin) --no-script-cache -i --rc @(host_xonshrc) -- @(host_settings_file) )
dst_plugins_rc = $(ssh -o LogLevel=QUIET @(dst) -t @(dst_xonsh_bin) --no-script-cache -i --rc @(dst_xonshrc) -- @(dst_xonsh_plugins_rc)).split('xxh-plugins#')[1]
ssh -o LogLevel=QUIET @(dst) -t @(dst_xonsh_bin) --no-script-cache -i --rc @(dst_xonshrc) @(dst_plugins_rc)
if opt.verbose:
print(f'Check xonsh result:\n{check}')
if check == '' or 'AppImages require FUSE to run' in check:
print('Check failed. Unpack AppImage...')
host_xonsh_bin_new = os.path.join(host_xxh_home, 'xonsh-squashfs/usr/python/bin/xonsh')
ssh @(ssh_v) @(ssh_arguments) @(host) -t @(f"cd {host_xxh_home} && ./{xonsh_bin_name} --appimage-extract | grep -E 'usr/python/bin/xonsh$' && mv squashfs-root xonsh-squashfs && mv {host_xonsh_bin} {host_xonsh_bin}-disabled && ln -s {host_xonsh_bin_new}")
host_xonsh_bin = host_xonsh_bin_new
print(f'First run xonsh on {host}\033[0m')
host_plugins_rc = $(ssh @(ssh_v) @(ssh_arguments) @(host) -t @(host_xonsh_bin) --no-script-cache -i --rc @(host_xonshrc) -- @(host_xonsh_plugins_rc) )
host_plugins_rc_list = host_plugins_rc.split('xxh-plugins#')
if len(host_plugins_rc_list) > 1:
host_plugins_rc_list = host_plugins_rc_list[1]
else:
print(f'Something went wrong while getting plugins info. Host answer: {host_plugins_rc}')
exit(1)
ssh @(ssh_v) @(ssh_arguments) @(host) -t @(host_xonsh_bin) --no-script-cache -i --rc @(host_xonshrc) @(host_plugins_rc_list)