You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

114 lines
3.6 KiB
Python

#!/usr/bin/env python3
'''
Author: sp4ke
Email: contact@honeybadger.systems
Github: https://github.com/sp4ke
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/'
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, package, debug=False):
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(CARGO_BIN_PATH, bin_name).expanduser()
local_path = Path(CARGO_BIN_PATH).expanduser().__str__()+'/'
try:
get_remote_bin = conn.get(bin_path, local=local_path)
except FileNotFoundError as e:
print('could not copy installed package {} from remote'.format(package))
print(f'installed [bold blue]{bin_name}[/bold blue] at {CARGO_BIN_PATH}')
def install(args):
package = args.package
conn = Connection(args.host)
cargo_install = conn.run('cargo install {}'.format(package), warn=True, echo=True)
print('{}'.format(cargo_install.stdout.strip()))
if cargo_install.failed:
print(cargo_install.stderr)
sys.exit(ERR_CMD)
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)
parser.add_argument('-d', '--debug', action='store_true')
#TODO: use a dynamic positional argument, we can pass anything to cargo
#TODO: it will be tried on the remote cargo and raise error if it does
#TODO: not exist. To do special handling (like install) just watch the
#TODO: argument value
subparsers = parser.add_subparsers(dest='command')
install_parser = subparsers.add_parser('install', help='install packages from crates.io with remote build')
install_parser.set_defaults(func=install)
install_parser.add_argument('package', help='package name', metavar='PACKAGE_NAME')
build = subparsers.add_parser('build', help='remote build local cargo package')
args = parser.parse_args()
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()