Plugin Architecture

Each command provided by KIWI NG is written as a task plugin under the kiwi.tasks namespace. As a developer, you can extend the KIWI NG system command space with custom task plugins, following the conventions below.

Naming conventions

Task plugin file name

The file name of a task plugin must follow the pattern system_<command>.py. This allows you to invoke the task with kiwi-ng system <command> ...

Task plugin option handling

KIWI NG uses the typer module to handle options. Each task plugin must use typer to allow option handling. The typer definition must be provided in a file named cli.py and must live in the toplevel of the plugin python namespace. The cli.py file must provide a dictionary named typers that maps the command name to its typer.Typer instance.

Task plugin class

The implementation of the plugin must be a class that matches the naming convention System<Command>Task. The class must inherit from the CliTask base class. On the plugin startup, KIWI NG expects an implementation of the process method.

Task plugin entry point

Registration of the plugin must be done in pyproject.toml using the tool.poetry.plugins concept. The entry point group must be kiwi.tasks, and the name of the toplevel plugin python namespace must contain the _plugin substring. Otherwise, KIWI NG does not load the cli.py of the plugin. The namespaces kiwi_boxed_plugin and kiwi_stackbuild_plugin are ignored because their commands are now part of KIWI NG itself.

[tool.poetry]
name = "kiwi_<pluginname>_plugin"

packages = [
    { include = "kiwi_<pluginname>_plugin"},
]

[tool.poetry.plugins]
[tool.poetry.plugins."kiwi.tasks"]
system_<command> = "kiwi_<pluginname>_plugin.tasks.system_<command>"

Example plugin

Note

The following example assumes an existing Python project which was set up using poetry and pyproject.toml.

  1. Assuming the project namespace is kiwi_relax_plugin, create the task plugin directory kiwi_relax_plugin/tasks.

  2. Create the entry point in pyproject.toml.

    Assuming we want to create the system command justdoit, this is the following entry point definition in pyproject.toml:

    [tool.poetry]
    name = "kiwi_relax_plugin"
    
    packages = [
        { include = "kiwi_relax_plugin"},
    ]
    
    [tool.poetry.plugins]
    [tool.poetry.plugins."kiwi.tasks"]
    system_justdoit = "kiwi_relax_plugin.tasks.system_justdoit"
    
  3. Create the typer cli interface in the file kiwi_relax_plugin/cli.py with the following content:

    import typer
    from typing import Annotated
    
    # typers variable must be provided for kiwi plugins
    typers = {
        'justdoit': typer.Typer(add_completion=False)
    }
    
    system = typers['justdoit']
    
    @system.callback(
        help='What is it good for',
        invoke_without_command=True,
        subcommand_metavar=''
    )
    def justdoit(
        ctx: typer.Context,
        now: Annotated[bool, typer.Option(help='For --now option')] = False
    ):
        Cli=ctx.obj
        Cli.subcommand_args['justdoit'] = {
            '--now': now,
            'help': False
        }
        Cli.global_args['command'] = 'justdoit'
        Cli.global_args['system'] = True
        Cli.cli_ok = True
    
  4. Create the plugin code in the file kiwi_relax_plugin/tasks/system_justdoit.py with the following content:

    # These imports requires kiwi to be part of your environment
    # It can be either installed from pip into a virtual development
    # environment or from the distribution package manager.
    from kiwi.tasks.base import CliTask
    from kiwi.help import Help
    
    class SystemJustdoitTask(CliTask):
        def process(self):
            self.manual = Help()
            if self.command_args.get('help') is True:
                # The following will invoke man to show the man page
                # for the requested command. Thus, for the call to
                # succeed, a man page needs to be written and
                # installed by the plugin.
                return self.manual.show('kiwi::relax::justdoit')
    
            if self.command_args.get('--now'):
                print(
                    'https://genius.com/Frankie-goes-to-hollywood-relax-lyrics'
                )
    
  5. Test the plugin

    $ poetry run kiwi-ng system justdoit --now