Using the SCM Command Line Interface in builds

By Evan Hughes, RTC Source Control Team
Last updated: June 24, 2009
Build basis: Rational Team Concert 2.0

The SCM Command Line Interface (SCM CLI) allows users to load, commit, and deliver changes
from scripts. Used in conjunction with the RTC build infrastructure, release
engineers have fine-grained control of every aspect of the build cycle.

This article provides a basic background to using the SCM CLI and
build, and provides recipes for handling common build scenarios. Readers
should be familiar with RTC build concepts
and functionality; and comfortable scripting
on the command line.

Basic Configuration

The following recipes require the following:

A running Jazz repository, properly configured to allow the user to
create builds, streams, and workspaces.

A properly configured build machine. The build engine must be able to
run the Jazz Build Engine, Jazz SCM CLI and Perl 5.
The Build Engine is available as the "Build System Toolkit" download
on the Rational Team Concert website.
The SCM CLI can be found in the jazz/scmtools/eclipse
of any RTC "client" download.

After the SCM CLI has been installed, you should run
scm login, providing the username, password, and URL of the
test Jazz repository on the command line. Make sure to run it as the
build user, so that the per-user logged-in configuration is stored in
the build user's ~/.jazz-scm directory.

Recipe 1: Delivering Generated Resources Back to the Parent Stream

Sometimes it is desirable to deliver build artifacts back into source
control, possibly to share common libraries or update bookkeeping files
for future builds.

This example covers pushing files
from the local build directory into a remote workspace and then delivering
it to the original stream.

Setup

To flow changes back to a stream, we require a workspace, a stream, and a build definition.
The build workspace is named "JUnit Exploration Build Workspace", and it flows with the
"JUnit Exploration Stream". We'll modify a build definition
named recipe1.deliver.

The Ant page of the build definition should include the following
configuration:

Build property

Value

Build File

${team.scm.fetchDestination}/recipe1.deliver/recipe1.deliver.ant

Working Directory

${team.scm.fetchDestination}/recipe1.deliver

Properties File

${team.scm.fetchDestination}/recipe1.deliver/build.properties

To keep things simple, the build artifact is a file with the
date of the last successful build. It is created with the following Ant script:

The bolded line creates the build artifact: a file named chatter.txt
containing the timestamps of the green builds. We call the builder
recipe1.deliver.ant and put it into the root of the shared project
recipe1.deliver.

The initial build definition is an Ant - Jazz Build, with the
Jazz Source Control pre-build step enabled.

You should be able to verify that the recipe1.deliver build
properly generates chatter.txt by running the build and
examining the build machine. After a successful build,
chatter.txt should be sitting in the root of
recipe1.deliver, with a fairly recent date.

Delivering the Build Artifact

Using the SCM CLI to commit and deliver a change is a two
step process. First, the change is committed to the remote workspace
with scm checkin:

The first problem is easy to solve. The SCM CLI
lists the change set that was updated during the commit. The only problem
comes when trying to parse the output in Ant. In order to strip off the
unnecessary text, and reveal only the change set UUID, we use the following
Perl script:

The script accepts the command line to run as the first argument, and a
regular expression that extracts a substring as the second argument. This
allows us to run scm checkin and extract the displayed
UUID.

To simplify deployment on the build machines, run_and_filter.pl
is kept in the directory recipe1.deliver/build.

Using run_and_filter.pl, we create a
macrodef
named scm-checkin to
checkin and deliver a change. It calls the following target:

Since the location of the scm executable is particular to the
build machine, ${scm} should be defined on the build engine
definition page.

Notice the flags passed to scm.
--non-interactive ensures that the SCM CLI does not ask for a
password and hang the build. The alphabet soup of "-a n -u y"
causes the SCM CLI to display the globally unique identifier (UUID) of each
displayed element.

Preventing personal builds from flowing to the stream is a harder nut to
crack, as it requires some Ant-fu. The Ant target __scm-checkin
should only be executed if we're running outside of a personal build.
The Jazz build engine flags personal builds with the property
${personalBuild}, which is available in the generated
build.properties file.

In the interest of reuse, we create a generic Ant target that will run
another target if the ${personalBuild} flag is absent:

