diff --git a/module_build_service/manage.py b/module_build_service/manage.py
index 925b5031..1215bdba 100755
--- a/module_build_service/manage.py
+++ b/module_build_service/manage.py
@@ -5,10 +5,9 @@
-from functools import wraps
+import click
import getpass
import logging
import os
import sys
-import textwrap
import flask_migrate
-from flask_script import Manager, prompt_bool
+from flask.cli import FlaskGroup
from werkzeug.datastructures import FileStorage
@@ -21,74 +20,34 @@ from module_build_service.builder.MockModuleBuilder import (
from module_build_service.common.errors import StreamAmbigous, StreamNotXyz
-from module_build_service.common.logger import level_flags
from module_build_service.common.utils import load_mmd_file, import_mmd
import module_build_service.scheduler.consumer
from module_build_service.scheduler.db_session import db_session
import module_build_service.scheduler.local
from module_build_service.web.submit import submit_module_build_from_yaml
-
-def create_app(debug=False, verbose=False, quiet=False):
- # logging (intended for flask-script, see manage.py)
- log = logging.getLogger(__name__)
- if debug:
- log.setLevel(level_flags["debug"])
- elif verbose:
- log.setLevel(level_flags["verbose"])
- elif quiet:
- log.setLevel(level_flags["quiet"])
-
- return app
-
-
-manager = Manager(create_app)
-help_args = ("-?", "--help")
-manager.help_args = help_args
-migrations_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
- 'migrations')
+migrations_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "migrations")
migrate = flask_migrate.Migrate(app, db, directory=migrations_dir)
-manager.add_command("db", flask_migrate.MigrateCommand)
-manager.add_option("-d", "--debug", dest="debug", action="store_true")
-manager.add_option("-v", "--verbose", dest="verbose", action="store_true")
-manager.add_option("-q", "--quiet", dest="quiet", action="store_true")
-def console_script_help(f):
- @wraps(f)
- def wrapped(*args, **kwargs):
- import sys
+@click.group(cls=FlaskGroup, create_app=lambda *args, **kwargs: app)
+def cli():
+ """MBS manager"""
- if any([arg in help_args for arg in sys.argv[1:]]):
- command = os.path.basename(sys.argv[0])
- print(textwrap.dedent(
- """\
- {0}
-
- Usage: {0} [{1}]
-
- See also:
- mbs-manager(1)
- """).strip().format(command, "|".join(help_args))
- )
- sys.exit(2)
- r = f(*args, **kwargs)
- return r
- return wrapped
-
-
-@console_script_help
-@manager.command
+@cli.command("upgradedb")
def upgradedb():
""" Upgrades the database schema to the latest revision
"""
app.config["SERVER_NAME"] = "localhost"
- # TODO: configurable?
- migrations_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "migrations")
with app.app_context():
flask_migrate.upgrade(directory=migrations_dir)
-@manager.command
+def upgradedb_entrypoint():
+ """Entrypoint for command mbs-upgradedb"""
+ upgradedb()
+
+
+@cli.command("cleardb")
def cleardb():
""" Clears the database
"""
@@ -96,8 +53,8 @@ def cleardb():
models.ComponentBuild.query.delete()
-@console_script_help
-@manager.command
+@cli.command("import_module")
+@click.argument("mmd_file", type=click.Path(exists=True))
def import_module(mmd_file):
""" Imports the module from mmd_file
"""
@@ -117,41 +74,56 @@ def import_module(mmd_file):
return collected
-@manager.option("--stream", action="store", dest="stream")
-@manager.option("--file", action="store", dest="yaml_file")
-@manager.option("--srpm", action="append", default=[], dest="srpms", metavar="SRPM")
-@manager.option("--skiptests", action="store_true", dest="skiptests")
-@manager.option("--offline", action="store_true", dest="offline")
-@manager.option(
- '--buildrequires', action='append', metavar='name:stream',
- dest='buildrequires', default=[],
- help='Buildrequires to override in the form of "name:stream"'
-)
-@manager.option(
- '--requires', action='append', metavar='name:stream',
- dest='requires', default=[],
- help='Requires to override in the form of "name:stream"'
-)
-@manager.option("-d", "--debug", action="store_true", dest="log_debug")
-@manager.option("-l", "--add-local-build", action="append", default=None, dest="local_build_nsvs")
-@manager.option("-s", "--set-stream", action="append", default=[], dest="default_streams")
-@manager.option(
- "-r", "--platform-repo-file", action="append", default=[], dest="platform_repofiles"
+@cli.command("build_module_locally")
+@click.option("--stream", metavar="STREAM")
+@click.option(
+ "--file", "yaml_file",
+ metavar="FILE",
+ required=True,
+ type=click.Path(exists=True),
+)
+@click.option("--srpm", "srpms", metavar="SRPM", multiple=True)
+@click.option("--skiptests", is_flag=True)
+@click.option("--offline", is_flag=True)
+@click.option(
+ '--buildrequires', "buildrequires", multiple=True,
+ metavar='name:stream', default=[],
+ help='Buildrequires to override in the form of "name:stream"'
+)
+@click.option(
+ '--requires', "requires", multiple=True,
+ metavar='name:stream', default=[],
+ help='Requires to override in the form of "name:stream"'
+)
+@click.option("-d", "--debug", "log_debug", is_flag=True)
+@click.option(
+ "-l", "--add-local-build", "local_build_nsvs",
+ metavar="NSV", multiple=True
+)
+@click.option(
+ "-s", "--set-stream", "default_streams",
+ metavar="STREAM", multiple=True
)
-@manager.option("-p", "--platform-id", action="store", default=None, dest="platform_id")
+@click.option(
+ "-r", "--platform-repo-file", "platform_repofiles",
+ metavar="FILE",
+ type=click.Path(exists=True),
+ multiple=True
+)
+@click.option("-p", "--platform-id", metavar="PLATFORM_ID")
def build_module_locally(
- local_build_nsvs=None,
+ stream=None,
yaml_file=None,
srpms=None,
- stream=None,
skiptests=False,
- default_streams=None,
offline=False,
+ log_debug=False,
+ local_build_nsvs=None,
+ default_streams=None,
platform_repofiles=None,
platform_id=None,
requires=None,
buildrequires=None,
- log_debug=False,
):
""" Performs local module build using Mock
"""
@@ -233,14 +193,11 @@ def build_module_locally(
sys.exit(1)
-@manager.option(
- "identifier",
- metavar="NAME:STREAM[:VERSION[:CONTEXT]]",
- help="Identifier for selecting module builds to retire",
-)
-@manager.option(
+@cli.command("retire")
+@click.argument("identifier", metavar="NAME:STREAM[:VERSION[:CONTEXT]]")
+@click.option(
"--confirm",
- action="store_true",
+ is_flag=True,
default=False,
help="Perform retire operation without prompting",
)
@@ -273,7 +244,8 @@ def retire(identifier, confirm=False):
logging.info("\t%s", ":".join((build.name, build.stream, build.version, build.context)))
# Prompt for confirmation
- is_confirmed = confirm or prompt_bool("Retire {} module builds?".format(len(module_builds)))
+ confirm_msg = "Retire {} module builds?".format(len(module_builds))
+ is_confirmed = confirm or click.confirm(confirm_msg, abort=False)
if not is_confirmed:
logging.info("Module builds were NOT retired.")
return
@@ -288,8 +260,10 @@ def retire(identifier, confirm=False):
logging.info("Module builds retired.")
-@console_script_help
-@manager.command
+@cli.command("run")
+@click.option("-h", "--host", metavar="HOST", help="Bind to this host.")
+@click.option("-p", "--port", metavar="PORT", help="Bind to this port along with --host.")
+@click.option("-d", "--debug", is_flag=True, default=False, help="Run frontend in debug mode.")
def run(host=None, port=None, debug=None):
""" Runs the Flask app, locally. Intended for dev instances, should not be used for production.
"""
@@ -302,9 +276,5 @@ def run(host=None, port=None, debug=None):
app.run(host=host, port=port, debug=debug)
-def manager_wrapper():
- manager.run()
-
-
if __name__ == "__main__":
- manager_wrapper()
+ cli()
diff --git a/requirements.txt b/requirements.txt
index 9ea96f50..17ea4df0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,10 +1,10 @@
+click
distro
dogpile.cache
enum34
fedmsg
Flask
Flask-Migrate
-Flask-Script
Flask-SQLAlchemy
funcsigs # Python2 only
futures # Python 2 only
diff --git a/setup.py b/setup.py
index 7eac415c..09cd3b23 100644
--- a/setup.py
+++ b/setup.py
@@ -39,9 +39,9 @@ setup(
dependency_links=deps_links,
entry_points={
"console_scripts": [
- "mbs-upgradedb = module_build_service.manage:upgradedb",
+ "mbs-upgradedb = module_build_service.manage:upgradedb_entrypoint",
"mbs-frontend = module_build_service.manage:run",
- "mbs-manager = module_build_service.manage:manager_wrapper",
+ "mbs-manager = module_build_service.manage:cli",
],
"moksha.consumer": "mbsconsumer = module_build_service.scheduler.consumer:MBSConsumer",
"mbs.messaging_backends": [
diff --git a/tests/test_manage.py b/tests/test_manage.py
index 6d3026c8..44ab8638 100644
--- a/tests/test_manage.py
+++ b/tests/test_manage.py
@@ -5,10 +5,9 @@ from __future__ import absolute_import
from mock import patch
import pytest
-from module_build_service import app
+from module_build_service import app, manage as mbs_manager
from module_build_service.common import models
from module_build_service.common.models import BUILD_STATES, ModuleBuild
-from module_build_service.manage import manager_wrapper, retire
from module_build_service.scheduler.db_session import db_session
from module_build_service.web.utils import deps_to_dict
from tests import clean_database, staged_data_filename
@@ -30,10 +29,12 @@ class TestMBSManage:
)
def test_retire_identifier_validation(self, identifier, is_valid):
if is_valid:
- retire(identifier)
+ with pytest.raises(SystemExit) as exc_info:
+ mbs_manager.cli(["retire", identifier])
+ assert 0 == exc_info
else:
with pytest.raises(ValueError):
- retire(identifier)
+ mbs_manager.cli(["retire", identifier])
@pytest.mark.parametrize(
("overrides", "identifier", "changed_count"),
@@ -47,9 +48,9 @@ class TestMBSManage:
({"context": "pickme"}, "spam:eggs:ham", 2),
),
)
- @patch("module_build_service.manage.prompt_bool")
- def test_retire_build(self, prompt_bool, overrides, identifier, changed_count):
- prompt_bool.return_value = True
+ @patch("click.confirm")
+ def test_retire_build(self, confirm, overrides, identifier, changed_count):
+ confirm.return_value = True
module_builds = (
db_session.query(ModuleBuild)
@@ -71,7 +72,10 @@ class TestMBSManage:
db_session.commit()
- retire(identifier)
+ with pytest.raises(SystemExit) as exc_info:
+ mbs_manager.cli(["retire", identifier])
+ assert 0 == exc_info.value
+
retired_module_builds = (
db_session.query(ModuleBuild)
.filter_by(state=BUILD_STATES["garbage"])
@@ -93,11 +97,11 @@ class TestMBSManage:
(False, True, True)
),
)
- @patch("module_build_service.manage.prompt_bool")
+ @patch("click.confirm")
def test_retire_build_confirm_prompt(
- self, prompt_bool, confirm_prompt, confirm_arg, confirm_expected
+ self, confirm, confirm_prompt, confirm_arg, confirm_expected
):
- prompt_bool.return_value = confirm_prompt
+ confirm.return_value = confirm_prompt
module_builds = db_session.query(ModuleBuild).filter_by(state=BUILD_STATES["ready"]).all()
# Verify our assumption of the amount of ModuleBuilds in database
@@ -106,15 +110,17 @@ class TestMBSManage:
for x, build in enumerate(module_builds):
build.name = "spam" + str(x) if x > 0 else "spam"
build.stream = "eggs"
-
db_session.commit()
- retire("spam:eggs", confirm_arg)
+ cmd = ["retire", "spam:eggs"] + (["--confirm"] if confirm_arg else [])
+ with pytest.raises(SystemExit) as exc_info:
+ mbs_manager.cli(cmd)
+ assert 0 == exc_info.value
+
+ expected_changed_count = 1 if confirm_expected else 0
retired_module_builds = (
db_session.query(ModuleBuild).filter_by(state=BUILD_STATES["garbage"]).all()
)
-
- expected_changed_count = 1 if confirm_expected else 0
assert len(retired_module_builds) == expected_changed_count
@@ -156,7 +162,7 @@ class TestCommandBuildModuleLocally:
original_db_uri = app.config["SQLALCHEMY_DATABASE_URI"]
try:
with patch("sys.argv", new=cli_cmd):
- manager_wrapper()
+ mbs_manager.cli()
finally:
app.config["SQLALCHEMY_DATABASE_URI"] = original_db_uri