7
0
mirror of https://github.com/ChristianLight/tutor.git synced 2024-06-09 01:02:21 +00:00
tutor/tutor/commands/plugins.py

422 lines
13 KiB
Python
Raw Normal View History

from __future__ import annotations
2019-07-03 14:09:33 +00:00
import os
import sys
import tempfile
import typing as t
import click
import click.shell_completion
feat: migrate to plugins.v1 with filters & actions This is a very large refactoring which aims at making Tutor both more extendable and more generic. Historically, the Tutor plugin system was designed as an ad-hoc solution to allow developers to modify their own Open edX platforms without having to fork Tutor. The plugin API was simple, but limited, because of its ad-hoc nature. As a consequence, there were many things that plugin developers could not do, such as extending different parts of the CLI or adding custom template filters. Here, we refactor the whole codebase to make use of a generic plugin system. This system was inspired by the Wordpress plugin API and the Open edX "hooks and filters" API. The various components are added to a small core thanks to a set of actions and filters. Actions are callback functions that can be triggered at different points of the application lifecycle. Filters are functions that modify some data. Both actions and filters are collectively named as "hooks". Hooks can optionally be created within a certain context, which makes it easier to keep track of which application created which callback. This new hooks system allows us to provide a Python API that developers can use to extend their applications. The API reference is added to the documentation, along with a new plugin development tutorial. The plugin v0 API remains supported for backward compatibility of existing plugins. Done: - Do not load commands from plugins which are not enabled. - Load enabled plugins once on start. - Implement contexts for actions and filters, which allow us to keep track of the source of every hook. - Migrate patches - Migrate commands - Migrate plugin detection - Migrate templates_root - Migrate config - Migrate template environment globals and filters - Migrate hooks to tasks - Generate hook documentation - Generate patch reference documentation - Add the concept of action priority Close #499.
2022-02-07 17:11:43 +00:00
from tutor import config as tutor_config
from tutor import exceptions, fmt, hooks, plugins, utils
from tutor.commands.config import save as config_save_command
from tutor.plugins import indexes
feat: migrate to plugins.v1 with filters & actions This is a very large refactoring which aims at making Tutor both more extendable and more generic. Historically, the Tutor plugin system was designed as an ad-hoc solution to allow developers to modify their own Open edX platforms without having to fork Tutor. The plugin API was simple, but limited, because of its ad-hoc nature. As a consequence, there were many things that plugin developers could not do, such as extending different parts of the CLI or adding custom template filters. Here, we refactor the whole codebase to make use of a generic plugin system. This system was inspired by the Wordpress plugin API and the Open edX "hooks and filters" API. The various components are added to a small core thanks to a set of actions and filters. Actions are callback functions that can be triggered at different points of the application lifecycle. Filters are functions that modify some data. Both actions and filters are collectively named as "hooks". Hooks can optionally be created within a certain context, which makes it easier to keep track of which application created which callback. This new hooks system allows us to provide a Python API that developers can use to extend their applications. The API reference is added to the documentation, along with a new plugin development tutorial. The plugin v0 API remains supported for backward compatibility of existing plugins. Done: - Do not load commands from plugins which are not enabled. - Load enabled plugins once on start. - Implement contexts for actions and filters, which allow us to keep track of the source of every hook. - Migrate patches - Migrate commands - Migrate plugin detection - Migrate templates_root - Migrate config - Migrate template environment globals and filters - Migrate hooks to tasks - Generate hook documentation - Generate patch reference documentation - Add the concept of action priority Close #499.
2022-02-07 17:11:43 +00:00
from tutor.plugins.base import PLUGINS_ROOT, PLUGINS_ROOT_ENV_VAR_NAME
from tutor.types import Config
feat: migrate to plugins.v1 with filters & actions This is a very large refactoring which aims at making Tutor both more extendable and more generic. Historically, the Tutor plugin system was designed as an ad-hoc solution to allow developers to modify their own Open edX platforms without having to fork Tutor. The plugin API was simple, but limited, because of its ad-hoc nature. As a consequence, there were many things that plugin developers could not do, such as extending different parts of the CLI or adding custom template filters. Here, we refactor the whole codebase to make use of a generic plugin system. This system was inspired by the Wordpress plugin API and the Open edX "hooks and filters" API. The various components are added to a small core thanks to a set of actions and filters. Actions are callback functions that can be triggered at different points of the application lifecycle. Filters are functions that modify some data. Both actions and filters are collectively named as "hooks". Hooks can optionally be created within a certain context, which makes it easier to keep track of which application created which callback. This new hooks system allows us to provide a Python API that developers can use to extend their applications. The API reference is added to the documentation, along with a new plugin development tutorial. The plugin v0 API remains supported for backward compatibility of existing plugins. Done: - Do not load commands from plugins which are not enabled. - Load enabled plugins once on start. - Implement contexts for actions and filters, which allow us to keep track of the source of every hook. - Migrate patches - Migrate commands - Migrate plugin detection - Migrate templates_root - Migrate config - Migrate template environment globals and filters - Migrate hooks to tasks - Generate hook documentation - Generate patch reference documentation - Add the concept of action priority Close #499.
2022-02-07 17:11:43 +00:00
from .context import Context
class PluginName(click.ParamType):
"""
Convenient param type that supports autocompletion of installed plugin names.
"""
def __init__(self, allow_all: bool = False):
self.allow_all = allow_all
def shell_complete(
self, ctx: click.Context, param: click.Parameter, incomplete: str
) -> list[click.shell_completion.CompletionItem]:
return [
click.shell_completion.CompletionItem(name)
for name in self.get_names(incomplete)
]
def get_names(self, incomplete: str) -> list[str]:
candidates = []
if self.allow_all:
candidates.append("all")
candidates += [name for name, _ in plugins.iter_info()]
return [name for name in candidates if name.startswith(incomplete)]
class IndexPluginName(click.ParamType):
"""
Param type for auto-completion of plugin names found in index cache.
"""
def shell_complete(
self, ctx: click.Context, param: click.Parameter, incomplete: str
) -> t.List[click.shell_completion.CompletionItem]:
return [
click.shell_completion.CompletionItem(entry.name)
for entry in indexes.iter_cache_entries()
if entry.name.startswith(incomplete.lower())
]
class IndexPluginNameOrLocation(IndexPluginName):
"""
Same as IndexPluginName but also auto-completes file location.
"""
def shell_complete(
self, ctx: click.Context, param: click.Parameter, incomplete: str
) -> t.List[click.shell_completion.CompletionItem]:
# Auto-complete plugin names
autocompleted = super().shell_complete(ctx, param, incomplete)
# Auto-complete local paths
autocompleted += click.Path().shell_complete(ctx, param, incomplete)
return autocompleted
@click.group(
name="plugins",
short_help="Manage Tutor plugins",
)
def plugins_command() -> None:
"""
Manage Tutor plugins to add new features and customise your Open edX platform.
Plugins can be downloaded from local and remote indexes. See the `tutor plugins
index` subcommand.
After the plugin index cache has been updated, plugins can be searched with:
tutor plugins search <pattern>
Plugins are installed with:
tutor plugins install <name>
"""
# All plugin commands should work even if there is no existing config file. This is
# because users might enable or install plugins prior to configuration or
# environment generation.
# Thus, usage of `config.load_full` is prohibited.
@click.command(
short_help="Print the location of file-based plugins",
help=f"""Print the location of yaml-based plugins: nboth python v1 and yaml v0 plugins. This location can be manually
defined by setting the {PLUGINS_ROOT_ENV_VAR_NAME} environment variable""",
)
def printroot() -> None:
fmt.echo(PLUGINS_ROOT)
feat: migrate to plugins.v1 with filters & actions This is a very large refactoring which aims at making Tutor both more extendable and more generic. Historically, the Tutor plugin system was designed as an ad-hoc solution to allow developers to modify their own Open edX platforms without having to fork Tutor. The plugin API was simple, but limited, because of its ad-hoc nature. As a consequence, there were many things that plugin developers could not do, such as extending different parts of the CLI or adding custom template filters. Here, we refactor the whole codebase to make use of a generic plugin system. This system was inspired by the Wordpress plugin API and the Open edX "hooks and filters" API. The various components are added to a small core thanks to a set of actions and filters. Actions are callback functions that can be triggered at different points of the application lifecycle. Filters are functions that modify some data. Both actions and filters are collectively named as "hooks". Hooks can optionally be created within a certain context, which makes it easier to keep track of which application created which callback. This new hooks system allows us to provide a Python API that developers can use to extend their applications. The API reference is added to the documentation, along with a new plugin development tutorial. The plugin v0 API remains supported for backward compatibility of existing plugins. Done: - Do not load commands from plugins which are not enabled. - Load enabled plugins once on start. - Implement contexts for actions and filters, which allow us to keep track of the source of every hook. - Migrate patches - Migrate commands - Migrate plugin detection - Migrate templates_root - Migrate config - Migrate template environment globals and filters - Migrate hooks to tasks - Generate hook documentation - Generate patch reference documentation - Add the concept of action priority Close #499.
2022-02-07 17:11:43 +00:00
@click.command(name="list")
@click.option(
"-e",
"--enabled",
"show_enabled_only",
is_flag=True,
help="Display enabled plugins only",
)
def list_command(show_enabled_only: bool) -> None:
"""
List installed plugins.
"""
plugins_table: list[tuple[str, ...]] = [("NAME", "STATUS", "VERSION")]
for plugin, plugin_info in plugins.iter_info():
is_enabled = plugins.is_loaded(plugin)
if is_enabled or not show_enabled_only:
plugins_table.append(
(
plugin,
plugin_status(plugin),
(plugin_info or "").replace("\n", " "),
)
)
fmt.echo(utils.format_table(plugins_table))
@click.command(help="Enable a plugin")
@click.argument("plugin_names", metavar="plugin", nargs=-1, type=PluginName())
@click.pass_context
def enable(context: click.Context, plugin_names: list[str]) -> None:
config = tutor_config.load_minimal(context.obj.root)
for plugin in plugin_names:
feat: migrate to plugins.v1 with filters & actions This is a very large refactoring which aims at making Tutor both more extendable and more generic. Historically, the Tutor plugin system was designed as an ad-hoc solution to allow developers to modify their own Open edX platforms without having to fork Tutor. The plugin API was simple, but limited, because of its ad-hoc nature. As a consequence, there were many things that plugin developers could not do, such as extending different parts of the CLI or adding custom template filters. Here, we refactor the whole codebase to make use of a generic plugin system. This system was inspired by the Wordpress plugin API and the Open edX "hooks and filters" API. The various components are added to a small core thanks to a set of actions and filters. Actions are callback functions that can be triggered at different points of the application lifecycle. Filters are functions that modify some data. Both actions and filters are collectively named as "hooks". Hooks can optionally be created within a certain context, which makes it easier to keep track of which application created which callback. This new hooks system allows us to provide a Python API that developers can use to extend their applications. The API reference is added to the documentation, along with a new plugin development tutorial. The plugin v0 API remains supported for backward compatibility of existing plugins. Done: - Do not load commands from plugins which are not enabled. - Load enabled plugins once on start. - Implement contexts for actions and filters, which allow us to keep track of the source of every hook. - Migrate patches - Migrate commands - Migrate plugin detection - Migrate templates_root - Migrate config - Migrate template environment globals and filters - Migrate hooks to tasks - Generate hook documentation - Generate patch reference documentation - Add the concept of action priority Close #499.
2022-02-07 17:11:43 +00:00
plugins.load(plugin)
fmt.echo_info(f"Plugin {plugin} enabled")
tutor_config.save_enabled_plugins(config)
tutor_config.save_config_file(context.obj.root, config)
context.invoke(config_save_command, env_only=True)
v11.0.0 (2020-12-09) - 💥[Improvement] Upgrade Open edX to Koa - 💥 Setting changes: - The ``ACTIVATE_HTTPS`` setting was renamed to ``ENABLE_HTTPS``. - Other ``ACTIVATE_*`` variables were all renamed to ``RUN_*``. - The ``WEB_PROXY`` setting was removed and ``RUN_CADDY`` was added. - The ``NGINX_HTTPS_PORT`` setting is deprecated. - Architectural changes: - Use Caddy as a web proxy for automated SSL/TLS certificate generation: - Nginx no longer listens to port 443 for https traffic - The Caddy configuration file comes with a new ``caddyfile`` patch for much simpler SSL/TLS management. - Configuration files for web proxies are no longer provided. - Kubernetes deployment no longer requires setting up a custom Ingress resource or custom manager. - Gunicorn and Whitenoise are replaced by uwsgi: this increases boostrap performance and makes it no longer necessary to mount media folders in the Nginx container. - Replace memcached and rabbitmq by redis. - Additional features: - Make it possible to disable all plugins at once with ``plugins disable all``. - Add ``tutor k8s wait`` command to wait for a pod to become ready - Faster, more reliable static assets with local memory caching - Deprecation: proxy files for Apache and Nginx are no longer provided out of the box. - Removed plugin `{{ patch (...) }}` statements: - "https-create", "k8s-ingress-rules", "k8s-ingress-tls-hosts": these are no longer necessary. Instead, declare your app in the "caddyfile" patch. - "local-docker-compose-nginx-volumes": this patch was primarily used to serve media assets. The recommended is now to serve assets with uwsgi.
2020-09-17 10:53:14 +00:00
@click.command(
short_help="Disable a plugin",
help="Disable one or more plugins. Specify 'all' to disable all enabled plugins at once.",
)
@click.argument(
"plugin_names", metavar="plugin", nargs=-1, type=PluginName(allow_all=True)
)
@click.pass_context
def disable(context: click.Context, plugin_names: list[str]) -> None:
config = tutor_config.load_minimal(context.obj.root)
disable_all = "all" in plugin_names
disabled: list[str] = []
feat: migrate to plugins.v1 with filters & actions This is a very large refactoring which aims at making Tutor both more extendable and more generic. Historically, the Tutor plugin system was designed as an ad-hoc solution to allow developers to modify their own Open edX platforms without having to fork Tutor. The plugin API was simple, but limited, because of its ad-hoc nature. As a consequence, there were many things that plugin developers could not do, such as extending different parts of the CLI or adding custom template filters. Here, we refactor the whole codebase to make use of a generic plugin system. This system was inspired by the Wordpress plugin API and the Open edX "hooks and filters" API. The various components are added to a small core thanks to a set of actions and filters. Actions are callback functions that can be triggered at different points of the application lifecycle. Filters are functions that modify some data. Both actions and filters are collectively named as "hooks". Hooks can optionally be created within a certain context, which makes it easier to keep track of which application created which callback. This new hooks system allows us to provide a Python API that developers can use to extend their applications. The API reference is added to the documentation, along with a new plugin development tutorial. The plugin v0 API remains supported for backward compatibility of existing plugins. Done: - Do not load commands from plugins which are not enabled. - Load enabled plugins once on start. - Implement contexts for actions and filters, which allow us to keep track of the source of every hook. - Migrate patches - Migrate commands - Migrate plugin detection - Migrate templates_root - Migrate config - Migrate template environment globals and filters - Migrate hooks to tasks - Generate hook documentation - Generate patch reference documentation - Add the concept of action priority Close #499.
2022-02-07 17:11:43 +00:00
for plugin in tutor_config.get_enabled_plugins(config):
if disable_all or plugin in plugin_names:
fmt.echo_info(f"Disabling plugin {plugin}...")
hooks.Actions.PLUGIN_UNLOADED.do(plugin, context.obj.root, config)
feat: migrate to plugins.v1 with filters & actions This is a very large refactoring which aims at making Tutor both more extendable and more generic. Historically, the Tutor plugin system was designed as an ad-hoc solution to allow developers to modify their own Open edX platforms without having to fork Tutor. The plugin API was simple, but limited, because of its ad-hoc nature. As a consequence, there were many things that plugin developers could not do, such as extending different parts of the CLI or adding custom template filters. Here, we refactor the whole codebase to make use of a generic plugin system. This system was inspired by the Wordpress plugin API and the Open edX "hooks and filters" API. The various components are added to a small core thanks to a set of actions and filters. Actions are callback functions that can be triggered at different points of the application lifecycle. Filters are functions that modify some data. Both actions and filters are collectively named as "hooks". Hooks can optionally be created within a certain context, which makes it easier to keep track of which application created which callback. This new hooks system allows us to provide a Python API that developers can use to extend their applications. The API reference is added to the documentation, along with a new plugin development tutorial. The plugin v0 API remains supported for backward compatibility of existing plugins. Done: - Do not load commands from plugins which are not enabled. - Load enabled plugins once on start. - Implement contexts for actions and filters, which allow us to keep track of the source of every hook. - Migrate patches - Migrate commands - Migrate plugin detection - Migrate templates_root - Migrate config - Migrate template environment globals and filters - Migrate hooks to tasks - Generate hook documentation - Generate patch reference documentation - Add the concept of action priority Close #499.
2022-02-07 17:11:43 +00:00
disabled.append(plugin)
fmt.echo_info(f"Plugin {plugin} disabled")
if disabled:
tutor_config.save_config_file(context.obj.root, config)
context.invoke(config_save_command, env_only=True)
2020-09-04 10:03:50 +00:00
@click.command(name="update")
@click.pass_obj
def update(context: Context) -> None:
"""
Update the list of available plugins.
"""
config = tutor_config.load(context.root)
update_indexes(config)
def update_indexes(config: Config) -> None:
all_plugins = indexes.fetch(config)
cache_path = indexes.save_cache(all_plugins)
fmt.echo_info(f"Plugin index local cache: {cache_path}")
@click.command()
@click.argument("names", metavar="name", type=IndexPluginNameOrLocation(), nargs=-1)
def install(names: list[str]) -> None:
"""
Install one or more plugins.
Each plugin name can be one of:
1. A plugin name from the plugin indexes (see `tutor plugins search`)
2. A local file that will be copied to the plugins root
3. An http(s) location that will be downloaded to the plugins root
In cases 2. and 3., the plugin root corresponds to the path given by `tutor plugins
printroot`.
"""
find_and_install(names, [])
@click.command()
@click.argument("names", metavar="name", type=IndexPluginName(), nargs=-1)
def upgrade(names: list[str]) -> None:
"""
Upgrade one or more plugins.
Specify "all" to upgrade all installed plugins. This command will only print a
warning for plugins which cannot be found.
"""
if "all" in names:
names = list(plugins.iter_installed())
available_names = []
for name in names:
try:
indexes.find_in_cache(name)
except exceptions.TutorError:
fmt.echo_error(
f"Failed to upgrade '{name}': plugin could not be found in indexes"
)
else:
available_names.append(name)
find_and_install(available_names, ["--upgrade"])
def find_and_install(names: list[str], pip_install_opts: t.List[str]) -> None:
"""
Find and install a list of plugins, given by name. Single-file plugins are
downloaded/copied. Python packages are or pip-installed.
"""
single_file_plugins = []
pip_requirements = []
for name in names:
if utils.is_url(name):
single_file_plugins.append(name)
else:
plugin = indexes.find_in_cache(name)
src = hooks.Filters.PLUGIN_INDEX_ENTRY_TO_INSTALL.apply(plugin.data)[
"src"
].strip()
if utils.is_url(src):
single_file_plugins.append(src)
else:
# Create requirements file where each plugin reqs is prefixed by a
# comment with its name
pip_requirements.append(f"# {name}\n{src}")
for url in single_file_plugins:
install_single_file_plugin(url)
if pip_requirements:
# pip install -r reqs.txt
requirements_txt = "\n".join(pip_requirements)
with tempfile.NamedTemporaryFile(
prefix="tutor-reqs-", suffix=".txt", mode="w"
) as tmp_reqs:
tmp_reqs.write(requirements_txt)
tmp_reqs.flush()
fmt.echo_info(f"Installing pip requirements:\n{requirements_txt}")
utils.execute(
"pip", "install", *pip_install_opts, "--requirement", tmp_reqs.name
)
def install_single_file_plugin(location: str) -> None:
"""
Download or copy a single file to the plugins root.
"""
plugin_path = os.path.join(PLUGINS_ROOT, os.path.basename(location))
if not plugin_path.endswith(".yml") and not plugin_path.endswith(".py"):
plugin_path += ".py"
# Read url
fmt.echo_info(f"Downloading plugin from {location}...")
content = utils.read_url(location)
# Save file
utils.ensure_file_directory_exists(plugin_path)
feat: migrate to plugins.v1 with filters & actions This is a very large refactoring which aims at making Tutor both more extendable and more generic. Historically, the Tutor plugin system was designed as an ad-hoc solution to allow developers to modify their own Open edX platforms without having to fork Tutor. The plugin API was simple, but limited, because of its ad-hoc nature. As a consequence, there were many things that plugin developers could not do, such as extending different parts of the CLI or adding custom template filters. Here, we refactor the whole codebase to make use of a generic plugin system. This system was inspired by the Wordpress plugin API and the Open edX "hooks and filters" API. The various components are added to a small core thanks to a set of actions and filters. Actions are callback functions that can be triggered at different points of the application lifecycle. Filters are functions that modify some data. Both actions and filters are collectively named as "hooks". Hooks can optionally be created within a certain context, which makes it easier to keep track of which application created which callback. This new hooks system allows us to provide a Python API that developers can use to extend their applications. The API reference is added to the documentation, along with a new plugin development tutorial. The plugin v0 API remains supported for backward compatibility of existing plugins. Done: - Do not load commands from plugins which are not enabled. - Load enabled plugins once on start. - Implement contexts for actions and filters, which allow us to keep track of the source of every hook. - Migrate patches - Migrate commands - Migrate plugin detection - Migrate templates_root - Migrate config - Migrate template environment globals and filters - Migrate hooks to tasks - Generate hook documentation - Generate patch reference documentation - Add the concept of action priority Close #499.
2022-02-07 17:11:43 +00:00
with open(plugin_path, "w", newline="\n", encoding="utf-8") as f:
f.write(content)
feat: migrate to plugins.v1 with filters & actions This is a very large refactoring which aims at making Tutor both more extendable and more generic. Historically, the Tutor plugin system was designed as an ad-hoc solution to allow developers to modify their own Open edX platforms without having to fork Tutor. The plugin API was simple, but limited, because of its ad-hoc nature. As a consequence, there were many things that plugin developers could not do, such as extending different parts of the CLI or adding custom template filters. Here, we refactor the whole codebase to make use of a generic plugin system. This system was inspired by the Wordpress plugin API and the Open edX "hooks and filters" API. The various components are added to a small core thanks to a set of actions and filters. Actions are callback functions that can be triggered at different points of the application lifecycle. Filters are functions that modify some data. Both actions and filters are collectively named as "hooks". Hooks can optionally be created within a certain context, which makes it easier to keep track of which application created which callback. This new hooks system allows us to provide a Python API that developers can use to extend their applications. The API reference is added to the documentation, along with a new plugin development tutorial. The plugin v0 API remains supported for backward compatibility of existing plugins. Done: - Do not load commands from plugins which are not enabled. - Load enabled plugins once on start. - Implement contexts for actions and filters, which allow us to keep track of the source of every hook. - Migrate patches - Migrate commands - Migrate plugin detection - Migrate templates_root - Migrate config - Migrate template environment globals and filters - Migrate hooks to tasks - Generate hook documentation - Generate patch reference documentation - Add the concept of action priority Close #499.
2022-02-07 17:11:43 +00:00
fmt.echo_info(f"Plugin installed at {plugin_path}")
@click.command()
@click.argument("pattern", default="")
def search(pattern: str) -> None:
"""
Search in plugin descriptions.
"""
results: list[tuple[str, ...]] = [("NAME", "STATUS", "DESCRIPTION")]
for plugin in indexes.iter_cache_entries():
if plugin.match(pattern):
results.append(
(
plugin.name,
plugin_status(plugin.name),
plugin.short_description,
)
)
print(utils.format_table(results))
@click.command()
@click.argument("name", type=IndexPluginName())
def show(name: str) -> None:
"""
Show plugin details from index.
"""
name = name.lower()
for plugin in indexes.iter_cache_entries():
if plugin.name == name:
fmt.echo(
f"""Name: {plugin.name}
Source: {plugin.src}
Status: {plugin_status(name)}
Author: {plugin.author}
Maintainer: {plugin.maintainer}
Homepage: {plugin.url}
Index: {plugin.index}
Description: {plugin.description}"""
)
return
raise exceptions.TutorError(
f"No information available for plugin: '{name}'. Plugin could not be found in indexes."
)
def plugin_status(name: str) -> str:
"""
Return the status of a plugin. Either: "enabled", "installed" or "not installed".
"""
if plugins.is_loaded(name):
return "✅ enabled"
if plugins.is_installed(name):
return "installed"
return "not installed"
@click.group(name="index", short_help="Manage plugin indexes")
def index_command() -> None:
"""
Manage plugin indices.
A plugin index is a list of Tutor plugins. An index can be public and shared with
the community, or private, for instance to share plugins with a select group of
users. Plugin indexes are a great way to share your plugins with other Tutor users.
By default, only the official plugin index is enabled.
Plugin indexes are fetched by running:
tutor plugins update
Plugin index cache is stored locally in the following subdirectory of the Tutor project environment:
plugins/index/cache.yml
"""
@click.command(name="list", help="List plugin indexes")
@click.pass_obj
def index_list(context: Context) -> None:
"""
Print plugin indexes.
"""
config = tutor_config.load(context.root)
for index in indexes.get_all(config):
fmt.echo(index)
@click.command(name="add")
@click.argument("url", type=click.Path())
@click.pass_obj
def index_add(context: Context, url: str) -> None:
"""
Add a plugin index.
The index URL will be appended with '{version}/plugins.yml'. The index path can be
either an http(s) url or a local file path.
For official indexes, there is no need to pass a full URL. Instead, use "main" or
"contrib".
"""
config = tutor_config.load_minimal(context.root)
if indexes.add(url, config):
tutor_config.save_config_file(context.root, config)
update_indexes(config)
else:
fmt.echo_alert("Plugin index was already added")
@click.command(name="remove")
@click.argument("url")
@click.pass_obj
def index_remove(context: Context, url: str) -> None:
"""
Remove a plugin index.
"""
config = tutor_config.load_minimal(context.root)
if indexes.remove(url, config):
tutor_config.save_config_file(context.root, config)
update_indexes(config)
else:
fmt.echo_alert("Plugin index not present")
index_command.add_command(index_add)
index_command.add_command(index_list)
index_command.add_command(index_remove)
plugins_command.add_command(index_command)
plugins_command.add_command(list_command)
plugins_command.add_command(printroot)
plugins_command.add_command(enable)
plugins_command.add_command(disable)
plugins_command.add_command(update)
plugins_command.add_command(search)
plugins_command.add_command(install)
plugins_command.add_command(upgrade)
plugins_command.add_command(show)