157 lines
4.9 KiB
Python
Executable File
157 lines
4.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
'''
|
|
Author: blob42
|
|
Email: contact@blob42.xyz
|
|
Github: https://github.com/blob42
|
|
Description: run cargo commands on a remote workstation/server
|
|
'''
|
|
|
|
|
|
import argparse
|
|
from invoke.exceptions import UnexpectedExit
|
|
from fabric import Connection
|
|
import sys
|
|
from pathlib import Path
|
|
import shutil
|
|
import sys
|
|
import os
|
|
from rich import print
|
|
import logging
|
|
from rich.logging import RichHandler
|
|
|
|
LOG_FORMAT = '%(message)s'
|
|
logging.basicConfig(
|
|
level='WARN', format=LOG_FORMAT, datefmt='[%X]', handlers=[RichHandler(markup=True)]
|
|
)
|
|
log = logging.getLogger('rcargo')
|
|
|
|
|
|
CARGO_BIN_PATH='~/.cargo/bin/'
|
|
LOCAL_BIN_PATH=''
|
|
DEPS = [
|
|
'rsync'
|
|
]
|
|
|
|
ERR_CMD = 1
|
|
ERR_DEPENDENCY = 2
|
|
|
|
#TODO: binary and package names might differ: use `cargo install --list to extract bin name`
|
|
def inst_get_binary(conn: Connection, package: str, debug: bool = False) -> None:
|
|
find_bin = f'cargo install --list | grep -A1 {package} | sed -nE "s/ *//; 2 p"'
|
|
bin_name = ''
|
|
try:
|
|
res = conn.run(find_bin, echo=False, hide='out')
|
|
if len(res.stdout) == 0:
|
|
raise ValueError
|
|
bin_name = res.stdout.strip()
|
|
log.info(f'fetching binary [bold blue]{bin_name}[/bold blue] from package [bold green]{package}[/bold green]')
|
|
except Exception as e:
|
|
raise e
|
|
|
|
bin_path = Path(LOCAL_BIN_PATH, bin_name).expanduser()
|
|
local_path = Path(LOCAL_BIN_PATH).expanduser().__str__()+'/'
|
|
|
|
try:
|
|
if os.path.exists(bin_path):
|
|
os.remove(bin_path)
|
|
conn.get(bin_path, local=local_path)
|
|
except FileNotFoundError as e:
|
|
print('could not copy installed package {} from remote'.format(package))
|
|
sys.exit(ERR_CMD)
|
|
|
|
print(f'installed [bold blue]{bin_name}[/bold blue] at {LOCAL_BIN_PATH}')
|
|
|
|
|
|
def list(args):
|
|
"""List installed rust binaries on remote host"""
|
|
conn = Connection(args.host)
|
|
cargo_list = conn.run("cargo install --list | grep 'v.*:$'", echo=True)
|
|
# print(f"{cargo_list.stdout.strip()}")
|
|
if cargo_list.failed:
|
|
print(cargo_list.stderr)
|
|
sys.exit(ERR_CMD)
|
|
|
|
|
|
def install(args):
|
|
"""Install rust packages from crates.io on remote host and copy binary to local machine."""
|
|
args.debug and print(f'{args}')
|
|
print(f'{LOCAL_BIN_PATH}')
|
|
|
|
# args could be a package name or a list of parameters for cargo install
|
|
package = ''
|
|
params = []
|
|
if len(args.package) == 1 and not args.package[0].startswith('-'):
|
|
package = args.package[0]
|
|
else:
|
|
for arg in args.package:
|
|
if arg.startswith('--') or arg.startswith('-'):
|
|
params.append(arg)
|
|
else:
|
|
package = arg
|
|
|
|
# if len(package) == 0:
|
|
# print('no package specified')
|
|
# sys.exit(ERR_CMD)
|
|
|
|
args.debug and print(f"package {package}, params: {params}")
|
|
conn = Connection(args.host)
|
|
|
|
param_str = ' '.join(params)
|
|
cargo_install = conn.run(f'cargo install {param_str} {package}', warn=True, echo=True)
|
|
# print('{}'.format(cargo_install.stdout.strip()))
|
|
if cargo_install.failed:
|
|
print(cargo_install.stderr)
|
|
sys.exit(ERR_CMD)
|
|
if len(package) > 0:
|
|
inst_get_binary(conn, package, args.debug)
|
|
|
|
|
|
def ensure_deps():
|
|
for dep in DEPS:
|
|
installed = shutil.which(dep)
|
|
if installed is None:
|
|
print('{} dependency missing'.format(dep), file=sys.stderr)
|
|
sys.exit(ERR_DEPENDENCY)
|
|
|
|
# def any_cmd(args):
|
|
# conn = Connection(args.host)
|
|
# run = conn.run(f'cargo {args.command}', echo=True)
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
ensure_deps()
|
|
default_host = os.environ.get('RCARGO_HOST', 'localhost')
|
|
parser = argparse.ArgumentParser(description='remote rust cargo commands')
|
|
parser.add_argument('-H', '--host', default=default_host,
|
|
help="remote cargo host")
|
|
parser.add_argument('-d', '--debug', action='store_true')
|
|
parser.add_argument('-b', '--bin-path',
|
|
default=CARGO_BIN_PATH,
|
|
help='local path for installed binary',
|
|
metavar='')
|
|
|
|
subparsers = parser.add_subparsers(dest='command')
|
|
list_parser = subparsers.add_parser('list', help='list installed packages on remote host')
|
|
list_parser.set_defaults(func=list)
|
|
|
|
install_parser = subparsers.add_parser('install', prefix_chars="+", help='install packages from crates.io with remote build')
|
|
install_parser.set_defaults(func=install)
|
|
|
|
install_parser.add_argument('package', nargs="*", help='package name', metavar='PACKAGE_NAME')
|
|
|
|
|
|
build = subparsers.add_parser('build', help='remote build local cargo package')
|
|
|
|
args = parser.parse_args()
|
|
LOCAL_BIN_PATH = args.bin_path
|
|
if args.debug:
|
|
log.setLevel('INFO')
|
|
if args.host == 'localhost':
|
|
print('you need to define a host with -H option or RCARGO_HOST env variable')
|
|
sys.exit(1)
|
|
if args.command is not None:
|
|
args.func(args)
|
|
else:
|
|
parser.print_help()
|