def run(self, spec): """Execute the various tasks given in the spec list.""" try: if output.debug: names = ", ".join(info[0] for info in spec) print("Tasks to run: %s" % names) call_hooks('commands.before', self.tasks, spec) # Initialise the default stage if none are given as the first task. if 'stages' in env: if spec[0][0] not in env.stages: self.execute_task(env.stages[0], (), {}, None) else: self.execute_task(*spec.pop(0)) # Load the config YAML file if specified. if env.config_file: config_path = realpath(expanduser(env.config_file)) config_path = join(self.directory, config_path) config_file = open(config_path, 'rb') config = load_yaml(config_file.read()) if not config: env.config = AttrDict() elif not isinstance(config, dict): abort("Invalid config file found at %s" % config_path) else: env.config = AttrDict(config) config_file.close() call_hooks('config.loaded') # Execute the tasks in order. for info in spec: self.execute_task(*info) if output.status: msg = "\nDone." if env.colors: msg = env.color_settings['finish'](msg) print(msg) except SystemExit: raise except KeyboardInterrupt: if output.status: msg = "\nStopped." if env.colors: msg = env.color_settings['finish'](msg) print >> sys.stderr, msg sys.exit(1) except: sys.excepthook(*sys.exc_info()) sys.exit(1) finally: call_hooks('commands.after') disconnect_all()
def handle_failure(cmd, warn_only): if hasattr(cmd, '__name__'): cmd = cmd.__name__ + '()' message = 'Error running `%s`\n\n%s' % (cmd, indent(format_exc())) if warn_only: warn(message) else: abort(message)
def init_task_runner(filename, cwd): """Return a TaskRunner initialised from the located Boltfile.""" cwd = abspath(cwd) if sep in filename: path = join(cwd, filename) if not exists(path): abort("Couldn't find Boltfile: %s" % filename) else: prev = None while cwd: if cwd == prev: abort("Couldn't find Boltfile: %s" % filename) path = join(cwd, filename) if exists(path): break elif filename == "Boltfile" and exists(join(cwd, "boltfile.py")): path = join(cwd, "boltfile.py") break prev = cwd cwd = dirname(cwd) directory = dirname(path) if directory not in sys.path: sys.path.insert(0, directory) chdir(directory) sys.dont_write_bytecode = 1 boltfile = load_source('boltfile', path) sys.dont_write_bytecode = 0 tasks = dict( (var.replace('_', '-'), obj) for var, obj in vars(boltfile).items() if hasattr(obj, '__task__') ) stages = environ.get('BOLT_STAGES', env.get('stages')) if stages: if isinstance(stages, basestring): stages = [stage.strip() for stage in stages.split(',')] env.stages = stages for stage in stages: set_env_stage_command(tasks, stage) return TaskRunner(directory, path, boltfile.__doc__, tasks)
def init_task_runner(filename, cwd): """Return a TaskRunner initialised from the located Boltfile.""" cwd = abspath(cwd) if sep in filename: path = join(cwd, filename) if not exists(path): abort("Couldn't find Boltfile: %s" % filename) else: prev = None while cwd: if cwd == prev: abort("Couldn't find Boltfile: %s" % filename) path = join(cwd, filename) if exists(path): break prev = cwd cwd = dirname(cwd) directory = dirname(path) if directory not in sys.path: sys.path.insert(0, directory) chdir(directory) sys.dont_write_bytecode = 1 boltfile = load_source('boltfile', path) sys.dont_write_bytecode = 0 tasks = dict( (var.replace('_', '-'), obj) for var, obj in vars(boltfile).items() if hasattr(obj, '__task__') ) stages = environ.get('BOLT_STAGES', env.get('stages')) if stages: if isinstance(stages, basestring): stages = [stage.strip() for stage in stages.split(',')] env.stages = stages for stage in stages: set_env_stage_command(tasks, stage) return TaskRunner(directory, path, boltfile.__doc__, tasks)
def multisudo(self, *args, **kwargs): abort("multisudo is not supported on this setup")
def multirun(self, *args, **kwargs): abort("multirun is not supported on this setup")
def multilocal(self, *args, **kwargs): abort("multilocal is not supported on this setup")
def main(argv=None): """Handle the bolt command line call.""" if argv is None: argv = sys.argv[1:] op = OptionParser( usage="bolt <command-1> <command-2> ... [options]", ) op.add_option( '-v', '--version', action='store_true', default=False, help="show program's version number and exit" ) op.add_option( '-f', dest='file', default="Boltfile", help="set the name or path of the bolt file [Boltfile]" ) op.add_option( '-d', dest='defaults_file', default=_rc_path(), help="set the path of the defaults file [~/.bolt.yaml]" ) op.add_option( '-i', dest='identity', action='append', default=None, help="path to SSH private key file(s) -- may be repeated" ) op.add_option( '--hide', metavar='LEVELS', help="comma-separated list of output levels to hide" ) op.add_option( '--show', metavar='LEVELS', help="comma-separated list of output levels to show" ) op.add_option( '--disable', metavar='HOOKS', help="comma-separated list of hooks to disable" ) op.add_option( '--enable', metavar='HOOKS', help="comma-separated list of hooks to enable" ) op.add_option( '--list', action='store_true', default=False, help="show the list of available tasks and exit" ) op.add_option( '--no-pty', action='store_true', default=False, help="do not use pseudo-terminal in run/sudo" ) options, args = op.parse_args(argv) setup_defaults(options.defaults_file) # Load the Boltfile. runner = init_task_runner(options.file, getcwd()) # Autocompletion support. autocomplete_items = runner.tasks.keys() if 'autocomplete' in env: autocomplete_items += env.autocomplete autocomplete(op, ListCompleter(autocomplete_items)) if options.version: print("bolt %s" % __version__) sys.exit() if options.no_pty: env.always_use_pty = False if options.identity: env.key_filename = options.identity split_string = lambda s: filter(None, map(str.strip, s.split(','))) # Handle output levels. if options.show: for level in split_string(options.show): output[level] = True if options.hide: for level in split_string(options.hide): output[level] = False if output.debug: print("Using Boltfile: %s" % runner.path) # Handle hooks related options. if options.disable: for hook in split_string(options.disable): DISABLED_HOOKS.append(hook) if options.enable: for hook in split_string(options.enable): ENABLED_HOOKS.append(hook) if options.list: print('\n'.join(sorted(runner.tasks))) sys.exit() tasks = [] idx = 0 # Parse command line arguments. for task in args: # Initialise variables. _args = [] _kwargs = {} _ctx = None # Handle +env flags. if task.startswith('+'): if ':' in task: name, value = task[1:].split(':', 1) env[name] = value else: env[task[1:]] = True continue # Handle @context specifiers. if task.startswith('@'): if not idx: continue ctx = (task[1:],) existing = tasks[idx-1][3] if existing: new = list(existing) new.extend(ctx) ctx = tuple(new) tasks[idx-1][3] = ctx continue # Handle tasks with parameters. if ':' in task: task, argstr = task.split(':', 1) for pair in _escape_split(',', argstr): k, _, v = pair.partition('=') if _: _kwargs[k] = v else: _args.append(k) idx += 1 task_name = task.replace('_', '-') if task_name not in runner.tasks: abort("Task not found:\n\n%s" % indent(task)) tasks.append([task_name, _args, _kwargs, _ctx]) if not tasks: runner.display_listing() sys.exit() runner.run(tasks)
def setup_defaults(path=None): """Initialise ``env`` and ``output`` to default values.""" env.update({ 'again_prompt': 'Sorry, try again.', 'always_use_pty': True, 'colors': False, 'color_settings': { 'abort': yellow, 'error': yellow, 'finish': cyan, 'host_prefix': green, 'prefix': red, 'prompt': blue, 'task': red, 'warn': yellow }, 'combine_stderr': True, 'command': None, 'command_prefixes': [], 'config_file': None, 'ctx': (), 'cwd': '', 'disable_known_hosts': False, 'echo_stdin': True, 'hook': None, 'host': None, 'host_string': None, 'key_filename': None, 'lcwd': '', 'multirun_child_timeout': 10, 'multirun_pool_size': 10, 'no_agent': False, 'no_keys': False, 'output_prefix': True, 'password': None, 'passwords': {}, 'port': None, 'reject_unknown_hosts': False, 'shell': '/bin/bash -l -c', 'shell_history_file': '~/.bolt-shell-history', 'sudo_prefix': "sudo -S -p '%s' ", 'sudo_prompt': 'sudo password:'******'use_shell': True, 'user': _get_system_username(), 'warn_only': False }) output.update({ 'aborts': True, 'debug': False, 'running': True, 'status': True, 'stderr': True, 'stdout': True, 'user': True, 'warnings': True, }, { 'everything': ['output', 'running', 'user', 'warnings'], 'output': [ 'stderr', 'stdout'] }) # Load defaults from a YAML file. if path and exists(path): fileobj = open(path, 'rb') mapping = load_yaml(fileobj.read()) if not isinstance(mapping, dict): abort( "Got a %r value when loading %r. Mapping expected." % (type(mapping), path) ) env.update(mapping)