"""
Run scripts to setup system dependencies and autopilot plugins
::
> # to list scripts
> python3 -m autopilot.setup.run_script --list
> # to execute one script (setup hifiberry soundcard)
> python3 -m autopilot.setup.run_script hifiberry
> # to execute multiple scripts
> python3 -m autopilot.setup.run_script hifiberry jackd
"""
import typing
import subprocess
from autopilot.setup.scripts import SCRIPTS
import argparse
import sys
parser = argparse.ArgumentParser(description="Run autopilot setup script(s)")
parser.add_argument('scripts', nargs='*', type=str)
parser.add_argument('--list', help="list available setup scripts!", action='store_true')
[docs]def call_series(commands:typing.List[typing.Union[str, dict]], series_name=None, verbose:bool=True) -> bool:
"""
Call a series of commands, giving a single return code on completion or failure
See :mod:`.setup.scripts` for syntax of command list.
Args:
commands (list): List of strings or dicts to call, see :mod:`.setup.scripts`
series_name (None, str): If provided, print name of currently running script
verbose (bool): If ``True`` (default), print command and status messages.
Returns:
bool - ``True`` if completed successfully
"""
if series_name and verbose:
print('\n\033[1;37;42m Running commands for {}\u001b[0m'.format(series_name))
# have to just combine them -- can't do multiple calls b/c shell doesn't preserve between them
combined_calls = ""
last_command = len(commands)-1
for i, command in enumerate(commands):
join_with = " && "
if isinstance(command, str):
# just a command, default necessary
combined_calls += command
elif isinstance(command, dict):
combined_calls += command['command']
if command.get('optional', False):
join_with = "; "
if i < last_command:
combined_calls += join_with
if verbose:
print('Executing:\n {}'.format(combined_calls))
result = subprocess.run(combined_calls, shell=True, executable='/bin/bash')
status = False
if result.returncode == 0:
status = True
if series_name and verbose:
if status:
print('\n\033[1;37;42m {} Successful, you lucky duck\u001b[0m'.format(series_name))
else:
print('\n\033[1;37;41m {} Failed, check the error message & ur crystal ball\u001b[0m'.format(series_name))
return status
[docs]def run_script(script_name):
"""
Thin wrapper around :func:`.call_series` that gets a script by name from
:data:`.scripts.SCRIPTS` and passes the list of ``commands``
Args:
script_name (str): name of a script in :data:`.scripts.SCRIPTS`
"""
if script_name in SCRIPTS.keys():
call_series(SCRIPTS[script_name]['commands'], script_name)
else:
raise NameError('No script named {}, must be one of {}'.format(script_name, "\n".join(SCRIPTS.keys())))
[docs]def run_scripts(scripts:typing.List[str], return_all:bool=False,print_status:bool=True) -> typing.Union[bool, typing.Dict[str,bool]]:
"""
Run a series of scripts, printing results
Args:
scripts (list): list of script names
return_all (bool): if True, return dict of ``{script:success}`` for each
called script. If ``False`` (default), return single bool if all commands were successful
print_status (bool): if ``True`` (default), print whether each script completed successfully or not.
Returns:
bool: success or failure of scripts - ``True`` if all were successful, ``False`` otherwise.
"""
env_results = {}
for script_name in scripts:
commands = SCRIPTS[script_name].get('commands', None)
if commands is not None:
env_results[script_name] = call_series(commands, script_name)
# indicate global success
success = True
# make results string
if print_status:
env_result = "\033[0;32;40m\n--------------------------------\nScript Results:\n"
for config, result in env_results.items():
if result:
env_result += " [ SUCCESS ] "
else:
env_result += " [ FAILURE ] "
success = False
env_result += config
env_result += '\n'
env_result += '--------------------------------\u001b[0m'
print(env_result)
if return_all:
return env_results
else:
return success
[docs]def list_scripts():
"""
Print a formatted list of names in :data:`.scripts.SCRIPTS`
"""
print('\nAvailable Scripts:\n----------------------------')
# find longest script name
longest_name = 0
for script_name in SCRIPTS.keys():
if len(script_name) > longest_name:
longest_name = len(script_name)
for script_name in sorted(SCRIPTS.keys()):
pad = " " * (longest_name - len(script_name))
print(f'\033[1;37;42m{script_name}{pad}\u001b[0m : {SCRIPTS[script_name]["text"]}')
if __name__ == "__main__":
args = parser.parse_args()
if args.list:
list_scripts()
sys.exit()
elif args.scripts:
res = run_scripts(args.scripts)
if res:
sys.exit()
else:
sys.exit(1)
else:
raise RuntimeError('Need to give name of one or multiple scripts, ')