RTC: Build On Deliver Participant

From some time ago I wanted to post a chunk of code to enable to launch a build as a result of a deliver operation. I have found myself in some situations in which this kind of automation has been asked, and I wanted to play a bit wit the SCM part of RTC SDK, so in this post I want to publish some code sample that I hope will be useful for people having this or a similar need.

In this post I am going to publish code, so our lawyers reminded me to state that the code in this post is derived from examples from Jazz.net as well as the RTC SDK. The usage of code from that example source code is governed by this license. Therefore this code is governed by this license. Please also remember, as stated in the disclaimer, that this code comes with lack of promise or guarantee.

You can download the code here.

Note that I have  just coded the sample for server side of the participant, without an aspect editor. If you just get started with extending RTC, I would suggest to first read the article Extending Rational Team Concert 3.x and follow the Rational Team Concert 4.0 Extensions Workshop

Identifying the operation

The first decision I faced was to decide the deliver phase in which I wanted to plug in the participant:

deliverCasesI will trigger the follow-up operation as result of the Phase 2 deliver in the server, so all the conditions have already passed. I went and had a check to the following wiki page covering the Custom Preconditions Table, to find out that the operation ID I have to use for my extension is “com.ibm.team.scm.service.changehistory.modification”. Therefore my extension is defined as follows:

operationDetails

The process configuration

For this participant I wanted to support a single configuration to several streams and builds, avoiding to make configure the Participant in the Project Area for each stream. Defining several streams will simplify the maintenance and I found that in some complex situations, customers wanted to launch more than one build as result of a deliver to  streams. So the process configuration structure I took as basis for my participant would be something like:

<target-stream target-stream-id="STREAM1">
   <build-definition build-definition-id="Build.Def.A"/>
   <build-definition build-definition-id="Build.Def.B"/>
</target-stream>
<target-stream target-stream-id="STREAM2">
   <build-definition build-definition-id="Build.Def.C"/>
</target-stream>

The attached code contains the schema for the participant process configuration. A bit of tweaking with the “<xsd:restriction” tag in the schema helps in throwing an error if the user types duplicate IDs for the streams or builds within a stream; just trying to prevent mistakes when you have to type your information in the process.

An example of the configuration could be:

JKEconfig

Checking permissions in the chain

The operation we are trying to chain (requesting a build), has different process permissions definition that the deliver itself. So we can find situations in which the participant could fail making the whole deliver operation to fail. The following method from IServerProcess allows us to check if the user has permission to execute the operations we query for:

boolean[] getPermittedActions(IProcessArea processArea, 
String operationId, String[] actions) 
throws TeamRepositoryException;

In this case we want to query for the “com.ibm.team.build.server.requestBuild” operation and its “request” action. Again the wiki page covering the Custom Preconditions Table, allows to get the ID of the operation and its action.

In this participant sample, if the user has no permission to request the build, the follow-up will flag a warning and exit, but always allowing the deliver operation to finish:

for (boolean permission : processService.getServerProcess(operation.getProcessArea()).
 getPermittedActions(operation.getProcessArea(),BUILD_OPERATION_ID,BUILD_ACTIONS_ID))
 {
  if (!permission)
  {
    String description = "No permission to request a build. " +
    "No build will we launched as result of the delvier";
    IReportInfo info = collector.createInfo("Unable to request build", description);
    info.setSeverity(IProcessReport.WARNING);
    collector.addInfo(info);

  // Exit the follow-up
  return;
}}

Where the “processService” has been retrieved by “getService(IProcessServerService.class);”

Getting the information from the deliver

For the Phase 2 deliver, we will get operation data which is an instance “IScmDeltaSource”. This data provide information from the deltas involved in the deliver operation, like the workspaces source and origin of the deliver:

ScmDeltaSourceSo once we have checked that we are in the right context, from this information we can get the target workspace of the delta changes, validate that is a stream, and compare its name from the participant configuration, to check if the follow-up has to trigger a build as a result of the executing deliver to the stream. A snippet of the code in that part in “BuildOnDeliverParticipant” class is:

for (IChangeHistoryModificationDelta delta : 
sourceData.getDeltas(IChangeHistoryModificationDelta.class))
{
   // Getting the target workspace information from the repository

   IWorkspace targetWK = (IWorkspace) repoService.fetchItem(delta.getTargetWorkspace(),
   IRepositoryItemService.COMPLETE);

   // Check that the workspace is a stream
   if (targetWK.isStream())
   {

     // get its configuration in the follow-up if any, and request the builds
     ...

Where the “repoService” has been retrieved by “getService(IRepositoryItemService.class);”

Requesting the build

The build request is basically the same code that the one you can find in the Rational Team Concert 4.0 Extensions Workshop but modified a little bit for error handling. As we can try to request several builds, we gather all the possible “warnings” (not to block the deliver operation), in a List<IReportInfo> and send all the issues to the collector at the end. Possible issues reported are:

  1. Issues launching the build definition, typically caused by problems with the build engine
  2. The specified build definition is not found

Summary

The code is experimental. I have tested it my local Tomcat/Derby installation with the MTM sample assets. Several improvements can be made like providing a presentation for the configuration, getting more information from the build definitions to improve control, better error handling … Therefore, I don’t think the code is production ready, but I hope that it can serve as help if you have to code this or similar functionality.

4 thoughts on “RTC: Build On Deliver Participant

  1. Really cool stuff. I gotta try this, this will be extremely helpful to automate some steps around baseline delivery (so I hope). Thanks for sharing this!

  2. Thanks for the efforts Jorge! This is a nice feature!
    I would like to leave a note for people interested in this participant. Since I tested it on my own, it might save time for those with few requirements in mind. Here’s what I can add:
    This participant is a trigger only and it does not “block” further deliveries until the snapshot is created. In other words, the participant triggers the build, but the snapshot creation is part of the build process, which is independent of SCM operations and is generally ruled by scheduled polling or so. Therefore, if another delivery is performed within those very few (milli-)seconds between the trigger and the creation of a snapshot, the build will include all the changes from both deliveries. And since the latest delivery triggers a build itself, the latest build will include no changes at all, because everything was already included in the first build.

    Does it trigger one build per delivery? Yes.
    Does it guarantee that the build will contain only the changes from the delivery that triggered it? No.

    • Another thing that might impact this feature: in some build engines (at least with Jenkins + Team Concert plugin I can say), the snapshot is created on load time. That means, the interval of few (mili-)seconds I mentioned above becomes as big as the interval a build request is waiting in the queue.
      For example, if a single build takes 1 hour, and 10 additional deliveries where performed within this interval, 10 build requests will be queued. The next build (1 hour later) will include those 10 deliveries and the following builds will simply rebuild the current state.
      So, in my experience, this participant should be used only when deliveries are made sporadically. Specifically to Jenkins, this is useful to make it react asap to a delivery without having to configure a so frequent polling policy.

Leave a comment