| |
@@ -26,208 +26,20 @@
|
| |
|
| |
from __future__ import absolute_import
|
| |
from __future__ import division
|
| |
+
|
| |
import logging
|
| |
- import optparse
|
| |
import os
|
| |
- import re
|
| |
- import six
|
| |
import sys
|
| |
- import types
|
| |
|
| |
- import six.moves.configparser
|
| |
import six.moves.xmlrpc_client
|
| |
|
| |
import koji
|
| |
- import koji.util
|
| |
- import koji.plugin
|
| |
-
|
| |
- from koji_cli.lib import _, OptionParser, get_epilog_str, greetings, \
|
| |
- warn, categories
|
| |
- from koji_cli.commands import *
|
| |
-
|
| |
-
|
| |
- def register_plugin(plugin):
|
| |
- """Scan a given plugin for handlers
|
| |
-
|
| |
- Handlers are functions marked with one of the decorators defined in koji.plugin
|
| |
- """
|
| |
- for v in six.itervalues(vars(plugin)):
|
| |
- if isinstance(v, six.class_types):
|
| |
- #skip classes
|
| |
- continue
|
| |
- if callable(v):
|
| |
- if getattr(v, 'exported_cli', False):
|
| |
- if hasattr(v, 'export_alias'):
|
| |
- name = getattr(v, 'export_alias')
|
| |
- else:
|
| |
- name = v.__name__
|
| |
- # copy object to local namespace
|
| |
- globals()[name] = v
|
| |
-
|
| |
-
|
| |
- def load_plugins(options, path):
|
| |
- """Load plugins specified by our configuration plus system plugins. Order
|
| |
- is that system plugins are first, so they can be overridden by
|
| |
- user-specified ones with same name."""
|
| |
- logger = logging.getLogger('koji.plugins')
|
| |
- if os.path.exists(path):
|
| |
- tracker = koji.plugin.PluginTracker(path=path)
|
| |
- for name in sorted(os.listdir(path)):
|
| |
- if not name.endswith('.py'):
|
| |
- continue
|
| |
- name = name[:-3]
|
| |
- logger.info('Loading plugin: %s', name)
|
| |
- tracker.load(name)
|
| |
- register_plugin(tracker.get(name))
|
| |
-
|
| |
+ from koji_cli import commands
|
| |
+ from koji_cli.lib import CommandExports, get_options
|
| |
|
| |
- def get_options():
|
| |
- """process options from command line and config file"""
|
| |
-
|
| |
- common_commands = ['build', 'help', 'download-build',
|
| |
- 'latest-pkg', 'search', 'list-targets']
|
| |
- usage = _("%%prog [global-options] command [command-options-and-arguments]"
|
| |
- "\n\nCommon commands: %s" % ', '.join(sorted(common_commands)))
|
| |
- parser = OptionParser(usage=usage)
|
| |
- parser.disable_interspersed_args()
|
| |
- progname = os.path.basename(sys.argv[0]) or 'koji'
|
| |
- parser.__dict__['origin_format_help'] = parser.format_help
|
| |
- parser.__dict__['format_help'] = lambda formatter=None: (
|
| |
- "%(origin_format_help)s%(epilog)s" % ({
|
| |
- 'origin_format_help': parser.origin_format_help(formatter),
|
| |
- 'epilog': get_epilog_str()}))
|
| |
- parser.add_option("-c", "--config", dest="configFile",
|
| |
- help=_("use alternate configuration file"), metavar="FILE")
|
| |
- parser.add_option("-p", "--profile", default=progname,
|
| |
- help=_("specify a configuration profile"))
|
| |
- parser.add_option("--keytab", help=_("specify a Kerberos keytab to use"), metavar="FILE")
|
| |
- parser.add_option("--principal", help=_("specify a Kerberos principal to use"))
|
| |
- parser.add_option("--krbservice", help=_("specify the Kerberos service name for the hub"))
|
| |
- parser.add_option("--runas", help=_("run as the specified user (requires special privileges)"))
|
| |
- parser.add_option("--user", help=_("specify user"))
|
| |
- parser.add_option("--password", help=_("specify password"))
|
| |
- parser.add_option("--noauth", action="store_true", default=False,
|
| |
- help=_("do not authenticate"))
|
| |
- parser.add_option("--force-auth", action="store_true", default=False,
|
| |
- help=_("authenticate even for read-only operations"))
|
| |
- parser.add_option("--authtype", help=_("force use of a type of authentication, options: noauth, ssl, password, or kerberos"))
|
| |
- parser.add_option("-d", "--debug", action="store_true",
|
| |
- help=_("show debug output"))
|
| |
- parser.add_option("--debug-xmlrpc", action="store_true",
|
| |
- help=_("show xmlrpc debug output"))
|
| |
- parser.add_option("-q", "--quiet", action="store_true", default=False,
|
| |
- help=_("run quietly"))
|
| |
- parser.add_option("--skip-main", action="store_true", default=False,
|
| |
- help=_("don't actually run main"))
|
| |
- parser.add_option("-s", "--server", help=_("url of XMLRPC server"))
|
| |
- parser.add_option("--topdir", help=_("specify topdir"))
|
| |
- parser.add_option("--weburl", help=_("url of the Koji web interface"))
|
| |
- parser.add_option("--topurl", help=_("url for Koji file access"))
|
| |
- parser.add_option("--pkgurl", help=optparse.SUPPRESS_HELP)
|
| |
- parser.add_option("--help-commands", action="store_true", default=False, help=_("list commands"))
|
| |
- (options, args) = parser.parse_args()
|
| |
-
|
| |
- # load local config
|
| |
- try:
|
| |
- result = koji.read_config(options.profile, user_config=options.configFile)
|
| |
- except koji.ConfigurationError as e:
|
| |
- parser.error(e.args[0])
|
| |
- assert False # pragma: no cover
|
| |
-
|
| |
- # update options according to local config
|
| |
- for name, value in six.iteritems(result):
|
| |
- if getattr(options, name, None) is None:
|
| |
- setattr(options, name, value)
|
| |
-
|
| |
- dir_opts = ('topdir', 'cert', 'serverca')
|
| |
- for name in dir_opts:
|
| |
- # expand paths here, so we don't have to worry about it later
|
| |
- value = os.path.expanduser(getattr(options, name))
|
| |
- setattr(options, name, value)
|
| |
-
|
| |
- #honor topdir
|
| |
- if options.topdir:
|
| |
- koji.BASEDIR = options.topdir
|
| |
- koji.pathinfo.topdir = options.topdir
|
| |
-
|
| |
- #pkgurl is obsolete
|
| |
- if options.pkgurl:
|
| |
- if options.topurl:
|
| |
- warn("Warning: the pkgurl option is obsolete")
|
| |
- else:
|
| |
- suggest = re.sub(r'/packages/?$', '', options.pkgurl)
|
| |
- if suggest != options.pkgurl:
|
| |
- warn("Warning: the pkgurl option is obsolete, using topurl=%r"
|
| |
- % suggest)
|
| |
- options.topurl = suggest
|
| |
- else:
|
| |
- warn("Warning: The pkgurl option is obsolete, please use topurl instead")
|
| |
-
|
| |
- plugins_path = '%s/lib/python%s.%s/site-packages/koji_cli_plugins' % \
|
| |
- (sys.prefix, sys.version_info[0], sys.version_info[1])
|
| |
- load_plugins(options, plugins_path)
|
| |
-
|
| |
- if options.help_commands:
|
| |
- list_commands()
|
| |
- sys.exit(0)
|
| |
- if not args:
|
| |
- list_commands()
|
| |
- sys.exit(0)
|
| |
-
|
| |
- aliases = {
|
| |
- 'cancel-task' : 'cancel',
|
| |
- 'cxl' : 'cancel',
|
| |
- 'list-commands' : 'help',
|
| |
- 'move-pkg': 'move-build',
|
| |
- 'move': 'move-build',
|
| |
- 'latest-pkg': 'latest-build',
|
| |
- 'tag-pkg': 'tag-build',
|
| |
- 'tag': 'tag-build',
|
| |
- 'untag-pkg': 'untag-build',
|
| |
- 'untag': 'untag-build',
|
| |
- 'watch-tasks': 'watch-task',
|
| |
- }
|
| |
- cmd = args[0]
|
| |
- cmd = aliases.get(cmd, cmd)
|
| |
- if cmd.lower() in greetings:
|
| |
- cmd = "moshimoshi"
|
| |
- cmd = cmd.replace('-', '_')
|
| |
- if ('anon_handle_' + cmd) in globals():
|
| |
- if not options.force_auth and '--mine' not in args:
|
| |
- options.noauth = True
|
| |
- cmd = 'anon_handle_' + cmd
|
| |
- elif ('handle_' + cmd) in globals():
|
| |
- cmd = 'handle_' + cmd
|
| |
- else:
|
| |
- list_commands()
|
| |
- parser.error('Unknown command: %s' % args[0])
|
| |
- assert False # pragma: no cover
|
| |
-
|
| |
- return options, cmd, args[1:]
|
| |
-
|
| |
-
|
| |
- def handle_help(options, session, args):
|
| |
- "[info] List available commands"
|
| |
- usage = _("usage: %prog help <category> ...")
|
| |
- usage += _("\n(Specify the --help global option for a list of other help options)")
|
| |
- parser = OptionParser(usage=usage)
|
| |
- # the --admin opt is for backwards compatibility. It is equivalent to: koji help admin
|
| |
- parser.add_option("--admin", action="store_true", help=optparse.SUPPRESS_HELP)
|
| |
-
|
| |
- (options, args) = parser.parse_args(args)
|
| |
-
|
| |
- chosen = set(args)
|
| |
- if options.admin:
|
| |
- chosen.add('admin')
|
| |
- avail = set(list(categories.keys()) + ['all'])
|
| |
- unavail = chosen - avail
|
| |
- for arg in unavail:
|
| |
- print("No such help category: %s" % arg)
|
| |
-
|
| |
- if not chosen:
|
| |
- list_commands()
|
| |
- else:
|
| |
- list_commands(chosen)
|
| |
+ for n, v in six.iteritems(vars(commands)):
|
| |
+ if not isinstance(v, six.class_types) and callable(v):
|
| |
+ setattr(CommandExports, n, staticmethod(v))
|
| |
|
| |
|
| |
def fix_pyver(options, logger):
|
| |
@@ -235,7 +47,7 @@
|
| |
pyver = getattr(options, 'pyver', None)
|
| |
if not pyver:
|
| |
return
|
| |
- if pyver not in [2,3]:
|
| |
+ if pyver not in [2, 3]:
|
| |
logger.warning('Invalid python version requested: %s', pyver)
|
| |
if sys.version_info[0] == pyver:
|
| |
return
|
| |
@@ -253,45 +65,14 @@
|
| |
logger.exception('Unable to execute with requested python version')
|
| |
|
| |
|
| |
- def list_commands(categories_chosen=None):
|
| |
- if categories_chosen is None or "all" in categories_chosen:
|
| |
- categories_chosen = list(categories.keys())
|
| |
- else:
|
| |
- # copy list since we're about to modify it
|
| |
- categories_chosen = list(categories_chosen)
|
| |
- categories_chosen.sort()
|
| |
- handlers = []
|
| |
- for name,value in globals().items():
|
| |
- if name.startswith('handle_'):
|
| |
- alias = name.replace('handle_','')
|
| |
- alias = alias.replace('_','-')
|
| |
- handlers.append((alias,value))
|
| |
- elif name.startswith('anon_handle_'):
|
| |
- alias = name.replace('anon_handle_','')
|
| |
- alias = alias.replace('_','-')
|
| |
- handlers.append((alias,value))
|
| |
- handlers.sort()
|
| |
- print(_("Available commands:"))
|
| |
- for category in categories_chosen:
|
| |
- print(_("\n%s:" % categories[category]))
|
| |
- for alias,handler in handlers:
|
| |
- desc = handler.__doc__ or ''
|
| |
- if desc.startswith('[%s] ' % category):
|
| |
- desc = desc[len('[%s] ' % category):]
|
| |
- elif category != 'misc' or desc.startswith('['):
|
| |
- continue
|
| |
- print(" %-25s %s" % (alias, desc))
|
| |
-
|
| |
- print("%s" % get_epilog_str().rstrip("\n"))
|
| |
-
|
| |
-
|
| |
if __name__ == "__main__":
|
| |
global options
|
| |
options, command, args = get_options()
|
| |
|
| |
logger = logging.getLogger("koji")
|
| |
handler = logging.StreamHandler(sys.stderr)
|
| |
- handler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s'))
|
| |
+ handler.setFormatter(
|
| |
+ logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s'))
|
| |
handler.setLevel(logging.DEBUG)
|
| |
logger.addHandler(handler)
|
| |
if options.debug:
|
| |
@@ -307,7 +88,7 @@
|
| |
session = koji.ClientSession(options.server, session_opts)
|
| |
rv = 0
|
| |
try:
|
| |
- rv = locals()[command].__call__(options, session, args)
|
| |
+ rv = getattr(CommandExports, command).__call__(options, session, args)
|
| |
if not rv:
|
| |
rv = 0
|
| |
except (KeyboardInterrupt, SystemExit):
|
| |
no_cmd
option toget_options
koji_cli.lib
CommandExports
to move cli handlers into this namespace instead ofglobals()