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:
- Plugin XML(
plugin.xml
): The metadata of your plugin. - Java Classes:
- The plugin extension classes e.g.
ExampleExtension.class
in the example shown - Any other classes used by the in the plugin.
- The plugin extension classes e.g.
- 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
- Artifact Plugin
- Authorization Plugin
- Elastic Agent Plugin
- Notification Plugin
- Secrets Plugin
- Task Plugin
- 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:
- Bundle XML(
gocd-bundle.xml
): The metadata of your plugin. - Plugins: Each plugin with the plugin extension class
ExampleSCMPlugin.class
,ExampleSecretsExtension.class
andExampleNotificationPlugin.class
in the example shown) and any other classes that form your plugin.
- 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
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
.