From 573bea2da029203a99daac1d884e75e3b850655c Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Thu, 23 Dec 2021 02:49:19 +0100 Subject: [PATCH] feat(cli): Added CLI command for handling plugins * Install / Uninstall plugins * List plugins --- flaschengeist/cli/__init__.py | 4 +- flaschengeist/cli/plugin_cmd.py | 96 +++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 flaschengeist/cli/plugin_cmd.py diff --git a/flaschengeist/cli/__init__.py b/flaschengeist/cli/__init__.py index e86e60c..ed93b5d 100644 --- a/flaschengeist/cli/__init__.py +++ b/flaschengeist/cli/__init__.py @@ -84,7 +84,7 @@ def cli(): def main(*args, **kwargs): - # from .plugin_cmd import plugin + from .plugin_cmd import plugin from .export_cmd import export from .docs_cmd import docs from .run_cmd import run @@ -92,8 +92,8 @@ def main(*args, **kwargs): # Override logging level environ.setdefault("FG_LOGGING", logging.getLevelName(LOGGING_MAX)) - # cli.add_command(plugin) cli.add_command(export) cli.add_command(docs) + cli.add_command(plugin) cli.add_command(run) cli(*args, **kwargs) diff --git a/flaschengeist/cli/plugin_cmd.py b/flaschengeist/cli/plugin_cmd.py new file mode 100644 index 0000000..bb0dd98 --- /dev/null +++ b/flaschengeist/cli/plugin_cmd.py @@ -0,0 +1,96 @@ +import click +from click.decorators import pass_context +from flask import current_app +from flask.cli import with_appcontext +from importlib_metadata import EntryPoint, entry_points + +from flaschengeist.config import config + + +@click.group() +def plugin(): + pass + + +@plugin.command() +@click.argument("plugin", nargs=-1, type=str) +@click.option("--all", help="Install all enabled plugins", is_flag=True) +@with_appcontext +@pass_context +def install(ctx, plugin, all): + """Install one or more plugins""" + if not all and len(plugin) == 0: + ctx.fail("At least one plugin must be specified, or use `--all` flag.") + if all: + plugins = current_app.config["FG_PLUGINS"].values() + else: + try: + plugins = [current_app.config["FG_PLUGINS"][p] for p in plugin] + except KeyError as e: + ctx.fail(f"Invalid plugin ID, could not find >{e.args[0]}<") + for p in plugins: + name = p.id.split(".")[-1] + click.echo(f"Installing {name}{'.'*(20-len(name))}", nl=False) + p.install() + click.secho(" ok", fg="green") + + +@plugin.command() +@click.argument("plugin", nargs=-1, type=str) +@with_appcontext +@pass_context +def uninstall(ctx: click.Context, plugin): + """Uninstall one or more plugins""" + + if len(plugin) == 0: + ctx.fail("At least one plugin must be specified") + try: + plugins = [current_app.config["FG_PLUGINS"][p] for p in plugin] + except KeyError as e: + ctx.fail(f"Invalid plugin ID, could not find >{e.args[0]}<") + if ( + click.prompt( + "You are going to uninstall:\n\n" + f"\t{', '.join([p.id.split('.')[-1] for p in plugins])}\n\n" + "Are you sure?", + default="n", + show_choices=True, + type=click.Choice(["y", "N"], False), + ).lower() + != "y" + ): + ctx.exit() + for p in plugins: + name = p.id.split(".")[-1] + click.echo(f"Uninstalling {name}{'.'*(20-len(name))}", nl=False) + p.uninstall() + click.secho(" ok", fg="green") + + +@plugin.command() +@click.option("--enabled", "-e", help="List only enabled plugins", is_flag=True) +@click.option("--no-header", "-n", help="Do not show header", is_flag=True) +@with_appcontext +def ls(enabled, no_header): + def plugin_version(p): + if isinstance(p, EntryPoint): + return p.dist.version + return p.version + + plugins = entry_points(group="flaschengeist.plugins") + enabled_plugins = [key for key, value in config.items() if "enabled" in value] + [config["FLASCHENGEIST"]["auth"]] + loaded_plugins = current_app.config["FG_PLUGINS"].keys() + + if not no_header: + print(f"{' '*13}{'name': <20}|{'version': >10}") + print("-" * 46) + for plugin in plugins: + if enabled and plugin.name not in enabled_plugins: + continue + print( + f"{plugin.name: <33}|{plugin_version(plugin): >12}" + f"{click.style(' (enabled)', fg='green') if plugin.name in enabled_plugins else ' (disabled)'}" + ) + + for plugin in [value for value in enabled_plugins if value not in loaded_plugins]: + print(f"{plugin: <33}|{' '*12}" f"{click.style(' (not found)', fg='red')}")