In the past months, I have been using a setup that allows me to automatically do CI of a (project plus a set of patches). The idea comes from my use of Linux, in particular Debian, where packages coming from upstream have to be patched by the distribution to:
- add distribution specific changes (doc, logo, different installation,…)
- backport changes (especially security fixes) to a stable package
My primary reason for creating this setup was to help the CruiseControl project. Not being a committer, I helped the patch review process. I also took time to complete the user submitted patches with documentation and unit tests and sent occasional patches. These patches got sent back to the developer mailing list or to the issue tracker. But they sometimes rot or were lost. So I decided to create a setup that would make me and the other developers be more productive. This setup helped me to quickly add patches to the queue while getting timely notice when something would break. After all, getting timely notice is one of the benefits of CI, why not expand the idea outside of the usage of the official tree?
How does it work
There are 2 parts to the setup: Patch Management and Continuous Integration.
Notes:
- I renamed the project been worked on from
cruisecontrol to myproject in order to remove confusion.
- The CruiseControl config extracts are valid with CruiseControl 2.3.
Patch Management
The basic foundation for this work is quilt. Quilt helps maintaining a serie of patches. quilt is a command line tool that support many commands, but in our case we will need only the push and pop commands which apply/remove patches from the queue. Quilt patches are stored in a single directory called patches along with a file series. This file defines the order in which to apply the patches and also allows for comments.
To work, the patches directory needs to be copied into the working directory, and quilt needs to be invoked from within this directory.
Continuous Integration
One also needs a Continuous Integration tool. CruiseControl was to be of course that tool. But this setup could work with any other CI tool, as long as it supports running scripts.
In order to make it work, there will be some needs to bridge those 2 things together. CruiseControl supports ant and ant supports running scripts. That should be sufficient.
overview & directory layout
The CruiseControl directory layout is not very special:
/home/cruisecontrol/config.xml
.../checkout/myproject-patched-trunk
.../logs/myproject-patched-trunk
.../artifacts/myproject-patched-trunk
Then the local working directory will contain:
myproject-patched-trunk/build.xml (official ant build file for myproject)
myproject-patched-trunk/cc_build.xml (build file used by CC to make build and notifications)
We first need to add a new project inside the cruisecontrol config.xml. A cruisecontrol project is configured in a XML block.
<project name="myproject-patched-trunk">
...
</project>
This block will define modificationsets, builders, and publishers along with some other less important settings.
modificationset
Modificationsets are used to identify changes and trigger builds.
For practical reasons, I decided to put my patch queue on the file system of the CI server, in a directory visible from the web (that allows automatic web publishing). But one could decide to store them in another SCM.
CruiseControl will then have to look for changes in both the remote official cvs repository and in the local filesystem, so we need 2 modificationsets:
<modificationset requiremodification="true">
<cvs localworkingcopy="${projectdir}"/>
<filesystem folder="${ccrundir}/public_html/projects/myproject-trunk/patches"/>
</modificationset>
builders
To summarize the build needs to be wrapped with patch management operations. We apply the latest version of the patches before building and pop them after. When the project is not being built, the local tree is similar to the last retrieved trunk version. That’s our invariant.
We then need a single builder that will make the following operations:
- pop all patches. Retrieving the latest version of the source code on top of a patched tree can lead to corruption of your patch queue.
- get the latest version of the patches (and associated scripts)
- update the local working directory (in my case using
cvs up -Pd)
- apply the patches. If this fails, pop all applied patches
- build your software as usual
- pop all patches.
We add a cc_build.xml script in the root of the local working directory and register it to the project by adding the following builder:
<schedule interval="120">
<ant antscript="${default-antscript}"
buildfile="cc_build.xml"
target="build"
antWorkingDir="${projectdir}"/>
</schedule>
The cc_build.xml contains 2 tasks related to the build. One called quilt and one called build. The build one will do a simple build of the project (possibly delegating the work to the official build.xml). The quilt target calls a shell script to perform the aforementionned patch management tasks. To summarize this shell script does:
remove_quilt_patches.sh
# which does a quilt pop -a
update_quilt_patches.sh
# sync quilt_patches/patches myproject-patched-trunk/patches
# sync quilt_patches/scripts myproject-patched-trunk/scripts
apply_quilt_patches.sh
# quilt pop -a
# cvs up -Pd -C
# quilt push -a
To trigger a build, one just need to update the quilt_patches directory (which happens to be in ~/public_html/projects/…).
publishers
Publishing happens after a build is done, whether it failed or not. Publishing is just a matter of generating some artifacts and sending some notifications (email).
We always send an email, but we publish artifacts only when the build was succesful. Once again we make use of the cc_build.xml ant build file to perform our notifications and distribution tasks.
<publishers>
<htmlemail spamwhilebroken="false" reportsuccess="fixes"
css="${ccreportingdir}/jsp/webcontent/css/cruisecontrol.css"
mailhost="localhost" buildresultsurl="${ccreportingurl}/buildresults/${project.name}"
subjectprefix="[Moca CC]" xsldir="${ccreportingdir}/jsp/webcontent/xsl"
skipusers="false" returnaddress="jerome.lacoste@gmail.com">
<always address="jerome.lacoste@gmail.com"/>
<always address="cruisecontrol-build@lists.sourceforge.net"/>
</htmlemail>
<onsuccess>
<antpublisher antscript="${default-antscript}"
buildfile="cc_build.xml"
target="publish"
antWorkingDir="${projectdir}"/>
</onsuccess>
The publish target in the cc_build.xml will perform various operations, including:
- create a changelog message. For that I use a summary.sh script that I store in the quilt patches directory to create a summary of the various patches statuses and scope. This script uses the changelog file (that I manually maintain) as well as parses the serie file
- send a mail with the link to the build and the summary
- publish the build and the sources in a directory.
misc
- It’s a good idea to create a different name for the distributed binaries. E.g. the artifacts distribution makes sure the builds are named after the machine that builds them, to differentiate from the official builds. The names would be similar to
myproject-2.3.1-dev-moca-build-101.zip
- I created a simple rsync script that I use to publish my patches onto the CI server (in the public_html/ directory). It’s a good idea to trigger this script after making a full build on your local directory.
- The scrip that updates the patches and scripts directory can also be used from the developer machine to retrieve the version of the latest patches and scripts as stored and published on the CI server
Results and files
The resulting setup can be seen on moca.
The builds are distributed here and the latest set of patches and scripts can be browsed.
Here’s the complete set of files for the setup as used on the moca server to build a patched version of the cruisecontrol.
When to use it?
Why one would like to set up a similar setup?
- in each situation where one has a set of patches on top of a moving tree. This can be when maintaining a parrallel unofficial development tree, when maintaining a stable tree of program (like Linux distributions do), when maintaining an inhouse version of an FLOSS program. All that can be achieved by using a set of patches without having the overhead to use a branch in a SCM
- to automatically validate user submitted patches. It would be simple to automatically create a system where developer patches could be automatically queue to be applied on top of the current tree. Patches could be sent by mail or web forms (or even better taken from the Issue Tracking System). Patch would be applied on top of the trunk, a build would be issued and the developper could be timely notified. I heard that the guys at continuum are going to do something like that.
Limitations
- quilt doesn’t support binaries. I haven’t had much the need for this but it could be an issue.
- setup is currently Linux only
- CruiseControl doesn’t detect removed files in the filesystem modificationset
- single patch queue ‘committer’. This works because I am alone to work on this patch queue. More than one worker would probably require one to use a SCM instead of a file system based solution to store the patches
- it’s easier to use quilt than maintain a fork or a separate tree. Allows for easy publishing of the whole setup of patches, which makes it easy for them to be applied on the main trunk
- does not catch when a patch is broken but a second one in the queue hides it
- if the anonymous access to your SCM is delayed (as is Sourceforge CVS), you can have some timing issues.
- CruiseControl has a known issue when it comes to dealing with hanging processes. That happens a lot if you make a tool that works with remote SCM. But I am going to fix that.
- the order you chose on your patch queue, may not be the same as the one taken by the commiters. Reordering patches can be a pain (not supported by quilt)
- the current publication process is not atomic
Tips?
- do not rewrite patches after publishing them. Someone may have grabbed them. Better to make new versions or complementary patches.
- comment your patches in their header
Conclusion
This solution while not perfect has served us well in the past months. I started using quilt some time ago, before making this setup and I highly recommend any open source developer to have a look at it.
For those interested, I also made a presentation on quilt.