This page is obsolete, please refer to our online documentation.

General information

TeamCity is a highly extendable system. You can add various plugins in different places, including, but not limited to:

Some of the TeamCity extensions require only server-side plugin, other may require build agent plugin.

The OpenAPI sources of the TeamCity is placed to the root directory of full TeamCity distribution under the name
openApi-XXXX.jar.

TeamCity is compiled using JDK 1.5, so plugins should use JDK 1.5 too.

Server-side plugins

Server-side plugin organization and deployment

To write a server-side plugin, you'll need to include jar libraries from TeamCity/webapps/ROOT/WEB-INF/lib directory to your classpath.

Server-side plugins are delivered as a set of jar files, one of which contains the plugin descriptor file.
Such plugins can process data which is logged from build agents in some specific way, provide additional web content, create custom notifiers, etc.

The plugin code should be active - plugin components should register themselves in various TeamCity managers and components. Corresponding TeamCity managers should be passed via constructor (they will be passed by Spring framework via autowiring, see Descriptor section below)

Descriptor

The plugin descriptor file is named build-server-plugin-<unique_plugin_name>.xml and contains component definitions according to Spring configuration file syntax.

Base configuration of the server can be found in /WEB-INF/buildServerSpring.xml file.

Example descriptor:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans default-autowire="constructor">
  <bean id="emailNotificator" class="jetbrains.buildServer.notification.email.EMailNotificator"/>
  <bean id="emailNotificatorSettingsController" class="jetbrains.buildServer.controllers.email.EMailNotificatorSettingsController"/>
</beans>

Deployment

Put the jar files with server side plugins into the /WEB-INF/lib/ directory on the server.

How to write server-side extensions

Build server listeners

Should extend jetbrains.buildServer.serverSide.BuildServerListener interface and register itself using
SBuildServer.addListener() method. There is convinient jetbrains.buildServer.serverSide.BuildServerAdapter class.

public class MyListener extends BuildServerAdapter {
    public MyListener(SBuildServer server) {
      server.addListener(this);
    }

    // Your code here
}

Server-side extension points

  TeamCity server has a number of extension points, where a plugin can provide some additional functionality. An extension point is defined in terms of interface, which extends jetbrains.buildServer.serverSide.ServerExtension. There are several such interfaces/extension points. To register an extension, plugin should invoke method jetbrains.buildServer.serverSide.SBuildServer#registerExtension(extension_class, pluginCode, extensionObject).

Here is a list of some extension points (some of them are available only in TeamCity 3.0/Benares):

Web-content

Web resources (jsp, css, js, etc) should be placed into the plugin jar file under the path buildServerResources/. After that you should register this jar in the WebResourcesManager. This should be done while Spring is creating objects (i.e. in the plugin constructor). For example:

public class MyPlugin {
   public MyPlugin(WebResourcesManager resourcesManager) {
       resourcesManager.addPluginResources("myPlugin", "myPlugin.jar");
   }
}

Plugin jar should be placed into the /WEB-INF/lib/ directory. Resources like buildServerResources/file.js will be unpacked under the name /plugins/aPluginName/file.js. You can obtain this path with help of WebResourcesManager.resourcePath() method.

Custom VCS

See jetbrains.buildServer.vcs.VcsManager

If custom VCS plugin requires some settings to be edited from the web UI then it should also provide a JSP file for its settings. This JSP file should be placed into the plugin jar under the path buildServerResources/. In the buildServerSpring.xml you should also configure webResourcesUnpacker (note that you should use your vcs name as a plugin name), see above.

In the JSP file you should use special custom tags for input fields, radio buttons and checkboxes that correspond to you VCS properties. These custom tags are located in the props tag library:

<%@ taglib prefix="props" tagdir="/WEB-INF/tags/props" %>

You can use JSP files of existing VCS plugins as an example.

Custom Notifiers

Custom template processing:
http://www.intellij.net/forums/thread.jspa?messageID=5187731

View build log tabs

Implement interface ViewLogTab and add the implementation to the build server using call
WebControllerManager.addViewLogTab(aPluginName, this)

Custom user authentication

Custom authentication API is based on Sun JAAS API. To provide your own authentication scheme you should provide login module class which must implement interface javax.security.auth.spi.LoginModule and register it in the jetbrains.buildServer.serverSide.auth.LoginConfiguration.

For example:

public class CustomLoginModule implements javax.security.auth.spi.LoginModule {
  private Subject mySubject;
  private CallbackHandler myCallbackHandler;
  private Callback[] myCallbacks;
  private NameCallback myNameCallback;
  private PasswordCallback myPasswordCallback;

  public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
    // We should remember callback handler and create our own callbacks.
    // TeamCity authorization scheme supports two callbacks only: NameCallback and PasswordCallback.
    // From these callbacks you will receive username and password entered on the login page.
    myCallbackHandler = callbackHandler;
    myNameCallback = new NameCallback("login:");
    myPasswordCallback = new PasswordCallback("password:", false);
    // remember references to newly created callbacks
    myCallbacks = new Callback[] {myNameCallback, myPasswordCallback};

