NAV Navbar
Logo
Switch version:

Task Plugins

The Task extension endpoint allows plugin authors to extend GoCD to run new tasks in addition to the defaults that GoCD provides.

If you’re looking to start away with a basic template for task plugins, we recommend forking this github repository.

Getting started

Plugins in GoCD are implemented in Java and packaged as a JAR file.

Structure of a GoCD Plugin

A plugin for GoCD is a JAR file with the following structure:

plugin.jar
|
|-- plugin.xml
|-- com
|   \-- example
|       \-- go
|           \-- testplugin
|               \-- EchoTaskPlugin.class
|               \-- x.class
|               \-- y.class
\-- lib
    \-- dependency-1.jar
    \-- dependency-2.jar

The plugin jar is a self contained-jar. It is expected to contain the following inside it:

The plugin metadata plugin.xml

Here is an example plugin.xml:

<?xml version="1.0" encoding="utf-8" ?>
<!-- Your plugin id and version
     of this XML document, the
     version must be set to "1" -->
<go-plugin
  id="com.example.rocket.launcher"
  version="1">
  <about>
    <!-- The name of your plugin -->
    <name>Launches rockets</name>
    <!--  The version of your plugin -->
    <version>0.0.1</version>
    <!-- The minimum version of GoCD that this plugin requires -->
    <target-go-version>15.1.0</target-go-version>
    <!-- A longer description of your plugin  -->
    <description>Launches rockets, spaceships and other things to a destination of your choice.</description>

    <!-- Tell us who you are -->
    <vendor>
      <name>ACME Corp</name>
      <url>https://www.example.com</url>
    </vendor>

    <!-- If this plugin only supports certain OSes -->
    <target-os>
      <value>Linux</value>
      <value>Windows</value>
    </target-os>
  </about>
</go-plugin>

This is an XML file that should be present in the plugin jar file at the top level.

You can find the XML Schema for the plugins in the main repository.

The plugin extension class

Add this to your maven pom.xml:

<dependency>
  <groupId>cd.go.plugin</groupId>
  <artifactId>go-plugin-api</artifactId>
  <version>18.6.0</version>
</dependency>

In order to use the plugin extension class, you must add the following to your maven dependencies. If you’re using gradle, then use cd.go.plugin:go-plugin-api:18.6.0. You can find the latest version of the plugin in maven central.

An example plugin class implementation:

package com.example.go.testplugin;

import com.thoughtworks.go.plugin.api.*;
import com.thoughtworks.go.plugin.api.annotation.*;
import com.thoughtworks.go.plugin.api.exceptions.*;
import com.thoughtworks.go.plugin.api.request.*;
import com.thoughtworks.go.plugin.api.response.*;
import com.google.gson.Gson;
import java.util.*;

@Extension
public class EchoTaskPlugin implements GoPlugin {
  private GoApplicationAccessor accessor;

  // this method is executed once at startup
  public void initializeGoApplicationAccessor(GoApplicationAccessor accessor) {
    this.accessor = accessor;
  }

  // a GoPluginIdentifier tells GoCD what kind of a plugin this is
  // and what version(s) of the request/response API it supports
  public GoPluginIdentifier pluginIdentifier() {
    return new GoPluginIdentifier("task", Arrays.asList("1.0"))
  }

