The package provides ability to write custom extensions which will be run on (the most) actions, e.g. after updates. By default ahriman provides three types of extensions - reporting, files uploading and PKGBUILD synchronization. Each extension must derive from the ahriman.core.triggers.Trigger
class and should implement at least one of the abstract methods:
on_result
- trigger action which will be called after build process, the build result and the list of repository packages will be supplied as arguments.on_start
- trigger action which will be called right before the start of the application process.on_stop
- action which will be called right before the exit.
Note, it isn't required to implement all of those methods (or even one of them), however, it is highly recommended to avoid trigger actions in __init__
method as it will be run on any application start (e.g. even if you are just searching in AUR).
For the configuration details and settings explanation kindly refer to the documentation <configuration>
.
Special trigger to be used to load workers from database on the start of the application rather than configuration. If the option is already set, it will skip processing.
Another trigger for the distributed system, which registers itself as remote worker, calling remote service periodically.
This trigger will be called before any action (on_start
) and pulls remote PKGBUILD repository locally; after that it copies found PKGBUILDs from the cloned repository to the local cache. It is useful in case if you have patched PGKBUILDs (or even missing in AUR) which you would like to use for package building and, technically, just simplifies the local package building.
In order to update those packages you would need to clone your repository separately, make changes in PKGBUILD (e.g. bump version and update checksums), commit them and push back. On the next ahriman's repository update, it will pull changes you committed and will perform package update.
This trigger will be called right after build process (on_result
). It will pick PKGBUILDs for the updated packages, pull them (together with any other files) and commit and push changes to remote repository. No real use cases, but the most of user repositories do it.
Trigger which can be used for reporting. It implements on_result
method and thus being called on each build update and generates report (e.g. html, telegram etc) according to the current settings.
Generator for keyring package. This trigger will extract keys from local keychain and pack them into keyring specific format. This trigger will generate sources including PKGBUILD, which can be used later for package building.
Simple generator for mirrorlist package, based on the URLs which were set by configuration. This trigger will generate sources including PKGBUILD, which can be used later for package building.
This trigger takes build result (on_result
) and performs syncing of the local packages to the remote mirror (e.g. S3 or just by rsync).
By default, only configuration and architecture are passed to triggers. However, some triggers might want to have access to other high-level wrappers. In order to provide such ability and avoid (double) initialization, the service provides a global context variables, which can be accessed from ahriman.core
package:
from ahriman.core import context
ctx = context.get()
Just because context is wrapped inside contexvars.ContextVar
, you need to explicitly extract variable by get()
method. Later you can extract any variable if it is set, e.g.:
from ahriman.core.database import SQLite
from ahriman.models.context_key import ContextKey
database = ctx.get(ContextKey("database", SQLite))
In order to provide typed API, all variables are stored together with their type. The get(ContextKey)
method will throw KeyError
in case if key is missing. Alternatively you can set your own variable inside context:
ctx.set(ContextKey("answer", int), 42)
context.set(ctx)
Note, however, that there are several limitations:
- Context variables are immutable, thus you cannot override value if the key already presented.
- The
return_type
ofContextKey
should match the value type, otherwise exception will be thrown.
The context
also implements collection methods such as __iter__
and __len__
.
Lets consider example of reporting trigger (e.g. slack, which provides easy HTTP API for integration triggers).
In order to post message to slack we will need a specific trigger URL (something like https://hooks.slack.com/services/company_id/trigger_id
), channel (e.g. #archrepo
) and username (repo-bot
).
As it has been mentioned, our trigger must derive from specific class:
from ahriman.core.triggers import Trigger
class SlackReporter(Trigger):
def __init__(self, repository_id, configuration):
Trigger.__init__(self, repository_id, configuration)
self.slack_url = configuration.get("slack", "url")
self.channel = configuration.get("slack", "channel")
self.username = configuration.get("slack", "username")
By now we have class with all required variables. Lets implement run method. Slack API requires positing data with specific payload by HTTP, thus:
import json
import requests
def notify(result, slack_url, channel, username):
text = f"""Build has been completed with packages: {", ".join([package.name for package in result.success])}"""
payload = {"channel": channel, "username": username, "text": text}
response = requests.post(slack_url, data={"payload": json.dumps(payload)})
response.raise_for_status()
Obviously you can implement the specified method in class, but for guide purpose it has been done as separated method. Now we can merge this method into the class:
class SlackReporter(Trigger):
def __init__(self, repository_id, configuration):
Trigger.__init__(self, repository_id, configuration)
self.slack_url = configuration.get("slack", "url")
self.channel = configuration.get("slack", "channel")
self.username = configuration.get("slack", "username")
def on_result(self, result, packages):
notify(result, self.slack_url, self.channel, self.username)
First, put the trigger in any path it can be exported, e.g. by packing the resource into python package (which will lead to import path as package.slack_reporter.SlackReporter
) or just put file somewhere it can be accessed by application (e.g. /usr/local/lib/slack_reporter.SlackReporter
).
After that run application as usual and receive notification in your slack channel.
Triggers can expose their configuration schema. It can be achieved by implementing CONFIGURATION_SCHEMA
class variable according to cerberus documentation. For more details and examples, please refer to built-in triggers implementations.