Monthly Archives: July 2008

New CI server from ThoughtWorks

I told you it was coming. And it’s here.

Link

Update: Thanks to tha-moose for pointing out the broken link.

Tagged ,

CruiseControl Best Practices: Boostrap with a Bootstrapper

This is the fourth post of the CruiseControl Practices series. This is a repost of the original, which was hosted via ThoughtWorks. Thanks to the kind people at ThoughtWorks Studios for letting me do this!

In my last post I demonstrated using a bootstrapper to make sure that the configuration for CruiseControl was up to date. I’d like to expand on that in this post. Here’s the question that led to the creation of the Bootstrappers in CruiseControl: How do you fix the build when you have broken your build files?

Picture this: you’re cheerfully making a change to the build to support a new feature. Your colleague Bob walks up to your desk for a chat about the feature. But he hasn’t burst your bubble yet. You were pretty much done anyway, so you check in quickly and turn around to engage Bob in a conversation about work before he can mention fishing.

So eventually Bob wanders over the water cooler to entrap someone else, and you get back to your work. You look over to the screen that displays your build status, and it’s red! “Amateurs.” you think. “Always breaking the build”. You refresh the CruiseControl Dashboard to see which checkins were in that build (everybody checks in frequently here), to find out that the build failed in about a second because the build.xml is invalid.

Suddenly a bit more tolerant of your build breaking colleagues, you look at the build.xml file., which is still open in your IDE. There it is. A dirty great hash symbol at the end of the file. “Curse my keyboard layout!” you exclaim. When you typed the last line of the file, you missed the return key and hit ‘#’. You didn’t see that in your haste to check in, and now the build script won’t update the checkout. The build file won’t run as it is invalid. The very first thing that it would normally do is update the working copy on the CruiseControl server, to get the latest changes. Looks like you’ll need to check in the fix and then get on the CruiseControl machine and update the working copy yourself. Oh dear.

Having been there (and sometimes not needing an imaginary colleague to break the build file), I can vouch for the bootstrapper approach:

<?xml version=”1.0″?>
<cruisecontrol>

<project name=”my_great_app”>

<bootstrappers>

<svnbootstrapper localWorkingCopy=”${working.dir}/${project.name}” file=”build.xml”>

<svnbootstrapper localWorkingCopy=”${working.dir}/${project.name}” file=”ccbuild.xml”>

</bootstrappers>

<modificationset quietperiod=”30″>

<svn LocalWorkingCopy=”${working.dir}/${project.name}”/>

</modificationset>

<schedule interval=”60″>

<ant antWorkingDir=”${working.dir}/${project.name}” antscript=”${working.dir}/tools/apache-ant-1.6.5/bin/ant” />

<<schedule> </project> </cruisecontrol>

In the example above, the bootstrapper will always fetch the latest files from Subversion. Bootstrappers are run before a build takes place, regardless of whether a build is necessary or not, but not if the build is paused. But why stop there? That will make sure that you get the most recent build files, which have responsibility for getting the latest changes from your VCS. But should your build need to do that? No. The bootstrapper can update all the working files, if you configure it like this:

<bootstrappers>

<svnbootstrapper localWorkingCopy=”${working.dir}/${project.name}”/>

</bootstrappers>

Now CruiseControl will update all the recently changed files in the working copy on your server. There used to be an issue with this approach – Bob will explain: “It’s like the time I caught a 10 pound marlin, but the line snapped. I checked in my change, saw a build get triggered shortly afterwards, and I waited for the build to complete. My change was there in the list of modifications, but the QA team said that change wasn’t in that build!”

This problem used to happen if you checked in a change while CruiseControl was bootstrapping and fetching the latest revision. Because of CruiseControl’s history with non-atomic version control systems, it internally refers to change sets by date. The ThoughtWorks Studios team fixed this in CruiseControl 2.7.1 by adding the useLocalRevision to the Subversion modification set:

<modificationset quietperiod=”0″>

<svn LocalWorkingCopy=”/etc/cruisecontrol” useLocalRevision=”true”/>