Although the facade is not necessary, it does provide something approaching
an API for our SCM-related Ant tasks. To keep things clean, we put the SCM CLI
related Ant definitions into cli.snippets.ant.

First, the autogenerated build.properties file is loaded. That
defines the ${personalBuild} property. Second, our library
of Ant macros is loaded with the import. Third,
scm-checkin does a combined commit and deliver to push our
change to the stream.

To test the script, make sure the changes to the Ant scripts are in the
stream then rerun recipe1.deliver. After the build completes
there should be a new change to chatter.txt in the stream.

Download

Limitations

Modifying files in the build and pushing them back to the stream is
inherently risky. The state of the stream may have changed between the time
the build started and the time the artifacts are committed to the build
workspace. If another user has modified chatter.txt, then there
will be a conflict and the delivery will fail.

To prevent concurrent modifications to the remote file:

Lock the artifacts in the stream. The lock should be acquired before
the remote workspace is loaded to prevent another user from modifying
chatter.txt after it has already been loaded. If the build
user is the only one modifying the built artifacts, the lock could be
kept in perpetuity.

Only allow the build user deliver access to the generated
artifacts. If the component containing the built artifacts is in a
team area that only allows team members to deliver to it, and the build
user is the only member of that team, then it will be impossible to
inadvertently modify the built artifacts.

Recipe 2: Promoting Changes to Another Stream When a Build Succeeds

Builds are often run to test the correctness of a stream. When a stream is
judged correct (by passing tests, for example), its contents
are often promoted to in integration where it can be tested in the presence
of other components.

This recipe builds on Recipe 1 to deliver the changes
in a successful build to another stream.

Setup

In this recipe we have two streams: the "JUnit Exploration Stream" where new
development happens, and the "JUnit Integration Stream" where changes from
the exploration stream are mixed with changes in other components. The
temporary build workspace acts as an intermediary where changes from
the exploration stream are automatically built and tested before being
flowed to the integration stream.

Make sure that the Ant page of the build definition includes the following settings:

Build property

Value

Build File

${team.scm.fetchDestination}/recipe2.promote/recipe2.promote.ant

Working Directory

${team.scm.fetchDestination}/recipe2.promote

Properties File

${team.scm.fetchDestination}/recipe2.promote/build.properties

Handling Promotion

Promotion is another way of saying "delivery to a more important stream."
Similar to recipe 1, delivery is performed with scm deliver. In
this case we are delivering to a non-default flow target, meaning
that we have to specify the --target/-t:

Since we want to promote everything in the workspace, it isn't necessary to
specify which changes should be flowed.

Returning to cli.snippets.ant, we add another build target
and another macrodef. The build target,
__scm-promote delivers in the build workspace to
the build target (note that this is only true when running in the workspace
root). This delivers the baselines that were
auto-generated at the start of the build.

As in recipe 1, scm-call-impersonally is used to prevent
delivery during personal builds.

Choosing the Flow Target

The new scm-promote macrodef is parameterized. It takes a
${target} argument which allows the script to control
which workspace or stream gets the changes. The easiest way to specify the
target stream is to add a build property to the recipe2.promote
build definition.

To add the property:

Open the recipe2.promote build definition.

Switch to the Properties tab.

Click the "Add..." button to create a new property.

Set the new property type to be "Repository Workspace"

Set the property name to be promote.target, and edit the
value field to point to the appropriate workspace.

Click "OK" and save the build target.

The property is available in the build.properties file
generated during builds.

Promotion

As with recipe 1, the build script needs three changes. The first two load
the build properties and import the library of Ant macrodefs. The third
triggers the deliver.

After the script runs, any changes delivered to the "Build Exploration Stream"
will also be in the "JUnit Integration Stream". To verify the promotion, go to
the
Pending Changes view in your RTC instance, and change the flow target of the
test workspace to be the "JUnit Integration Stream." You should see an
incoming baseline, similar to:

Download

Recipe 3: Emailing Contributors When Tests Fail

So far we've considered cases where builds succeed. This recipe concerns
broken builds. When a build fails, we
want to tell our contributors about the failure, in the hopes that they fix
the problem. Our recipe will only notify the contributors that have delivered
changes since the last successful build.

