RegisterLog In to dW

Using the SCM Command Line Interface in builds

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.


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 'JUnit Exploration Workspace' flows to the 'JUnit Exploration Stream'

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

Build propertyValue
Build File ${team.scm.fetchDestination}/recipe1.deliver/recipe1.deliver.ant
Working Directory ${team.scm.fetchDestination}/recipe1.deliver
Properties File ${team.scm.fetchDestination}/recipe1.deliver/

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:

<?xml version="1.0" encoding="UTF-8"?>
<project name="project" default="publish">
    <target name="publish">
        <exec executable="date" failonerror="true" output="chatter.txt" append="true"/>

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:

recipe1.deliver$ scm checkin chatter.txt
Workspace: (6500) "JUnit Exploration Build Workspace" <-> (6501) "JUnit Exploration Stream"
  Component: (6502) "BuildComp"
      Change sets:
        (6545) --@ <No comment>

In order for the change to be available to the rest of the team it must be flowed to a stream. The flow is performed with scm deliver:

recipe1.deliver$ scm deliver 6545
Delivering changes from "JUnit Exploration Build Workspace" into "JUnit Exploration Stream"
  No baselines to flow.

The general shape of the solution is simple: an Ant macrodef that calls exec on scm checkin and scm deliver. But there are a couple of gotchas:

  1. We should only deliver the change set that contains the build artifact we built. A blanket deliver could flow more changes than the one we want to push.
  2. The artifacts generated by personal builds should not flow to the stream.

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:


$out = `$ARGV[0]`;

if ($out =~ /$ARGV[1]/) {
    print $1;
    exit 0;

die("Could not find $ARGV[1] in $out");

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, is kept in the directory recipe1.deliver/build.

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

<property name="run" value="/path/to/"/>
<property name="scm" value="/path/to/scm"/>

<target name="__scm-checkin">
        <!-- Do the initial commit -->
        <exec executable="${run}" failonerror="true" outputproperty="cs">
            <arg value="${scm} --non-interactive -a n -u y checkin ${roots}"/>
            <arg value="      \(([^)]+)\)"/>
        <!-- Deliver -->
        <exec executable="${scm}" failonerror="true">
            <arg value="--non-interactive"/>
            <arg value="deliver"/>
            <arg value="${cs}"/>

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

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

<target name="scm-call-impersonally" unless="personalBuild" description="Conditional antcall if we're in a non-personal build">
	<echo message="Running ${toCall} impersonally"/>
	<antcall target="${toCall}"/>

scm-call-impersonally will run the Ant target present in the ${toCall} property if ${personalBuild} is not set.

We provide a facade for scm-call-impersonally:

<macrodef name="scm-checkin" description="Commit and deliver changes below the given filesystem roots">
    <attribute name="roots" description="Space separated list of paths to check-in and deliver"/>
        <antcall target="scm-call-impersonally">
            <param name="toCall" value="__scm-checkin"/>
            <param name="roots" value="@{roots}"/>

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.

The Final Build Script

The initial build script needs three additions:

<?xml version="1.0" encoding="UTF-8"?>
<project name="project" default="publish">
        <file file=""/>
    <import file="build/cli.snippets.ant"/>

    <target name="publish">
        <exec executable="date" failonerror="true" output="chatter.txt" append="true"/>

        <scm-checkin roots="chatter.txt"/>
First, the autogenerated 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.


The final content of the recipe1.deliver project is available in


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:

  1. 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.
  2. 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.


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.
The 'JUnit Exploration Workspace' flows to the temporary build workspace, and changes are promoted to the 'JUnit Integration Workspace'

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

Build propertyValue
Build File ${team.scm.fetchDestination}/recipe2.promote/recipe2.promote.ant
Working Directory ${team.scm.fetchDestination}/recipe2.promote
Properties File ${team.scm.fetchDestination}/recipe2.promote/

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:

recipe2.promotion$ scm deliver --target "JUnit Integration Stream"
Delivering changes from "JUnit Exploration Build Workspace" into "JUnit Integration Stream"

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.

<macrodef name="scm-promote" description="Promote a set of baselines to another stream">
    <attribute name="target" description="Selector identifying the target stream"/>
        <antcall target="scm-call-impersonally">
            <param name="toCall" value="__scm-promote"/>
            <param name="target" value="@{target}"/>
<target name="__scm-promote">
    <exec executable="${scm}" failonerror="true">
        <arg value="--non-interactive"/>
        <arg value="deliver"/>
        <arg value="-t"/>
        <arg value="${target}"/>

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:

  1. Open the recipe2.promote build definition.
  2. Switch to the Properties tab.
  3. Click the "Add..." button to create a new property.
  4. Set the new property type to be "Repository Workspace"
  5. Set the property name to be, and edit the value field to point to the appropriate workspace.
    Screenshot of 'Add Build Property' dialog
  6. Click "OK" and save the build target.
The property is available in the file generated during builds.


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.

<?xml version="1.0" encoding="UTF-8"?>
<project name="project">
        <file file=""/>

    <import file="build/cli.snippets.ant"/>
    <target name="default">
        <!-- Build tasks go here -->
        <scm-promote target="${}"/>

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: Promoted changes in the 'JUnit Integration Stream'


The final content of the recipe2.promote project is available in

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)


