I recently switched a project's build process from Ant to Maven, but what's left to do is to integrate it with CruiseControl.

I know, Continuum would be the first choise for Maven builds, but I don't want to set up a second CI server (and Continuum doesn't offer the flexibility of CruiseControl, I might add).

The suggested way is to add the SCM plugin to each project's pom.xml and use the CruiseControl "maven2" tag to start the build process.
However, this has one big disadvantage: You have to perform a initial checkout manually before you can start the first CI build (first because Maven needs a pom.xml to start with, and second because the SCM plugin supports only CVS updates).

Now, a CI build (as I understand it) should be able to run out-of-the-box in a clean environment, and should definitly not be dependent of some artifacts that a previous build might (and might not) have left.

For a simple approch, I am using two CruiseControl builders: First, to CVS-checkout the required files with Ant (a four-line script), then to start the Maven build.
This way, I don't even have to extend my pom.xml files...

<ant buildfile="myproject.xml" target="checkout"/>
<maven2 mvnhome="${env.M2_HOME}"
goal="install" flags="-U">
<property name="maven.test.skip" value="true"/>

Well, works almost.

The "maven2" builder expects the "checkoutdir/myproject/pom.xml" file to be present even before the "ant" task has been executed. I fixed that with a bootstrapper:

<antbootstrapper buildfile="myproject.xml" target="bootstrap"/>

The Ant script's "bootstrap" target performs a minimal CVS checkout that just contains the single pom.xml file.

Note: You cannot use the cvsbootstrapper since it - deja vu - doesn't support initial checkouts.