(Note that this recipe isn't necessary when using an RTC client, since
change events offer similar functionality and greater configurability)

Setup

This recipe has the same layout as recipe 1: a single build workspace
used to build the contents of a single stream.

In addition to the layout, we have an Ant build named
recipe3.notify. The build runs a set of JUnit tests.

The Ant page of the build definition should include the following
configuration:

Build property

Value

Build File

${team.scm.fetchDestination}/recipe3.notify/recipe3.notify.ant

Working Directory

${team.scm.fetchDestination}/recipe3.notify

Properties File

${team.scm.fetchDestination}/recipe3.notify/build.properties

Similarly, the path to the build machine definition should have the
following values specified:

Property

Value

Example

${junit.jar}

Absolute path of the JUnit Jar on the build machine.

/jazz/lib/junit.jar

${scm}

Absolute path of the Jazz CLI on the build machine.

/jazz/bin/scm

In the test project, include a test.HelloTest class that
implements the base JUnit TestCase, with an empty
testHello() method.

Skeletal Solution

This is our most complex recipe so far. It behaves differently depending on
build success or failure. On a successful test run, the build:

stores the UUID of the build snapshot in a file, and

commits the file to the build workspace, and delivers it to the stream.

When the tests fail, the build:

gets the UUID of the last successful build from the snapshot file,

uses scm compare to find the email addresses of the
contributors who delivered changes since the last green build, and

emails the contributors a message.

To start out, we create two Ant targets in our build script. One for
failed builds, and one for successful builds:

When the Build Passes

When a build succeeds, we record the build snapshot
ID in a file. scm list snapshot provides the UUID of the snapshot
for the current build, and the output is written directly to a file called
green_builds.txt. The --maximum/-m
option ensures that only the most recent snapshot is written:

To verify that the build behaves properly during a success, run the
recipe3.notify build. The result should contain a single (green)
test, and an incoming addition of the file green_snapshots.txt
from the "JUnit Exploration Stream."

When the Build Fails

Things are a lot more interesting when a build fails. Unfortunately they get
so interesting that using Ant becomes onerous, so we switch our scripting to
Perl - mostly for the data structure necessary to collate email addresses.

The Perl script is a thin wrapper around scm compare, and the
platform-dependent mail transfer agent.

Finding Differences Between Snapshots with scm compare

scm compare calculates the differences between two workspaces,
streams, or snapshots. The differences can be displayed in terms of
change sets, baselines, work items, or files. We're interested in
change sets, since they are the smallest unit of modification - and they
each have an author.

The output contains a lot of information that we don't need: the direction
of change ("Incoming Changes"), the component name, as well as the
author's full name. The unnecessary lines can be removed by specifying
--include-types/-I to limit the displayed item
types to change sets. The email address can be made more apparent by
specifying --format-contributor/-C to surround
it with three pointy brackets:

Download

Recipe 4: Building Without Ant

The preceding examples have used Ant build scripts and relied on the
Jazz Source Control pre-build step to automatically load remote workspaces.
This recipe describes how to write a build script without relying on Ant or
the Jazz Source Control pre-build step.
It also includes pointers on updating the build status, and publishing
build artifacts from outside of an Ant script.

Creating the Build Definition

Start by copying the script named fullbuild.pl from the
build_bin.zip onto the build engine,
and ensure that it is executable by running chmod u+x on it.
Copy the two companion files into the same directory.

Create a new build called recipe4.commandline. Give it a type
of "Command Line - Jazz Build Engine", and make sure to disable the "Jazz
Source Control" pre-build step.

Update the build machine definition to include the following properties:

Property

Value

Example

${build_bin}

Path to the directory containing fullbuild.pl

/jazz/build/

${build_dir}

Absolute path of the directory where the build is to take place.

/var/builds/

${scm}

Absolute path of the Jazz CLI on the build machine.

/jazz/bin/scm

${buildToolkit}

Absolute path of the JBE buildtoolkit directory.

/jazz/bin/scm

Add the following properties to the build recipe4.commandline
definition:

Property

Type

Value

Example

${toBuild}

Repository Workspace

Stream to build.

${jbe.properties}

String

Absolute path of the build engine's properties file.

