From 675aa594d5cde9efc466e302f9a2442cb708d784 Mon Sep 17 00:00:00 2001 From: anki-code Date: Tue, 11 Feb 2020 22:27:54 +0300 Subject: [PATCH 01/13] #7 and #11 --- README.md | 61 +++++++++----- xonssh_xxh/settings.py | 2 +- xxh | 186 +++++++++++++++++++++++++++++------------ 3 files changed, 172 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index fa07efd..1de0b72 100644 --- a/README.md +++ b/README.md @@ -21,41 +21,56 @@ pip install xonssh-xxh ``` Then try: ``` -xxh +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] + [+rxh REMOTE_XXH_HOME] [+m METHOD] [+v] [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 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: appimage + +v, ++verbose Verbose mode. ``` ## Plugins diff --git a/xonssh_xxh/settings.py b/xonssh_xxh/settings.py index fd50939..da28c4f 100644 --- a/xonssh_xxh/settings.py +++ b/xonssh_xxh/settings.py @@ -1,7 +1,7 @@ import sys global_settings = { - 'XXH_VERSION': '0.1' + 'XXH_VERSION': '0.2.0' } if __name__ == "__main__": diff --git a/xxh b/xxh index 9c888c7..58c1ba7 100755 --- a/xxh +++ b/xxh @@ -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 @@ -15,10 +17,12 @@ url_appimage = 'https://github.com/niess/linuxdeploy-plugin-python/releases/down local_xxh_home_path = '~/.xxh' local_xxh_version = global_settings['XXH_VERSION'] remote_xxh_home_path = '~/.xxh' +install_methods = ['appimage'] +install_methods_str = ', '.join(install_methods) 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,40 +34,92 @@ 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 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=f"Installation method: {install_methods_str}") +argp.add_argument('+v','++verbose', default=False, action='store_true', help="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.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 install_methods: + print(f'Currently supported methods: {install_methods_str}') + exit(1) dst = opt.destination + + +if 'ssh://' not in dst: + dst = f'ssh://{dst}' + +url = urlparse(dst) +dst = url.hostname + +if not dst: + print(f"Wrong distination '{dst}'") + 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) + opt.install = True if opt.install_force else opt.install local_xxh_home_path = os.path.expanduser(opt.local_xxh_home) @@ -73,36 +129,37 @@ 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 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" -if opt.remote_xxh_home == '~/.xxh': - dst_user_home = $(ssh @(dst) -T "bash -c 'cd ~ && pwd'").strip() +if opt.remote_xxh_home[:2] == '~/': + dst_user_home = $(echo 'cd ~ && pwd' | ssh @(ssh_arguments) @(dst) -T "bash -s").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) + print(f'Unknown answer from host when checking user home path. Check your connection parameters using ordinary ssh') + exit(1) - dst_xxh_home = os.path.join(dst_user_home, '.xxh') + dst_xxh_home = os.path.join(dst_user_home, opt.remote_xxh_home[2:]) else: dst_xxh_home = opt.remote_xxh_home_path -dst_xxh_home_abspath = os.path.abspath(dst_xxh_home) -if dst_xxh_home_abspath == '/': +dst_xxh_home = os.path.abspath(dst_xxh_home) + +if dst_xxh_home == '/': print("Remote xxh home path {dst_xxh_home} looks like / and is not supported ;)") exit(1) @@ -111,18 +168,20 @@ 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') -dst_has_xxh = $(echo @(f"[[ -d {dst_xxh_home} ]] && echo -n 1 || echo -n 0") | ssh @(dst) -T "bash -s") +dst_has_xxh = $(echo @(f"[[ -d {dst_xxh_home} ]] && echo -n 1 || echo -n 0") | ssh @(ssh_arguments) @(dst) -T "bash -s") 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) + exit(1) -if dst_has_xxh == '1' and opt.install_force == False: +dst_has_xxh = dst_has_xxh == '1' + +if dst_has_xxh and 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() + dst_xxh_version = $(ssh @(ssh_arguments) @(dst) -t @(dst_xonsh_bin) --no-script-cache @(dst_xxh_settings) XXH_VERSION).strip() except: dst_xxh_version = None @@ -137,7 +196,7 @@ if dst_has_xxh == '1' and opt.install_force == False: 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"s - [default] Stop here. You'll try to connect using 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"i - Ignore, cross fingers and continue the connection.\n" @@ -145,13 +204,13 @@ if dst_has_xxh == '1' and opt.install_force == False: 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" + echo @(f"mv {dst_xxh_home} {dst_xxh_home}-{local_time}") | ssh @(ssh_arguments) @(dst) -T "bash -s" opt.install = True - dst_has_xxh = '0' + dst_has_xxh = False elif choice == 'f': opt.install = True opt.install_force = True @@ -159,39 +218,60 @@ 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': +if not opt.install and not dst_has_xxh: yn = input(f"{dst}:{dst_xxh_home} not found. Install xxh? [y/n] ").lower() if yn == 'y': opt.install = True else: print('Unknown answer') - sys.exit(1) + exit(1) if opt.install: + if not which('rsync'): + print('Please install rsync before trying to install xxh. Howto: https://duckduckgo.com/?q=how+to+install+rsync+in+linux') + exit(1) + 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): print(f'First time download and save xonsh AppImage from {url_appimage}') - wget -q --show-progress @(url_appimage) -O @(appimage_fullpath) + if which('wget'): + wget -q --show-progress @(url_appimage) -O @(appimage_fullpath) + elif which('curl'): + curl @(url_appimage) -o @(appimage_fullpath) + 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 @(appimage_fullpath) else: print(f'Method "{opt.method}" is not supported now') - if dst_has_xxh == "1": + if dst_has_xxh: 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" + echo @(f"rm -rf {dst_xxh_home}") | ssh @(ssh_arguments) @(dst) -T "bash -s" else: print(f'Remote directory exists: {dst_xxh_home}') - sys.exit(1) + exit(1) + else: + print(f'Create {dst}:{dst_xxh_home} ') + echo @(f"mkdir -p {dst_xxh_home}") | ssh @(ssh_arguments) @(dst) -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)/ + + if which('rsync'): + print('Upload using rsync') + rsync -e @(f"ssh {' '.join(ssh_arguments)}") -az --info=progress2 --include ".*" @(local_xxh_home_path)/ @(dst):@(dst_xxh_home)/ + rsync -e @(f"ssh {' '.join(ssh_arguments)}") -az --info=progress2 --include ".*" @(package_dir_path)/ @(dst):@(dst_xxh_home)/ + else: + print("Upload using scp. To increase speed install rsync!") + scp_dst = f"{dst}:{dst_xxh_home}/" + scp @(ssh_arguments) -r -C @([] if opt.verbose else ['-q']) @(local_xxh_home_path)/* @(scp_dst) + scp @(ssh_arguments) -r -C @([] if opt.verbose else ['-q']) @(package_dir_path)/* @(scp_dst) plugins_fullpath = os.path.join(local_xxh_home_path, 'plugins') if os.path.exists(plugins_fullpath): @@ -202,9 +282,9 @@ if opt.install: print(f' * {script}') if scripts: - echo @(f"cd {dst_xxh_home} {scripts}" ) | ssh -o LogLevel=QUIET @(dst) -T "bash -s" + echo @(f"cd {dst_xxh_home} {scripts}" ) | ssh @(ssh_arguments) @(dst) -T "bash -s" print(f'First run xonsh on {dst}\033[0m') -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) +dst_plugins_rc = $(ssh @(ssh_arguments) @(dst) -t @(dst_xonsh_bin) --no-script-cache -i --rc @(dst_xonshrc) -- @(dst_xonsh_plugins_rc)).split('xxh-plugins#')[1] +ssh @(ssh_arguments) @(dst) -t @(dst_xonsh_bin) --no-script-cache -i --rc @(dst_xonshrc) @(dst_plugins_rc) From b77d39d32abf25a09630291a74bed9de08911d18 Mon Sep 17 00:00:00 2001 From: anki-code Date: Tue, 11 Feb 2020 22:30:38 +0300 Subject: [PATCH 02/13] ;) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1de0b72..3215157 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ usage: xxh [-h] [-V] [-p SSH_PORT] [-l SSH_LOGIN_NAME] [-i SSH_IDENTITY_FILE] The xxh is for using the xonsh shell wherever you go through the ssh. - ___ __________ @ _ + ___ __________ @ @ _____ / \ \__/ ___ / ______ \ / \ contribution ____ / / __ \ \ / _/ https://github.com/xonssh/xxh From 6b2bbab746be46c306b2f000d48ca2b5bfbeebc2 Mon Sep 17 00:00:00 2001 From: anki-code Date: Tue, 11 Feb 2020 23:34:48 +0300 Subject: [PATCH 03/13] typo --- README.md | 4 ++-- xxh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3215157..b01b782 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ ssh arguments: -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 distanation host. - +if, ++install-force Delete remote xxh home and install xonsh to distanation host. + +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 +rxh REMOTE_XXH_HOME, ++remote-xxh-home REMOTE_XXH_HOME diff --git a/xxh b/xxh index 58c1ba7..67b9cc5 100755 --- a/xxh +++ b/xxh @@ -57,8 +57,8 @@ argp.add_argument('-l', dest='ssh_login_name', help="Specifies the user to log i 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 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('+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('+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=f"Installation method: {install_methods_str}") From a5e7fe2c523e9ca2caee5c04e4d5ca8ed04901c3 Mon Sep 17 00:00:00 2001 From: anki-code Date: Wed, 12 Feb 2020 03:00:00 +0300 Subject: [PATCH 04/13] clean --- xxh | 146 +++++++++++++++++++++++++++++------------------------------- 1 file changed, 70 insertions(+), 76 deletions(-) diff --git a/xxh b/xxh index 67b9cc5..ffb1540 100755 --- a/xxh +++ b/xxh @@ -14,11 +14,11 @@ 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' -install_methods = ['appimage'] -install_methods_str = ', '.join(install_methods) +local_xxh_home_path = '~/.xxh' +host_xxh_home_path = '~/.xxh' +portable_methods = ['appimage'] +portable_methods_str = ', '.join(portable_methods) 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}") @@ -60,8 +60,8 @@ argp.add_argument('destination', nargs='?', help="Destination may be specified a 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('+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=f"Installation method: {install_methods_str}") +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.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 ')\ @@ -77,21 +77,18 @@ if not opt.destination: print('Destination required. Try --help') exit(1) -if opt.method not in install_methods: - print(f'Currently supported methods: {install_methods_str}') +if opt.method not in portable_methods: + print(f'Currently supported methods: {portable_methods_str}') exit(1) -dst = opt.destination +if 'ssh://' not in opt.destination: + opt.destination = f'ssh://{opt.destination}' +url = urlparse(opt.destination) +host = url.hostname -if 'ssh://' not in dst: - dst = f'ssh://{dst}' - -url = urlparse(dst) -dst = url.hostname - -if not dst: - print(f"Wrong distination '{dst}'") +if not host: + print(f"Wrong distination '{host}'") exit(1) if url.port: @@ -145,60 +142,60 @@ else: 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" -if opt.remote_xxh_home[:2] == '~/': - dst_user_home = $(echo 'cd ~ && pwd' | ssh @(ssh_arguments) @(dst) -T "bash -s").strip() +if opt.host_xxh_home[:2] == '~/': + host_user_home = $(echo 'cd ~ && pwd' | ssh @(ssh_arguments) @(host) -T "bash -s").strip() - if dst_user_home == '': + if host_user_home == '': # https://github.com/xonsh/xonsh/issues/3367 print(f'Unknown answer from host when checking user home path. Check your connection parameters using ordinary ssh') exit(1) - dst_xxh_home = os.path.join(dst_user_home, opt.remote_xxh_home[2:]) + host_xxh_home = os.path.join(host_user_home, opt.host_xxh_home[2:]) else: - dst_xxh_home = opt.remote_xxh_home_path + host_xxh_home = opt.host_xxh_home_path -dst_xxh_home = os.path.abspath(dst_xxh_home) +host_xxh_home = os.path.abspath(host_xxh_home) -if dst_xxh_home == '/': - print("Remote xxh home path {dst_xxh_home} looks like / and is not supported ;)") +if host_xxh_home == '/': + print("Host xxh home path {host_xxh_home} looks like / and is not supported ;)") 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_xonsh_bin = os.path.join(host_xxh_home, xonsh_bin) +host_xonshrc = os.path.join(host_xxh_home, 'xonshrc.xsh') +host_xonsh_plugins_rc = os.path.join(host_xxh_home, 'xxh_plugins_rc.xsh') -dst_has_xxh = $(echo @(f"[[ -d {dst_xxh_home} ]] && echo -n 1 || echo -n 0") | ssh @(ssh_arguments) @(dst) -T "bash -s") +host_has_xxh = $(echo @(f"[[ -d {host_xxh_home} ]] && echo -n 1 || echo -n 0") | ssh @(ssh_arguments) @(host) -T "bash -s") -if dst_has_xxh not in ['0','1']: +if host_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}') + print(f'Unknown answer from host when checking direcotry {host_xxh_home}: {host_has_xxh}') exit(1) -dst_has_xxh = dst_has_xxh == '1' +host_has_xxh = host_has_xxh == '1' -if dst_has_xxh and opt.install_force == False: +if host_has_xxh and opt.install_force == False: # Check version try: - dst_xxh_settings = os.path.join(dst_xxh_home, 'settings.py') - dst_xxh_version = $(ssh @(ssh_arguments) @(dst) -t @(dst_xonsh_bin) --no-script-cache @(dst_xxh_settings) XXH_VERSION).strip() + host_xxh_settings = os.path.join(host_xxh_home, 'settings.py') + host_xxh_version = $(ssh @(ssh_arguments) @(host) -t @(host_xonsh_bin) --no-script-cache @(host_xxh_settings) XXH_VERSION).strip() except: - dst_xxh_version = None + host_xxh_version = None ask_type = None ask = False - if dst_xxh_version is None: + if host_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 = f'Something went wrong while getting the host xxh version.' + elif host_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}'." + 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 using 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"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() @@ -207,10 +204,10 @@ if dst_has_xxh and opt.install_force == False: 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 @(ssh_arguments) @(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_arguments) @(host) -T "bash -s" opt.install = True - dst_has_xxh = False + host_has_xxh = False elif choice == 'f': opt.install = True opt.install_force = True @@ -220,8 +217,8 @@ if dst_has_xxh and opt.install_force == False: print('Unknown answer') exit(1) -if not opt.install and not dst_has_xxh: - yn = input(f"{dst}:{dst_xxh_home} not found. Install xxh? [y/n] ").lower() +if not opt.install and not host_has_xxh: + yn = input(f"{host}:{host_xxh_home} not found. Install xxh? [y/n] ").lower() if yn == 'y': opt.install = True else: @@ -229,62 +226,59 @@ if not opt.install and not dst_has_xxh: exit(1) if opt.install: - if not which('rsync'): - print('Please install rsync before trying to install xxh. Howto: https://duckduckgo.com/?q=how+to+install+rsync+in+linux') - exit(1) - 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) + if not os.path.isfile(local_xonsh_appimage_fullpath): print(f'First time download and save xonsh AppImage from {url_appimage}') if which('wget'): - wget -q --show-progress @(url_appimage) -O @(appimage_fullpath) + wget -q --show-progress @(url_appimage) -O @(local_xonsh_appimage_fullpath) elif which('curl'): - curl @(url_appimage) -o @(appimage_fullpath) + curl @(url_appimage) -o @(local_xonsh_appimage_fullpath) 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 @(appimage_fullpath) + chmod +x @(local_xonsh_appimage_fullpath) else: print(f'Method "{opt.method}" is not supported now') - if dst_has_xxh: + if host_has_xxh: if opt.install_force: - print(f'Before install xxh remove remote directory {dst}:{dst_xxh_home}') - echo @(f"rm -rf {dst_xxh_home}") | ssh @(ssh_arguments) @(dst) -T "bash -s" + print(f'Before upload xxh remove host directory {host}:{host_xxh_home}') + echo @(f"rm -rf {host_xxh_home}") | ssh @(ssh_arguments) @(host) -T "bash -s" else: - print(f'Remote directory exists: {dst_xxh_home}') + print(f'Host directory exists: {host_xxh_home}') exit(1) else: - print(f'Create {dst}:{dst_xxh_home} ') - echo @(f"mkdir -p {dst_xxh_home}") | ssh @(ssh_arguments) @(dst) -T "bash -s" - - print(f"Install xxh to {dst}:{dst_xxh_home}" ) + print(f'Create {host}:{host_xxh_home} ') + echo @(f"mkdir -p {host_xxh_home}") | ssh @(ssh_arguments) @(host) -T "bash -s" + print(f"Install xxh to {host}:{host_xxh_home}" ) if which('rsync'): print('Upload using rsync') - rsync -e @(f"ssh {' '.join(ssh_arguments)}") -az --info=progress2 --include ".*" @(local_xxh_home_path)/ @(dst):@(dst_xxh_home)/ - rsync -e @(f"ssh {' '.join(ssh_arguments)}") -az --info=progress2 --include ".*" @(package_dir_path)/ @(dst):@(dst_xxh_home)/ - else: + rsync -e @(f"ssh {' '.join(ssh_arguments)}") -az --info=progress2 --include ".*" @(local_xxh_home_path)/ @(host):@(host_xxh_home)/ + rsync -e @(f"ssh {' '.join(ssh_arguments)}") -az --info=progress2 --include ".*" @(package_dir_path)/ @(host):@(host_xxh_home)/ + elif which('scp'): print("Upload using scp. To increase speed install rsync!") - scp_dst = f"{dst}:{dst_xxh_home}/" - scp @(ssh_arguments) -r -C @([] if opt.verbose else ['-q']) @(local_xxh_home_path)/* @(scp_dst) - scp @(ssh_arguments) -r -C @([] if opt.verbose else ['-q']) @(package_dir_path)/* @(scp_dst) + scp_host = f"{host}:{host_xxh_home}/" + scp @(ssh_arguments) -r -C @([] if opt.verbose else ['-q']) @(local_xxh_home_path)/* @(scp_host) + scp @(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 @(ssh_arguments) @(dst) -T "bash -s" + echo @(f"cd {host_xxh_home} {scripts}" ) | ssh @(ssh_arguments) @(host) -T "bash -s" - print(f'First run xonsh on {dst}\033[0m') + print(f'First run xonsh on {host}\033[0m') -dst_plugins_rc = $(ssh @(ssh_arguments) @(dst) -t @(dst_xonsh_bin) --no-script-cache -i --rc @(dst_xonshrc) -- @(dst_xonsh_plugins_rc)).split('xxh-plugins#')[1] -ssh @(ssh_arguments) @(dst) -t @(dst_xonsh_bin) --no-script-cache -i --rc @(dst_xonshrc) @(dst_plugins_rc) +host_plugins_rc = $(ssh @(ssh_arguments) @(host) -t @(host_xonsh_bin) --no-script-cache -i --rc @(host_xonshrc) -- @(host_xonsh_plugins_rc)).split('xxh-plugins#')[1] +ssh @(ssh_arguments) @(host) -t @(host_xonsh_bin) --no-script-cache -i --rc @(host_xonshrc) @(host_plugins_rc) \ No newline at end of file From a1a92838c9ad2dc799fb1540d34ab04303bb4ec1 Mon Sep 17 00:00:00 2001 From: anki-code Date: Wed, 12 Feb 2020 03:51:13 +0300 Subject: [PATCH 05/13] #2 --- host_info.sh | 13 ++++++++ xxh | 86 +++++++++++++++++++++------------------------------- 2 files changed, 47 insertions(+), 52 deletions(-) create mode 100644 host_info.sh diff --git a/host_info.sh b/host_info.sh new file mode 100644 index 0000000..c17fad6 --- /dev/null +++ b/host_info.sh @@ -0,0 +1,13 @@ +#!/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 diff --git a/xxh b/xxh index ffb1540..31df8d2 100755 --- a/xxh +++ b/xxh @@ -19,6 +19,7 @@ local_xxh_home_path = '~/.xxh' host_xxh_home_path = '~/.xxh' portable_methods = ['appimage'] portable_methods_str = ', '.join(portable_methods) +xonsh_bin = '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}") @@ -142,53 +143,42 @@ else: 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" -if opt.host_xxh_home[:2] == '~/': - host_user_home = $(echo 'cd ~ && pwd' | ssh @(ssh_arguments) @(host) -T "bash -s").strip() - - if host_user_home == '': - # https://github.com/xonsh/xonsh/issues/3367 - print(f'Unknown answer from host when checking user home path. Check your connection parameters using ordinary ssh') - exit(1) - - host_xxh_home = os.path.join(host_user_home, opt.host_xxh_home[2:]) -else: - host_xxh_home = opt.host_xxh_home_path - -host_xxh_home = os.path.abspath(host_xxh_home) - -if host_xxh_home == '/': - print("Host xxh home path {host_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' +host_info = $(cat host_info.sh | sed @(f's|_xxh_home_|{opt.host_xxh_home}|') | ssh @(ssh_arguments) @(host) -T "bash -s" ).strip() + +if host_info == '': + print(f'Unknown answer from host when checking user home path. Check your connection parameters using ordinary ssh') + exit(1) + +host_info = dict([l.split('=') for l in host_info.split('\n')]) + +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) + +host_xxh_home = host_info['xxh_home_realpath'] host_xonsh_bin = os.path.join(host_xxh_home, xonsh_bin) 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_has_xxh = $(echo @(f"[[ -d {host_xxh_home} ]] && echo -n 1 || echo -n 0") | ssh @(ssh_arguments) @(host) -T "bash -s") +host_xxh_version = host_info['xxh_version'] -if host_has_xxh not in ['0','1']: - # https://github.com/xonsh/xonsh/issues/3367 - print(f'Unknown answer from host when checking direcotry {host_xxh_home}: {host_has_xxh}') - exit(1) - -host_has_xxh = host_has_xxh == '1' - -if host_has_xxh and opt.install_force == False: +if opt.install_force == False: # Check version - try: - host_xxh_settings = os.path.join(host_xxh_home, 'settings.py') - host_xxh_version = $(ssh @(ssh_arguments) @(host) -t @(host_xonsh_bin) --no-script-cache @(host_xxh_settings) XXH_VERSION).strip() - except: - host_xxh_version = None - - ask_type = None ask = False - if host_xxh_version is None: - ask_type = 1 - ask = f'Something went wrong while getting the host xxh version.' - elif host_xxh_version != local_xxh_version: - ask_type = 2 + 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: @@ -207,7 +197,6 @@ if host_has_xxh and opt.install_force == False: 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_arguments) @(host) -T "bash -s" opt.install = True - host_has_xxh = False elif choice == 'f': opt.install = True opt.install_force = True @@ -217,9 +206,9 @@ if host_has_xxh and opt.install_force == False: print('Unknown answer') exit(1) -if not opt.install and not host_has_xxh: - yn = input(f"{host}:{host_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') @@ -243,16 +232,9 @@ if opt.install: else: print(f'Method "{opt.method}" is not supported now') - if host_has_xxh: - 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_arguments) @(host) -T "bash -s" - else: - print(f'Host directory exists: {host_xxh_home}') - exit(1) - else: - print(f'Create {host}:{host_xxh_home} ') - echo @(f"mkdir -p {host_xxh_home}") | ssh @(ssh_arguments) @(host) -T "bash -s" + 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_arguments) @(host) -T "bash -s" print(f"Install xxh to {host}:{host_xxh_home}" ) if which('rsync'): From 3923020bb8b4ba19360ca87da6a115ab3238ccf0 Mon Sep 17 00:00:00 2001 From: anki-code Date: Wed, 12 Feb 2020 13:29:22 +0300 Subject: [PATCH 06/13] #2 micro --- setup.py | 2 +- host_info.sh => xonssh_xxh/host_info.sh | 0 xxh | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename host_info.sh => xonssh_xxh/host_info.sh (100%) diff --git a/setup.py b/setup.py index de1fb6c..7a80666 100644 --- a/setup.py +++ b/setup.py @@ -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", diff --git a/host_info.sh b/xonssh_xxh/host_info.sh similarity index 100% rename from host_info.sh rename to xonssh_xxh/host_info.sh diff --git a/xxh b/xxh index 31df8d2..ab5c0a4 100755 --- a/xxh +++ b/xxh @@ -147,7 +147,7 @@ if os.path.abspath(opt.host_xxh_home) == '/': print("Host xxh home path {host_xxh_home} looks like /. Please check twice!") exit(1) -host_info = $(cat host_info.sh | sed @(f's|_xxh_home_|{opt.host_xxh_home}|') | ssh @(ssh_arguments) @(host) -T "bash -s" ).strip() +host_info = $(cat @(f"{package_dir_path}/host_info.sh") | sed @(f's|_xxh_home_|{opt.host_xxh_home}|') | ssh @(ssh_arguments) @(host) -T "bash -s" ).strip() if host_info == '': print(f'Unknown answer from host when checking user home path. Check your connection parameters using ordinary ssh') From 155facd3047fa0f576be6f2629fd4d5aac8eebbf Mon Sep 17 00:00:00 2001 From: anki-code Date: Wed, 12 Feb 2020 16:37:05 +0300 Subject: [PATCH 07/13] text --- xxh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xxh b/xxh index ab5c0a4..a086a6e 100755 --- a/xxh +++ b/xxh @@ -130,7 +130,7 @@ if os.path.exists(local_xxh_home_path): 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}') + print(f'Create local xxh home path: {local_xxh_home_path}') mkdir -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}") From 383f3913a0f81860f1e055e9846c9eee17cf5480 Mon Sep 17 00:00:00 2001 From: anki-code Date: Wed, 12 Feb 2020 17:08:21 +0300 Subject: [PATCH 08/13] README xxh-tests --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index b01b782..538265e 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,9 @@ When you run `xxh ` 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 or your own hosts for development. + ## Known Issues ### GLIBs versions From 46aea5891fb9ec9e8dc1e52143d91156dfc5796d Mon Sep 17 00:00:00 2001 From: anki-code Date: Wed, 12 Feb 2020 22:10:48 +0300 Subject: [PATCH 09/13] check rsync scp --- xonssh_xxh/host_info.sh | 3 +++ xxh | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/xonssh_xxh/host_info.sh b/xonssh_xxh/host_info.sh index c17fad6..e1db766 100644 --- a/xonssh_xxh/host_info.sh +++ b/xonssh_xxh/host_info.sh @@ -11,3 +11,6 @@ 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` \ No newline at end of file diff --git a/xxh b/xxh index a086a6e..0000838 100755 --- a/xxh +++ b/xxh @@ -166,6 +166,10 @@ 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) host_xonshrc = os.path.join(host_xxh_home, 'xonshrc.xsh') @@ -242,7 +246,7 @@ if opt.install: rsync -e @(f"ssh {' '.join(ssh_arguments)}") -az --info=progress2 --include ".*" @(local_xxh_home_path)/ @(host):@(host_xxh_home)/ rsync -e @(f"ssh {' '.join(ssh_arguments)}") -az --info=progress2 --include ".*" @(package_dir_path)/ @(host):@(host_xxh_home)/ elif which('scp'): - print("Upload using scp. To increase speed install rsync!") + print("Upload using scp. Note: install rsync on local and remote host to increase speed.") scp_host = f"{host}:{host_xxh_home}/" scp @(ssh_arguments) -r -C @([] if opt.verbose else ['-q']) @(local_xxh_home_path)/* @(scp_host) scp @(ssh_arguments) -r -C @([] if opt.verbose else ['-q']) @(package_dir_path)/* @(scp_host) From 93bee546a5c74b686b7a585ea313f75205753408 Mon Sep 17 00:00:00 2001 From: anki-code Date: Wed, 12 Feb 2020 22:53:32 +0300 Subject: [PATCH 10/13] check rsync scp --- xxh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xxh b/xxh index 0000838..702ffd6 100755 --- a/xxh +++ b/xxh @@ -241,11 +241,11 @@ if opt.install: echo @(f"rm -rf {host_xxh_home}/*") | ssh @(ssh_arguments) @(host) -T "bash -s" print(f"Install xxh to {host}:{host_xxh_home}" ) - if which('rsync'): + if which('rsync') and host_info['rsync']: print('Upload using rsync') rsync -e @(f"ssh {' '.join(ssh_arguments)}") -az --info=progress2 --include ".*" @(local_xxh_home_path)/ @(host):@(host_xxh_home)/ rsync -e @(f"ssh {' '.join(ssh_arguments)}") -az --info=progress2 --include ".*" @(package_dir_path)/ @(host):@(host_xxh_home)/ - elif which('scp'): + 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_arguments) -r -C @([] if opt.verbose else ['-q']) @(local_xxh_home_path)/* @(scp_host) From 5ae0364923cbf960882ae503b1f7200d9ef466c9 Mon Sep 17 00:00:00 2001 From: anki-code Date: Thu, 13 Feb 2020 13:22:57 +0300 Subject: [PATCH 11/13] README --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 538265e..486b53a 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,11 @@ When you run `xxh ` 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 or your own hosts for 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 From 4aba82de18a3c18aadd0635243b8a28370890d96 Mon Sep 17 00:00:00 2001 From: anki-code Date: Thu, 13 Feb 2020 22:16:03 +0300 Subject: [PATCH 12/13] vverbose + freespace + appimage unpack --- xonssh_xxh/host_info.sh | 3 +- xonssh_xxh/settings.py | 7 ++++- xxh | 67 ++++++++++++++++++++++++++++++----------- 3 files changed, 58 insertions(+), 19 deletions(-) diff --git a/xonssh_xxh/host_info.sh b/xonssh_xxh/host_info.sh index e1db766..2829791 100644 --- a/xonssh_xxh/host_info.sh +++ b/xonssh_xxh/host_info.sh @@ -13,4 +13,5 @@ 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` \ No newline at end of file +echo scp=`command -v scp` +echo xxh_home_freespace=`df -k --output=avail $xxh_home_realpath | tail -n1` \ No newline at end of file diff --git a/xonssh_xxh/settings.py b/xonssh_xxh/settings.py index da28c4f..3543105 100644 --- a/xonssh_xxh/settings.py +++ b/xonssh_xxh/settings.py @@ -1,10 +1,15 @@ -import sys +import sys, os global_settings = { '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: diff --git a/xxh b/xxh index 702ffd6..1a4108b 100755 --- a/xxh +++ b/xxh @@ -19,7 +19,7 @@ local_xxh_home_path = '~/.xxh' host_xxh_home_path = '~/.xxh' portable_methods = ['appimage'] portable_methods_str = ', '.join(portable_methods) -xonsh_bin = 'xonsh' +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}") @@ -64,12 +64,16 @@ argp.add_argument('+lxh','++local-xxh-home', default=local_xxh_home_path, help=f 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}") exit(0) @@ -120,6 +124,8 @@ if not which('ssh'): 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__)) @@ -131,7 +137,7 @@ if os.path.exists(local_xxh_home_path): elif os.path.exists(local_xxh_home_parent): if os.access(local_xxh_home_parent, os.W_OK): print(f'Create local xxh home path: {local_xxh_home_path}') - mkdir -p @(local_xxh_home_path) @(local_xxh_home_path)/plugins + 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}") exit(1) @@ -141,13 +147,13 @@ else: # 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 os.path.abspath(opt.host_xxh_home) == '/': print("Host xxh home path {host_xxh_home} looks like /. Please check twice!") exit(1) -host_info = $(cat @(f"{package_dir_path}/host_info.sh") | sed @(f's|_xxh_home_|{opt.host_xxh_home}|') | ssh @(ssh_arguments) @(host) -T "bash -s" ).strip() +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() if host_info == '': print(f'Unknown answer from host when checking user home path. Check your connection parameters using ordinary ssh') @@ -171,7 +177,7 @@ if host_info['scp'] == '' and host_info['rsync'] == '': exit(1) host_xxh_home = host_info['xxh_home_realpath'] -host_xonsh_bin = os.path.join(host_xxh_home, xonsh_bin) +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') @@ -199,7 +205,7 @@ if opt.install_force == False: elif choice == 'u': local_time = datetime.datetime.now().isoformat()[:19] 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_arguments) @(host) -T "bash -s" + echo @(f"mv {host_xxh_home} {host_xxh_home}-{local_time}") | ssh @(ssh_v) @(ssh_arguments) @(host) -T "bash -s" opt.install = True elif choice == 'f': opt.install = True @@ -221,13 +227,19 @@ if host_xxh_version == '-1': if opt.install: print("\033[0;33m", end='') if opt.method == 'appimage': - local_xonsh_appimage_fullpath = os.path.join(local_xxh_home_path, xonsh_bin) + 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}') if which('wget'): - wget -q --show-progress @(url_appimage) -O @(local_xonsh_appimage_fullpath) + 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'): - curl @(url_appimage) -o @(local_xonsh_appimage_fullpath) + 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) @@ -238,18 +250,18 @@ if opt.install: 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_arguments) @(host) -T "bash -s" + echo @(f"rm -rf {host_xxh_home}/*") | ssh @(ssh_v) @(ssh_arguments) @(host) -T "bash -s" print(f"Install xxh to {host}:{host_xxh_home}" ) if which('rsync') and host_info['rsync']: print('Upload using rsync') - rsync -e @(f"ssh {' '.join(ssh_arguments)}") -az --info=progress2 --include ".*" @(local_xxh_home_path)/ @(host):@(host_xxh_home)/ - rsync -e @(f"ssh {' '.join(ssh_arguments)}") -az --info=progress2 --include ".*" @(package_dir_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' @(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_arguments) -r -C @([] if opt.verbose else ['-q']) @(local_xxh_home_path)/* @(scp_host) - scp @(ssh_arguments) -r -C @([] if opt.verbose else ['-q']) @(package_dir_path)/* @(scp_host) + 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!') @@ -262,9 +274,30 @@ if opt.install: print(f' * {script}') if scripts: - echo @(f"cd {host_xxh_home} {scripts}" ) | ssh @(ssh_arguments) @(host) -T "bash -s" + echo @(f"cd {host_xxh_home} {scripts}" ) | ssh @(ssh_v) @(ssh_arguments) @(host) -T "bash -s" + + 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) ) + + 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_arguments) @(host) -t @(host_xonsh_bin) --no-script-cache -i --rc @(host_xonshrc) -- @(host_xonsh_plugins_rc)).split('xxh-plugins#')[1] -ssh @(ssh_arguments) @(host) -t @(host_xonsh_bin) --no-script-cache -i --rc @(host_xonshrc) @(host_plugins_rc) \ No newline at end of file +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) From 2a916e3a059706196dc03e8a45c985867530725b Mon Sep 17 00:00:00 2001 From: anki-code Date: Thu, 13 Feb 2020 23:19:48 +0300 Subject: [PATCH 13/13] readme --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 486b53a..54d73c3 100644 --- a/README.md +++ b/README.md @@ -26,14 +26,14 @@ xxh <[user@]host[:port] or servername from ~/.ssh/config> ## Usage ``` -$ ./xxh --help +$ 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] - [+rxh REMOTE_XXH_HOME] [+m METHOD] [+v] + [+hxh HOST_XXH_HOME] [+m METHOD] [+v] [+vv] [destination] The xxh is for using the xonsh shell wherever you go through the ssh. @@ -44,9 +44,9 @@ The xxh is for using the xonsh shell wherever you go through the ssh. ____ / / __ \ \ / _/ https://github.com/xonssh/xxh __ ( / / / \ \ / \ \___/ / / / plugins - ____\ /__/ / https://github.com/search?q=xxh-plugin - / \________/ / - /____________________/ + ___\ /__/ / https://github.com/search?q=xxh-plugin + / \________/ / + /___________________/ required arguments: destination Destination may be specified as [user@]hostname[:port] or server name from ~/.ssh/config @@ -66,11 +66,12 @@ xxh arguments: +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 - +rxh REMOTE_XXH_HOME, ++remote-xxh-home REMOTE_XXH_HOME - Remote 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 - Installation method: appimage + Portable method: appimage +v, ++verbose Verbose mode. + +vv, ++vverbose Super verbose mode. ``` ## Plugins