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
ack
in 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.responder
decorator make sure you use the exact plugin name. (ack
in this case) - All GitMate responder functions are registered by providing default
arguments. (
pr
andcomment
in 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
Settings
model. (ack_strs
andunack_strs
in 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
ack
plugin it isMergeRequestActions.COMMENTED
. - Second parameter is
repo_obj
and it is mandatory. - The positional arguments for your responder follow next.
options
param 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.