NAV
Switch version:

Structure of a GoCD Plugin

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

Directory Structure Conventions

plugin.jar
|
|-- plugin.xml
|-- com
|   \-- example
|       \-- go
|           \-- testplugin
|               \-- ExampleExtension.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:

  1. Plugin XML(plugin.xml): The metadata of your plugin.
  2. Java Classes:
    • The plugin extension classes e.g. ExampleExtension.class in the example shown
    • Any other classes used by the in the plugin.
  3. Dependencies: Any optional dependencies your plugin may requires (inside the lib).

The Plugin XML

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.

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>19.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>

Example/Skeleton plugins

  1. Artifact Plugin
  2. Authorization Plugin
  3. Elastic Agent Plugin
  4. Notification Plugin
  5. Secrets Plugin
  6. Task Plugin
  7. Analytics Plugin

Structure of a Plugins Bundle

Starting from GoCD version 19.11.0, it allows developer to package multiple plugins in a bundle. e.g. It allows developer to package all GitHub plugins in a bundle which may use the same libraries. This will allows users to just download one jar instead downloading various jars for each extensions.

Directory Structure Conventions

bundle.jar
|
|-- gocd-bundle.xml
|-- com
|   \-- example
|       \-- go
|           \-- test_plugin_1
|               \-- ExampleSCMExtension.class
|               \-- ExampleSecretsExtension.class
|               \-- x.class
|               \-- y.class
|
|-- com
|   \-- example
|       \-- go
|           \-- test_plugin_2
|               \-- ExampleNotificationExtension.class
|               \-- x.class
|               \-- y.class
|
\-- lib
    \-- shared-dependency-1.jar
    \-- shared-dependency-2.jar

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

  1. Bundle XML(gocd-bundle.xml): The metadata of your plugin.
  2. Plugins: Each plugin with the plugin extension class
    • ExampleSCMPlugin.class, ExampleSecretsExtension.class and ExampleNotificationPlugin.class in the example shown) and any other classes that form your plugin.
  3. Dependencies: Any shared dependencies for all bundles (inside the lib)

The Bundle XML

This is an XML file that should be present in the bundle jar at the top level. You can find the XML Schema for the plugins in the main repository.

Example gocd-bundle.xml

<?xml version="1.0" encoding="utf-8"?>
<gocd-bundle version="1">
  <!-- Allows to register multiple plugins and each of the plugin can have one or more extensions -->  
  <plugins>
    <!-- The id of your first plugin -->
    <plugin id="id-of-the-plugin-1">
      <about>
        <!-- The name of your plugin -->
        <name>Example Git SCM plugin</name>
        <!--  The version of your plugin -->
        <version>1.0.0</version>
        <!-- A longer description of your plugin  -->
        <description>Allows to example git SCM as material</description>
        <!-- The minimum version of GoCD that this plugin requires -->
        <target-go-version>19.11.0</target-go-version>

        <!-- If this plugin only supports certain OSes -->
        <target-os>
          <value>Linux</value>
          <value>Windows</value>
        </target-os>
        <vendor>
          <name>ACME Corp</name>
          <url>https://www.example.com</url>
        </vendor>
      </about>
      <!-- Register extension classes in the plugin -->  
      <extensions>
        <extension class="com.example.go.test_plugin_1.ExampleSCMPlugin" />
        <extension class="com.example.go.test_plugin_1.ExampleSecretsExtension" />
      </extensions>
    </plugin>

    <!-- The id of your second plugin -->
    <plugin id="id-of-the-plugin-2">
      ...
    </plugin>
  </plugins>
</gocd-bundle>

The GoCD server will look for the gocd-bundle.xml in the jar and uses it to load the plugins. The XML also allows developer to register class(es) as extensions and unregister extension classes will be ignored by the server.

Examples

  1. Docker bundle
  2. Github bundle

Dependencies

Add this to your maven pom.xml:

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

In order to use the plugin extension class, you must add go-plugin-api as dependency. Where replace GOCD_VERSION with the actual version. You can find the available versions of the library in maven central.

Add this to your build.gradle under dependencies section:

dependencies {
  compileOnly group: 'cd.go.plugin', name: 'go-plugin-api', version: 'GOCD_VERSION'
}

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

Example extension class

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 ExampleExtension 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("<%= plugin_type %>", Arrays.asList("<%= endpoint_version %>"));
  }

  // 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.

Logging

An example for plugin logging:

package com.example.go.testplugin;

import com.thoughtworks.go.plugin.api.*;
import com.thoughtworks.go.plugin.api.logging.Logger;
import com.google.gson.Gson;
import java.util.*;

@Extension
public class ExampleExtension implements GoPlugin {
  private static final Logger LOG = Logger.getLoggerFor(ExampleExtension.class);
  private GoApplicationAccessor accessor;

  public GoPluginApiResponse handle(GoPluginApiRequest request) throws UnhandledRequestTypeException {
    LOG.info("Request from server: " + request.requestName());
  }
}

Plugins can optionally log events to a log file. Plugin logging is supported by com.thoughtworks.go.plugin.api.logging.Logger class, the Logger class should be available on including the go-plugin-api dependency as mentioned earlier.

Log location

Each plugin will have a separate log file and the name of the log file will be in the format plugin-<plugin_id>.log. The location of the plugin log file will be the same as the location of the GoCD server logs.

Log level

The default log level for a plugin is info. The log level can be controlled using a Java system property which should be provided to the GoCD server.

The system property to control the log level is specific to each plugin and is of the format plugin.<plugin_id>.log.level=<log_level>. The log level can be either of info, debug, warn, error.