    // Subject is a place where authorized entity credentials are stored.
    // When user is successfully authorized the jetbrains.buildServer.serverSide.auth.ServerPrincipal
    // instance should be added to the subject. From this principal server will know real name of
    // the authorized entity and realm where this entity was authorized.
    mySubject = subject;
  }

  public boolean login() throws LoginException {
    // invoke callback handler so that username and password were added
    // to the name and password callbacks
    try {
      myCallbackHandler.handle(myCallbacks);
    }
    catch (Throwable t) {
      throw new LoginException(t.toString());
    }

    // retrieve login and password
    final String login = myNameCallback.getName();
    final String password = new String(myPasswordCallback.getPassword());

    // perform authentication
    if (checkPassword(login, password)) {
      // create ServerPrincipal and put it in the subject
      mySubject.getPrincipals().add(new ServerPrincipal(null, login));
      return true;
    }

    return false;
  }

  private boolean checkPassword(final String login, final String password) {
    return true;
  }

  public boolean commit() throws LoginException {
    // simply return true
    return true;
  }

  public boolean abort() throws LoginException {
    return true;
  }

  public boolean logout() throws LoginException {
    return true;
  }
}

Now we should register this module in the server. For this we create login module descriptor:

public class CustomLoginModuleDescriptor implements jetbrains.buildServer.serverSide.auth.LoginModuleDescriptor {
  // Spring framework will provide reference to LoginConfiguration when
  // the CustomLoginModuleDescriptor is instantiated
  public CustomLoginModuleDescriptor(LoginConfiguration loginConfiguration) {
    // register this descriptor in the login configuration
    loginConfiguration.registerLoginModule(this);
  }

  public Class<? extends LoginModule> getLoginModuleClass() {
    // return our custom login module class
    return CustomLoginModule.class;
  }

  public Map<String, ?> getOptions() {
    // return options which can be passed to our custom login module
    // for example, we could store reference to SBuildServer instance here
    return null;
  }

  public String getTextForLoginPage() {
    // return text to show on the login page which could describe
    // what should be entered in the user name and password fields
    return null;
  }

  public String getDisplayName() {
    // presentable name of this plugin
    return "My custom authentication plugin";
  }
}

Finally we should create build-server-plugin-ourUserAuth.xml as it is described above and write there CustomLoginModuleDescriptor bean:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-autowire="constructor">
  <bean id="customLoginModuleDescriptor" class="some.package.CustomLoginModuleDescriptor"/>
</beans>

Create jar, put it into the WEB-INF/lib directory of the TeamCity server and restart it. When server started you should be able to choose your custom plugin from the drop down on the Administration -> Server Configuration page.

Build Agent plugins

Currently, there are two ways to extend build agent:

  1. Write a custom build runner
  2. Write an extension to AntRunner (for instance, to process custom tasks)

Both ways use similar way of plugin packaging and deployment (see below).

Descriptor

Plugin descriptor for the build agent plugins is named build-agent-plugin.xml. A build agent reads its plugins using
NanoContainer:

<container>
  <component class="jetbrains.buildServer.agent.nant.NAntBuildRunner"/>
</container>

Package your build agent plugin as a zip archive with the following content:

<pluginName>
 <pluginName>/lib/
 <pluginName>/lib/pluginJar1.jar
 <pluginName>/lib/pluginJar2.jar
 <pluginName>/otherDir/
 <pluginName>/otherDir/someFile.txt

This archive will be unpackaged in the plugins subdirectory during the build agent installation:

buildAgent/
 buildAgent/bin/
 buildAgent/lib/
 buildAgent/plugins/<pluginName>/
 buildAgent/plugins/<pluginName>/lib/
 buildAgent/plugins/<pluginName>/otherDir/

Writing custom build runner

Agent-side part

Implement jetbrains.buildServer.agent.BuildRunner interface (you may also extend GenericProgramRunner or even JavaProgramRunner helpers for this). To log your messages you may obtain instance of ServerLogger singleton (you may also use ServerLoggerFacade for convinience).

TBD

Server-side part

Build agent plugin should also provide server side jar with a class implementing jetbrains.buildServer.serverSide.RunType interface. The implementation of the RunType interface must be registered in the jetbrains.buildServer.serverSide.RunTypeRegistry. You can do this in the constructor of the RunType implementation, like this:

public class CustomRunTypeImpl {
...
  public CustomRunType(RunTypeRegistry runTypeRegistry) {
    runTypeRegistry.registerRunType(this);
  }
...
}

Please note that you should also provide the plugin descriptor file named build-server-plugin-<unique_plugin_name>.xml which will contain reference to the RunType implementation:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans default-autowire="constructor">
  <bean id="customRunType" class="some.package.CustomRunTypeImpl"/>
</beans>

Writing ant extension

Implement jetbrains.buildServer.agent.ant.AntTaskExtension interface and register it as a component in
build-agent-plugin.xml file.

TBD

Deployment

To deploy a build agent plugin, copy its zip archive to the /WEB-INF/update/plugins directory on the Team Server. Server part of the plugin must be copied into the WEB-INF/lib directory of the Tomcat server. If server part was modified, you should also restart Tomcat server. Agents will download updated plugin automatically.

Unit testing

TBD