This recipe has the same layout as recipe 1: a single build workspace used to build the contents of a single stream.
The 'JUnit Exploration Workspace' flows to the 'JUnit Exploration 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 propertyValue
Build File ${team.scm.fetchDestination}/recipe3.notify/recipe3.notify.ant
Working Directory ${team.scm.fetchDestination}/recipe3.notify
Properties File ${team.scm.fetchDestination}/recipe3.notify/
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:

  1. stores the UUID of the build snapshot in a file, and
  2. commits the file to the build workspace, and delivers it to the stream.
When the tests fail, the build:
  1. gets the UUID of the last successful build from the snapshot file,
  2. uses scm compare to find the email addresses of the contributors who delivered changes since the last green build, and
  3. 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:

<target name="handle_junit_completion" 

<!-- Record the successful completion of the junits -->
<target name="handle_junit_success" unless="junit.failed">
<!-- Tell whoever has changed the stream that the tests are failing -->
<target name="handle_junit_failure" if="junit.failed">

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:

$ scm -u y -a n list snapshot --repository-uri l "JUnit Exploration Build Workspace" -m 1
(_iPqeEE-ZEd6Vqtty79T39Q) "" 2-Jun-2009 1:19 PM

The -u y -a n jumble is used to force the SCM CLI to emit the UUID of the snapshot. The --repository-uri l selects the logged-in target repository with the nickname of "l".

After the file has been modified, it is committed with the scm-checkin macro:

<property name="build.persistence" value="."/>
<property name="green_snapshots" value="${build.persistence}/green_snapshots.txt"/>

<!-- Record the successful completion of the junits -->
<target name="handle_junit_success" unless="junit.failed">
   <!-- Store the last successful snapshot. -->
   <exec executable="${scm}" output="${green_snapshots}" append="false">
      <arg value="--non-interactive"/>
      <!-- Set output flags -->
      <arg value="-a"/>
      <arg value="n"/>
      <arg value="-u"/>
      <arg value="y"/>
      <arg value="list"/>
      <arg value="snapshot"/>
      <arg value="${team.scm.workspaceUUID}"/>
      <arg value="-m"/>
      <arg value="1"/>
      <arg value="-r"/>
      <arg value="${repositoryAddress}"/>
   <!-- Check it in. -->
   <scm-checkin roots="${build.persistence}/green_snapshots.txt"/>

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.

To call the Perl script, modify the handle_junit_failure target:

<!-- Tell whoever has changed the stream that the tests are failing -->
<target name="handle_junit_failure" if="junit.failed">
   <!-- Run a script to handle the mailination. -->
   <exec executable="build/" outputproperty="out">
      <env key="buildDefinitionId" value="${buildDefinitionId}"/>
      <env key="buildLabel" value="${buildLabel}"/>
      <arg value="${scm}"/>
      <arg value="${green_snapshots}"/>
      <arg value="${repositoryAddress}"/>
      <arg value="${team.scm.workspaceUUID}"/>
   <echo message="${out}"/>

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.

Here's a sample comparison:

$ scm compare -r l ws "JUnit Exploration Build Workspace" stream "JUnit Exploration Stream" -f i
Incoming Changes
  Component (6502) "BuildComp"
    (6547) J. Doe <> Degauss the flux capacitor 2009/06/09

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:

$ scm compare -r l ws "JUnit Exploration Build Workspace" stream "JUnit Exploration Stream" -f i -I s -C "<<<{email}>>>"
(6547) <<<>>> Degauss the flux capacitor 2009/06/09

