Writing GitMate Plugins¶
This tutorial contains step-by-step guide to create a GitMate Plugin with an example.
We'll be creating a plugin to acknowledge/unacknowledge commits in response to ack requests.
Let's name our plugin ack.
Note
- Make sure you have set up the development environment before proceeding any further.
- Replace every occurence of
ackin this tutorial with your plugin name.
1. Initializing the plugin¶
From the project directory, run:
$ python manage.py startplugin ack
This will create a template plugin directory named gitmate_ack with following structure.
plugins/gitmate_ack
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── responders.py
└── tests
├── __init__.py
└── test_gitmate_ack.py
2. Check system status¶
$ python manage.py check
You can run this command again in the future to check if everything is working.
3. Adding plugin settings¶
Plugin settings are the additional data your plugins might require to do their job.
Our ack plugin requires two settings:
ack_strs: Phrases that will be recognized as ack commands(comma seperated)unack_strs: Phrases that will be recognized as unack commands(comma seperated)
To add settings open plugins/gitmate_ack/models.py and add
# Auto generated code
from gitmate_config.models import SettingsBase
class Settings(SettingsBase):
# Add your custom plugin settings here
ack_strs = models.TextField(
default='ack',
help_text='Phrases that will be recognized as ack commands.')
unack_strs = models.TextField(
default='unack',
help_text='Phrases that will be recognized as unack commands.')
Then migrate the settings to the database by running,
python3 manage.py makemigrations
python3 manage.py migrate
4. Writing a responder¶
Plugins respond to distinctive events received through webhooks.
Our plugin responds to pull request comments. This means we have to attach our
plugin to the event IGitt.Interfaces.Actions.MergeActions.COMMENTED. We could
add in multiple events too and the responder would be executed for all such
events. To do so, open plugins/gitmate_ack/responders.py and edit the
ResponderRegistrar.responder decorator params. Then add your desired
functionality to the responder.
A word about Responders
- While registering the responder with
ResponderRegistrar.responderdecorator make sure you use the exact plugin name. (ackin this case) - All GitMate responder functions are registered by providing default
arguments. (
prandcommentin this case) - Take note that the responder should consist of the arguments that would be patched in through gitmate_hooks/views.py.
- All keyword arguments passed in should match with the exact names
provided in the
Settingsmodel. (ack_strsandunack_strsin this case)
@ResponderRegistrar.responder(
'ack',
MergeRequestActions.COMMENTED
)
def gitmate_ack(pr: MergeRequest,
comment: Comment,
ack_strs: str = 'ack', # remember settings
unack_strs: str = 'unack'): # make sure to add defaults
"""
A responder to ack and unack commits
"""
# This is responder logic for ack plugin
# Add your own logic for your plugin
body = comment.body.lower()
commits = pr.commits
pattern = '(^{k}\s)|(\s{k}\s)|(\s{k}$)'
unack_strs = get_keywords(unack_strs)
for kw in unack_strs:
if re.search(pattern.format(k=kw), body):
for commit in commits:
if commit.sha[:6] in body:
commit.unack()
ack_strs = get_keywords(ack_strs)
for kw in ack_strs:
if re.search(pattern.format(k=kw), body):
for commit in commits:
if commit.sha[:6] in body:
commit.ack()
Choose your responder event according to your needs. You could add multiple responders as well as multiple events to a responder. You can use any number of the plugin settings required by your responder. If default value is not provided, the settings won't be passed to the responder.
5. Adding a listener¶
Now you need to actually register the MergeRequestAction.COMMENTED listener with GitMate's
webhook handler (otherwise it wouldn't know what to listen for and your function in responders.py
will never be called). To do this, go to gitmate_hooks/views.py and add a listener.
For example, for the MergeRequestAction.COMMENTED action we did:
if event == 'issue_comment':
if webhook_data['action'] != 'deleted':
comment = webhook_data['comment']
pull_request_obj = GitHubMergeRequest(
token,
repository['full_name'],
webhook_data['issue']['number'])
comment_obj = GitHubComment(
token,
repository['full_name'],
comment['id'])
ResponderRegistrar.respond(
MergeRequestActions.COMMENTED,
repo_obj,
pull_request_obj,
comment_obj,
options=repo_obj.get_plugin_settings())
Note
A PR comment is called issue_comment in GitHub's API - hence the
if event == 'issue_comment'.
The body of webhook is available in webhook_data which contains additional information required
by our responder. The listener and the responder are connected via ResponderRegistrar.respond.
A word about params
- First parameter is the event you registered your responder with.
For
ackplugin it isMergeRequestActions.COMMENTED. - Second parameter is
repo_objand it is mandatory. - The positional arguments for your responder follow next.
optionsparam provide the keyword arguments(settings) to the responder.
Finally, to register the plugin to gitmate's database, run:
$ python3 manage.py upmate
6. Writing tests¶
Let's not forget to write tests for our newly created plugin.
To add tests, open gitmate_ack/tests/test_gitmate_ack.py. Follow the
template and add test methods. We use pytest along with pytest-cov, and
pytest-django plugins for tests.
Success
Our ack plugin is completed and fully functional.