${build_dir}/jbe.properties

In the recipe4.commandline build definition, modify the
Command Line page to set the following values:

It runs the passed command line, and terminates the script if it doesn't
return 0.

The build's configuration is placed in the build definition (and, in the case
of machine-specific fields, in the build machine definition). The configuration
is passed into the script as command line arguments, so we add the following
to the top of the script:

Loading the Workspace

To keep the build simple, we recreate the local build directory every time
we run:

run("rm -rf $buildDir");
run("mkdir -p $buildDir");

The script creates a new workspace in case change sets have been discarded from the
stream. If there were discards, and we did a simple accept, then the build
workspace would contain more changes than the stream. Normally, the Jazz
pre-build
participant would implicitly discard all outgoing changes from the build
workspace, but recreating a new workspace is a less verbose way of achieving
the same result.

With the workspace loaded, the script needs to build the contents of the
workspace. Since building is project dependent, we'll zip the contents of
the build workspace and attach that to the build. The zip part is easy:

run("zip -r $fullPath/everything.zip $fullPath");

The script can be verified by running the recipe4.commandline
build. At this point, a successful build will generate a file named
everything.zip in the JBE's build_dir directory.

For integration into an existing build system where the build artifacts are
published with some non-Jazz mechanism, this level of
integration may be sufficient. But in a scenario where build artifacts are
to be published to the Jazz build result, the recipe must go further.

Publishing Build Status and Build Artifacts

The build script described so far uses the Jazz build infrastructure as a
job scheduling service. Along with other features, Jazz builds also allow
progress monitoring and artifact storage. Builds can report their progress
by writing human-meaningful strings into the build result, which the Jazz
server then uses to estimate completion time. Similarly, build artifacts can
be posted to the build result for later download from the rich client or
web UI.

The Jazz build toolkit does not provide command line utilities for publishing
status or artifacts. To achieve the same functionality in the Perl
script, we have three options:

Write a Java command line front end to the build API,

Use an HTTP library (such as cURL) to fake access the build services
directly,

Wrap the existing Ant tasks in thin build scripts, and use those to
publish.

The third option requires much less work than the other two, so we'll use
that.

The
artifactFilePublisher Ant task requires a number of attributes
to be set. To make things easy, we load the JBE jbe.properties
file to gather most of them, and force the caller to pass the remainder
as -D arguments on the Ant command line. The three required
properties are:

filepath

The path of the file to upload.

label

The label of the file to show on the build download page.

componentName

The group that will contain the file on the build download page.

The Ant build script needs to know the path to the .jar file containing the
artifactFilePublisher task, so -lib ${buildToolkit} must also be
included on the command line.

The full Ant script can be found in the publish_file.ant script
in build_bin.zip. It is a thin wrapper
around the artifactFilePublisher task. The fields set by
command line arguments are shown below:

Similarly, the build script can use the startBuildActivity Ant
task to show progress. Like the artifactFilePublisher we need
to specify properties on the Ant command line, but in this case, we need only
one: label, which is the status string to add to the build.

After the script has successfully run, the build result should have an
"Activities" tab and a "Downloads" tab.

The activities tab will have a row
for each call to publish_status() from the build script:

The downloads tab will have a single entry for everything.zip.
Note that the download is shown under a heading whose name corresponds to the
componentName attribute in artifactFilePublisher
task, and that the description corresponds to the label
attribute:

Summary

Combining the functionality of the SCM Command Line Interface and the Jazz build
infrastructure allows Jazz users to customize the build process to their
specific needs.

Builds can be made to flow build artifacts back
into the original stream (recipe 1) or promote changes
into another stream (recipe 2). When builds fail,
the SCM CLI can be used to find contributors and automatically email them
(recipe 3). Ant can be pulled out of the build process
entirely (recipe 4).

It is also worth considering the scripts used to build each recipe, as they
can be the building blocks for further customization.
With the
scm-call-impersonally
macro, it's possible to conditionally execute a target when running as part
of a scheduled build. scm-checkin
provides the same functionality as RTC's "Check-in and Deliver" gesture in
the Pending Changes view. Similarly, the included Perl scripts show the
little bit of glue necessary to take the output of one SCM CLI command to
pass into another.