The breakage mailer Perl script finds unique contributor email addresses by repeatedly calling /<<<([^>]+)>>>/g and storing the first matching group in a dictionary.

Integration Into the Build Script

Although builds can fail for a number of reasons, we're only going to consider JUnit-induced failures. Deep in the heart of our build script is the following Ant stanza:

<target name="test">
        <formatter type="xml" />
        <test name="test.HelloTest" outfile="HelloTest" />
            <pathelement path="${basedir}/hello.jar" />
            <pathelement path="${junitJar}" />

To integrate the breakage mailer, we have to modify the test target to call our script:

<target name="test">
    <junit failureproperty="junit.failed">
        <formatter type="xml" />
        <test name="test.HelloTest" outfile="HelloTest" />
            <pathelement path="${basedir}/hello.jar" />
            <pathelement path="${junitJar}" />
    <antcall target="scm-call-impersonally">
        <param name="toCall" value="handle_junit_completion"/>

Expected Output on JUnit Failure

When the JUnit fails an email is sent to whoever authored the change sets delivered since the last green build.

If your mail is not being delivered, verify:

  1. that the mail transport agent on the build engine is properly configured to deliver mail, and provides an interface via /usr/sbin/sendmail.
  2. each contributor has a valid email address. Email addresses can be found (and changed) by opening the user editor on the contributor in question.


The final content of the recipe3.notify project is available in

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 from the 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 /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.
${} String Absolute path of the build engine's properties file. ${build_dir}/
In the recipe4.commandline build definition, modify the Command Line page to set the following values:
Command ${build_bin}/
Arguments ${buildWorkspace} ${build_dir} ${repositoryAddress} ${} ${scm} ${ant} ${buildToolkit}
Properties file ${}
Working Directory ${build_dir}

Command Line Infrastructure

Once again, we return to Perl as a quasi-platform-independent scripting language. Since the script is going to be launching a number of other programs, we start with the following function:


sub run {
    my $cmd = shift(@_);

    $out = `$cmd`;
    $ret = $? >> 8;
    if ($ret != 0) {
        die("Failed ($ret): $cmd\n$out");

    return $out;

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:

$toBuild = $ARGV[0];
$buildDir = $ARGV[1] . '/scripted';
$repoUrl = $ARGV[2];
$antLib = $ARGV[3];
$jbeProperites = $ARGV[4];
$scm = $ARGV[5];
$ant = $ARGV[6];
$buildToolkit = $ARGV[7];

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.

print "Creating temporary workspace\n";
$wsName = "buildWorkspace-" . time();
$r = run("$scm $baseArgs create workspace -r $repoUrl -u ADMIN -s \"$toBuild\" $wsName");
$r =~ /^Workspace \(([^)]+)\)/;
$tmpWorkspace = $1;

The script loads the workspace:

print "Loading workspace $wsName\n";
$fullPath = "$buildDir/$wsName";
run("$scm $baseArgs load -r $repoUrl -u ADMIN -d $fullPath $tmpWorkspace");

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/ $fullPath");

The script can be verified by running the recipe4.commandline build. At this point, a successful build will generate a file named 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:

  1. Write a Java command line front end to the build API,
  2. Use an HTTP library (such as cURL) to fake access the build services directly,
  3. 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 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:

The path of the file to upload.
The label of the file to show on the build download page.
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 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.

Adding Publishing to the Build Script

Files are published by calling the following Perl subroutine:

sub publish_file($$$$) {
    my $file = shift(@_);
    my $component = shift(@_);
    my $label = shift(@_);
    my $contentType = shift(@_);
    print "Publishing $file ($component/$label)\n";
    run("$ant -lib $buildToolkit -buildfile $fullPath/$antLib/publish_file.ant$jbeProperites -Dfilepath=$file -Dlabel=\"$label\" -DcomponentName=\"$component\" -DcontentType=$contentType");

Calling the publish subroutine from the script is straight forward:

publish_status("Generating zip");
run("zip -r $fullPath/ $fullPath");

publish_status("Uploading zip");
publish_file("$fullPath/", "Some component", "With a label", "application/zip");

publish_status("Done upload");

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:
Activities tab from build result.

The downloads tab will have a single entry for 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:
Downloads tab from build result.


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.

Was this information helpful? Yes No 8 people rated this as helpful.