</modificationset>

Once the bootstrapper has updated the project’s working copy on the CruiseControl server, it makes the Subversion revision number of the working copy available to the ModificationSet. When the ModificationSet attempts to work out what changes are new in that build, it then constrains it’s query to the revision number of the working copy. This makes CruiseControl accurately report changes. Its a huge boost to larger projects that use CruiseControl. This will also save you from more conversations with Bob. Another useful side effect of the useLocalRevision property with Subversion is that the Subversion reposistory revision number gets passed to the build as a property called${svnrevision}. This makes it trivial to tag the build later at the correct revision.

While I’m on the subject of tweaks for CruiseControl under Subversion (or any truly atomic VCS): never use a QuietPeriod. The QuietPeriod is there to stop CruiseControl prematurely building when you’re using CVS or another non-atomic VCS. It will only slow down your build.

There are more bootstrappers available to play with for fun and profit: CruiseControl supports many Version Control Systems and most of the VCS plugins have an optional bootstrapper. Bob and I have explained the big picture on VCS bootstrappers above, so I’ll outline the rest of them here:

  • AntBootStrapper will execute an Apache Ant file before the project builds. Most of the time you shouldn’t need it, but I have seen it used to fetch dependencies and then trigger a build if those dependencies have changed.
  • ExecBootStrapper will execute any script or command that you tell it to.
  • LockFileBootStrapper is an interesting one. Read on for more.

Do you have many projects on the same CruiseControl server? Good. Do you have two or more Builder Threads configured? Do you build two or more projects at once? Great. Multiple, simultaneous projects is something that CruiseControl has supported since the summer of 2004. Do 2 of those projects sometimes compete for the same resources?. Oh my, that’s bad. Especially annoying if it only happens once a day or so. Here’s what you do:

<cruisecontrol>

<system>
<configuration>
<threads count=”2″ />
</configuration>
</system>

<project name=”test-1″ >

<listeners>
<lockfilelistener lockFile=”/tmp/lock” projectName=”${project.name}”/>
</listeners>

<bootstrappers>
<lockfilebootstrapper lockFile=”/tmp/lock” projectName=”${project.name}” />
</bootstrappers>

<schedule interval=”30″>
<exec command=”/bin/true” />
</schedule>

</project>

<project name=”test-2″ >
<listeners>
<lockfilelistener lockFile=”/tmp/lock” projectName=”${project.name}”/>
</listeners>

<bootstrappers>
<lockfilebootstrapper lockFile=”/tmp/lock” projectName=”${project.name}” />
</bootstrappers>

<schedule interval=”30″>
<exec command=”/bin/true” />
</schedule>

</project>

</cruisecontrol>

The lockfilebootstrapper will run before the build, like a bootstrapper should. It will look for the lockfile for the name of the of the project. If the lockfile exists and the name of the project inside doesn’t match the expectation set in the configuration, it will cancel the entire build attempt. This way, one project can warn all the others that it is building, so you never see the two projects that don’t sit well together executing at the same time. However, you could have 5 other builds that don’t share the same external dependencies that the first project does, and they can run just fine at the same time. The lockfilelistener will cleanup any old lock files left oiver after the build, so you don’t prevent some builds running all the time.

“Hang on,” says Bob: “I just had to wait 10 minutes for the FooLib project to build! What’s going on?.” “Now Bob.” you say, not unkindly: “You know there’s only one integration test server. Now, so does the CruiseControl build. Next time you go fishing with the CTO, you can tell him about the delays to the project and ask if we can spend some of next year’s budget on for another test server.”

Tagged

CruiseControl Best Practices: Keep your dependencies to yourself

This is the second of ten practices for CruiseControl

The average Java project has many dependencies – open source tools and frameworks, third party libraries, libraries that come from your project or organization – the list is endless. When I wrote this article, my current project had 84 jar files that it depended on (or could have depended on!). Much pain can come from the way you manage these dependencies. Sometimes it’s harder than you think to make software build or execute if it’s not clear what the dependencies are. A lot of the pain of joining a new project and taking a day to make the code build comes from unclear code or environmental dependencies.

