Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Git Harvest Plugin and Tutorial #256

Merged
merged 5 commits into from
Aug 9, 2024
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions docs/source/tutorials/writing-a-plugin-for-hermes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<!--
SPDX-FileCopyrightText: 2024 German Aerospace Center (DLR)

SPDX-License-Identifier: CC-BY-SA-4.0
-->

<!--
SPDX-FileContributor: Michael Meinel
SPDX-FileContributor: Sophie Kernchen
-->

# Write a plugin for HERMES


This tutorial will present the basic steps for writing an additional harvester.
At the moment only the harvest architecture is stable.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
The full code and structure is available at [harvest-git](https://github.com/hermes-hmc/hermes-git).
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
This plugin extracts information from the local git history.
The harvest-git plugin will help to gather contributing and branch metadata.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
```{note}
For this tutorial you should be familiar with HERMES.
If you never used HERMES before, you might want to check the tutorial: [Automated Publication with HERMES](https://docs.software-metadata.pub/en/latest/tutorials/automated-publication-with-ci.html).
```

## Plugin Architecture

HERMES uses a plugin architecture. Therefore, users are invited to contribute own features.
The structure for every plugin follows the same schema.
There is a base class for every plugin. In this HermesPlugin class there is one abstract method __ call __ which needs to be overwritten.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
Furthermore, the HermesCommand class provides all needs for writing a plugin used in a HERMES command.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
So the HermesPlugins call method uses an Instance of the HermesCommand that triggered this plugin to run.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
In our case this will be the HermesHarvestCommand which calls all harvest plugins.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
The Plugin class also uses a derivative of HermesSettings to add parameters.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
HermesSettings are the base class for command specific settings.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
It uses pydantic settings to specify and validate the parameters.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
The user can either set the parameters in the hermes.toml or overwrite them in the command line.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
To overwrite the configuration, you use the -O operator with the dotted parameter name and the value.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved

## Set Up Plugin
To write a new plugin, it is important to follow the given structure.
This means your plugins source code has a pydantic class with Settings and the plugin class which inherits from one base class.
For our specific case, we want to write a git harvest plugin.
Our class Structure should look like this:


```{code-block} python
from hermes.commands.harvest.base import HermesHarvestPlugin
from pydantic import BaseModel


class GitHarvestSettings(BaseModel):
from_branch: str = 'main'


class GitHarvestPlugin(HermesHarvestPlugin):
settings_class = GitHarvestSettings

def __call__(self, command):
print("Hello World!")

return {}, {}
```

The Code uses the HermesHarvestPlugin as base class and pydantics Basemodel for the Settings. In the GitHarvestSettings you
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
can see that one setting is made. The Parameter from_branch is specific for this plugin and can be reached through self.settings.harvest.git.git_branch as long as our plugin will be named git.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
In the hermes.toml this would be achieved by [harvest.{plugin_name}].
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
The GitHarvestSettings are assigned to the GitHarvestPlugin. In the plugin you need to overwrite the __ call __ method.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
For now a simple Hello World will do. The method return two dictionaries. These will later depict the harvested data in codemeta (json-ld) and information for generating hermes metadata.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
That is the basic structure for the plugins source code.

To integrate this code, you have to register it as a plugin in the pyproject.toml. To learn more about the pyproject.toml check https://python-poetry.org/docs/pyproject/.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
We will just look at the important places for this plugin. There are two ways to integrate this plugin. First we will show how to use the plugin environment as the running base with HERMES as a dependency.
Then we say how to integrate this plugin in HERMES itself.
led02 marked this conversation as resolved.
Show resolved Hide resolved

### Include HERMES as Dependency
This is probably the more common way, where you can see HERMES as a framework.
The idea is that your project is the main part. You create the pyproject.toml as usual.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
In the dependencies block you need to include hermes. Then you just have to declare your plugin.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
The HERMES software will look for plugins and install these.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
In the code below you can see the parts of the pyproject.toml that are important.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
```{code-block} toml
...
[tool.poetry.dependencies]
python = "^3.10"
hermes = "^0.8.0"
...
...
[tool.poetry.plugins."hermes.harvest"]
git = "hermes_git.harvest:GitHarvestPlugin"
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
...
```
As you can see the plugin class from hermes_git is declared as git for hermes.harvest.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
To use the plugin you have to adapt the harvest Settings in the hermes.toml.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
We will discuss the exact step after showing the other pyproject.toml configuration.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
```{note}
You have to run poetry install to add and install all entrypoints declared in the pyproject.toml.
```

### Write Plugin to be included in HERMES
This variant is used to contribute to the HERMES community or adapt the HERMES workflow for own purposes.
If you want to contribute, see the [Contribution Guidelines](https://docs.software-metadata.pub/en/latest/dev/contribute.html).
After cloning the HERMES workflow repository you can adapt the pyproject.toml.
In the code below you see the parts with the important lines.
```{code-block} toml
...
[tool.poetry.dependencies]
...
pydantic-settings = "^2.1.0"
hermes-git = { git = "https://github.com/hermes-hmc/hermes-git.git", branch = "main" }
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
...
...
[tool.poetry.plugins."hermes.harvest"]
cff = "hermes.commands.harvest.cff:CffHarvestPlugin"
codemeta = "hermes.commands.harvest.codemeta:CodeMetaHarvestPlugin"
git = "hermes_git.harvest:GitHarvestPlugin"
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
...
```
In the dependencies you have to install your plugin. If your Plugin is pip installable than you can just give the name and the version.
If your plugin is in a buildable git repository, you can install it with the given expression.
Note that this differs with the accessibility and your wishes, check [Explicit Package Sources](https://python-poetry.org/docs/repositories/#explicit-package-sources).

The second thing to adapt is to declare the access point for the plugin.
You can do that with `git = "hermes_git.harvest:GitHarvestPlugin"`.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
This expression makes the GitHarvestPlugin from the hermes_git package, a hermes.harvest plugin named git.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
So you need to configure this line with your plugin properties.

Now you just need to add the plugin to the hermes.toml and reinstall the adapted poetry package.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved

### Configure hermes.toml
To use the plugin, you have to set it in the hermes.toml.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
The settings for the plugins are also set there.
For the harvest plugin the hermes.toml could look like this:
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
```{code-block} toml
[harvest]
sources = [ "cff", "git" ] # ordered priority (first one is most important)

[harvest.cff]
enable_validation = false

[harvest.git]
from_branch = "develop"
...
```
In [harvest] you define that this plugin is used with less priority than the built-in cff plugin.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
in [harvest.git] you set the configuration for the plugin.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
In the Beginning of this tutorial we set the parameter `from_branch` in the git settings. Now we change the default from_branch to develop.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
With this Configuration the plugin will be used. If you run hermes harvest, you should see the "Hello World" message.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
Of course the hermes.toml is always changeable as you desire.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved

```{admonition} Congratulations!
You can now write plugins for HERMES.
```
To fill the plugin with code, you can check our [harvest_git](https://github.com/hermes-hmc/hermes-git) repository.
SKernchen marked this conversation as resolved.
Show resolved Hide resolved
There is the code to check the local git history and extract contributors of the given branch.

If you have any questions, wishes or requests, feel free to contact us.
Loading