Create custom task types

In XL Release you can add custom task types that appear in the user interface and integrate seamlessly with other tasks in the release flow. You can use custom tasks to integrate with third-party components. For example, XL Release includes JIRA integration tasks, which are a set of custom tasks.

Custom tasks are written in the Python language.

To create a custom task, you need:

  • The definition of the task and its properties
  • The implementation of the task in a Python script

Defining a custom task

Custom tasks are stored in the ext or plugins directory of XL Release server:

  • ext is used when you are developing a custom task. It contains custom task definitions in a file called synthetic.xml. Python scripts are placed in subdirectories of ext.
  • plugins/__local__ contains bundled custom tasks that are packaged in a single zip file with extension .jar

This is an example of the layout of the ext directory:


You define the custom task in XML in synthetic.xml. As an example, this is the definition of the “Create Jira Issue” task:

<synthetic xmlns:xsi=""
       xsi:schemaLocation=" synthetic.xsd">

  <type type="jira.CreateIssue" extends="xlrelease.PythonScript" label="Create issue">
    <property name="jiraServer"  category="input" label="Server" referenced-type="jira.Server" kind="ci"/>
    <property name="username"    category="input"/>
    <property name="password"    category="input" kind="string" password="true" />
    <property name="project"     category="input"/>
    <property name="title"       category="input"/>
    <property name="description" category="input" size="large" />
    <property name="issueType"   category="input" default="Task"/>
    <property name="issueId"     category="output"/>


Synthetic element

The <synthetic> element is the root node. It contains the XML grammar definitions and should not be changed. For XL Deploy users: this is the same definition language that is used to extend XL Deploy.

The <type> element in <synthetic> defines the custom task. The type attribute defines the name of the custom task and is always of the form prefix.TaskName:

  • The prefix is the category of the task (for example, ‘jira’). It should be in lowercase.
  • The task name is a descriptive name of the task (for example, “CreateIssue”). It should be in camel case.

Important: The extends="xlrelease.PythonScript" attribute defines the type as a custom task for XL Release. You must not change it.

The label attribute defines the name of the task that will appear when adding a new task in the release flow. By default, if is not defined, XL Release will create a label for you with the following format: Prefix: Task Name using the capital letters as new words (example: “Jira: Create Issue”). You can override the prefix by adding a colon in the label attribute. Example: label="Integration: Create Issue".

You can add the following properties to the <type> element to further customize your task:

  • scriptLocation: Specifies a custom script location that overrides the default rules.
  • iconLocation: Location of an icon file (PNG or GIF format) that is used in the UI for this task.
  • taskColor: The color to use for the task in the UI, specified in HTML hexadecimal RGB format.


<type type="myplugin.MyTask" extends="xlrelease.PythonScript">
    <property name="scriptLocation" required="false" hidden="true" default="my/custom/dir/" />
    <property name="iconLocation" required="false" hidden="true" default="my/custom/dir/icon.png" />
    <property name="taskColor" hidden="true" default="#9C00DB" />

Property element

The properties are defined as nested <property> elements. The following attributes can be set on each property:

Property Description
name Name of the property. This is also the name of the variable by which it is referred in the Python script.
category XL Release supports two categories.
input appear in the task in the XL Release UI and must be specified before the task starts. They are then passed to the Python script. If you add an input type and do not specify the value for the required field, it will be set to the default value true.
output can be set in the Python script. When the script completes, they can be copied into release variables in XL Release.
label Group and label used in the XL Release UI. If you do not specify a group and label, XL Release will attempt to make a readable version. For example, myCompany.myTask will appear as a My Task task type in the My Company group.

You can group task types in your preferred groups by adding the group before a colon in the label; for example, Other Items: My Task.
description Help text explaining the property in more detail. This will appear in the UI.
kind The property type, which is string, integer, boolean, ci, list_of_string, set_of_string, map_string_string, or enum.

If omitted, this attribute defaults to string.
password Set this attribute to true to instruct XL Release to treat the property as a password. The content of password fields are obscured in the UI and encrypted in network traffic and storage.
size Indicates how much space the UI assigns to the property. Supported levels are default, small, medium, and large.
default The default value of the property.
referenced-type Indicates the type of CI this property can reference (only apply if kind is set to ci).

Output properties size limit

To prevent performance issues, output properties of type string are limited to 32 Kb. If your script adds content that exceeds the limit, XL Release will truncate the property. You can still print the property inside the script and XL Release will attach the content to the task.

You can change this limit for each task type in the XL_RELEASE_SERVER_HOME/conf/ file. For example:


To change the limit, delete the number sign (#) at the beginning of the relevant line, change the limit as desired, save the file, and restart the XL Release server.

Add custom tasks

After you save synthetic.xml and restart the XL Release server, the custom task appears in the UI and you can add it to the release flow editor like any other task.

Select custom task

This is how the above task definition looks like in the task details window:

Jira task

Python scripts

When the custom task becomes active, it triggers the Python script that is associated with it. For information about the script, see API and scripting overview.

Store scripts in a directory that has the same name as the prefix of the task type definition. The script file name has the same name as the name of the task, followed by the .py extension. For example, the Python script for the jira.CreateIssue task must be stored in jira/

Input properties are available as variables in the Python script. You can set output values by assigning values to their corresponding variables in the script. After execution, the script variables are copied to the release variables that were specified on the task in the UI.

Tip: To concatenate multiple Python scripts and have XL Release schedule them, see Using scheduling in scripts to connect to long running jobs.

For example, this is a possible implementation of the jira.CreateIssue task in Python:

import sys, string
import com.xhaus.jyson.JysonCodec as json


content = """
    "fields": {
          "key": "%s"
       "summary": "%s",
       "description": "%s",
       "issuetype": {
          "name": "%s"
""" % (project, title, description, string.capwords(issueType))

if jiraServer is None:
    print "No server provided."

jiraURL = jiraServer['url']
if jiraURL.endswith('/'):
    jiraURL = jiraURL[:len(jiraURL)-1]

# You can pass custom headers to the request
headers = {'myCustomHeaders': 'myValue'}

# jiraServer object may contains proxy information (field proxyHost and proxyPort)
request = HttpRequest(jiraServer, username, password)
response ='/rest/api/2/issue', content, contentType = 'application/json', headers = headers)