It’s a good idea to keep your CruiseControl installation and its dependencies clearly separated from your projects and their dependencies. So for the most basic example, lay out your CruiseControl install something like this:

|– cruise
| |– 2.7.0
| `– 2.7.1
|– logs
| |– blowfish
| `– scorpion
`– projects
|– blowfish
`– scorpion

Upgrading CruiseControl should be the work of minutes.If you need to add libraries to CruiseControl itself, this should be a warning sign that your project has dependencies it can’t itself satisfy. The only exception to this rule that I can think of is custom CruiseControl bootstrappers or publishers and the like.

There are two things in particular to take note of here: the logs and the ‘.ser’ files. The logfiles represent the history of your project: this is why I always try to keep them in a different directory hierarchy from the CruiseControl installs. CruiseControl will also by default persist the state of its projects in files with the extension ‘.ser’. Make sure to keep those when you upgrade. If you do this, and your project has no dependencies on CruiseControl, it should be simple to upgrade.

Next, think about the dependencies you have baked into your build tools. There’s a common pattern of putting dependencies into your Apache Ant’s ‘lib’ directory. For things that are hard to wire in like Junit, fine. But if your build depends on something that is actually resident in your build tool, then you have a problem. It will work today, but not necessarily tomorrow. If you upgrade libraries that your code depends on in the build tool, you can’t build yesterday’s code – which makes things interesting when you’re trying to resolve a production bug!

