====================== Writing Tarmac Plugins ====================== What's the point in having a plugin system if you can't actually write plugins easily? Tarmac's plugins make it easy for you to bend Tarmac's functionality to your will. This document describes the process of writing the plugins. Writing a Plugin ================ A plugin is merely a python class inheriting from ``tarmac.plugins.TarmacPlugin``. It has a pre-determined API, but is very trivial to connect. For example, a basic HelloWorldPlugin would start like this:: from tarmac.plugins import TarmacPlugin class HelloWorldPlugin(TarmacPlugin): '''A basic plugin that prints "Hello World" when fired.''' This is an entirely plausible/valid plugin. It's worthless, it's true, but it's still valid. The plugin does need to be registered with before it can be used. For instance, in order to register the plugin to be fired before commit, you would use the following code:: from tarmac.hooks import tarmac_hooks tarmac_hooks['tarmac_pre_commit'].hook( HelloWorldPlugin(), 'HelloWorld plugin') You can put this into helloworld.py and pop it into `$HOME/.config/tarmac/plugins/``. The plugin is now available at ``tarmac.plugins.helloworld.HelloWorldPlugin`` and is registered to be fired after the merge and before the commit. You can use this plugin to make sure the merge creates a kosher tree and reject it if it doesn't make you happy. Now that we have our hook registered and available, we can make it actually do something! Let's have print "Hello World!" TarmacPlugins are merely callables, the ``run`` method should be implemented. For HelloWorldPlugin, it would look like this:: class HelloWorldPlugin(TarmacPlugin): '''A basic plugin that prints "Hello World" when fired.''' def run(self, command, target, source, proposal): print 'Hello World!' Now when we run the plugin, it will print "Hello World!" before each commit. This is a trivial example, but one that demonstrates how it works. The arguments to the plugin are explained below. **command** The tarmac command. For instance, a ``tarmac merge`` call. **target** An instance of ``tarmac.branch.Branch`` containing details about the target branch. **source** An instance of ``tarmac.branch.Branch`` containing details about the source branch. **proposal** The merge proposal that proposes the source branch for merge into the target. Handling Errors =============== If you are writing a plug-in to validate against some prerequisites for a merge to be successful, you may wish to raise an exception if this criteria goes unmet, within your plug-in, to prevent the successful merge of a branch. The Tarmac merge command automatically handles exceptions. All you need to do, is raise the right one. To do this, simply use TarmacMergeError, or create your own exception class as a sub-class thereof. For example, if you were writing a plug-in to run a test before committing a change, you might define your exception like this: from tarmac.exceptions import TarmacMergeError class TestCommandFailed(TarmacMergeError): """The test command failed to complete successfully.""" And when you raise your exception to prevent the commit from happening, you might do the following: message = u'The test command failed to complete successfully.' comment = (u'Tests failed to pass, with the following errors:' u'\n\n%(output)s' % {'output': test_output}) raise TestCommandFailed(message, comment) The TarmacMergeCommand exception takes two arguments. The first is a short message summarizing what failed, and the second is a comment that would be posted to the merge proposal on Launchpad, to let the developer know what needs to be fixed in their branch. The ``message`` argument is similar to a normal Python Exception message argument. However, in this case, it should be kept to a brief summary of what failed, as the message will be appended to another short string for writing to a log. The ``comment`` argument is typically a longer message describing the issue in more detail. This comment will be posted to the merge proposal on Launchpad, to inform the developer of what went wrong during the merge of their branch. The merge proposal will also be set back to the ``Needs Review`` status, to avoid having tarmac attempt to merge the branch again, without having the issues resolved. If no ``comment`` argument is supplied, the ``message`` argument will be used for the comment when posting to Launchpad, instead. If your plug-in is a ``pre_commit`` hook, and you wish to tell tarmac to simply skip your proposal for now, as there is some possibly transient problem, such as a 500 error from a web service you need to use, and delay the merger until later, you can raise the ``TarmacMergeSkipError`` exception, and tarmac will simply log that the merge was skipped, and continue on. Passing a ``message`` will also result in the message being logged. Caveats ======= Since Tarmac is still a young project, the API for plugins is subject to drastic change in the next few versions of Tarmac, but is planned to be stable when Tarmac reaches 1.0.