  // handle the request and return a response
  // the response is very much like a HTTP response —
  // it has a status code, a response body and optional headers
  public GoPluginApiResponse handle(GoPluginApiRequest request) throws UnhandledRequestTypeException {
    if (request.requestName().equals("go.plugin-settings.get-view")) {
      String viewHtml = read(getClass().getResourceAsStream("/plugin-settings.template.html"));
      return new DefaultGoPluginApiResponse(200, viewHtml);
    if (request.requestName().equals("go.plugin-settings.validate-configuration")) {
      List errors = validate(request);
      return new DefaultGoPluginApiResponse(200, new Gson().toJson(errors));
    } else {
      throw new UnhandledRequestTypeException(request.requestName());
    }
  }
}

The plugin extension class is a Java class that implements the GoPlugin interface.

The other types in the example are:

Type Description
GoApplicationAccessor So that the plugin can make requests to the GoCD application to get additional information that is not provided by each request. For example, the plugin can ask for settings, credentials etc.
GoPluginIdentifier Provides information about the type of plugin and the version of the request/response it supports.
GoPluginApiRequest Represents the request message sent from GoCD to a plugin. The message will have a name and an optional JSON request body and the version of the extension.
GoPluginApiResponse Represents the response message as a result of processing the GoPluginApiRequest. Similar to GoPluginApiRequest, the response will have a status code and an optional JSON response body.

If you’re familiar with http request/responses handled by a web application, you will find this very familiar.

The plugin dependencies

Any dependencies that your plugin requires, should go into the lib directory inside the plugin JAR.

Requests from GoCD

In order to implement a task point the following messages must be implemented by the plugin.

Task Configuration

An example response body:

{
  "url": {
    "default-value": "",
    "secure": false,
    "required": true
  },
  "user": {
    "default-value": "bob",
    "secure": true,
    "required": true
  },
  "password": {}
}

This message is sent by the GoCD server to the plugin to know what properties are supported by this plugin that should to be stored in the cruise-config.xml file.

Request name

configuration

Request body

The server will not provide a request body.

Response code

The plugin is expected to return status 200 if it can understand the request.

Response Body

The plugin is expected to return an object containing the configuration property along with task configuration properties

Task View

An example response body:

{
  "displayValue": "MavenTask",
  "template": "<div class=\"form_item_block\">...</div>"
}

This message is sent by the GoCD server to the plugin to get an AngularJS based HTML template to allow the task to be configured.

Request name

view

Request body

The server will not provide a request body.

Response code

The plugin is expected to return status 200 if it can understand the request.

Response Body

The plugin is expected to return an object containing the view rendering information task view response object

Validate Configuration

An example validation request body

{
  "URL": {
    "secure": false,
    "value": "http://localhost.com",
    "required": true
  },
  "USERNAME": {
    "secure": false,
    "value": "user",
    "required": false
  },
  "PASSWORD": {
    "secure": true,
    "value": "password",
    "required": false
  }
}

An example response body, in case there are validation errors

{
  "errors": {
    "URL": "URL is not well formed",
    "USERNAME": "Invalid character present"
  }
}

An example response body, in case there are no validation errors

{
  "errors": {}
}

This message is sent by the GoCD server to the plugin to validate if the settings entered by the user are valid, so that the server may persist those settings in the cruise-config.xml file.

Request name

validate

Request body

The request body will contain a JSON, which contains an object with the configuration keys and values that the plugin is expected to validate. If the configuration is valid, the errors object should be empty {}.

Response code

The plugin is expected to return status 200 if it can understand the request.

Response Body

The plugin is expected to return an error object validation errors.

Execute Task

An example request body:

{
  "config": {
    "ftp_server": {
      "secure": false,
      "value": "ftp.example.com",
      "required": true
    },
    "remote_dir": {
      "secure": false,
      "value": "/pub/",
      "required": true
    }
  },
  "context": {
    "workingDirectory": "working-dir",
    "environmentVariables": {
      "ENV1": "VAL1",
      "ENV2": "VAL2"
    }
  }
}

An example response body, in case of success

{
  "success": true,
  "message": "Finished executing task"
}

An example response body, in case of failure

{
  "success": false,
  "message": "Failed to execute task. The error was: 'Server not found'"
}

Here’s an example snippet of code to help execute custom commands and send their output to GoCD

import com.google.gson.GsonBuilder;
import com.thoughtworks.go.plugin.api.request.GoPluginApiRequest;
import com.thoughtworks.go.plugin.api.response.DefaultGoPluginApiResponse;
import com.thoughtworks.go.plugin.api.task.JobConsoleLogger;

import java.io.File;
import java.util.*;

public class PluginExecutor {
  public DefaultGoPluginApiResponse execute(GoPluginApiRequest request) throws Exception {
    JobConsoleLogger console = JobConsoleLogger.getConsoleLogger();

    // create the process with the right cli args environment variables and working directory
    // all this is available in the requestBody
    ProcessBuilder builder = new ProcessBuilder(...);
    builder.directory(...);
    builder.environment().putAll(...);

    // print out the environment variables to the console
    console.printEnvironment(builder.environment());

    // start the process
    Process process = builder.start();

    // ensure that the process's stderr and stdout and connected to GoCD
    console.readErrorOf(process.getErrorStream());
    console.readOutputOf(process.getInputStream());

    // wait for the process to exit
    int exitCode = process.waitFor();

    if (exitCode != 0) {
      // send error response
      return new DefaultGoPluginApiResponse(200, "...");
    } else {
      // send success response
      return new DefaultGoPluginApiResponse(200, "...");
    }
  }
}

This message is sent by the GoCD agent to the plugin to execute the task.

Request name

execute

Request body

The request body will contain a JSON containing the necessary configuration provided by the user, along with the working directory where the task is expected to run, and the environment variables configured.

Response code

The plugin is expected to return status 200 if it can understand the request.

Response Body

The plugin is expected to return a status object indicating success, and a message. During the execution of the plugin, messages that need to be shown on the output console of the job can be sent to the Go Server using the an instance of JobConsoleLogger.

Request/Response JSON Objects

The Task Configuration Response Object

Here’s an example of the task configuration response object:

{
  "url": {
    "default-value": "",
    "secure": false,
    "required": true
  },
  "user": {
    "default-value": "bob",
    "secure": true,
    "required": true
  },
  "password": {
  }
}

Attribute Type Description
default-value String A value to be used as the default if the user provides no value.
secure Boolean If the value should be stored in an encrypted form in the cruise-config.xml file.
required Boolean Whether the field is required.

The Task View Response Object

Here’s an example of the task view response object:

{
  "displayValue": "MavenTask",
  "template": "<div class=\"form_item_block\">...</div>"
}

Attribute Type Description
displayValue String The name of the task that should be rendered in the view.
template String A string containing an HTML AngularJS based view.

This template is an AngularJS based template.

GoCD uses Angular JS as its template engine for the plugin UI. This allows plugin authors to use a limited set of AngularJS features to specify how the UI of their plugins looks.

Getting started with AngularJS based templates

Given a configuration:

<configuration>
  <property>
    <key>username</key>
    <value>alice</username>
  </property>
</configuration>

This gets converted into the following JSON representation in the browser:

{
  "username": "alice"
}

The AngularJS template is expected to bind to the JSON object shown above:

<div class="form_item_block">
  <label>Username:<span class='asterix'>*</span></label>
  <input ng-model="username" />
</div>

When an Angular template is used in a Go plugin, to define the configuration UI, the configuration key which is stored in the configuration XML is used everywhere and is expected to be consistent. Since Angular works off of JSON, GoCD will make sure that the key in the JSON provided to the Angular template is the same as the key in the configuration XML.

Suppose the key of the configuration property stored in the XML is “username”, with value, “alice”, then Go will make sure that the value is available to the template as “username” when used in an Angular-specific HTML attribute like “ng-model”.

Showing validation errors in the UI

We use some simple string replacement
to substitute GOINPUTNAME with a unique identifier
for your plugin in order to render
any server side errors

<div class="form_item_block">
  <label>Username:<span class='asterix'>*</span></label>
  <input ng-model="username" />
  <span class="form_error" ng-show="GOINPUTNAME[username].$error.server">
    {{ GOINPUTNAME[username].$error.server}}
  </span>
</div>

In case of validation errors returned by go.plugin-settings.validate-configuration, the error messages needs to be populated on the UI, use the snippet here to show the validation errors.