The good news is, it’s easy to fix: make the project contain its dependencies. For example, if your Ant build depends on Oracle (say, to support rebuild the database with the ‘sql’ task, and your standard project Ant build contains the Oracle drivers, your build may look like this:

			<target name="drop_all_tables">

			<sql driver="oracle.jdbc.driver.OracleDriver"
			userid="user" password="donttell"
			url="jdbc:oracle:thin:@localhost:1521:orcl"
			delimiter=";">

			<transaction src="${sql.dir}/drop_all_tables.sql"/>

			</sql>

			</target>
			

However in this case, the dependency on the ‘in the
default classpath’ Oracle driver isn’t stated; Ant will just look for the ‘oracle.jdbc.driver.OracleDriver’
class in the default classpath. The first thing to do is put the driver jar file
into the project and set a classpath:

lib/
`– ojdbc14.jar

			<path id="buildtime">
			<pathelement location="${lib.dir}/oracle.jar"/>
			</path>



			<target name="drop_all_tables">
			<sql driver="oracle.jdbc.driver.OracleDriver" userid="user" password="donttell"
			url="jdbc:oracle:thin:@localhost:1521:orcl"
			delimiter=";" classpathref="buildtime">
			<transaction src="${sql.dir}/drop_all_tables.sql"/>
			</sql>

			</target>
			

Note the addition of the ‘classpathref’ attribute in the ‘sql’
element. This useful little attribute allows you to refer to paths elsewhere
within the build, reducing duplication. When you have moved a dependency into a
project, take to ceremonially deleting the dependency the moment that you can.
On a project where I recently undertook this process, I was fortunate to have a
regular release working in my favour. I fixed the dependencies in the trunk and
revisited a few weeks later, once the release branches that depended on things
outside the project weren’t used.

So in summary, think about your project’s dependencies, including the ones that
are satisfied because they just happen to be satisfied. If you make those
dependencies explicit, I promise you that you’ll be asked less questions from
your colleagues about why they can’t compile! If you have many projects with the
same dependencies, that’s something that I hope to address in a future post.

Tagged ,

CruiseControl Best Practices: Publish with a Publisher

Welcome to the first article in the CruiseControl Best Practices series here at build-doctor.com.

Today’s article is about Publishers: Many projects use the concept of publishing: placing built artifacts into a repository, or making test results available to end users. The ArtifactsPublisher is a very popular way to publish to CruiseControl’s own repository of timestamped directories, making logs and archives available via the CruiseControl dashboard or legacy reporting application. But ArtifactsPublisher is just one of a family of Publishers, my favourite being the AntPublisher. If your Ant build finishes by publishing artifacts to a repository or tagging your version control system, there may be another way to do things. Consider the Ant example below:


<project>
<target name="dist" description="build everything">
 </target>
<target name="clean" description="delete all build artifacts">
</target>
<target name="test" description="run unit tests">
</target>
<target name="publish" description="publish to repository">
</target>
<target name="cruise" depends="clean,dist,test,publish"/>
</project>

The end goal of the build is to publish unit-tested artifacts to an
unspecified repository of some kind via the publish target. This
is well and good, but the concept of publishing doesn’t really fit the
developer build; if we can disconnect the publishing activity from the
rest of the build, we can run the same build in CruiseControl as the
developers. To do this, have CruiseControl invoke your developer
build, leaving publishing as a separate Ant target:


<project>
<target name="dist" description="build everything">
</target>
<target name="clean" description="delete all build artifacts">
</target>
<target name="test" description="run unit tests">
</target>
<target name="publish" description="publish to repository" if="logfile">
</target>
<target name="dev" depends="clean,dist,test"/>
</project>

The ‘if’ attribute on the ‘publish’ target ensures that the target can
only ever be run by a publisher or a determined person. Next, you
need to make some changes to your CruiseControl configuration:


<!-- some config removed from this example -->
<schedule interval="60">
<ant antWorkingDir="checkout.dir}" buildfile="build.xml" target="dev"/>
</schedule>
<publishers>
<onsuccess>
<antpublisher antWorkingDir="checkout.dir}" buildfile="build.xml" target="publish">
<onsuccess>
</publishers>

And you’re done. Once you apply the new configuration to
CruiseControl, it will start to run the same Ant build as the
developers, meaning that there’s no mystery when the build
breaks. It may also shorten the build feedback loop by reporting
success faster. While the developers celebrate or lament the
state of the build, the AntPublisher can get on with the work of
pushing artifacts around your network. I also use it to tag the
codebase on a successful build – another thing that is specific to
Continuous Integration. Publishers can be configured unadorned
inside the <publishers> element in the config file to run every
time a build completes regardless of status, or they can be wrapped in <onsuccess> or <onfailure> to run conditionally.

Publishers have a cousin element called Bootstrappers, but I’ll cover that another time.

Tagged

CruiseControl Best Practices: Configuration the CruiseControl way

This is the third article in the CruiseControl practices series

You just started using CruiseControl. You use a Version Control System to manage your code. You installed CruiseControl on a spare computer in the office; now it is giving you immediate feedback on the changes that occur in that codebase. Life is good. Then the disk on that spare computer fails, and your build server resumes its previous role as a doorstop.

“No problem”. you think: “All the code changes are in the VCS. We can regenerate any of the artifacts that we need to. In fact, all we need is the config file … “. Yes. That config file. The config file on the hard disk that doesn’t work anymore. This post will outline how to manage your configuration for CruiseControl without fear of losing it. Like many tools, CruiseControl becomes quite useless without configuration.

In projects that I did for ThoughtWorks, we always needed to allow someone to have access to the build server to make configuration changes for CruiseControl. Once projects grow past a few developers, it becomes hard to have everyone be familiar with the installation. Typically we end up with one person (sometimes your humble narrator) becoming the dedicated CruiseControl administrator for the project. This change creates a bottleneck in the team because all the changes to CruiseControl then become funnelled through that one person.

The first step in mending this situation is getting CruiseControl to apply its own configuration. Let’s get started. In addition to the projects that build and test your code, you will need a new project to apply the configuration to the server. We have been doing this to put the configuration file in the right place, using CruiseControl’s <bootstrapper> plug-in to update the configuration files when they change:


<?xml version="1.0"?>
<cruisecontrol>
<project name="config">
<labelincrementer defaultLabel="${project.name}-1" separator="-"/>
<listeners>
<currentbuildstatuslistener file="/var/spool/cruisecontrol/logs/${project.name}/currentbuildstatus.txt"/>
</listeners>
<bootstrappers>
<svnbootstrapper localWorkingCopy="/etc/cruisecontrol"/>
</bootstrappers>
<modificationset quietperiod="30">
<svn LocalWorkingCopy="/etc/cruisecontrol"/>
</modificationset>
<schedule interval="60">
<ant antWorkingDir="/etc/cruisecontrol"antscript="/var/spool/cruisecontrol/tools/apache-ant-1.6.5/bin/ant"  uselogger="true"/>
</schedule>
<publishers>
<artifactspublisher  file="${project.name}/build.log"  dest="logs/${project.name}" />
</publishers>
</project>
</cruisecontrol>

This will robotically update the configuration until the end of time. It’s simple but surprisingly effective. There’s no longer a dependency on the person who can make changes to CruiseControl. Suddenly they don’t need to make the trivial changes on behalf of the rest of the team because anybody can safely change the configuration. If someone does check in a broken configuration, it’s all under version control. Once you revert the change you can find the person who changed it and try and understand what they wanted to do.This is a big step forward. But if you check in a broken configuration it will still be applied to CruiseControl. Fortunately CruiseControl has the good sense not to apply broken configuration; but you’re missing a vital piece of feedback, and for that you need to write a simple validator like this one:

package org.juliansimpson;

import java.io.File;
import net.sourceforge.cruisecontrol.CruiseControlException;
import net.sourceforge.cruisecontrol.config.XMLConfigManager;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

public class ConfigValidator extends Task {
    public String configFile;

    public void execute() throws BuildException {
	try {
	    File file = new File(configFile);
	    new XMLConfigManager(file);
	} catch (CruiseControlException e) {
	    throw new BuildException("Invalid CruiseControl Config");
	}
    }

    public void setConfigFile(String config) {
	configFile = config;
    }
}

The validator uses internal classes of CruiseControl itself to validate the configuration. Ideally we would have an external interface to do this – perhaps a command line option or an “official” Ant task. This approach does mean that you need to set the classpath so that the validator can find your CruiseControl install, but this way you find out with certainty that the configuration is valid for your version of CruiseControl. I like to run these as an Ant task. It’s very simple and easy for everyone to see what it does. Here’s how I included it in a simple Ant build:


<project name="cruisevalidator" default="publish" >
 <import file="build-library.xml"/>
 <target name="validated-config"depends="cruise-validator.jar">
<taskdef name="validate" classname="org.juliansimpson.ConfigValidator" classpathref="main"/>     <echo message="validating ${config}" />
<validate configFile="${config}" />
</target>
<target name="publish" depends="validated-config">
<echo level="info" message="copying CruiseControl config to server" />
<copy file="${config}" todir="${cruisecontrol.dir}" failonerror="true" description="Copy configuration to CruiseControl server" />
<echo level="info" message="forcing a reload of config on server" />
<get src="http://localhost:8000/invoke?operation=reloadConfigFile&amp;objectname=CruiseControl+Manager%3Aid%3Dunique"      dest="${build.dir}/reload.html" />
</target>
</project>

It all works together like this: The CruiseControl BootStrapper fetches us the latest CruiseControl configuration, but in isolation from CruiseControl install – you still don’t know if it is a valid configuration file yet. The “validated-config” target calls the ConfigValidator Ant task. This invokes enough of CruiseControl to make sure that the configuration is legal, and that some of the directories referred to in the configuration exist. If that passes, the “publish” target copies the configuration to the CruiseControl server itself. Finally the same target forces a reload of the CruiseControl configuration using a simple HTTP request to the JMX interface. This ensures that the configuration is reloaded immediately, so that the team knows the configuration is valid. Thanks to my erstwhile colleague Tim Brown for this great idea.

Summary: I have to admit being careless sometimes with XML configuration files. This approach works particularly well for me because I have the safety net of the validation. I do a similar thing with my email and web server installation as well, which I hope to write about soon. The validator code and build files are available here.

Update 2008-12-04: Fixed the link. Thanks to Tom Howard for pointing this out!

Tagged

Take some time to thank your sysadmin today

Systems administration is demanding, hard work. Why don’t you take some time to thank the person who keeps your network and servers running? You can’t go wrong with beer and/or cake. Though maybe not together.

Link (via Ram)

Ant Best Practices: The Clean Test

Ant Best Practices: The Clean Test. Last time, we discussed the ZipFileSet. Today’s article is much more important – it’s a subject very close to my heart: only dong necessary work. If you don’t execute the clean target, (do tell me your build has a clean target and you didn’t change any of files in your project, what should happen?

I’ll tell you what should happen. Nothing. Diddly. Well, no compilation. If no compilation happened, should it bother packaging up artefacts or anything? I don’t think so. If you want to run tests, so be it; you might have an unreliable test. But for the most part, nothing should happen if nothing changed. This is the clean test.

Why? Think of it like this. If you change a single CSS file on your project, do you want to go and recompile all your code and run your tests? Hold that thought.

On the first and largest project that I ever did at ThoughtWorks, the fast build was 45 minutes. Don’t even go there with the build that did automated functional tests. One evening I was working late with one of the performance testers. The CruiseControl build was firing all the time. We realised that every time he did a performance test run, he checked in the results into the VCS alongside the code. That triggered a build. Whoops. We didn’t always have thatcapacity to spare. I have since sat there on many a project and watched builds trigger because a developer had to make the tiniest change. It’s dull to sit through a tedious CI build, just because the build didn’t discriminate between the files that had changed.

This stuff can make a difference. When your project has grown to a larger size, rest assured that it will be hard to change these things. So how do you convince Ant to be less zealous? Start by making sure that your default target doesn’t depend on your ‘clean’ target. When you refactor code and move classes around, you might need to execute the clean target. But treat that as the exception and not the rule.

That’s fine until you start packaging up the code in zip or jar files. What happens then? The uptodate task is your friend. This little guy will take a look at the sources and output of a task, and tell you if you should bother running it. Take a look:


<project default="archive">
<property name="build.directory" location="build" />
<property name="source.directory" location="src" />
<property name="archive" location="${build.directory}/stooges.zip" />
<fileset dir="${source.directory}" includes="*.xml" id="archive.files"/>
<target name="clean">
<delete>
<fileset dir="${build.directory}" includes="**/*"/>  </delete>
</target>
<target name="check-archive">
<echo message="Checking zip file time stamp" />
<uptodate property="-archive.is.unchanged" targetfile="${archive}">
<srcfiles refid="archive.files"/>
</uptodate>
</target>
<target name="archive" unless="-archive.is.unchanged" depends="check-archive">
<echo message="Making a zip file"/>
<zip file="${archive}" >
<fileset refid="archive.files" />
</zip>
</target>
</project>

The first time that you run this build, it does what you expect: makes a zipfile. The clever bit is what it does the second time you run it. It doesn’t bother. Here’s the first run:

Detected Java version: 1.6 in: /usr/lib/jvm/java-6-sun-1.6.0.06/jre
Detected OS: Linux
parsing buildfile /home/jsimpson/Documents/workspace/playpen/code/up2date.build.xml with URI = file:/home/jsimpson/Documents/workspace/playpen/code/up2date.build.xml
Project base dir set to: /home/jsimpson/Documents/workspace/playpen/code
[antlib:org.apache.tools.ant] Could not load definitions from resource org/apache/tools/ant/antlib.xml. It could not be found.
Build sequence for target(s) `archive' is [check-archive, archive]
Complete build sequence is [check-archive, archive, clean, ]

check-archive:
[echo] Checking zip file time stamp
[uptodate] The targetfile "/home/jsimpson/Documents/workspace/playpen/code/build/stooges.zip" does not exist.

archive:
[echo] Making a zip file
[zip] Building zip: /home/jsimpson/Documents/workspace/playpen/code/build/stooges.zip
[zip] adding entry iggy.xml
[zip] adding entry dave.xml

BUILD SUCCESSFUL
Total time: 1 second

You can tell that it was checking the lie of the land before it executed the main task. You might like the output from the second task:


Detected Java version: 1.6 in: /usr/lib/jvm/java-6-sun-1.6.0.06/jre
Detected OS: Linux
parsing buildfile /home/jsimpson/Documents/workspace/playpen/code/up2date.build.xml with URI = file:/home/jsimpson/Documents/workspace/playpen/code/up2date.build.xml
Project base dir set to: /home/jsimpson/Documents/workspace/playpen/code
[antlib:org.apache.tools.ant] Could not load definitions from resource org/apache/tools/ant/antlib.xml. It could not be found.
Build sequence for target(s) `archive' is [check-archive, archive]
Complete build sequence is [check-archive, archive, clean, ]

check-archive:
[echo] Checking zip file time stamp
[uptodate] iggy.xml omitted as /home/jsimpson/Documents/workspace/playpen/code/build/stooges.zip is up to date.
[uptodate] dave.xml omitted as /home/jsimpson/Documents/workspace/playpen/code/build/stooges.zip is up to date.
[uptodate] No sources found.
[uptodate] File "/home/jsimpson/Documents/workspace/playpen/code/build/stooges.zip" is up-to-date.

archive:
Skipped because property '-archive.is.unchanged' set.

BUILD SUCCESSFUL
Total time: 1 second

There you have it. It didn’t bother creating the zip file because the sources hadn’t changed. That’s useful. But what about the duplication?

Admittedly you do need to define the sources and target of the task twice. This could lead to duplication. If you look at the example, you’ll see how I got around that:

  • the target of the zip task is a property. So that’s fine.
  • the sources of the zip task are all types of FileSet. So I defined the FileSet once and passed the reference to both tasks.

Everyone’s build should undertake the Clean Test.

Tagged

Spellcheck as you type with Vim

Spellcheck as you type with VimI use Vim on my little Asus eeepc to do my writing on the train. As I only write text or html, it’s been fine. But little luxuries like highlighting miss-spelled words are nice. I just found out that Vim supports this. Here’s the lines to add to your .vimrc:

setlocal spell spelllang=en_gb
set mousemodel=popup
set spellfile=~/spellfile.add

There’s loads of help on the feature if you type :help spell inside Vim. But here’s the basics: ]s will skip to the next word that it doesn’t know about. If it needs to go into your custom dictionary, zg will add it. And in graphical mode, you can use the mouse to suggest words. Lovely.

Tagged

Should every target have a secret?

Should every target have a secret(image taken from electricinca’s photostream)

There’s a question that I wanted to ask you, dear reader. Consider the following code snippet:

<project name="my_great_web_app">
<!-- snip -->
<target name="backend-zip-static">
<zip zipfile="${build}/backend-static.zip">
<fileset dir="${backend.static.dir}"/>
</zip> </target> </project>

Some of my erstwhile ThoughtWorks colleagues used to use the motto “Every class should have a secret”. Recently I discovered that this evolved from work on Information Hiding by David Parnas:

In computer science, the principle of information hiding is the hiding of design decisions in a computer program that are most likely to change, thus protecting other parts of the program from change if the design decision is changed. The protection involves providing a stable interface which shields the remainder of the program from the implementation (the details that are most likely to change).

(from http://en.wikipedia.org/wiki/Information_hiding)

So OO developers believe that classes should contain secrets, should targets in your build have secrets? Does that mean that you shouldn’t expose (in the name of the target) how a target does what it does? In this example, how about backend-static-archive as a target name instead of backend-zip-static ?

The example above came from a real project. We changed the way we deployed the app, and had to painstakingly rename the target and its references across a hierarchy of build files. There was definately more than one issue with the build at that project: we shouldn’t have had such a large collection of build files. Them’s the breaks.

Should you ever expose the process that you apply inside a target? Or should the name of the target just describe the output? What do you think?

some Phing refactorings

Raphael Stolt, a PHP developer and blogger has written an article called “Six valuable Phing build file refactorings”. He’s taken the refactorings from my ThoughtWorks Anthology article and translated to Phing, the PHP build tool (which is based on Ant). Sweet!

Link

Tagged