if response.status == ISSUE_CREATED_STATUS:
    # In order to debug your script, you can print the status, the body and the headers
    print response.status
    print response.response
    print response.headers

    # you can access the http headers of the response
    if response.headers['Content-Type'].startswith('application/json'):
        # Parsing the response body as JSON
        data = json.loads(response.response)
        issueId = data.get('key')
        print "Created %s in JIRA at %s." % (issueId, jiraURL)
    print "Failed to create issue in JIRA at %s." % jiraURL


HttpRequest is a class provided by XL Release that is used to perform HTTP calls. For more information, see the Jython API.


Posting JSON

request = HttpRequest({'url': 'http://site'})
response =
    '{"json": "content"}',
    contentType = 'application/json')
content = response.getResponse()
status = response.getStatus()

Using connection credentials

request = HttpRequest({'url': 'http://site'}, "username", "password")
response = request.get('/api/tasks')
content = response.getResponse()
status = response.getStatus()

Using a proxy

request = HttpRequest({
    'url': 'http://site',
    'proxyHost': '',
    'proxyPort': '8080'})
response = request.get('/api/tasks')
content = response.getResponse()
status = response.getStatus()

Getting results using a configuration object

If your task definition has a configuration object defined using:

<property name="jiraServer" category="input" label="Server" referenced-type="jira.Server" kind="ci"/>

You can use the jiraServer instance like that:

request = HttpRequest(jiraServer)
response = request.get('/api/tasks', contentType = 'application/json')
content = response.getResponse()
status = response.getStatus()

HttpRequest will then use the settings of the HttpConnection.

XL Release API

The XL Release API is available for scripts. To find a complete description of the methods you can call, see the Jython API reference.

Changing or removing customizations

Before changing or removing a custom task type or one of the properties of a custom task type, ensure that the type is not used in any releases or templates. Changing or removing a type that is in use may result in errors.

Note: From XL Release 8.5.3 onwards the process of changing or removing the custom script types from XL Release plugins has been improved.

  • Missing types will be replaced by an UnknownType task type, which can only be skipped or duplicated. By default when this task runs it will always fail; this is to ensure that the missing types are announced to the user and appropriate actions can be taken.

    • Only comments, attachments, assigned user or team, and watchers are editable in this UnknownType task.
  • This method of handling missing task types extends xlrelease.Tile, xlrelease.PythonScript, xlrelease.ReleaseTrigger, xlrelease.TaskFacet, and xlrelease.JythonProvider.

    • Out of these, only the types which extend xlrelease.PythonScript and xlrelease.TaskFacet are recoverable. This means that as soon as the type definition has been registered again, the UnknownType task will be replaced with the original task, or the task facet will be visible again.
  • A MissingTypesChecker is implemented which runs after the XL Release was started. This asks for confirmation from the user that all the CI references for the missing types will be removed and are not recoverable. The user can opt to cancel this and replace the missing types, or they can continue with the removal of these CI references.

  • With UnknownType the user can only skip the running task. It is not possible to complete or update this task type.

  • Triggers on a template with a missing task type will work the same; in other words a release will be created with missing types without any issues, but the tasks with UnknownType tasks will always fail and user will have to skip the task manually or programatically to proceed further.

    • This is to ensure that the missing types are known to the user, and it should be updated either by replacing the missing task type in the plugin, or removing the task if it is not needed.
  • UnknownType tasks will neither be visible in the Task access page, nor will the user be able to select it from the list of valid types.

  • This new xlrelease.UnknownType type will only be visible on the XL Release GUI, and it is not possible to use this type via the public API.


You can compress the contents of the ext folder into a single file with a .jar extension and place this file in the plugins directory. XL Release reads custom tasks from this location.

When compressing, do not include the ext folder as part of the path. The synthetic.xml file should be in the root of the .jar file. On Unix systems, use these commands:

cd ext
zip -r ../myplugin.jar .

On Microsoft Windows systems, use Windows Explorer to compress the individual files in the ext folder.

Note: Do not use .zip as the extension of the compressed file.

You can have multiple plugins and define contents in the ext folder at the same time. The ext directory takes precedence over the plugins directory.

If you change ext/synthetic.xml or the contents of the plugins folder, you must restart the XL Release server. You do not need to restart the server if you change the contents of a Python script.

Prevent cleaning output properties when retrying custom script task

You can keep previously executed output results by using property keepOutputPropertiesOnRetry. If that property is set to true, the output will not be updated.

If, on retry, you do not want to execute the part of the script that already populated a specific output property, then you must write your own custom script section.

Here is an example of a python script that does not execute a part of the original script, if the output is already populated:

prev_result = task.getPythonScript().getProperty("result")

if not task.keepOutputPropertiesOnRetry or not prev_result:
    result = calculate_new_value()

You can also use it on the UI in the custom HTML:

<div class="form-group">
    <label class="col-xs-3 control-label" for="keepPreviousOutputPropertiesOnRetry">
        Retain Properties
    <div class="col-xs-9">
        <input type="checkbox" id="keepPreviousOutputPropertiesOnRetry"
               data-ng-disabled="ctrl.areTaskPropertiesReadonlyOrLocked" title="Keep Previous Output Properties On Retry">
        <div class="description">Keep the previous output properties on retry</div>