def05688c0
This used to be the behavior, and thenb5499c7dc5
changed this, breaking some plugins (some of which subsequently implemented workarounds). This patch restores the behavior, so that if a Dispatcher is constructed without explicitly passing in plugins, it has access to all plugins. This change seems safe, in that it's unlikely that any plugins were relying on the behavior betweenb5499c7dc5
and this patch, where a Dispatcher without an explicit plugin list behaved as if there were no plugins---because such a Dispatcher is not very useful!
173 lines
6 KiB
Python
173 lines
6 KiB
Python
import glob
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from argparse import ArgumentParser, RawTextHelpFormatter
|
|
|
|
import dotbot
|
|
|
|
from .config import ConfigReader, ReadingError
|
|
from .dispatcher import Dispatcher, DispatchError, _all_plugins
|
|
from .messenger import Level, Messenger
|
|
from .plugins import Clean, Create, Link, Shell
|
|
from .util import module
|
|
|
|
|
|
def add_options(parser):
|
|
parser.add_argument(
|
|
"-Q", "--super-quiet", action="store_true", help="suppress almost all output"
|
|
)
|
|
parser.add_argument("-q", "--quiet", action="store_true", help="suppress most output")
|
|
parser.add_argument(
|
|
"-v",
|
|
"--verbose",
|
|
action="count",
|
|
default=0,
|
|
help="enable verbose output\n"
|
|
"-v: typical verbose\n"
|
|
"-vv: also, set shell commands stderr/stdout to true",
|
|
)
|
|
parser.add_argument(
|
|
"-d", "--base-directory", help="execute commands from within BASEDIR", metavar="BASEDIR"
|
|
)
|
|
parser.add_argument(
|
|
"-c", "--config-file", help="run commands given in CONFIGFILE", metavar="CONFIGFILE"
|
|
)
|
|
parser.add_argument(
|
|
"-p",
|
|
"--plugin",
|
|
action="append",
|
|
dest="plugins",
|
|
default=[],
|
|
help="load PLUGIN as a plugin",
|
|
metavar="PLUGIN",
|
|
)
|
|
parser.add_argument(
|
|
"--disable-built-in-plugins", action="store_true", help="disable built-in plugins"
|
|
)
|
|
parser.add_argument(
|
|
"--plugin-dir",
|
|
action="append",
|
|
dest="plugin_dirs",
|
|
default=[],
|
|
metavar="PLUGIN_DIR",
|
|
help="load all plugins in PLUGIN_DIR",
|
|
)
|
|
parser.add_argument(
|
|
"--only", nargs="+", help="only run specified directives", metavar="DIRECTIVE"
|
|
)
|
|
parser.add_argument(
|
|
"--except", nargs="+", dest="skip", help="skip specified directives", metavar="DIRECTIVE"
|
|
)
|
|
parser.add_argument(
|
|
"--force-color", dest="force_color", action="store_true", help="force color output"
|
|
)
|
|
parser.add_argument(
|
|
"--no-color", dest="no_color", action="store_true", help="disable color output"
|
|
)
|
|
parser.add_argument(
|
|
"--version", action="store_true", help="show program's version number and exit"
|
|
)
|
|
parser.add_argument(
|
|
"-x",
|
|
"--exit-on-failure",
|
|
dest="exit_on_failure",
|
|
action="store_true",
|
|
help="exit after first failed directive",
|
|
)
|
|
|
|
|
|
def read_config(config_file):
|
|
reader = ConfigReader(config_file)
|
|
return reader.get_config()
|
|
|
|
|
|
def main():
|
|
log = Messenger()
|
|
try:
|
|
parser = ArgumentParser(formatter_class=RawTextHelpFormatter)
|
|
add_options(parser)
|
|
options = parser.parse_args()
|
|
if options.version:
|
|
try:
|
|
with open(os.devnull) as devnull:
|
|
git_hash = subprocess.check_output(
|
|
["git", "rev-parse", "HEAD"],
|
|
cwd=os.path.dirname(os.path.abspath(__file__)),
|
|
stderr=devnull,
|
|
).decode("ascii")
|
|
hash_msg = " (git %s)" % git_hash[:10]
|
|
except (OSError, subprocess.CalledProcessError):
|
|
hash_msg = ""
|
|
print("Dotbot version %s%s" % (dotbot.__version__, hash_msg))
|
|
exit(0)
|
|
if options.super_quiet:
|
|
log.set_level(Level.WARNING)
|
|
if options.quiet:
|
|
log.set_level(Level.INFO)
|
|
if options.verbose > 0:
|
|
log.set_level(Level.DEBUG)
|
|
|
|
if options.force_color and options.no_color:
|
|
log.error("`--force-color` and `--no-color` cannot both be provided")
|
|
exit(1)
|
|
elif options.force_color:
|
|
log.use_color(True)
|
|
elif options.no_color:
|
|
log.use_color(False)
|
|
else:
|
|
log.use_color(sys.stdout.isatty())
|
|
|
|
plugins = []
|
|
plugin_directories = list(options.plugin_dirs)
|
|
if not options.disable_built_in_plugins:
|
|
plugins.extend([Clean, Create, Link, Shell])
|
|
plugin_paths = []
|
|
for directory in plugin_directories:
|
|
for plugin_path in glob.glob(os.path.join(directory, "*.py")):
|
|
plugin_paths.append(plugin_path)
|
|
for plugin_path in options.plugins:
|
|
plugin_paths.append(plugin_path)
|
|
for plugin_path in plugin_paths:
|
|
abspath = os.path.abspath(plugin_path)
|
|
plugins.extend(module.load(abspath))
|
|
# ensure plugins are unique to avoid duplicate execution, which
|
|
# can happen if, for example, a third-party plugin loads a
|
|
# built-in plugin, which will cause it to appear in the list
|
|
# returned by module.load above
|
|
plugins = set(plugins)
|
|
if not options.config_file:
|
|
log.error("No configuration file specified")
|
|
exit(1)
|
|
tasks = read_config(options.config_file)
|
|
if tasks is None:
|
|
log.warning("Configuration file is empty, no work to do")
|
|
tasks = []
|
|
if not isinstance(tasks, list):
|
|
raise ReadingError("Configuration file must be a list of tasks")
|
|
if options.base_directory:
|
|
base_directory = os.path.abspath(options.base_directory)
|
|
else:
|
|
# default to directory of config file
|
|
base_directory = os.path.dirname(os.path.abspath(options.config_file))
|
|
os.chdir(base_directory)
|
|
_all_plugins[:] = plugins # for backwards compatibility, see dispatcher.py
|
|
dispatcher = Dispatcher(
|
|
base_directory,
|
|
only=options.only,
|
|
skip=options.skip,
|
|
exit_on_failure=options.exit_on_failure,
|
|
options=options,
|
|
plugins=plugins,
|
|
)
|
|
success = dispatcher.dispatch(tasks)
|
|
if success:
|
|
log.info("\n==> All tasks executed successfully")
|
|
else:
|
|
raise DispatchError("\n==> Some tasks were not executed successfully")
|
|
except (ReadingError, DispatchError) as e:
|
|
log.error("%s" % e)
|
|
exit(1)
|
|
except KeyboardInterrupt:
|
|
log.error("\n==> Operation aborted")
|
|
exit(1)
|